From 4781064bf853ab01768f0bc7face67a0c2dd76f1 Mon Sep 17 00:00:00 2001 From: John Marino Date: Tue, 27 May 2014 21:42:16 +0200 Subject: [PATCH] hostapd vendor branch: Update version from 0.6.10 => 2.1 --- contrib/hostapd-0.4.9/COPYING | 340 - contrib/hostapd-0.4.9/README | 395 - contrib/hostapd-0.4.9/README.DELETE | 18 - contrib/hostapd-0.4.9/README.DRAGONFLY | 4 - contrib/hostapd-0.4.9/accounting.c | 457 - contrib/hostapd-0.4.9/accounting.h | 13 - contrib/hostapd-0.4.9/aes_wrap.c | 725 - contrib/hostapd-0.4.9/aes_wrap.h | 42 - contrib/hostapd-0.4.9/ap.h | 99 - contrib/hostapd-0.4.9/common.c | 388 - contrib/hostapd-0.4.9/common.h | 293 - contrib/hostapd-0.4.9/config.c | 1230 -- contrib/hostapd-0.4.9/config.h | 160 - contrib/hostapd-0.4.9/config_types.h | 14 - contrib/hostapd-0.4.9/crypto.c | 157 - contrib/hostapd-0.4.9/crypto.h | 123 - contrib/hostapd-0.4.9/ctrl_iface.c | 456 - contrib/hostapd-0.4.9/ctrl_iface.h | 9 - contrib/hostapd-0.4.9/defconfig | 75 - contrib/hostapd-0.4.9/defs.h | 131 - contrib/hostapd-0.4.9/developer.txt | 219 - contrib/hostapd-0.4.9/driver.h | 271 - contrib/hostapd-0.4.9/driver_wired.c | 399 - contrib/hostapd-0.4.9/eap.c | 944 -- contrib/hostapd-0.4.9/eap.h | 89 - contrib/hostapd-0.4.9/eap_defs.h | 56 - contrib/hostapd-0.4.9/eap_gtc.c | 158 - contrib/hostapd-0.4.9/eap_i.h | 112 - contrib/hostapd-0.4.9/eap_identity.c | 185 - contrib/hostapd-0.4.9/eap_md5.c | 184 - contrib/hostapd-0.4.9/eap_mschapv2.c | 483 - contrib/hostapd-0.4.9/eap_pax.c | 519 - contrib/hostapd-0.4.9/eap_pax_common.c | 152 - contrib/hostapd-0.4.9/eap_pax_common.h | 84 - contrib/hostapd-0.4.9/eap_peap.c | 726 - contrib/hostapd-0.4.9/eap_psk.c | 458 - contrib/hostapd-0.4.9/eap_psk_common.c | 57 - contrib/hostapd-0.4.9/eap_psk_common.h | 92 - contrib/hostapd-0.4.9/eap_sim.c | 431 - contrib/hostapd-0.4.9/eap_sim_common.c | 794 - contrib/hostapd-0.4.9/eap_sim_common.h | 116 - contrib/hostapd-0.4.9/eap_sim_db.c | 243 - contrib/hostapd-0.4.9/eap_sim_db.h | 44 - contrib/hostapd-0.4.9/eap_tls.c | 253 - contrib/hostapd-0.4.9/eap_tls_common.c | 278 - contrib/hostapd-0.4.9/eap_tls_common.h | 47 - contrib/hostapd-0.4.9/eap_tlv.c | 248 - contrib/hostapd-0.4.9/eap_ttls.c | 1193 -- contrib/hostapd-0.4.9/eap_ttls.h | 71 - contrib/hostapd-0.4.9/eapol_sm.c | 1259 -- contrib/hostapd-0.4.9/eapol_sm.h | 224 - contrib/hostapd-0.4.9/eloop.c | 395 - contrib/hostapd-0.4.9/eloop.h | 154 - contrib/hostapd-0.4.9/hostap_common.h | 558 - contrib/hostapd-0.4.9/hostapd.8 | 56 - contrib/hostapd-0.4.9/hostapd.accept | 5 - contrib/hostapd-0.4.9/hostapd.c | 846 - contrib/hostapd-0.4.9/hostapd.conf | 341 - contrib/hostapd-0.4.9/hostapd.deny | 5 - contrib/hostapd-0.4.9/hostapd.eap_user | 49 - contrib/hostapd-0.4.9/hostapd.h | 149 - contrib/hostapd-0.4.9/hostapd.radius_clients | 4 - contrib/hostapd-0.4.9/hostapd.sim_db | 9 - contrib/hostapd-0.4.9/hostapd.wpa_psk | 9 - contrib/hostapd-0.4.9/hostapd_cli.1 | 83 - contrib/hostapd-0.4.9/hostapd_cli.c | 601 - contrib/hostapd-0.4.9/iapp.c | 522 - contrib/hostapd-0.4.9/ieee802_11.c | 1220 -- contrib/hostapd-0.4.9/ieee802_11.h | 99 - contrib/hostapd-0.4.9/ieee802_11_auth.c | 458 - contrib/hostapd-0.4.9/ieee802_11_auth.h | 16 - contrib/hostapd-0.4.9/ieee802_1x.c | 1788 --- contrib/hostapd-0.4.9/ieee802_1x.h | 94 - contrib/hostapd-0.4.9/l2_packet.h | 133 - contrib/hostapd-0.4.9/madwifi.conf | 278 - contrib/hostapd-0.4.9/md5.h | 25 - contrib/hostapd-0.4.9/ms_funcs.c | 503 - contrib/hostapd-0.4.9/ms_funcs.h | 49 - contrib/hostapd-0.4.9/radius.c | 1158 -- contrib/hostapd-0.4.9/radius.h | 226 - contrib/hostapd-0.4.9/radius_client.c | 1116 -- contrib/hostapd-0.4.9/radius_client.h | 87 - contrib/hostapd-0.4.9/radius_server.c | 1074 -- contrib/hostapd-0.4.9/radius_server.h | 46 - contrib/hostapd-0.4.9/rc4.c | 85 - contrib/hostapd-0.4.9/rc4.h | 22 - contrib/hostapd-0.4.9/sha1.c | 994 -- contrib/hostapd-0.4.9/sha1.h | 33 - contrib/hostapd-0.4.9/sta_info.c | 355 - contrib/hostapd-0.4.9/sta_info.h | 19 - contrib/hostapd-0.4.9/tls.h | 463 - contrib/hostapd-0.4.9/tls_none.c | 28 - contrib/hostapd-0.4.9/tls_openssl.c | 2144 --- contrib/hostapd-0.4.9/version.h | 6 - contrib/hostapd-0.4.9/wired.conf | 40 - contrib/hostapd-0.4.9/wpa.c | 2897 ---- contrib/hostapd-0.4.9/wpa.h | 196 - contrib/hostapd-0.4.9/wpa_ctrl.c | 239 - contrib/hostapd-0.4.9/wpa_ctrl.h | 185 - contrib/hostapd/COPYING | 352 +- contrib/hostapd/README | 57 +- contrib/hostapd/README.DELETED | 36 - contrib/hostapd/README.DRAGONFLY | 23 - contrib/hostapd/hostapd/ChangeLog | 340 +- contrib/hostapd/hostapd/README | 34 +- contrib/hostapd/hostapd/README-WPS | 121 +- contrib/hostapd/hostapd/accounting.h | 26 - contrib/hostapd/hostapd/ap.h | 139 - contrib/hostapd/hostapd/ap_list.c | 501 - contrib/hostapd/hostapd/ap_list.h | 71 - contrib/hostapd/hostapd/beacon.c | 467 - contrib/hostapd/hostapd/beacon.h | 24 - .../hostapd/{config.c => config_file.c} | 2364 +-- contrib/hostapd/hostapd/config_file.h | 17 + contrib/hostapd/hostapd/ctrl_iface.c | 1508 +- contrib/hostapd/hostapd/ctrl_iface.h | 34 +- contrib/hostapd/hostapd/driver.h | 798 - contrib/hostapd/hostapd/driver_atheros.c | 1457 -- contrib/hostapd/hostapd/driver_bsd.c | 839 - contrib/hostapd/hostapd/driver_hostap.c | 1279 -- contrib/hostapd/hostapd/driver_madwifi.c | 1483 -- contrib/hostapd/hostapd/driver_nl80211.c | 2708 ---- contrib/hostapd/hostapd/driver_none.c | 62 - contrib/hostapd/hostapd/driver_prism54.c | 1091 -- contrib/hostapd/hostapd/driver_test.c | 1300 -- contrib/hostapd/hostapd/driver_wired.c | 372 - contrib/hostapd/hostapd/drivers.c | 77 - contrib/hostapd/hostapd/eap_register.c | 143 + contrib/hostapd/hostapd/eap_register.h | 14 + contrib/hostapd/hostapd/hlr_auc_gw.txt | 104 + contrib/hostapd/hostapd/hostap_common.h | 216 - contrib/hostapd/hostapd/hostapd.c | 2039 --- contrib/hostapd/hostapd/hostapd.h | 238 - contrib/hostapd/hostapd/hostapd_cli.c | 823 +- contrib/hostapd/hostapd/hw_features.c | 494 - contrib/hostapd/hostapd/hw_features.h | 62 - contrib/hostapd/hostapd/iapp.h | 54 - contrib/hostapd/hostapd/ieee802_11.c | 1833 --- contrib/hostapd/hostapd/ieee802_11.h | 56 - contrib/hostapd/hostapd/logwatch/README | 9 - contrib/hostapd/hostapd/main.c | 720 + contrib/hostapd/hostapd/nt_password_hash.c | 52 - contrib/hostapd/hostapd/prism54.h | 177 - contrib/hostapd/hostapd/priv_netlink.h | 71 - contrib/hostapd/hostapd/radiotap.h | 242 - contrib/hostapd/hostapd/sta_info.h | 43 - contrib/hostapd/hostapd/vlan_init.h | 31 - contrib/hostapd/hostapd/wme.h | 112 - contrib/hostapd/hostapd/wps_hostapd.c | 1029 -- contrib/hostapd/hostapd/wps_hostapd.h | 48 - .../openssl-0.9.8x-tls-extensions.patch | 396 + .../hostapd/{hostapd => src/ap}/accounting.c | 221 +- contrib/hostapd/src/ap/accounting.h | 44 + contrib/hostapd/src/ap/acs.c | 802 + contrib/hostapd/src/ap/acs.h | 27 + contrib/hostapd/src/ap/ap_config.c | 885 ++ .../{hostapd/config.h => src/ap/ap_config.h} | 315 +- contrib/hostapd/src/ap/ap_drv_ops.c | 775 + contrib/hostapd/src/ap/ap_drv_ops.h | 283 + contrib/hostapd/src/ap/ap_list.c | 315 + contrib/hostapd/src/ap/ap_list.h | 53 + .../{hostapd/mlme.c => src/ap/ap_mlme.c} | 22 +- .../{hostapd/mlme.h => src/ap/ap_mlme.h} | 10 +- contrib/hostapd/src/ap/authsrv.c | 214 + contrib/hostapd/src/ap/authsrv.h | 15 + contrib/hostapd/src/ap/beacon.c | 941 ++ contrib/hostapd/src/ap/beacon.h | 25 + contrib/hostapd/src/ap/ctrl_iface_ap.c | 510 + contrib/hostapd/src/ap/ctrl_iface_ap.h | 28 + contrib/hostapd/src/ap/dfs.c | 803 + contrib/hostapd/src/ap/dfs.h | 25 + contrib/hostapd/src/ap/drv_callbacks.c | 1038 ++ contrib/hostapd/src/ap/eap_user_db.c | 270 + contrib/hostapd/src/ap/gas_serv.c | 1199 ++ contrib/hostapd/src/ap/gas_serv.h | 72 + contrib/hostapd/src/ap/hostapd.c | 2255 +++ contrib/hostapd/src/ap/hostapd.h | 424 + contrib/hostapd/src/ap/hs20.c | 31 + contrib/hostapd/src/ap/hs20.h | 16 + contrib/hostapd/src/ap/hw_features.c | 1031 ++ contrib/hostapd/src/ap/hw_features.h | 66 + contrib/hostapd/{hostapd => src/ap}/iapp.c | 105 +- .../{hostapd-0.4.9 => hostapd/src/ap}/iapp.h | 8 + contrib/hostapd/src/ap/ieee802_11.c | 2266 +++ contrib/hostapd/src/ap/ieee802_11.h | 85 + .../{hostapd => src/ap}/ieee802_11_auth.c | 342 +- .../{hostapd => src/ap}/ieee802_11_auth.h | 17 +- contrib/hostapd/src/ap/ieee802_11_ht.c | 281 + contrib/hostapd/src/ap/ieee802_11_shared.c | 494 + contrib/hostapd/src/ap/ieee802_11_vht.c | 171 + .../hostapd/{hostapd => src/ap}/ieee802_1x.c | 1057 +- .../hostapd/{hostapd => src/ap}/ieee802_1x.h | 57 +- contrib/hostapd/src/ap/p2p_hostapd.c | 114 + contrib/hostapd/src/ap/p2p_hostapd.h | 35 + .../peerkey.c => src/ap/peerkey_auth.c} | 28 +- .../ap/pmksa_cache_auth.c} | 157 +- .../ap/pmksa_cache_auth.h} | 37 +- .../preauth.c => src/ap/preauth_auth.c} | 29 +- .../preauth.h => src/ap/preauth_auth.h} | 10 +- .../hostapd/{hostapd => src/ap}/sta_info.c | 570 +- contrib/hostapd/src/ap/sta_info.h | 198 + contrib/hostapd/src/ap/tkip_countermeasures.c | 105 + contrib/hostapd/src/ap/tkip_countermeasures.h | 15 + contrib/hostapd/src/ap/utils.c | 85 + .../hostapd/{hostapd => src/ap}/vlan_init.c | 478 +- contrib/hostapd/src/ap/vlan_init.h | 53 + contrib/hostapd/src/ap/vlan_util.c | 177 + contrib/hostapd/src/ap/vlan_util.h | 15 + .../hostapd/{hostapd/wme.c => src/ap/wmm.c} | 131 +- contrib/hostapd/src/ap/wmm.h | 23 + contrib/hostapd/src/ap/wnm_ap.c | 508 + contrib/hostapd/src/ap/wnm_ap.h | 22 + .../{hostapd/wpa.c => src/ap/wpa_auth.c} | 1266 +- .../{hostapd/wpa.h => src/ap/wpa_auth.h} | 88 +- .../wpa_ft.c => src/ap/wpa_auth_ft.c} | 576 +- contrib/hostapd/src/ap/wpa_auth_glue.c | 628 + contrib/hostapd/src/ap/wpa_auth_glue.h | 16 + .../hostapd/{hostapd => src/ap}/wpa_auth_i.h | 43 +- .../hostapd/{hostapd => src/ap}/wpa_auth_ie.c | 372 +- .../hostapd/{hostapd => src/ap}/wpa_auth_ie.h | 16 +- contrib/hostapd/src/ap/wps_hostapd.c | 2002 +++ contrib/hostapd/src/ap/wps_hostapd.h | 92 + contrib/hostapd/src/common/defs.h | 164 +- contrib/hostapd/src/common/eapol_common.h | 50 +- contrib/hostapd/src/common/gas.c | 273 + contrib/hostapd/src/common/gas.h | 37 + .../hostapd/src/common/ieee802_11_common.c | 347 +- .../hostapd/src/common/ieee802_11_common.h | 100 +- contrib/hostapd/src/common/ieee802_11_defs.h | 866 +- contrib/hostapd/src/common/nl80211_copy.h | 1434 -- contrib/hostapd/src/common/privsep_commands.h | 13 +- contrib/hostapd/src/common/qca-vendor.h | 51 + contrib/hostapd/src/common/sae.c | 1047 ++ contrib/hostapd/src/common/sae.h | 64 + contrib/hostapd/src/common/version.h | 6 +- contrib/hostapd/src/common/wireless_copy.h | 1099 -- contrib/hostapd/src/common/wpa_common.c | 912 +- contrib/hostapd/src/common/wpa_common.h | 102 +- contrib/hostapd/src/common/wpa_ctrl.c | 250 +- contrib/hostapd/src/common/wpa_ctrl.h | 179 +- contrib/hostapd/src/crypto/aes-cbc.c | 80 + contrib/hostapd/src/crypto/aes-ccm.c | 212 + contrib/hostapd/src/crypto/aes-ctr.c | 55 + contrib/hostapd/src/crypto/aes-eax.c | 145 + contrib/hostapd/src/crypto/aes-encblock.c | 32 + contrib/hostapd/src/crypto/aes-gcm.c | 327 + contrib/hostapd/src/crypto/aes-internal-dec.c | 161 + contrib/hostapd/src/crypto/aes-internal-enc.c | 126 + .../src/crypto/aes-internal.c} | 404 +- contrib/hostapd/src/crypto/aes-omac1.c | 118 + contrib/hostapd/src/crypto/aes-unwrap.c | 73 + contrib/hostapd/src/crypto/aes-wrap.c | 70 + contrib/hostapd/src/crypto/aes.c | 1127 -- contrib/hostapd/src/crypto/aes.h | 12 +- contrib/hostapd/src/crypto/aes_i.h | 125 + contrib/hostapd/src/crypto/aes_wrap.c | 533 - contrib/hostapd/src/crypto/aes_wrap.h | 34 +- contrib/hostapd/src/crypto/crypto.h | 390 +- contrib/hostapd/src/crypto/crypto_cryptoapi.c | 39 +- contrib/hostapd/src/crypto/crypto_gnutls.c | 34 +- .../src/crypto/crypto_internal-cipher.c | 243 + .../src/crypto/crypto_internal-modexp.c | 49 + .../hostapd/src/crypto/crypto_internal-rsa.c | 108 + contrib/hostapd/src/crypto/crypto_internal.c | 715 +- .../hostapd/src/crypto/crypto_libtomcrypt.c | 34 +- contrib/hostapd/src/crypto/crypto_none.c | 13 +- contrib/hostapd/src/crypto/crypto_nss.c | 207 + contrib/hostapd/src/crypto/crypto_openssl.c | 1083 +- .../src/crypto/{des.c => des-internal.c} | 50 +- contrib/hostapd/src/crypto/des_i.h | 25 + contrib/hostapd/src/crypto/dh_group5.c | 40 + contrib/hostapd/src/crypto/dh_group5.h | 18 + contrib/hostapd/src/crypto/dh_groups.c | 683 +- contrib/hostapd/src/crypto/dh_groups.h | 13 +- .../hostapd/src/crypto/fips_prf_cryptoapi.c | 19 + contrib/hostapd/src/crypto/fips_prf_gnutls.c | 20 + .../hostapd/src/crypto/fips_prf_internal.c | 69 + contrib/hostapd/src/crypto/fips_prf_nss.c | 19 + contrib/hostapd/src/crypto/fips_prf_openssl.c | 78 + .../src/crypto/{md4.c => md4-internal.c} | 18 +- .../src/crypto/md5-internal.c} | 156 +- contrib/hostapd/src/crypto/md5.c | 317 +- contrib/hostapd/src/crypto/md5.h | 29 +- contrib/hostapd/src/crypto/md5_i.h | 23 + contrib/hostapd/src/crypto/milenage.c | 323 + .../src/{hlr_auc_gw => crypto}/milenage.h | 14 +- contrib/hostapd/src/crypto/ms_funcs.c | 256 +- contrib/hostapd/src/crypto/ms_funcs.h | 64 +- contrib/hostapd/src/crypto/random.c | 446 + contrib/hostapd/src/crypto/random.h | 28 + contrib/hostapd/src/crypto/rc4.c | 30 +- contrib/hostapd/src/crypto/rc4.h | 21 - contrib/hostapd/src/crypto/sha1-internal.c | 302 + contrib/hostapd/src/crypto/sha1-pbkdf2.c | 92 + contrib/hostapd/src/crypto/sha1-prf.c | 66 + contrib/hostapd/src/crypto/sha1-tlsprf.c | 99 + contrib/hostapd/src/crypto/sha1-tprf.c | 70 + contrib/hostapd/src/crypto/sha1.c | 657 +- contrib/hostapd/src/crypto/sha1.h | 45 +- contrib/hostapd/src/crypto/sha1_i.h | 23 + .../crypto/{sha256.c => sha256-internal.c} | 208 +- contrib/hostapd/src/crypto/sha256-prf.c | 98 + contrib/hostapd/src/crypto/sha256-tlsprf.c | 66 + contrib/hostapd/src/crypto/sha256.c | 312 +- contrib/hostapd/src/crypto/sha256.h | 26 +- contrib/hostapd/src/crypto/sha256_i.h | 25 + contrib/hostapd/src/crypto/tls.h | 217 +- contrib/hostapd/src/crypto/tls_gnutls.c | 514 +- contrib/hostapd/src/crypto/tls_internal.c | 217 +- contrib/hostapd/src/crypto/tls_none.c | 82 +- contrib/hostapd/src/crypto/tls_nss.c | 645 + contrib/hostapd/src/crypto/tls_openssl.c | 1230 +- contrib/hostapd/src/crypto/tls_schannel.c | 195 +- contrib/hostapd/src/drivers/Apple80211.h | 156 - .../hostapd/src/drivers/MobileApple80211.c | 189 - .../hostapd/src/drivers/MobileApple80211.h | 43 - contrib/hostapd/src/drivers/android_drv.h | 56 + contrib/hostapd/src/drivers/driver.h | 3536 ++++- contrib/hostapd/src/drivers/driver_atheros.c | 2193 +++ contrib/hostapd/src/drivers/driver_atmel.c | 506 - contrib/hostapd/src/drivers/driver_broadcom.c | 604 - contrib/hostapd/src/drivers/driver_bsd.c | 1653 +- contrib/hostapd/src/drivers/driver_common.c | 86 + contrib/hostapd/src/drivers/driver_hostap.c | 1330 +- contrib/hostapd/src/drivers/driver_hostap.h | 81 +- contrib/hostapd/src/drivers/driver_ipw.c | 463 - contrib/hostapd/src/drivers/driver_madwifi.c | 1466 +- contrib/hostapd/src/drivers/driver_ndis.c | 289 +- contrib/hostapd/src/drivers/driver_ndis.h | 11 +- contrib/hostapd/src/drivers/driver_ndis_.c | 10 +- .../hostapd/src/drivers/driver_ndiswrapper.c | 370 - contrib/hostapd/src/drivers/driver_nl80211.c | 12939 +++++++++++++--- contrib/hostapd/src/drivers/driver_none.c | 93 + contrib/hostapd/src/drivers/driver_openbsd.c | 136 + contrib/hostapd/src/drivers/driver_prism54.c | 381 - contrib/hostapd/src/drivers/driver_privsep.c | 144 +- contrib/hostapd/src/drivers/driver_ps3.c | 186 - contrib/hostapd/src/drivers/driver_ralink.c | 1505 -- contrib/hostapd/src/drivers/driver_ralink.h | 382 - .../hostapd/src/drivers/driver_roboswitch.c | 34 +- contrib/hostapd/src/drivers/driver_test.c | 2198 ++- contrib/hostapd/src/drivers/driver_wext.c | 771 +- contrib/hostapd/src/drivers/driver_wext.h | 25 +- contrib/hostapd/src/drivers/driver_wired.c | 476 +- contrib/hostapd/src/drivers/drivers.c | 104 +- contrib/hostapd/src/drivers/linux_ioctl.c | 221 + contrib/hostapd/src/drivers/linux_ioctl.h | 22 + contrib/hostapd/src/drivers/linux_wext.h | 45 + contrib/hostapd/src/drivers/ndis_events.c | 10 +- contrib/hostapd/src/drivers/netlink.c | 228 + contrib/hostapd/src/drivers/netlink.h | 28 + contrib/hostapd/src/drivers/nl80211_copy.h | 4040 +++++ contrib/hostapd/src/drivers/priv_netlink.h | 20 +- contrib/hostapd/src/drivers/radiotap.c | 287 - contrib/hostapd/src/drivers/radiotap_iter.h | 41 - contrib/hostapd/src/drivers/rfkill.c | 188 + contrib/hostapd/src/drivers/rfkill.h | 25 + contrib/hostapd/src/drivers/scan_helpers.c | 182 - contrib/hostapd/src/eap_common/chap.c | 19 +- contrib/hostapd/src/eap_common/chap.h | 16 +- contrib/hostapd/src/eap_common/eap_common.c | 59 +- contrib/hostapd/src/eap_common/eap_common.h | 13 +- contrib/hostapd/src/eap_common/eap_defs.h | 20 +- .../hostapd/src/eap_common/eap_eke_common.c | 768 + .../hostapd/src/eap_common/eap_eke_common.h | 114 + .../hostapd/src/eap_common/eap_fast_common.c | 20 +- .../hostapd/src/eap_common/eap_fast_common.h | 10 +- .../hostapd/src/eap_common/eap_gpsk_common.c | 152 +- .../hostapd/src/eap_common/eap_gpsk_common.h | 16 +- .../hostapd/src/eap_common/eap_ikev2_common.c | 10 +- .../hostapd/src/eap_common/eap_ikev2_common.h | 10 +- .../hostapd/src/eap_common/eap_pax_common.c | 12 +- .../hostapd/src/eap_common/eap_pax_common.h | 10 +- .../hostapd/src/eap_common/eap_peap_common.c | 25 +- .../hostapd/src/eap_common/eap_peap_common.h | 18 +- .../hostapd/src/eap_common/eap_psk_common.c | 12 +- .../hostapd/src/eap_common/eap_psk_common.h | 10 +- .../hostapd/src/eap_common/eap_pwd_common.c | 345 + .../hostapd/src/eap_common/eap_pwd_common.h | 67 + .../hostapd/src/eap_common/eap_sake_common.c | 12 +- .../hostapd/src/eap_common/eap_sake_common.h | 10 +- .../hostapd/src/eap_common/eap_sim_common.c | 37 +- .../hostapd/src/eap_common/eap_sim_common.h | 16 +- .../hostapd/src/eap_common/eap_tlv_common.h | 10 +- contrib/hostapd/src/eap_common/eap_ttls.h | 10 +- .../hostapd/src/eap_common/eap_wsc_common.c | 10 +- .../hostapd/src/eap_common/eap_wsc_common.h | 10 +- contrib/hostapd/src/eap_common/ikev2_common.c | 25 +- contrib/hostapd/src/eap_common/ikev2_common.h | 12 +- contrib/hostapd/src/eap_peer/eap.c | 465 +- contrib/hostapd/src/eap_peer/eap.h | 60 +- contrib/hostapd/src/eap_peer/eap_aka.c | 238 +- contrib/hostapd/src/eap_peer/eap_config.h | 71 +- contrib/hostapd/src/eap_peer/eap_eke.c | 765 + contrib/hostapd/src/eap_peer/eap_fast.c | 91 +- contrib/hostapd/src/eap_peer/eap_fast_pac.c | 48 +- contrib/hostapd/src/eap_peer/eap_fast_pac.h | 10 +- contrib/hostapd/src/eap_peer/eap_gpsk.c | 72 +- contrib/hostapd/src/eap_peer/eap_gtc.c | 10 +- contrib/hostapd/src/eap_peer/eap_i.h | 34 +- contrib/hostapd/src/eap_peer/eap_ikev2.c | 50 +- contrib/hostapd/src/eap_peer/eap_leap.c | 45 +- contrib/hostapd/src/eap_peer/eap_md5.c | 22 +- contrib/hostapd/src/eap_peer/eap_methods.c | 167 +- contrib/hostapd/src/eap_peer/eap_methods.h | 36 +- contrib/hostapd/src/eap_peer/eap_mschapv2.c | 42 +- contrib/hostapd/src/eap_peer/eap_otp.c | 10 +- contrib/hostapd/src/eap_peer/eap_pax.c | 17 +- contrib/hostapd/src/eap_peer/eap_peap.c | 164 +- contrib/hostapd/src/eap_peer/eap_proxy.h | 49 + .../hostapd/src/eap_peer/eap_proxy_dummy.c | 77 + contrib/hostapd/src/eap_peer/eap_psk.c | 42 +- contrib/hostapd/src/eap_peer/eap_pwd.c | 927 ++ contrib/hostapd/src/eap_peer/eap_sake.c | 36 +- contrib/hostapd/src/eap_peer/eap_sim.c | 239 +- contrib/hostapd/src/eap_peer/eap_tls.c | 131 +- contrib/hostapd/src/eap_peer/eap_tls_common.c | 327 +- contrib/hostapd/src/eap_peer/eap_tls_common.h | 52 +- contrib/hostapd/src/eap_peer/eap_tnc.c | 16 +- contrib/hostapd/src/eap_peer/eap_ttls.c | 544 +- .../hostapd/src/eap_peer/eap_vendor_test.c | 12 +- contrib/hostapd/src/eap_peer/eap_wsc.c | 168 +- contrib/hostapd/src/eap_peer/ikev2.c | 18 +- contrib/hostapd/src/eap_peer/ikev2.h | 10 +- contrib/hostapd/src/eap_peer/mschapv2.c | 71 +- contrib/hostapd/src/eap_peer/mschapv2.h | 24 +- contrib/hostapd/src/eap_peer/tncc.c | 18 +- contrib/hostapd/src/eap_peer/tncc.h | 10 +- contrib/hostapd/src/eap_server/eap.h | 25 +- contrib/hostapd/src/eap_server/eap_i.h | 26 +- contrib/hostapd/src/eap_server/eap_methods.h | 49 +- .../src/eap_server/{eap.c => eap_server.c} | 96 +- .../{eap_aka.c => eap_server_aka.c} | 428 +- .../hostapd/src/eap_server/eap_server_eke.c | 793 + .../{eap_fast.c => eap_server_fast.c} | 121 +- .../{eap_gpsk.c => eap_server_gpsk.c} | 39 +- .../{eap_gtc.c => eap_server_gtc.c} | 18 +- .../{eap_identity.c => eap_server_identity.c} | 10 +- .../{eap_ikev2.c => eap_server_ikev2.c} | 20 +- .../{eap_md5.c => eap_server_md5.c} | 23 +- .../{eap_methods.c => eap_server_methods.c} | 189 +- .../{eap_mschapv2.c => eap_server_mschapv2.c} | 55 +- .../{eap_pax.c => eap_server_pax.c} | 13 +- .../{eap_peap.c => eap_server_peap.c} | 300 +- .../{eap_psk.c => eap_server_psk.c} | 43 +- .../hostapd/src/eap_server/eap_server_pwd.c | 1045 ++ .../{eap_sake.c => eap_server_sake.c} | 50 +- .../{eap_sim.c => eap_server_sim.c} | 276 +- .../{eap_tls.c => eap_server_tls.c} | 90 +- ...p_tls_common.c => eap_server_tls_common.c} | 190 +- .../{eap_tnc.c => eap_server_tnc.c} | 113 +- .../{eap_ttls.c => eap_server_ttls.c} | 414 +- ...vendor_test.c => eap_server_vendor_test.c} | 12 +- .../{eap_wsc.c => eap_server_wsc.c} | 62 +- contrib/hostapd/src/eap_server/eap_sim_db.c | 995 +- contrib/hostapd/src/eap_server/eap_sim_db.h | 104 +- .../hostapd/src/eap_server/eap_tls_common.h | 54 +- contrib/hostapd/src/eap_server/ikev2.c | 21 +- contrib/hostapd/src/eap_server/ikev2.h | 10 +- contrib/hostapd/src/eap_server/tncs.c | 19 +- contrib/hostapd/src/eap_server/tncs.h | 10 +- .../hostapd/src/eapol_auth/eapol_auth_dump.c | 289 + .../eapol_auth/eapol_auth_sm.c} | 355 +- .../hostapd/src/eapol_auth/eapol_auth_sm.h | 90 + .../eapol_auth/eapol_auth_sm_i.h} | 110 +- .../hostapd/src/eapol_supp/eapol_supp_sm.c | 352 +- .../hostapd/src/eapol_supp/eapol_supp_sm.h | 94 +- contrib/hostapd/src/hlr_auc_gw/hlr_auc_gw.c | 714 - contrib/hostapd/src/hlr_auc_gw/milenage.c | 1142 -- contrib/hostapd/src/l2_packet/l2_packet.h | 10 +- .../hostapd/src/l2_packet/l2_packet_freebsd.c | 43 +- .../hostapd/src/l2_packet/l2_packet_linux.c | 40 +- .../hostapd/src/l2_packet/l2_packet_ndis.c | 26 +- .../hostapd/src/l2_packet/l2_packet_none.c | 10 +- .../hostapd/src/l2_packet/l2_packet_pcap.c | 10 +- .../hostapd/src/l2_packet/l2_packet_privsep.c | 14 +- .../hostapd/src/l2_packet/l2_packet_winpcap.c | 10 +- contrib/hostapd/src/p2p/p2p.c | 4598 ++++++ contrib/hostapd/src/p2p/p2p.h | 1944 +++ contrib/hostapd/src/p2p/p2p_build.c | 477 + contrib/hostapd/src/p2p/p2p_dev_disc.c | 325 + contrib/hostapd/src/p2p/p2p_go_neg.c | 1223 ++ contrib/hostapd/src/p2p/p2p_group.c | 1015 ++ contrib/hostapd/src/p2p/p2p_i.h | 759 + contrib/hostapd/src/p2p/p2p_invitation.c | 605 + contrib/hostapd/src/p2p/p2p_parse.c | 767 + contrib/hostapd/src/p2p/p2p_pd.c | 477 + contrib/hostapd/src/p2p/p2p_sd.c | 879 ++ contrib/hostapd/src/p2p/p2p_utils.c | 471 + contrib/hostapd/src/radius/radius.c | 624 +- contrib/hostapd/src/radius/radius.h | 83 +- contrib/hostapd/src/radius/radius_client.c | 440 +- contrib/hostapd/src/radius/radius_client.h | 239 +- contrib/hostapd/src/radius/radius_das.c | 362 + contrib/hostapd/src/radius/radius_das.h | 47 + contrib/hostapd/src/radius/radius_server.c | 406 +- contrib/hostapd/src/radius/radius_server.h | 212 +- contrib/hostapd/src/rsn_supp/peerkey.c | 103 +- contrib/hostapd/src/rsn_supp/peerkey.h | 11 +- contrib/hostapd/src/rsn_supp/pmksa_cache.c | 234 +- contrib/hostapd/src/rsn_supp/pmksa_cache.h | 52 +- contrib/hostapd/src/rsn_supp/preauth.c | 200 +- contrib/hostapd/src/rsn_supp/preauth.h | 33 +- contrib/hostapd/src/rsn_supp/tdls.c | 2638 ++++ contrib/hostapd/src/rsn_supp/wpa.c | 1320 +- contrib/hostapd/src/rsn_supp/wpa.h | 148 +- contrib/hostapd/src/rsn_supp/wpa_ft.c | 484 +- contrib/hostapd/src/rsn_supp/wpa_i.h | 131 +- contrib/hostapd/src/rsn_supp/wpa_ie.c | 313 +- contrib/hostapd/src/rsn_supp/wpa_ie.h | 40 +- contrib/hostapd/src/tls/asn1.c | 53 +- contrib/hostapd/src/tls/asn1.h | 11 +- contrib/hostapd/src/tls/asn1_test.c | 210 - contrib/hostapd/src/tls/bignum.c | 10 +- contrib/hostapd/src/tls/bignum.h | 10 +- contrib/hostapd/src/tls/libtommath.c | 34 +- contrib/hostapd/src/tls/pkcs1.c | 195 + contrib/hostapd/src/tls/pkcs1.h | 22 + contrib/hostapd/src/tls/pkcs5.c | 232 + contrib/hostapd/src/tls/pkcs5.h | 16 + contrib/hostapd/src/tls/pkcs8.c | 187 + contrib/hostapd/src/tls/pkcs8.h | 16 + contrib/hostapd/src/tls/rsa.c | 13 +- contrib/hostapd/src/tls/rsa.h | 10 +- contrib/hostapd/src/tls/tlsv1_client.c | 265 +- contrib/hostapd/src/tls/tlsv1_client.h | 23 +- contrib/hostapd/src/tls/tlsv1_client_i.h | 15 +- contrib/hostapd/src/tls/tlsv1_client_read.c | 57 +- contrib/hostapd/src/tls/tlsv1_client_write.c | 172 +- contrib/hostapd/src/tls/tlsv1_common.c | 106 +- contrib/hostapd/src/tls/tlsv1_common.h | 69 +- contrib/hostapd/src/tls/tlsv1_cred.c | 118 +- contrib/hostapd/src/tls/tlsv1_cred.h | 10 +- contrib/hostapd/src/tls/tlsv1_record.c | 234 +- contrib/hostapd/src/tls/tlsv1_record.h | 21 +- contrib/hostapd/src/tls/tlsv1_server.c | 112 +- contrib/hostapd/src/tls/tlsv1_server.h | 14 +- contrib/hostapd/src/tls/tlsv1_server_i.h | 10 +- contrib/hostapd/src/tls/tlsv1_server_read.c | 167 +- contrib/hostapd/src/tls/tlsv1_server_write.c | 137 +- contrib/hostapd/src/tls/x509v3.c | 457 +- contrib/hostapd/src/tls/x509v3.h | 95 +- contrib/hostapd/src/utils/base64.c | 72 +- contrib/hostapd/src/utils/base64.h | 12 +- contrib/hostapd/src/utils/bitfield.c | 89 + contrib/hostapd/src/utils/bitfield.h | 21 + contrib/hostapd/src/utils/build_config.h | 49 +- contrib/hostapd/src/utils/common.c | 534 +- contrib/hostapd/src/utils/common.h | 275 +- contrib/hostapd/src/utils/edit.c | 1174 ++ contrib/hostapd/src/utils/edit.h | 21 + contrib/hostapd/src/utils/edit_readline.c | 192 + contrib/hostapd/src/utils/edit_simple.c | 92 + contrib/hostapd/src/utils/eloop.c | 627 +- contrib/hostapd/src/utils/eloop.h | 91 +- contrib/hostapd/src/utils/eloop_none.c | 410 - contrib/hostapd/src/utils/eloop_win.c | 276 +- contrib/hostapd/src/utils/ext_password.c | 116 + contrib/hostapd/src/utils/ext_password.h | 33 + contrib/hostapd/src/utils/ext_password_i.h | 23 + contrib/hostapd/src/utils/ext_password_test.c | 90 + contrib/hostapd/src/utils/includes.h | 13 +- contrib/hostapd/src/utils/ip_addr.c | 10 +- contrib/hostapd/src/utils/ip_addr.h | 13 +- contrib/hostapd/src/utils/list.h | 95 + contrib/hostapd/src/utils/os.h | 176 +- contrib/hostapd/src/utils/os_internal.c | 46 +- contrib/hostapd/src/utils/os_none.c | 21 +- contrib/hostapd/src/utils/os_unix.c | 287 +- contrib/hostapd/src/utils/os_win32.c | 40 +- contrib/hostapd/src/utils/pcsc_funcs.c | 339 +- contrib/hostapd/src/utils/pcsc_funcs.h | 48 +- .../hostapd/{hostapd => src/utils}/radiotap.c | 0 .../hostapd/src/{drivers => utils}/radiotap.h | 1 + .../{hostapd => src/utils}/radiotap_iter.h | 15 + contrib/hostapd/src/utils/state_machine.h | 10 +- contrib/hostapd/src/utils/trace.c | 323 + contrib/hostapd/src/utils/trace.h | 68 + contrib/hostapd/src/utils/uuid.c | 40 +- contrib/hostapd/src/utils/uuid.h | 11 +- contrib/hostapd/src/utils/wpa_debug.c | 426 +- contrib/hostapd/src/utils/wpa_debug.h | 126 +- contrib/hostapd/src/utils/wpabuf.c | 115 +- contrib/hostapd/src/utils/wpabuf.h | 42 +- contrib/hostapd/src/wps/http.h | 29 + contrib/hostapd/src/wps/http_client.c | 368 + contrib/hostapd/src/wps/http_client.h | 40 + contrib/hostapd/src/wps/http_server.c | 310 + contrib/hostapd/src/wps/http_server.h | 33 + contrib/hostapd/src/wps/httpread.c | 61 +- contrib/hostapd/src/wps/httpread.h | 12 +- contrib/hostapd/src/wps/ndef.c | 196 + contrib/hostapd/src/wps/upnp_xml.c | 252 + contrib/hostapd/src/wps/upnp_xml.h | 25 + contrib/hostapd/src/wps/wps.c | 430 +- contrib/hostapd/src/wps/wps.h | 564 +- contrib/hostapd/src/wps/wps_attr_build.c | 270 +- contrib/hostapd/src/wps/wps_attr_parse.c | 231 +- contrib/hostapd/src/wps/wps_attr_parse.h | 108 + contrib/hostapd/src/wps/wps_attr_process.c | 54 +- contrib/hostapd/src/wps/wps_common.c | 641 +- contrib/hostapd/src/wps/wps_defs.h | 76 +- contrib/hostapd/src/wps/wps_dev_attr.c | 225 +- contrib/hostapd/src/wps/wps_dev_attr.h | 28 +- contrib/hostapd/src/wps/wps_enrollee.c | 508 +- contrib/hostapd/src/wps/wps_er.c | 2098 +++ contrib/hostapd/src/wps/wps_er.h | 112 + contrib/hostapd/src/wps/wps_er_ssdp.c | 207 + contrib/hostapd/src/wps/wps_i.h | 156 +- contrib/hostapd/src/wps/wps_registrar.c | 1774 ++- contrib/hostapd/src/wps/wps_upnp.c | 507 +- contrib/hostapd/src/wps/wps_upnp.h | 29 +- contrib/hostapd/src/wps/wps_upnp_ap.c | 87 + contrib/hostapd/src/wps/wps_upnp_event.c | 441 +- contrib/hostapd/src/wps/wps_upnp_i.h | 110 +- contrib/hostapd/src/wps/wps_upnp_ssdp.c | 183 +- contrib/hostapd/src/wps/wps_upnp_web.c | 1166 +- contrib/hostapd/src/wps/wps_validate.c | 1975 +++ 618 files changed, 117547 insertions(+), 89767 deletions(-) delete mode 100644 contrib/hostapd-0.4.9/COPYING delete mode 100644 contrib/hostapd-0.4.9/README delete mode 100644 contrib/hostapd-0.4.9/README.DELETE delete mode 100644 contrib/hostapd-0.4.9/README.DRAGONFLY delete mode 100644 contrib/hostapd-0.4.9/accounting.c delete mode 100644 contrib/hostapd-0.4.9/accounting.h delete mode 100644 contrib/hostapd-0.4.9/aes_wrap.c delete mode 100644 contrib/hostapd-0.4.9/aes_wrap.h delete mode 100644 contrib/hostapd-0.4.9/ap.h delete mode 100644 contrib/hostapd-0.4.9/common.c delete mode 100644 contrib/hostapd-0.4.9/common.h delete mode 100644 contrib/hostapd-0.4.9/config.c delete mode 100644 contrib/hostapd-0.4.9/config.h delete mode 100644 contrib/hostapd-0.4.9/config_types.h delete mode 100644 contrib/hostapd-0.4.9/crypto.c delete mode 100644 contrib/hostapd-0.4.9/crypto.h delete mode 100644 contrib/hostapd-0.4.9/ctrl_iface.c delete mode 100644 contrib/hostapd-0.4.9/ctrl_iface.h delete mode 100644 contrib/hostapd-0.4.9/defconfig delete mode 100644 contrib/hostapd-0.4.9/defs.h delete mode 100644 contrib/hostapd-0.4.9/developer.txt delete mode 100644 contrib/hostapd-0.4.9/driver.h delete mode 100644 contrib/hostapd-0.4.9/driver_wired.c delete mode 100644 contrib/hostapd-0.4.9/eap.c delete mode 100644 contrib/hostapd-0.4.9/eap.h delete mode 100644 contrib/hostapd-0.4.9/eap_defs.h delete mode 100644 contrib/hostapd-0.4.9/eap_gtc.c delete mode 100644 contrib/hostapd-0.4.9/eap_i.h delete mode 100644 contrib/hostapd-0.4.9/eap_identity.c delete mode 100644 contrib/hostapd-0.4.9/eap_md5.c delete mode 100644 contrib/hostapd-0.4.9/eap_mschapv2.c delete mode 100644 contrib/hostapd-0.4.9/eap_pax.c delete mode 100644 contrib/hostapd-0.4.9/eap_pax_common.c delete mode 100644 contrib/hostapd-0.4.9/eap_pax_common.h delete mode 100644 contrib/hostapd-0.4.9/eap_peap.c delete mode 100644 contrib/hostapd-0.4.9/eap_psk.c delete mode 100644 contrib/hostapd-0.4.9/eap_psk_common.c delete mode 100644 contrib/hostapd-0.4.9/eap_psk_common.h delete mode 100644 contrib/hostapd-0.4.9/eap_sim.c delete mode 100644 contrib/hostapd-0.4.9/eap_sim_common.c delete mode 100644 contrib/hostapd-0.4.9/eap_sim_common.h delete mode 100644 contrib/hostapd-0.4.9/eap_sim_db.c delete mode 100644 contrib/hostapd-0.4.9/eap_sim_db.h delete mode 100644 contrib/hostapd-0.4.9/eap_tls.c delete mode 100644 contrib/hostapd-0.4.9/eap_tls_common.c delete mode 100644 contrib/hostapd-0.4.9/eap_tls_common.h delete mode 100644 contrib/hostapd-0.4.9/eap_tlv.c delete mode 100644 contrib/hostapd-0.4.9/eap_ttls.c delete mode 100644 contrib/hostapd-0.4.9/eap_ttls.h delete mode 100644 contrib/hostapd-0.4.9/eapol_sm.c delete mode 100644 contrib/hostapd-0.4.9/eapol_sm.h delete mode 100644 contrib/hostapd-0.4.9/eloop.c delete mode 100644 contrib/hostapd-0.4.9/eloop.h delete mode 100644 contrib/hostapd-0.4.9/hostap_common.h delete mode 100644 contrib/hostapd-0.4.9/hostapd.8 delete mode 100644 contrib/hostapd-0.4.9/hostapd.accept delete mode 100644 contrib/hostapd-0.4.9/hostapd.c delete mode 100644 contrib/hostapd-0.4.9/hostapd.conf delete mode 100644 contrib/hostapd-0.4.9/hostapd.deny delete mode 100644 contrib/hostapd-0.4.9/hostapd.eap_user delete mode 100644 contrib/hostapd-0.4.9/hostapd.h delete mode 100644 contrib/hostapd-0.4.9/hostapd.radius_clients delete mode 100644 contrib/hostapd-0.4.9/hostapd.sim_db delete mode 100644 contrib/hostapd-0.4.9/hostapd.wpa_psk delete mode 100644 contrib/hostapd-0.4.9/hostapd_cli.1 delete mode 100644 contrib/hostapd-0.4.9/hostapd_cli.c delete mode 100644 contrib/hostapd-0.4.9/iapp.c delete mode 100644 contrib/hostapd-0.4.9/ieee802_11.c delete mode 100644 contrib/hostapd-0.4.9/ieee802_11.h delete mode 100644 contrib/hostapd-0.4.9/ieee802_11_auth.c delete mode 100644 contrib/hostapd-0.4.9/ieee802_11_auth.h delete mode 100644 contrib/hostapd-0.4.9/ieee802_1x.c delete mode 100644 contrib/hostapd-0.4.9/ieee802_1x.h delete mode 100644 contrib/hostapd-0.4.9/l2_packet.h delete mode 100644 contrib/hostapd-0.4.9/madwifi.conf delete mode 100644 contrib/hostapd-0.4.9/md5.h delete mode 100644 contrib/hostapd-0.4.9/ms_funcs.c delete mode 100644 contrib/hostapd-0.4.9/ms_funcs.h delete mode 100644 contrib/hostapd-0.4.9/radius.c delete mode 100644 contrib/hostapd-0.4.9/radius.h delete mode 100644 contrib/hostapd-0.4.9/radius_client.c delete mode 100644 contrib/hostapd-0.4.9/radius_client.h delete mode 100644 contrib/hostapd-0.4.9/radius_server.c delete mode 100644 contrib/hostapd-0.4.9/radius_server.h delete mode 100644 contrib/hostapd-0.4.9/rc4.c delete mode 100644 contrib/hostapd-0.4.9/rc4.h delete mode 100644 contrib/hostapd-0.4.9/sha1.c delete mode 100644 contrib/hostapd-0.4.9/sha1.h delete mode 100644 contrib/hostapd-0.4.9/sta_info.c delete mode 100644 contrib/hostapd-0.4.9/sta_info.h delete mode 100644 contrib/hostapd-0.4.9/tls.h delete mode 100644 contrib/hostapd-0.4.9/tls_none.c delete mode 100644 contrib/hostapd-0.4.9/tls_openssl.c delete mode 100644 contrib/hostapd-0.4.9/version.h delete mode 100644 contrib/hostapd-0.4.9/wired.conf delete mode 100644 contrib/hostapd-0.4.9/wpa.c delete mode 100644 contrib/hostapd-0.4.9/wpa.h delete mode 100644 contrib/hostapd-0.4.9/wpa_ctrl.c delete mode 100644 contrib/hostapd-0.4.9/wpa_ctrl.h delete mode 100644 contrib/hostapd/README.DELETED delete mode 100644 contrib/hostapd/README.DRAGONFLY delete mode 100644 contrib/hostapd/hostapd/accounting.h delete mode 100644 contrib/hostapd/hostapd/ap.h delete mode 100644 contrib/hostapd/hostapd/ap_list.c delete mode 100644 contrib/hostapd/hostapd/ap_list.h delete mode 100644 contrib/hostapd/hostapd/beacon.c delete mode 100644 contrib/hostapd/hostapd/beacon.h rename contrib/hostapd/hostapd/{config.c => config_file.c} (57%) create mode 100644 contrib/hostapd/hostapd/config_file.h delete mode 100644 contrib/hostapd/hostapd/driver.h delete mode 100644 contrib/hostapd/hostapd/driver_atheros.c delete mode 100644 contrib/hostapd/hostapd/driver_bsd.c delete mode 100644 contrib/hostapd/hostapd/driver_hostap.c delete mode 100644 contrib/hostapd/hostapd/driver_madwifi.c delete mode 100644 contrib/hostapd/hostapd/driver_nl80211.c delete mode 100644 contrib/hostapd/hostapd/driver_none.c delete mode 100644 contrib/hostapd/hostapd/driver_prism54.c delete mode 100644 contrib/hostapd/hostapd/driver_test.c delete mode 100644 contrib/hostapd/hostapd/driver_wired.c delete mode 100644 contrib/hostapd/hostapd/drivers.c create mode 100644 contrib/hostapd/hostapd/eap_register.c create mode 100644 contrib/hostapd/hostapd/eap_register.h create mode 100644 contrib/hostapd/hostapd/hlr_auc_gw.txt delete mode 100644 contrib/hostapd/hostapd/hostap_common.h delete mode 100644 contrib/hostapd/hostapd/hostapd.c delete mode 100644 contrib/hostapd/hostapd/hostapd.h delete mode 100644 contrib/hostapd/hostapd/hw_features.c delete mode 100644 contrib/hostapd/hostapd/hw_features.h delete mode 100644 contrib/hostapd/hostapd/iapp.h delete mode 100644 contrib/hostapd/hostapd/ieee802_11.c delete mode 100644 contrib/hostapd/hostapd/ieee802_11.h delete mode 100644 contrib/hostapd/hostapd/logwatch/README create mode 100644 contrib/hostapd/hostapd/main.c delete mode 100644 contrib/hostapd/hostapd/nt_password_hash.c delete mode 100644 contrib/hostapd/hostapd/prism54.h delete mode 100644 contrib/hostapd/hostapd/priv_netlink.h delete mode 100644 contrib/hostapd/hostapd/radiotap.h delete mode 100644 contrib/hostapd/hostapd/sta_info.h delete mode 100644 contrib/hostapd/hostapd/vlan_init.h delete mode 100644 contrib/hostapd/hostapd/wme.h delete mode 100644 contrib/hostapd/hostapd/wps_hostapd.c delete mode 100644 contrib/hostapd/hostapd/wps_hostapd.h create mode 100644 contrib/hostapd/patches/openssl-0.9.8x-tls-extensions.patch rename contrib/hostapd/{hostapd => src/ap}/accounting.c (67%) create mode 100644 contrib/hostapd/src/ap/accounting.h create mode 100644 contrib/hostapd/src/ap/acs.c create mode 100644 contrib/hostapd/src/ap/acs.h create mode 100644 contrib/hostapd/src/ap/ap_config.c rename contrib/hostapd/{hostapd/config.h => src/ap/ap_config.h} (54%) create mode 100644 contrib/hostapd/src/ap/ap_drv_ops.c create mode 100644 contrib/hostapd/src/ap/ap_drv_ops.h create mode 100644 contrib/hostapd/src/ap/ap_list.c create mode 100644 contrib/hostapd/src/ap/ap_list.h rename contrib/hostapd/{hostapd/mlme.c => src/ap/ap_mlme.c} (92%) rename contrib/hostapd/{hostapd/mlme.h => src/ap/ap_mlme.h} (73%) create mode 100644 contrib/hostapd/src/ap/authsrv.c create mode 100644 contrib/hostapd/src/ap/authsrv.h create mode 100644 contrib/hostapd/src/ap/beacon.c create mode 100644 contrib/hostapd/src/ap/beacon.h create mode 100644 contrib/hostapd/src/ap/ctrl_iface_ap.c create mode 100644 contrib/hostapd/src/ap/ctrl_iface_ap.h create mode 100644 contrib/hostapd/src/ap/dfs.c create mode 100644 contrib/hostapd/src/ap/dfs.h create mode 100644 contrib/hostapd/src/ap/drv_callbacks.c create mode 100644 contrib/hostapd/src/ap/eap_user_db.c create mode 100644 contrib/hostapd/src/ap/gas_serv.c create mode 100644 contrib/hostapd/src/ap/gas_serv.h create mode 100644 contrib/hostapd/src/ap/hostapd.c create mode 100644 contrib/hostapd/src/ap/hostapd.h create mode 100644 contrib/hostapd/src/ap/hs20.c create mode 100644 contrib/hostapd/src/ap/hs20.h create mode 100644 contrib/hostapd/src/ap/hw_features.c create mode 100644 contrib/hostapd/src/ap/hw_features.h rename contrib/hostapd/{hostapd => src/ap}/iapp.c (85%) rename contrib/{hostapd-0.4.9 => hostapd/src/ap}/iapp.h (72%) create mode 100644 contrib/hostapd/src/ap/ieee802_11.c create mode 100644 contrib/hostapd/src/ap/ieee802_11.h rename contrib/hostapd/{hostapd => src/ap}/ieee802_11_auth.c (61%) rename contrib/hostapd/{hostapd => src/ap}/ieee802_11_auth.h (53%) create mode 100644 contrib/hostapd/src/ap/ieee802_11_ht.c create mode 100644 contrib/hostapd/src/ap/ieee802_11_shared.c create mode 100644 contrib/hostapd/src/ap/ieee802_11_vht.c rename contrib/hostapd/{hostapd => src/ap}/ieee802_1x.c (70%) rename contrib/hostapd/{hostapd => src/ap}/ieee802_1x.h (50%) create mode 100644 contrib/hostapd/src/ap/p2p_hostapd.c create mode 100644 contrib/hostapd/src/ap/p2p_hostapd.h rename contrib/hostapd/{hostapd/peerkey.c => src/ap/peerkey_auth.c} (94%) rename contrib/hostapd/{hostapd/pmksa_cache.c => src/ap/pmksa_cache_auth.c} (73%) rename contrib/hostapd/{hostapd/pmksa_cache.h => src/ap/pmksa_cache_auth.h} (52%) rename contrib/hostapd/{hostapd/preauth.c => src/ap/preauth_auth.c} (92%) rename contrib/hostapd/{hostapd/preauth.h => src/ap/preauth_auth.h} (78%) rename contrib/hostapd/{hostapd => src/ap}/sta_info.c (51%) create mode 100644 contrib/hostapd/src/ap/sta_info.h create mode 100644 contrib/hostapd/src/ap/tkip_countermeasures.c create mode 100644 contrib/hostapd/src/ap/tkip_countermeasures.h create mode 100644 contrib/hostapd/src/ap/utils.c rename contrib/hostapd/{hostapd => src/ap}/vlan_init.c (51%) create mode 100644 contrib/hostapd/src/ap/vlan_init.h create mode 100644 contrib/hostapd/src/ap/vlan_util.c create mode 100644 contrib/hostapd/src/ap/vlan_util.h rename contrib/hostapd/{hostapd/wme.c => src/ap/wmm.c} (80%) create mode 100644 contrib/hostapd/src/ap/wmm.h create mode 100644 contrib/hostapd/src/ap/wnm_ap.c create mode 100644 contrib/hostapd/src/ap/wnm_ap.h rename contrib/hostapd/{hostapd/wpa.c => src/ap/wpa_auth.c} (64%) rename contrib/hostapd/{hostapd/wpa.h => src/ap/wpa_auth.h} (79%) rename contrib/hostapd/{hostapd/wpa_ft.c => src/ap/wpa_auth_ft.c} (75%) create mode 100644 contrib/hostapd/src/ap/wpa_auth_glue.c create mode 100644 contrib/hostapd/src/ap/wpa_auth_glue.h rename contrib/hostapd/{hostapd => src/ap}/wpa_auth_i.h (85%) rename contrib/hostapd/{hostapd => src/ap}/wpa_auth_ie.c (71%) rename contrib/hostapd/{hostapd => src/ap}/wpa_auth_ie.h (76%) create mode 100644 contrib/hostapd/src/ap/wps_hostapd.c create mode 100644 contrib/hostapd/src/ap/wps_hostapd.h create mode 100644 contrib/hostapd/src/common/gas.c create mode 100644 contrib/hostapd/src/common/gas.h delete mode 100644 contrib/hostapd/src/common/nl80211_copy.h create mode 100644 contrib/hostapd/src/common/qca-vendor.h create mode 100644 contrib/hostapd/src/common/sae.c create mode 100644 contrib/hostapd/src/common/sae.h delete mode 100644 contrib/hostapd/src/common/wireless_copy.h create mode 100644 contrib/hostapd/src/crypto/aes-cbc.c create mode 100644 contrib/hostapd/src/crypto/aes-ccm.c create mode 100644 contrib/hostapd/src/crypto/aes-ctr.c create mode 100644 contrib/hostapd/src/crypto/aes-eax.c create mode 100644 contrib/hostapd/src/crypto/aes-encblock.c create mode 100644 contrib/hostapd/src/crypto/aes-gcm.c create mode 100644 contrib/hostapd/src/crypto/aes-internal-dec.c create mode 100644 contrib/hostapd/src/crypto/aes-internal-enc.c rename contrib/{hostapd-0.4.9/aes.c => hostapd/src/crypto/aes-internal.c} (82%) create mode 100644 contrib/hostapd/src/crypto/aes-omac1.c create mode 100644 contrib/hostapd/src/crypto/aes-unwrap.c create mode 100644 contrib/hostapd/src/crypto/aes-wrap.c delete mode 100644 contrib/hostapd/src/crypto/aes.c create mode 100644 contrib/hostapd/src/crypto/aes_i.h delete mode 100644 contrib/hostapd/src/crypto/aes_wrap.c create mode 100644 contrib/hostapd/src/crypto/crypto_internal-cipher.c create mode 100644 contrib/hostapd/src/crypto/crypto_internal-modexp.c create mode 100644 contrib/hostapd/src/crypto/crypto_internal-rsa.c create mode 100644 contrib/hostapd/src/crypto/crypto_nss.c rename contrib/hostapd/src/crypto/{des.c => des-internal.c} (95%) create mode 100644 contrib/hostapd/src/crypto/des_i.h create mode 100644 contrib/hostapd/src/crypto/dh_group5.c create mode 100644 contrib/hostapd/src/crypto/dh_group5.h create mode 100644 contrib/hostapd/src/crypto/fips_prf_cryptoapi.c create mode 100644 contrib/hostapd/src/crypto/fips_prf_gnutls.c create mode 100644 contrib/hostapd/src/crypto/fips_prf_internal.c create mode 100644 contrib/hostapd/src/crypto/fips_prf_nss.c create mode 100644 contrib/hostapd/src/crypto/fips_prf_openssl.c rename contrib/hostapd/src/crypto/{md4.c => md4-internal.c} (94%) rename contrib/{hostapd-0.4.9/md5.c => hostapd/src/crypto/md5-internal.c} (68%) create mode 100644 contrib/hostapd/src/crypto/md5_i.h create mode 100644 contrib/hostapd/src/crypto/milenage.c rename contrib/hostapd/src/{hlr_auc_gw => crypto}/milenage.h (67%) create mode 100644 contrib/hostapd/src/crypto/random.c create mode 100644 contrib/hostapd/src/crypto/random.h delete mode 100644 contrib/hostapd/src/crypto/rc4.h create mode 100644 contrib/hostapd/src/crypto/sha1-internal.c create mode 100644 contrib/hostapd/src/crypto/sha1-pbkdf2.c create mode 100644 contrib/hostapd/src/crypto/sha1-prf.c create mode 100644 contrib/hostapd/src/crypto/sha1-tlsprf.c create mode 100644 contrib/hostapd/src/crypto/sha1-tprf.c create mode 100644 contrib/hostapd/src/crypto/sha1_i.h copy contrib/hostapd/src/crypto/{sha256.c => sha256-internal.c} (50%) create mode 100644 contrib/hostapd/src/crypto/sha256-prf.c create mode 100644 contrib/hostapd/src/crypto/sha256-tlsprf.c create mode 100644 contrib/hostapd/src/crypto/sha256_i.h create mode 100644 contrib/hostapd/src/crypto/tls_nss.c delete mode 100644 contrib/hostapd/src/drivers/Apple80211.h delete mode 100644 contrib/hostapd/src/drivers/MobileApple80211.c delete mode 100644 contrib/hostapd/src/drivers/MobileApple80211.h create mode 100644 contrib/hostapd/src/drivers/android_drv.h create mode 100644 contrib/hostapd/src/drivers/driver_atheros.c delete mode 100644 contrib/hostapd/src/drivers/driver_atmel.c delete mode 100644 contrib/hostapd/src/drivers/driver_broadcom.c create mode 100644 contrib/hostapd/src/drivers/driver_common.c delete mode 100644 contrib/hostapd/src/drivers/driver_ipw.c delete mode 100644 contrib/hostapd/src/drivers/driver_ndiswrapper.c create mode 100644 contrib/hostapd/src/drivers/driver_none.c create mode 100644 contrib/hostapd/src/drivers/driver_openbsd.c delete mode 100644 contrib/hostapd/src/drivers/driver_prism54.c delete mode 100644 contrib/hostapd/src/drivers/driver_ps3.c delete mode 100644 contrib/hostapd/src/drivers/driver_ralink.c delete mode 100644 contrib/hostapd/src/drivers/driver_ralink.h create mode 100644 contrib/hostapd/src/drivers/linux_ioctl.c create mode 100644 contrib/hostapd/src/drivers/linux_ioctl.h create mode 100644 contrib/hostapd/src/drivers/linux_wext.h create mode 100644 contrib/hostapd/src/drivers/netlink.c create mode 100644 contrib/hostapd/src/drivers/netlink.h create mode 100644 contrib/hostapd/src/drivers/nl80211_copy.h delete mode 100644 contrib/hostapd/src/drivers/radiotap.c delete mode 100644 contrib/hostapd/src/drivers/radiotap_iter.h create mode 100644 contrib/hostapd/src/drivers/rfkill.c create mode 100644 contrib/hostapd/src/drivers/rfkill.h delete mode 100644 contrib/hostapd/src/drivers/scan_helpers.c create mode 100644 contrib/hostapd/src/eap_common/eap_eke_common.c create mode 100644 contrib/hostapd/src/eap_common/eap_eke_common.h create mode 100644 contrib/hostapd/src/eap_common/eap_pwd_common.c create mode 100644 contrib/hostapd/src/eap_common/eap_pwd_common.h create mode 100644 contrib/hostapd/src/eap_peer/eap_eke.c create mode 100644 contrib/hostapd/src/eap_peer/eap_proxy.h create mode 100644 contrib/hostapd/src/eap_peer/eap_proxy_dummy.c create mode 100644 contrib/hostapd/src/eap_peer/eap_pwd.c rename contrib/hostapd/src/eap_server/{eap.c => eap_server.c} (93%) rename contrib/hostapd/src/eap_server/{eap_aka.c => eap_server_aka.c} (78%) create mode 100644 contrib/hostapd/src/eap_server/eap_server_eke.c rename contrib/hostapd/src/eap_server/{eap_fast.c => eap_server_fast.c} (94%) rename contrib/hostapd/src/eap_server/{eap_gpsk.c => eap_server_gpsk.c} (93%) rename contrib/hostapd/src/eap_server/{eap_gtc.c => eap_server_gtc.c} (92%) rename contrib/hostapd/src/eap_server/{eap_identity.c => eap_server_identity.c} (91%) rename contrib/hostapd/src/eap_server/{eap_ikev2.c => eap_server_ikev2.c} (96%) rename contrib/hostapd/src/eap_server/{eap_md5.c => eap_server_md5.c} (87%) rename contrib/hostapd/src/eap_server/{eap_methods.c => eap_server_methods.c} (50%) rename contrib/hostapd/src/eap_server/{eap_mschapv2.c => eap_server_mschapv2.c} (93%) rename contrib/hostapd/src/eap_server/{eap_pax.c => eap_server_pax.c} (97%) rename contrib/hostapd/src/eap_server/{eap_peap.c => eap_server_peap.c} (81%) rename contrib/hostapd/src/eap_server/{eap_psk.c => eap_server_psk.c} (92%) create mode 100644 contrib/hostapd/src/eap_server/eap_server_pwd.c rename contrib/hostapd/src/eap_server/{eap_sake.c => eap_server_sake.c} (90%) rename contrib/hostapd/src/eap_server/{eap_sim.c => eap_server_sim.c} (77%) rename contrib/hostapd/src/eap_server/{eap_tls.c => eap_server_tls.c} (73%) rename contrib/hostapd/src/eap_server/{eap_tls_common.c => eap_server_tls_common.c} (67%) rename contrib/hostapd/src/eap_server/{eap_tnc.c => eap_server_tnc.c} (84%) rename contrib/hostapd/src/eap_server/{eap_ttls.c => eap_server_ttls.c} (75%) rename contrib/hostapd/src/eap_server/{eap_vendor_test.c => eap_server_vendor_test.c} (91%) rename contrib/hostapd/src/eap_server/{eap_wsc.c => eap_server_wsc.c} (89%) create mode 100644 contrib/hostapd/src/eapol_auth/eapol_auth_dump.c rename contrib/hostapd/{hostapd/eapol_sm.c => src/eapol_auth/eapol_auth_sm.c} (74%) create mode 100644 contrib/hostapd/src/eapol_auth/eapol_auth_sm.h rename contrib/hostapd/{hostapd/eapol_sm.h => src/eapol_auth/eapol_auth_sm_i.h} (60%) delete mode 100644 contrib/hostapd/src/hlr_auc_gw/hlr_auc_gw.c delete mode 100644 contrib/hostapd/src/hlr_auc_gw/milenage.c create mode 100644 contrib/hostapd/src/p2p/p2p.c create mode 100644 contrib/hostapd/src/p2p/p2p.h create mode 100644 contrib/hostapd/src/p2p/p2p_build.c create mode 100644 contrib/hostapd/src/p2p/p2p_dev_disc.c create mode 100644 contrib/hostapd/src/p2p/p2p_go_neg.c create mode 100644 contrib/hostapd/src/p2p/p2p_group.c create mode 100644 contrib/hostapd/src/p2p/p2p_i.h create mode 100644 contrib/hostapd/src/p2p/p2p_invitation.c create mode 100644 contrib/hostapd/src/p2p/p2p_parse.c create mode 100644 contrib/hostapd/src/p2p/p2p_pd.c create mode 100644 contrib/hostapd/src/p2p/p2p_sd.c create mode 100644 contrib/hostapd/src/p2p/p2p_utils.c create mode 100644 contrib/hostapd/src/radius/radius_das.c create mode 100644 contrib/hostapd/src/radius/radius_das.h create mode 100644 contrib/hostapd/src/rsn_supp/tdls.c delete mode 100644 contrib/hostapd/src/tls/asn1_test.c create mode 100644 contrib/hostapd/src/tls/pkcs1.c create mode 100644 contrib/hostapd/src/tls/pkcs1.h create mode 100644 contrib/hostapd/src/tls/pkcs5.c create mode 100644 contrib/hostapd/src/tls/pkcs5.h create mode 100644 contrib/hostapd/src/tls/pkcs8.c create mode 100644 contrib/hostapd/src/tls/pkcs8.h create mode 100644 contrib/hostapd/src/utils/bitfield.c create mode 100644 contrib/hostapd/src/utils/bitfield.h create mode 100644 contrib/hostapd/src/utils/edit.c create mode 100644 contrib/hostapd/src/utils/edit.h create mode 100644 contrib/hostapd/src/utils/edit_readline.c create mode 100644 contrib/hostapd/src/utils/edit_simple.c delete mode 100644 contrib/hostapd/src/utils/eloop_none.c create mode 100644 contrib/hostapd/src/utils/ext_password.c create mode 100644 contrib/hostapd/src/utils/ext_password.h create mode 100644 contrib/hostapd/src/utils/ext_password_i.h create mode 100644 contrib/hostapd/src/utils/ext_password_test.c create mode 100644 contrib/hostapd/src/utils/list.h rename contrib/hostapd/{hostapd => src/utils}/radiotap.c (100%) rename contrib/hostapd/src/{drivers => utils}/radiotap.h (99%) rename contrib/hostapd/{hostapd => src/utils}/radiotap_iter.h (75%) create mode 100644 contrib/hostapd/src/utils/trace.c create mode 100644 contrib/hostapd/src/utils/trace.h create mode 100644 contrib/hostapd/src/wps/http.h create mode 100644 contrib/hostapd/src/wps/http_client.c create mode 100644 contrib/hostapd/src/wps/http_client.h create mode 100644 contrib/hostapd/src/wps/http_server.c create mode 100644 contrib/hostapd/src/wps/http_server.h create mode 100644 contrib/hostapd/src/wps/ndef.c create mode 100644 contrib/hostapd/src/wps/upnp_xml.c create mode 100644 contrib/hostapd/src/wps/upnp_xml.h create mode 100644 contrib/hostapd/src/wps/wps_attr_parse.h create mode 100644 contrib/hostapd/src/wps/wps_er.c create mode 100644 contrib/hostapd/src/wps/wps_er.h create mode 100644 contrib/hostapd/src/wps/wps_er_ssdp.c create mode 100644 contrib/hostapd/src/wps/wps_upnp_ap.c create mode 100644 contrib/hostapd/src/wps/wps_validate.c diff --git a/contrib/hostapd-0.4.9/COPYING b/contrib/hostapd-0.4.9/COPYING deleted file mode 100644 index 60549be514..0000000000 --- a/contrib/hostapd-0.4.9/COPYING +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/contrib/hostapd-0.4.9/README b/contrib/hostapd-0.4.9/README deleted file mode 100644 index 13f38ee051..0000000000 --- a/contrib/hostapd-0.4.9/README +++ /dev/null @@ -1,395 +0,0 @@ -hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP - Authenticator and RADIUS authentication server -================================================================ - -Copyright (c) 2002-2006, Jouni Malinen and -contributors -All Rights Reserved. - -This program is dual-licensed under both the GPL version 2 and BSD -license. Either license may be used at your option. - - - -License -------- - -GPL v2: - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 as -published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -(this copy of the license is in COPYING file) - - -Alternatively, this software may be distributed under the terms of BSD -license: - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name(s) of the above-listed copyright holder(s) nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -Introduction -============ - -Originally, hostapd was an optional user space component for Host AP -driver. It adds more features to the basic IEEE 802.11 management -included in the kernel driver: using external RADIUS authentication -server for MAC address based access control, IEEE 802.1X Authenticator -and dynamic WEP keying, RADIUS accounting, WPA/WPA2 (IEEE 802.11i/RSN) -Authenticator and dynamic TKIP/CCMP keying. - -The current version includes support for other drivers, an integrated -EAP server (i.e., allow full authentication without requiring -an external RADIUS authentication server), and RADIUS authentication -server for EAP authentication. - - -Requirements ------------- - -Current hardware/software requirements: -- drivers: - Host AP driver for Prism2/2.5/3. - (http://hostap.epitest.fi/) - Please note that station firmware version needs to be 1.7.0 or newer - to work in WPA mode. - - madwifi driver for cards based on Atheros chip set (ar521x) - (http://sourceforge.net/projects/madwifi/) - Please note that you will need to add the correct path for - madwifi driver root directory in .config (see defconfig file for - an example: CFLAGS += -I) - - Prism54 driver for Intersil/Conexant Prism GT/Duette/Indigo - (http://www.prism54.org/) - - Any wired Ethernet driver for wired IEEE 802.1X authentication - (experimental code) - - FreeBSD -current (with some kernel mods that have not yet been - committed when hostapd v0.3.0 was released) - BSD net80211 layer (e.g., Atheros driver) - - -Build configuration -------------------- - -In order to be able to build hostapd, you will need to create a build -time configuration file, .config that selects which optional -components are included. See defconfig file for example configuration -and list of available options. - - - -IEEE 802.1X -=========== - -IEEE Std 802.1X-2001 is a standard for port-based network access -control. In case of IEEE 802.11 networks, a "virtual port" is used -between each associated station and the AP. IEEE 802.11 specifies -minimal authentication mechanism for stations, whereas IEEE 802.1X -introduces a extensible mechanism for authenticating and authorizing -users. - -IEEE 802.1X uses elements called Supplicant, Authenticator, Port -Access Entity, and Authentication Server. Supplicant is a component in -a station and it performs the authentication with the Authentication -Server. An access point includes an Authenticator that relays the packets -between a Supplicant and an Authentication Server. In addition, it has a -Port Access Entity (PAE) with Authenticator functionality for -controlling the virtual port authorization, i.e., whether to accept -packets from or to the station. - -IEEE 802.1X uses Extensible Authentication Protocol (EAP). The frames -between a Supplicant and an Authenticator are sent using EAP over LAN -(EAPOL) and the Authenticator relays these frames to the Authentication -Server (and similarly, relays the messages from the Authentication -Server to the Supplicant). The Authentication Server can be colocated with the -Authenticator, in which case there is no need for additional protocol -for EAP frame transmission. However, a more common configuration is to -use an external Authentication Server and encapsulate EAP frame in the -frames used by that server. RADIUS is suitable for this, but IEEE -802.1X would also allow other mechanisms. - -Host AP driver includes PAE functionality in the kernel driver. It -is a relatively simple mechanism for denying normal frames going to -or coming from an unauthorized port. PAE allows IEEE 802.1X related -frames to be passed between the Supplicant and the Authenticator even -on an unauthorized port. - -User space daemon, hostapd, includes Authenticator functionality. It -receives 802.1X (EAPOL) frames from the Supplicant using the wlan#ap -device that is also used with IEEE 802.11 management frames. The -frames to the Supplicant are sent using the same device. - -hostapd includes a minimal colocated Authentication Server for testing -purposes. It only requests the identity of the Supplicant and -authorizes any host that is able to send a valid EAP Response -frame. This can be used for quick testing since it does not require an -external Authentication Server, but it should not be used for any real -authentication purposes since no keys are required and anyone can -authenticate. - -The normal configuration of the Authenticator would use an external -Authentication Server. hostapd supports RADIUS encapsulation of EAP -packets, so the Authentication Server should be a RADIUS server, like -FreeRADIUS (http://www.freeradius.org/). The Authenticator in hostapd -relays the frames between the Supplicant and the Authentication -Server. It also controls the PAE functionality in the kernel driver by -controlling virtual port authorization, i.e., station-AP -connection, based on the IEEE 802.1X state. - -When a station would like to use the services of an access point, it -will first perform IEEE 802.11 authentication. This is normally done -with open systems authentication, so there is no security. After -this, IEEE 802.11 association is performed. If IEEE 802.1X is -configured to be used, the virtual port for the station is set in -Unauthorized state and only IEEE 802.1X frames are accepted at this -point. The Authenticator will then ask the Supplicant to authenticate -with the Authentication Server. After this is completed successfully, -the virtual port is set to Authorized state and frames from and to the -station are accepted. - -Host AP configuration for IEEE 802.1X -------------------------------------- - -The user space daemon has its own configuration file that can be used to -define AP options. Distribution package contains an example -configuration file (hostapd/hostapd.conf) that can be used as a basis -for configuration. It includes examples of all supported configuration -options and short description of each option. hostapd should be started -with full path to the configuration file as the command line argument, -e.g., './hostapd /etc/hostapd.conf'. If you have more that one wireless -LAN card, you can use one hostapd process for multiple interfaces by -giving a list of configuration files (one per interface) in the command -line. - -hostapd includes a minimal co-located IEEE 802.1X server which can be -used to test IEEE 802.1X authentication. However, it should not be -used in normal use since it does not provide any security. This can be -configured by setting ieee8021x and minimal_eap options in the -configuration file. - -An external Authentication Server (RADIUS) is configured with -auth_server_{addr,port,shared_secret} options. In addition, -ieee8021x and own_ip_addr must be set for this mode. With such -configuration, the co-located Authentication Server is not used and EAP -frames will be relayed using EAPOL between the Supplicant and the -Authenticator and RADIUS encapsulation between the Authenticator and -the Authentication Server. Other than this, the functionality is similar -to the case with the co-located Authentication Server. - -Authentication Server and Supplicant ------------------------------------- - -Any RADIUS server supporting EAP should be usable as an IEEE 802.1X -Authentication Server with hostapd Authenticator. FreeRADIUS -(http://www.freeradius.org/) has been successfully tested with hostapd -Authenticator and both Xsupplicant (http://www.open1x.org) and Windows -XP Supplicants. EAP/TLS was used with Xsupplicant and -EAP/MD5-Challenge with Windows XP. - -http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information -about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace -Cisco access point with Host AP driver, hostapd daemon, and a Prism2 -card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information -about using EAP/MD5 with FreeRADIUS, including instructions for WinXP -configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on -EAP/TLS use with WinXP Supplicant. - -Automatic WEP key configuration -------------------------------- - -EAP/TLS generates a session key that can be used to send WEP keys from -an AP to authenticated stations. The Authenticator in hostapd can be -configured to automatically select a random default/broadcast key -(shared by all authenticated stations) with wep_key_len_broadcast -option (5 for 40-bit WEP or 13 for 104-bit WEP). In addition, -wep_key_len_unicast option can be used to configure individual unicast -keys for stations. This requires support for individual keys in the -station driver. - -WEP keys can be automatically updated by configuring rekeying. This -will improve security of the network since same WEP key will only be -used for a limited period of time. wep_rekey_period option sets the -interval for rekeying in seconds. - - -WPA/WPA2 -======== - -Features --------- - -Supported WPA/IEEE 802.11i features: -- WPA-PSK ("WPA-Personal") -- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise") -- key management for CCMP, TKIP, WEP104, WEP40 -- RSN/WPA2 (IEEE 802.11i), including PMKSA caching and pre-authentication - -WPA ---- - -The original security mechanism of IEEE 802.11 standard was not -designed to be strong and has proved to be insufficient for most -networks that require some kind of security. Task group I (Security) -of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked -to address the flaws of the base standard and has in practice -completed its work in May 2004. The IEEE 802.11i amendment to the IEEE -802.11 standard was approved in June 2004 and this amendment is likely -to be published in July 2004. - -Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the -IEEE 802.11i work (draft 3.0) to define a subset of the security -enhancements that can be implemented with existing wlan hardware. This -is called Wi-Fi Protected Access (WPA). This has now become a -mandatory component of interoperability testing and certification done -by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web -site (http://www.wi-fi.org/OpenSection/protected_access.asp). - -IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm -for protecting wireless networks. WEP uses RC4 with 40-bit keys, -24-bit initialization vector (IV), and CRC32 to protect against packet -forgery. All these choices have proven to be insufficient: key space is -too small against current attacks, RC4 key scheduling is insufficient -(beginning of the pseudorandom stream should be skipped), IV space is -too small and IV reuse makes attacks easier, there is no replay -protection, and non-keyed authentication does not protect against bit -flipping packet data. - -WPA is an intermediate solution for the security issues. It uses -Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a -compromise on strong security and possibility to use existing -hardware. It still uses RC4 for the encryption like WEP, but with -per-packet RC4 keys. In addition, it implements replay protection, -keyed packet authentication mechanism (Michael MIC). - -Keys can be managed using two different mechanisms. WPA can either use -an external authentication server (e.g., RADIUS) and EAP just like -IEEE 802.1X is using or pre-shared keys without need for additional -servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal", -respectively. Both mechanisms will generate a master session key for -the Authenticator (AP) and Supplicant (client station). - -WPA implements a new key handshake (4-Way Handshake and Group Key -Handshake) for generating and exchanging data encryption keys between -the Authenticator and Supplicant. This handshake is also used to -verify that both Authenticator and Supplicant know the master session -key. These handshakes are identical regardless of the selected key -management mechanism (only the method for generating master session -key changes). - - -IEEE 802.11i / WPA2 -------------------- - -The design for parts of IEEE 802.11i that were not included in WPA has -finished (May 2004) and this amendment to IEEE 802.11 was approved in -June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new -version of WPA called WPA2. This includes, e.g., support for more -robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC) -to replace TKIP and optimizations for handoff (reduced number of -messages in initial key handshake, pre-authentication, and PMKSA caching). - -Some wireless LAN vendors are already providing support for CCMP in -their WPA products. There is no "official" interoperability -certification for CCMP and/or mixed modes using both TKIP and CCMP, so -some interoperability issues can be expected even though many -combinations seem to be working with equipment from different vendors. -Testing for WPA2 is likely to start during the second half of 2004. - -hostapd configuration for WPA/WPA2 ----------------------------------- - -TODO - -# Enable WPA. Setting this variable configures the AP to require WPA (either -# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either -# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. -# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), -# RADIUS authentication server must be configured, and WPA-EAP must be included -# in wpa_key_mgmt. -# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) -# and/or WPA2 (full IEEE 802.11i/RSN): -# bit0 = WPA -# bit1 = IEEE 802.11i/RSN (WPA2) -#wpa=1 - -# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit -# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase -# (8..63 characters) that will be converted to PSK. This conversion uses SSID -# so the PSK changes when ASCII passphrase is used and the SSID is changed. -#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef -#wpa_passphrase=secret passphrase - -# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The -# entries are separated with a space. -#wpa_key_mgmt=WPA-PSK WPA-EAP - -# Set of accepted cipher suites (encryption algorithms) for pairwise keys -# (unicast packets). This is a space separated list of algorithms: -# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i] -# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i] -# Group cipher suite (encryption algorithm for broadcast and multicast frames) -# is automatically selected based on this configuration. If only CCMP is -# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, -# TKIP will be used as the group cipher. -#wpa_pairwise=TKIP CCMP - -# Time interval for rekeying GTK (broadcast/multicast encryption keys) in -# seconds. -#wpa_group_rekey=600 - -# Time interval for rekeying GMK (master key used internally to generate GTKs -# (in seconds). -#wpa_gmk_rekey=86400 - -# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up -# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN -# authentication and key handshake before actually associating with a new AP. -#rsn_preauth=1 -# -# Space separated list of interfaces from which pre-authentication frames are -# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all -# interface that are used for connections to other APs. This could include -# wired interfaces and WDS links. The normal wireless data interface towards -# associated stations (e.g., wlan0) should not be added, since -# pre-authentication is only used with APs other than the currently associated -# one. -#rsn_preauth_interfaces=eth0 diff --git a/contrib/hostapd-0.4.9/README.DELETE b/contrib/hostapd-0.4.9/README.DELETE deleted file mode 100644 index 7c7b59d55d..0000000000 --- a/contrib/hostapd-0.4.9/README.DELETE +++ /dev/null @@ -1,18 +0,0 @@ -.cvsignore -ChangeLog -Makefile -README.DELETE -driver.c -driver_bsd.c -driver_madwifi.c -driver_prism54.c -driver_test.c -l2_packet_freebsd.c -l2_packet_linux.c -l2_packet_pcap.c -logwatch/README -logwatch/hostapd -logwatch/hostapd.conf -prism54.h -priv_netlink.h -wireless_copy.h diff --git a/contrib/hostapd-0.4.9/README.DRAGONFLY b/contrib/hostapd-0.4.9/README.DRAGONFLY deleted file mode 100644 index 219b8d75a7..0000000000 --- a/contrib/hostapd-0.4.9/README.DRAGONFLY +++ /dev/null @@ -1,4 +0,0 @@ -Original source can be downloaded at: - - -A list of deleted files is in README.DELETED. diff --git a/contrib/hostapd-0.4.9/accounting.c b/contrib/hostapd-0.4.9/accounting.c deleted file mode 100644 index 5ee3d750f9..0000000000 --- a/contrib/hostapd-0.4.9/accounting.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / Accounting - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include "hostapd.h" -#include "radius.h" -#include "radius_client.h" -#include "eloop.h" -#include "accounting.h" -#include "ieee802_1x.h" -#include "driver.h" - - -/* Default interval in seconds for polling TX/RX octets from the driver if - * STA is not using interim accounting. This detects wrap arounds for - * input/output octets and updates Acct-{Input,Output}-Gigawords. */ -#define ACCT_DEFAULT_UPDATE_INTERVAL 300 - -static struct radius_msg * accounting_msg(hostapd *hapd, struct sta_info *sta, - int status_type) -{ - struct radius_msg *msg; - char buf[128]; - u8 *val; - size_t len; - int i; - - msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, - radius_client_get_id(hapd->radius)); - if (msg == NULL) { - printf("Could not create net RADIUS packet\n"); - return NULL; - } - - if (sta) { - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); - - snprintf(buf, sizeof(buf), "%08X-%08X", - sta->acct_session_id_hi, sta->acct_session_id_lo); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, - (u8 *) buf, strlen(buf))) { - printf("Could not add Acct-Session-Id\n"); - goto fail; - } - } else { - radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); - } - - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, - status_type)) { - printf("Could not add Acct-Status-Type\n"); - goto fail; - } - - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, - hapd->conf->ieee802_1x ? - RADIUS_ACCT_AUTHENTIC_RADIUS : - RADIUS_ACCT_AUTHENTIC_LOCAL)) { - printf("Could not add Acct-Authentic\n"); - goto fail; - } - - if (sta) { - val = ieee802_1x_get_identity(sta->eapol_sm, &len); - if (!val) { - snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, - MAC2STR(sta->addr)); - val = (u8 *) buf; - len = strlen(buf); - } - - if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, - len)) { - printf("Could not add User-Name\n"); - goto fail; - } - } - - if (hapd->conf->own_ip_addr.af == AF_INET && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - printf("Could not add NAS-IP-Address\n"); - goto fail; - } - -#ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - printf("Could not add NAS-IPv6-Address\n"); - goto fail; - } -#endif /* CONFIG_IPV6 */ - - if (hapd->conf->nas_identifier && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, - (u8 *) hapd->conf->nas_identifier, - strlen(hapd->conf->nas_identifier))) { - printf("Could not add NAS-Identifier\n"); - goto fail; - } - - if (sta && - !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { - printf("Could not add NAS-Port\n"); - goto fail; - } - - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, strlen(buf))) { - printf("Could not add Called-Station-Id\n"); - goto fail; - } - - if (sta) { - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(sta->addr)); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, strlen(buf))) { - printf("Could not add Calling-Station-Id\n"); - goto fail; - } - - if (!radius_msg_add_attr_int32( - msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - printf("Could not add NAS-Port-Type\n"); - goto fail; - } - - snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, strlen(buf))) { - printf("Could not add Connect-Info\n"); - goto fail; - } - - for (i = 0; ; i++) { - val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, - i); - if (val == NULL) - break; - - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, - val, len)) { - printf("Could not add Class\n"); - goto fail; - } - } - } - - return msg; - - fail: - radius_msg_free(msg); - free(msg); - return NULL; -} - - -static int accounting_sta_update_stats(struct hostapd_data *hapd, - struct sta_info *sta, - struct hostap_sta_driver_data *data) -{ - if (hostapd_read_sta_data(hapd, data, sta->addr)) - return -1; - - if (sta->last_rx_bytes > data->rx_bytes) - sta->acct_input_gigawords++; - if (sta->last_tx_bytes > data->tx_bytes) - sta->acct_output_gigawords++; - sta->last_rx_bytes = data->rx_bytes; - sta->last_tx_bytes = data->tx_bytes; - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " - "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " - "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", - sta->last_rx_bytes, sta->acct_input_gigawords, - sta->last_tx_bytes, sta->acct_output_gigawords); - - return 0; -} - - -static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) -{ - hostapd *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - int interval; - - if (sta->acct_interim_interval) { - accounting_sta_interim(hapd, sta); - interval = sta->acct_interim_interval; - } else { - struct hostap_sta_driver_data data; - accounting_sta_update_stats(hapd, sta, &data); - interval = ACCT_DEFAULT_UPDATE_INTERVAL; - } - - eloop_register_timeout(interval, 0, accounting_interim_update, - hapd, sta); -} - - -void accounting_sta_start(hostapd *hapd, struct sta_info *sta) -{ - struct radius_msg *msg; - int interval; - - if (sta->acct_session_started) - return; - - time(&sta->acct_session_start); - sta->last_rx_bytes = sta->last_tx_bytes = 0; - sta->acct_input_gigawords = sta->acct_output_gigawords = 0; - hostapd_sta_clear_stats(hapd, sta->addr); - - if (!hapd->conf->radius->acct_server) - return; - - if (sta->acct_interim_interval) - interval = sta->acct_interim_interval; - else - interval = ACCT_DEFAULT_UPDATE_INTERVAL; - eloop_register_timeout(interval, 0, accounting_interim_update, - hapd, sta); - - msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); - if (msg) - radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr); - - sta->acct_session_started = 1; -} - - -void accounting_sta_report(hostapd *hapd, struct sta_info *sta, int stop) -{ - struct radius_msg *msg; - int cause = sta->acct_terminate_cause; - struct hostap_sta_driver_data data; - u32 gigawords; - - if (!hapd->conf->radius->acct_server) - return; - - msg = accounting_msg(hapd, sta, - stop ? RADIUS_ACCT_STATUS_TYPE_STOP : - RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); - if (!msg) { - printf("Could not create RADIUS Accounting message\n"); - return; - } - - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, - time(NULL) - sta->acct_session_start)) { - printf("Could not add Acct-Session-Time\n"); - goto fail; - } - - if (accounting_sta_update_stats(hapd, sta, &data) == 0) { - if (!radius_msg_add_attr_int32(msg, - RADIUS_ATTR_ACCT_INPUT_PACKETS, - data.rx_packets)) { - printf("Could not add Acct-Input-Packets\n"); - goto fail; - } - if (!radius_msg_add_attr_int32(msg, - RADIUS_ATTR_ACCT_OUTPUT_PACKETS, - data.tx_packets)) { - printf("Could not add Acct-Output-Packets\n"); - goto fail; - } - if (!radius_msg_add_attr_int32(msg, - RADIUS_ATTR_ACCT_INPUT_OCTETS, - data.rx_bytes)) { - printf("Could not add Acct-Input-Octets\n"); - goto fail; - } - gigawords = sta->acct_input_gigawords; -#if __WORDSIZE == 64 - gigawords += data.rx_bytes >> 32; -#endif - if (gigawords && - !radius_msg_add_attr_int32( - msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, - gigawords)) { - printf("Could not add Acct-Input-Gigawords\n"); - goto fail; - } - if (!radius_msg_add_attr_int32(msg, - RADIUS_ATTR_ACCT_OUTPUT_OCTETS, - data.tx_bytes)) { - printf("Could not add Acct-Output-Octets\n"); - goto fail; - } - gigawords = sta->acct_output_gigawords; -#if __WORDSIZE == 64 - gigawords += data.tx_bytes >> 32; -#endif - if (gigawords && - !radius_msg_add_attr_int32( - msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, - gigawords)) { - printf("Could not add Acct-Output-Gigawords\n"); - goto fail; - } - } - - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, - time(NULL))) { - printf("Could not add Event-Timestamp\n"); - goto fail; - } - - if (eloop_terminated()) - cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; - - if (stop && cause && - !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, - cause)) { - printf("Could not add Acct-Terminate-Cause\n"); - goto fail; - } - - radius_client_send(hapd->radius, msg, - stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, - sta->addr); - return; - - fail: - radius_msg_free(msg); - free(msg); -} - - -void accounting_sta_interim(hostapd *hapd, struct sta_info *sta) -{ - if (sta->acct_session_started) - accounting_sta_report(hapd, sta, 0); -} - - -void accounting_sta_stop(hostapd *hapd, struct sta_info *sta) -{ - if (sta->acct_session_started) { - accounting_sta_report(hapd, sta, 1); - eloop_cancel_timeout(accounting_interim_update, hapd, sta); - sta->acct_session_started = 0; - } -} - - -void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) -{ - sta->acct_session_id_lo = hapd->acct_session_id_lo++; - if (hapd->acct_session_id_lo == 0) { - hapd->acct_session_id_hi++; - } - sta->acct_session_id_hi = hapd->acct_session_id_hi; -} - - -/* Process the RADIUS frames from Accounting Server */ -static RadiusRxResult -accounting_receive(struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, void *data) -{ - if (msg->hdr->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { - printf("Unknown RADIUS message code\n"); - return RADIUS_RX_UNKNOWN; - } - - if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { - printf("Incoming RADIUS packet did not have correct " - "Authenticator - dropped\n"); - return RADIUS_RX_INVALID_AUTHENTICATOR; - } - - return RADIUS_RX_PROCESSED; -} - - -static void accounting_report_state(struct hostapd_data *hapd, int on) -{ - struct radius_msg *msg; - - if (!hapd->conf->radius->acct_server || hapd->radius == NULL) - return; - - /* Inform RADIUS server that accounting will start/stop so that the - * server can close old accounting sessions. */ - msg = accounting_msg(hapd, NULL, - on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON : - RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF); - if (!msg) - return; - - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, - RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) - { - printf("Could not add Acct-Terminate-Cause\n"); - radius_msg_free(msg); - free(msg); - return; - } - - radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); -} - - -int accounting_init(hostapd *hapd) -{ - /* Acct-Session-Id should be unique over reboots. If reliable clock is - * not available, this could be replaced with reboot counter, etc. */ - hapd->acct_session_id_hi = time(NULL); - - if (radius_client_register(hapd->radius, RADIUS_ACCT, - accounting_receive, hapd)) - return -1; - - accounting_report_state(hapd, 1); - - return 0; -} - - -void accounting_deinit(hostapd *hapd) -{ - accounting_report_state(hapd, 0); -} diff --git a/contrib/hostapd-0.4.9/accounting.h b/contrib/hostapd-0.4.9/accounting.h deleted file mode 100644 index 8af3eac59c..0000000000 --- a/contrib/hostapd-0.4.9/accounting.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef ACCOUNTING_H -#define ACCOUNTING_H - - -void accounting_sta_start(hostapd *hapd, struct sta_info *sta); -void accounting_sta_interim(hostapd *hapd, struct sta_info *sta); -void accounting_sta_stop(hostapd *hapd, struct sta_info *sta); -void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta); -int accounting_init(hostapd *hapd); -void accounting_deinit(hostapd *hapd); - - -#endif /* ACCOUNTING_H */ diff --git a/contrib/hostapd-0.4.9/aes_wrap.c b/contrib/hostapd-0.4.9/aes_wrap.c deleted file mode 100644 index a5925ca2ec..0000000000 --- a/contrib/hostapd-0.4.9/aes_wrap.c +++ /dev/null @@ -1,725 +0,0 @@ -/* - * AES-based functions - * - * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * - One-Key CBC MAC (OMAC1) hash with AES-128 - * - AES-128 CTR mode encryption - * - AES-128 EAX mode encryption/decryption - * - AES-128 CBC - * - * Copyright (c) 2003-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include "common.h" -#include "aes_wrap.h" -#include "crypto.h" - -#ifndef EAP_TLS_FUNCS -#include "aes.c" -#endif /* EAP_TLS_FUNCS */ - - -/** - * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * @kek: Key encryption key (KEK) - * @n: Length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes - * @plain: Plaintext key to be wrapped, n * 64 bit - * @cipher: Wrapped key, (n + 1) * 64 bit - * Returns: 0 on success, -1 on failure - */ -int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) -{ - u8 *a, *r, b[16]; - int i, j; - void *ctx; - - a = cipher; - r = cipher + 8; - - /* 1) Initialize variables. */ - memset(a, 0xa6, 8); - memcpy(r, plain, 8 * n); - - ctx = aes_encrypt_init(kek, 16); - if (ctx == NULL) - return -1; - - /* 2) Calculate intermediate values. - * For j = 0 to 5 - * For i=1 to n - * B = AES(K, A | R[i]) - * A = MSB(64, B) ^ t where t = (n*j)+i - * R[i] = LSB(64, B) - */ - for (j = 0; j <= 5; j++) { - r = cipher + 8; - for (i = 1; i <= n; i++) { - memcpy(b, a, 8); - memcpy(b + 8, r, 8); - aes_encrypt(ctx, b, b); - memcpy(a, b, 8); - a[7] ^= n * j + i; - memcpy(r, b + 8, 8); - r += 8; - } - } - aes_encrypt_deinit(ctx); - - /* 3) Output the results. - * - * These are already in @cipher due to the location of temporary - * variables. - */ - - return 0; -} - - -/** - * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * @kek: Key encryption key (KEK) - * @n: Length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes - * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bit - * @plain: Plaintext key, n * 64 bit - * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) - */ -int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) -{ - u8 a[8], *r, b[16]; - int i, j; - void *ctx; - - /* 1) Initialize variables. */ - memcpy(a, cipher, 8); - r = plain; - memcpy(r, cipher + 8, 8 * n); - - ctx = aes_decrypt_init(kek, 16); - if (ctx == NULL) - return -1; - - /* 2) Compute intermediate values. - * For j = 5 to 0 - * For i = n to 1 - * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i - * A = MSB(64, B) - * R[i] = LSB(64, B) - */ - for (j = 5; j >= 0; j--) { - r = plain + (n - 1) * 8; - for (i = n; i >= 1; i--) { - memcpy(b, a, 8); - b[7] ^= n * j + i; - - memcpy(b + 8, r, 8); - aes_decrypt(ctx, b, b); - memcpy(a, b, 8); - memcpy(r, b + 8, 8); - r -= 8; - } - } - aes_decrypt_deinit(ctx); - - /* 3) Output results. - * - * These are already in @plain due to the location of temporary - * variables. Just verify that the IV matches with the expected value. - */ - for (i = 0; i < 8; i++) { - if (a[i] != 0xa6) - return -1; - } - - return 0; -} - - -#define BLOCK_SIZE 16 - -static void gf_mulx(u8 *pad) -{ - int i, carry; - - carry = pad[0] & 0x80; - for (i = 0; i < BLOCK_SIZE - 1; i++) - pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); - pad[BLOCK_SIZE - 1] <<= 1; - if (carry) - pad[BLOCK_SIZE - 1] ^= 0x87; -} - - -/** - * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 - * @key: Key for the hash operation - * @data: Data buffer for which a MAC is determined - * @data: Length of data buffer in bytes - * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) - * Returns: 0 on success, -1 on failure - */ -int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) -{ - void *ctx; - u8 cbc[BLOCK_SIZE], pad[BLOCK_SIZE]; - const u8 *pos = data; - int i; - size_t left = data_len; - - ctx = aes_encrypt_init(key, 16); - if (ctx == NULL) - return -1; - memset(cbc, 0, BLOCK_SIZE); - - while (left >= BLOCK_SIZE) { - for (i = 0; i < BLOCK_SIZE; i++) - cbc[i] ^= *pos++; - if (left > BLOCK_SIZE) - aes_encrypt(ctx, cbc, cbc); - left -= BLOCK_SIZE; - } - - memset(pad, 0, BLOCK_SIZE); - aes_encrypt(ctx, pad, pad); - gf_mulx(pad); - - if (left || data_len == 0) { - for (i = 0; i < left; i++) - cbc[i] ^= *pos++; - cbc[left] ^= 0x80; - gf_mulx(pad); - } - - for (i = 0; i < BLOCK_SIZE; i++) - pad[i] ^= cbc[i]; - aes_encrypt(ctx, pad, mac); - aes_encrypt_deinit(ctx); - return 0; -} - - -/** - * aes_128_encrypt_block - Perform one AES 128-bit block operation - * @key: Key for AES - * @in: Input data (16 bytes) - * @out: Output of the AES block operation (16 bytes) - * Returns: 0 on success, -1 on failure - */ -int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) -{ - void *ctx; - ctx = aes_encrypt_init(key, 16); - if (ctx == NULL) - return -1; - aes_encrypt(ctx, in, out); - aes_encrypt_deinit(ctx); - return 0; -} - - -/** - * aes_128_ctr_encrypt - AES-128 CTR mode encryption - * @key: Key for encryption (16 bytes) - * @nonce: Nonce for counter mode (16 bytes) - * @data: Data to encrypt in-place - * @data_len: Length of data in bytes - * Returns: 0 on success, -1 on failure - */ -int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, - u8 *data, size_t data_len) -{ - void *ctx; - size_t len, left = data_len; - int i; - u8 *pos = data; - u8 counter[BLOCK_SIZE], buf[BLOCK_SIZE]; - - ctx = aes_encrypt_init(key, 16); - if (ctx == NULL) - return -1; - memcpy(counter, nonce, BLOCK_SIZE); - - while (left > 0) { - aes_encrypt(ctx, counter, buf); - - len = (left < BLOCK_SIZE) ? left : BLOCK_SIZE; - for (i = 0; i < len; i++) - pos[i] ^= buf[i]; - pos += len; - left -= len; - - for (i = BLOCK_SIZE - 1; i >= 0; i--) { - counter[i]++; - if (counter[i]) - break; - } - } - aes_encrypt_deinit(ctx); - return 0; -} - - -/** - * aes_128_eax_encrypt - AES-128 EAX mode encryption - * @key: Key for encryption (16 bytes) - * @nonce: Nonce for counter mode - * @nonce_len: Nonce length in bytes - * @hdr: Header data to be authenticity protected - * @hdr_len: Length of the header data bytes - * @data: Data to encrypt in-place - * @data_len: Length of data in bytes - * @tag: 16-byte tag value - * Returns: 0 on success, -1 on failure - */ -int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, - const u8 *hdr, size_t hdr_len, - u8 *data, size_t data_len, u8 *tag) -{ - u8 *buf; - size_t buf_len; - u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; - int i; - - if (nonce_len > data_len) - buf_len = nonce_len; - else - buf_len = data_len; - if (hdr_len > buf_len) - buf_len = hdr_len; - buf_len += 16; - - buf = malloc(buf_len); - if (buf == NULL) - return -1; - - memset(buf, 0, 15); - - buf[15] = 0; - memcpy(buf + 16, nonce, nonce_len); - omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac); - - buf[15] = 1; - memcpy(buf + 16, hdr, hdr_len); - omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac); - - aes_128_ctr_encrypt(key, nonce_mac, data, data_len); - buf[15] = 2; - memcpy(buf + 16, data, data_len); - omac1_aes_128(key, buf, 16 + data_len, data_mac); - - free(buf); - - for (i = 0; i < BLOCK_SIZE; i++) - tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]; - - return 0; -} - - -/** - * aes_128_eax_decrypt - AES-128 EAX mode decryption - * @key: Key for decryption (16 bytes) - * @nonce: Nonce for counter mode - * @nonce_len: Nonce length in bytes - * @hdr: Header data to be authenticity protected - * @hdr_len: Length of the header data bytes - * @data: Data to encrypt in-place - * @data_len: Length of data in bytes - * @tag: 16-byte tag value - * Returns: 0 on success, -1 on failure, -2 if tag does not match - */ -int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, - const u8 *hdr, size_t hdr_len, - u8 *data, size_t data_len, const u8 *tag) -{ - u8 *buf; - size_t buf_len; - u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; - int i; - - if (nonce_len > data_len) - buf_len = nonce_len; - else - buf_len = data_len; - if (hdr_len > buf_len) - buf_len = hdr_len; - buf_len += 16; - - buf = malloc(buf_len); - if (buf == NULL) - return -1; - - memset(buf, 0, 15); - - buf[15] = 0; - memcpy(buf + 16, nonce, nonce_len); - omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac); - - buf[15] = 1; - memcpy(buf + 16, hdr, hdr_len); - omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac); - - buf[15] = 2; - memcpy(buf + 16, data, data_len); - omac1_aes_128(key, buf, 16 + data_len, data_mac); - - free(buf); - - for (i = 0; i < BLOCK_SIZE; i++) { - if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i])) - return -2; - } - - aes_128_ctr_encrypt(key, nonce_mac, data, data_len); - - return 0; -} - - -/** - * aes_128_cbc_encrypt - AES-128 CBC encryption - * @key: Encryption key - * @iv: Encryption IV for CBC mode (16 bytes) - * @data: Data to encrypt in-place - * @data_len: Length of data in bytes (must be divisible by 16) - * Returns: 0 on success, -1 on failure - */ -int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) -{ - void *ctx; - u8 cbc[BLOCK_SIZE]; - u8 *pos = data; - int i, j, blocks; - - ctx = aes_encrypt_init(key, 16); - if (ctx == NULL) - return -1; - memcpy(cbc, iv, BLOCK_SIZE); - - blocks = data_len / BLOCK_SIZE; - for (i = 0; i < blocks; i++) { - for (j = 0; j < BLOCK_SIZE; j++) - cbc[j] ^= pos[j]; - aes_encrypt(ctx, cbc, cbc); - memcpy(pos, cbc, BLOCK_SIZE); - pos += BLOCK_SIZE; - } - aes_encrypt_deinit(ctx); - return 0; -} - - -/** - * aes_128_cbc_decrypt - AES-128 CBC decryption - * @key: Decryption key - * @iv: Decryption IV for CBC mode (16 bytes) - * @data: Data to decrypt in-place - * @data_len: Length of data in bytes (must be divisible by 16) - * Returns: 0 on success, -1 on failure - */ -int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) -{ - void *ctx; - u8 cbc[BLOCK_SIZE], tmp[BLOCK_SIZE]; - u8 *pos = data; - int i, j, blocks; - - ctx = aes_decrypt_init(key, 16); - if (ctx == NULL) - return -1; - memcpy(cbc, iv, BLOCK_SIZE); - - blocks = data_len / BLOCK_SIZE; - for (i = 0; i < blocks; i++) { - memcpy(tmp, pos, BLOCK_SIZE); - aes_decrypt(ctx, pos, pos); - for (j = 0; j < BLOCK_SIZE; j++) - pos[j] ^= cbc[j]; - memcpy(cbc, tmp, BLOCK_SIZE); - pos += BLOCK_SIZE; - } - aes_decrypt_deinit(ctx); - return 0; -} - - -#ifdef TEST_MAIN - -#ifdef __i386__ -#define rdtscll(val) \ - __asm__ __volatile__("rdtsc" : "=A" (val)) - -static void test_aes_perf(void) -{ - const int num_iters = 10; - int i; - unsigned int start, end; - u8 key[16], pt[16], ct[16]; - void *ctx; - - printf("keySetupEnc:"); - for (i = 0; i < num_iters; i++) { - rdtscll(start); - ctx = aes_encrypt_init(key, 16); - rdtscll(end); - aes_encrypt_deinit(ctx); - printf(" %d", end - start); - } - printf("\n"); - - printf("Encrypt:"); - ctx = aes_encrypt_init(key, 16); - for (i = 0; i < num_iters; i++) { - rdtscll(start); - aes_encrypt(ctx, pt, ct); - rdtscll(end); - printf(" %d", end - start); - } - aes_encrypt_deinit(ctx); - printf("\n"); -} -#endif /* __i386__ */ - - -static int test_eax(void) -{ - u8 msg[] = { 0xF7, 0xFB }; - u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B, - 0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 }; - u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84, - 0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD }; - u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA }; - u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D, - 0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79, - 0x67, 0xE5 }; - u8 data[sizeof(msg)], tag[BLOCK_SIZE]; - - memcpy(data, msg, sizeof(msg)); - if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr), - data, sizeof(data), tag)) { - printf("AES-128 EAX mode encryption failed\n"); - return 1; - } - if (memcmp(data, cipher, sizeof(data)) != 0) { - printf("AES-128 EAX mode encryption returned invalid cipher " - "text\n"); - return 1; - } - if (memcmp(tag, cipher + sizeof(data), BLOCK_SIZE) != 0) { - printf("AES-128 EAX mode encryption returned invalid tag\n"); - return 1; - } - - if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr), - data, sizeof(data), tag)) { - printf("AES-128 EAX mode decryption failed\n"); - return 1; - } - if (memcmp(data, msg, sizeof(data)) != 0) { - printf("AES-128 EAX mode decryption returned invalid plain " - "text\n"); - return 1; - } - - return 0; -} - - -static int test_cbc(void) -{ - struct cbc_test_vector { - u8 key[16]; - u8 iv[16]; - u8 plain[32]; - u8 cipher[32]; - size_t len; - } vectors[] = { - { - { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, - 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 }, - { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, - 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 }, - "Single block msg", - { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, - 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }, - 16 - }, - { - { 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0, - 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a }, - { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, - 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 }, - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }, - { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a, - 0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a, - 0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9, - 0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 }, - 32 - } - }; - int i, ret = 0; - u8 *buf; - - for (i = 0; i < sizeof(vectors) / sizeof(vectors[0]); i++) { - struct cbc_test_vector *tv = &vectors[i]; - buf = malloc(tv->len); - if (buf == NULL) { - ret++; - break; - } - memcpy(buf, tv->plain, tv->len); - aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len); - if (memcmp(buf, tv->cipher, tv->len) != 0) { - printf("AES-CBC encrypt %d failed\n", i); - ret++; - } - memcpy(buf, tv->cipher, tv->len); - aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len); - if (memcmp(buf, tv->plain, tv->len) != 0) { - printf("AES-CBC decrypt %d failed\n", i); - ret++; - } - free(buf); - } - - return ret; -} - - -/* OMAC1 AES-128 test vectors from - * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf - */ - -struct omac1_test_vector { - u8 k[16]; - u8 msg[64]; - int msg_len; - u8 tag[16]; -}; - -static struct omac1_test_vector test_vectors[] = -{ - { - { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, - 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, - { }, - 0, - { 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, - 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 } - }, - { - { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, - 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, - { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, - 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}, - 16, - { 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, - 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c } - }, - { - { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, - 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, - { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, - 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, - 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, - 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, - 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 }, - 40, - { 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, - 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 } - }, - { - { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, - 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }, - { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, - 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, - 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, - 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, - 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, - 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, - 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, - 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 }, - 64, - { 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, - 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe } - }, -}; - - -int main(int argc, char *argv[]) -{ - u8 kek[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f - }; - u8 plain[] = { - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff - }; - u8 crypt[] = { - 0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, - 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, - 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5 - }; - u8 result[24]; - int ret = 0, i; - struct omac1_test_vector *tv; - - if (aes_wrap(kek, 2, plain, result)) { - printf("AES-WRAP-128-128 reported failure\n"); - ret++; - } - if (memcmp(result, crypt, 24) != 0) { - printf("AES-WRAP-128-128 failed\n"); - ret++; - } - if (aes_unwrap(kek, 2, crypt, result)) { - printf("AES-UNWRAP-128-128 reported failure\n"); - ret++; - } - if (memcmp(result, plain, 16) != 0) { - int i; - printf("AES-UNWRAP-128-128 failed\n"); - ret++; - for (i = 0; i < 16; i++) - printf(" %02x", result[i]); - printf("\n"); - } - -#ifdef __i386__ - test_aes_perf(); -#endif /* __i386__ */ - - for (i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); i++) { - tv = &test_vectors[i]; - omac1_aes_128(tv->k, tv->msg, tv->msg_len, result); - if (memcmp(result, tv->tag, 16) != 0) { - printf("OMAC1-AES-128 test vector %d failed\n", i); - ret++; - } - } - - ret += test_eax(); - - ret += test_cbc(); - - if (ret) - printf("FAILED!\n"); - - return ret; -} -#endif /* TEST_MAIN */ diff --git a/contrib/hostapd-0.4.9/aes_wrap.h b/contrib/hostapd-0.4.9/aes_wrap.h deleted file mode 100644 index cb1a539677..0000000000 --- a/contrib/hostapd-0.4.9/aes_wrap.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * AES-based functions - * - * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * - One-Key CBC MAC (OMAC1) hash with AES-128 - * - AES-128 CTR mode encryption - * - AES-128 EAX mode encryption/decryption - * - AES-128 CBC - * - * Copyright (c) 2003-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef AES_WRAP_H -#define AES_WRAP_H - -int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); -int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); -int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac); -int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); -int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, - u8 *data, size_t data_len); -int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, - const u8 *hdr, size_t hdr_len, - u8 *data, size_t data_len, u8 *tag); -int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, - const u8 *hdr, size_t hdr_len, - u8 *data, size_t data_len, const u8 *tag); -int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, - size_t data_len); -int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, - size_t data_len); - -#endif /* AES_WRAP_H */ diff --git a/contrib/hostapd-0.4.9/ap.h b/contrib/hostapd-0.4.9/ap.h deleted file mode 100644 index e874ffd2b2..0000000000 --- a/contrib/hostapd-0.4.9/ap.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef AP_H -#define AP_H - -/* STA flags */ -#define WLAN_STA_AUTH BIT(0) -#define WLAN_STA_ASSOC BIT(1) -#define WLAN_STA_PS BIT(2) -#define WLAN_STA_TIM BIT(3) -#define WLAN_STA_PERM BIT(4) -#define WLAN_STA_AUTHORIZED BIT(5) -#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ -#define WLAN_STA_PREAUTH BIT(7) - -#define WLAN_RATE_1M BIT(0) -#define WLAN_RATE_2M BIT(1) -#define WLAN_RATE_5M5 BIT(2) -#define WLAN_RATE_11M BIT(3) -#define WLAN_RATE_COUNT 4 - -/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8, - * but some pre-standard IEEE 802.11g products use longer elements. */ -#define WLAN_SUPP_RATES_MAX 32 - - -struct sta_info { - struct sta_info *next; /* next entry in sta list */ - struct sta_info *hnext; /* next entry in hash table list */ - u8 addr[6]; - u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ - u32 flags; - u16 capability; - u16 listen_interval; /* or beacon_int for APs */ - u8 supported_rates[WLAN_SUPP_RATES_MAX]; - u8 tx_supp_rates; - - enum { - STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE - } timeout_next; - - /* IEEE 802.1X related data */ - struct eapol_state_machine *eapol_sm; - - /* IEEE 802.11f (IAPP) related data */ - struct ieee80211_mgmt *last_assoc_req; - - u32 acct_session_id_hi; - u32 acct_session_id_lo; - time_t acct_session_start; - int acct_session_started; - int acct_terminate_cause; /* Acct-Terminate-Cause */ - int acct_interim_interval; /* Acct-Interim-Interval */ - - unsigned long last_rx_bytes; - unsigned long last_tx_bytes; - u32 acct_input_gigawords; /* Acct-Input-Gigawords */ - u32 acct_output_gigawords; /* Acct-Output-Gigawords */ - - u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ - - int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ - u8 *wpa_ie; - size_t wpa_ie_len; - struct wpa_state_machine *wpa_sm; - enum { - WPA_VERSION_NO_WPA = 0 /* WPA not used */, - WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, - WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ - } wpa; - int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ - struct rsn_pmksa_cache *pmksa; - struct rsn_preauth_interface *preauth_iface; - u8 req_replay_counter[8 /* WPA_REPLAY_COUNTER_LEN */]; - int req_replay_counter_used; - u32 dot11RSNAStatsTKIPLocalMICFailures; - u32 dot11RSNAStatsTKIPRemoteMICFailures; -}; - - -#define MAX_STA_COUNT 1024 - -/* Maximum number of AIDs to use for STAs; must be 2007 or lower - * (8802.11 limitation) */ -#define MAX_AID_TABLE_SIZE 128 - -#define STA_HASH_SIZE 256 -#define STA_HASH(sta) (sta[5]) - - -/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has - * passed since last received frame from the station, a nullfunc data frame is - * sent to the station. If this frame is not acknowledged and no other frames - * have been received, the station will be disassociated after - * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated - * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ -#define AP_MAX_INACTIVITY (5 * 60) -#define AP_DISASSOC_DELAY (1) -#define AP_DEAUTH_DELAY (1) - -#endif /* AP_H */ diff --git a/contrib/hostapd-0.4.9/common.c b/contrib/hostapd-0.4.9/common.c deleted file mode 100644 index 4b756d8f92..0000000000 --- a/contrib/hostapd-0.4.9/common.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * wpa_supplicant/hostapd / common helper functions, etc. - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_NATIVE_WINDOWS -#include -#include -#endif /* CONFIG_NATIVE_WINDOWS */ - -#include "common.h" - - -int wpa_debug_level = MSG_INFO; -int wpa_debug_show_keys = 0; -int wpa_debug_timestamp = 0; - - -int hostapd_get_rand(u8 *buf, size_t len) -{ -#ifdef CONFIG_NATIVE_WINDOWS - HCRYPTPROV prov; - BOOL ret; - - if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) - return -1; - - ret = CryptGenRandom(prov, len, buf); - CryptReleaseContext(prov, 0); - - return ret ? 0 : -1; -#else /* CONFIG_NATIVE_WINDOWS */ - FILE *f; - size_t rc; - - f = fopen("/dev/urandom", "r"); - if (f == NULL) { - printf("Could not open /dev/urandom.\n"); - return -1; - } - - rc = fread(buf, 1, len, f); - fclose(f); - - return rc != len ? -1 : 0; -#endif /* CONFIG_NATIVE_WINDOWS */ -} - - -void hostapd_hexdump(const char *title, const u8 *buf, size_t len) -{ - size_t i; - printf("%s - hexdump(len=%lu):", title, (unsigned long) len); - for (i = 0; i < len; i++) - printf(" %02x", buf[i]); - printf("\n"); -} - - -static int hex2num(char c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - return -1; -} - - -static int hex2byte(const char *hex) -{ - int a, b; - a = hex2num(*hex++); - if (a < 0) - return -1; - b = hex2num(*hex++); - if (b < 0) - return -1; - return (a << 4) | b; -} - - -/** - * hwaddr_aton - Convert ASCII string to MAC address - * @txt: MAC address as a string (e.g., "00:11:22:33:44:55") - * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) - * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) - */ -int hwaddr_aton(const char *txt, u8 *addr) -{ - int i; - - for (i = 0; i < 6; i++) { - int a, b; - - a = hex2num(*txt++); - if (a < 0) - return -1; - b = hex2num(*txt++); - if (b < 0) - return -1; - *addr++ = (a << 4) | b; - if (i < 5 && *txt++ != ':') - return -1; - } - - return 0; -} - - -/** - * hexstr2bin - Convert ASCII hex string into binary data - * @hex: ASCII hex string (e.g., "01ab") - * @buf: Buffer for the binary data - * @len: Length of the text to convert in bytes (of buf); hex will be double - * this size - * Returns: 0 on success, -1 on failure (invalid hex string) - */ -int hexstr2bin(const char *hex, u8 *buf, size_t len) -{ - int i, a; - const char *ipos = hex; - u8 *opos = buf; - - for (i = 0; i < len; i++) { - a = hex2byte(ipos); - if (a < 0) - return -1; - *opos++ = a; - ipos += 2; - } - return 0; -} - - -char * rel2abs_path(const char *rel_path) -{ - char *buf = NULL, *cwd, *ret; - size_t len = 128, cwd_len, rel_len, ret_len; - - if (rel_path[0] == '/') - return strdup(rel_path); - - for (;;) { - buf = malloc(len); - if (buf == NULL) - return NULL; - cwd = getcwd(buf, len); - if (cwd == NULL) { - free(buf); - if (errno != ERANGE) { - return NULL; - } - len *= 2; - } else { - break; - } - } - - cwd_len = strlen(cwd); - rel_len = strlen(rel_path); - ret_len = cwd_len + 1 + rel_len + 1; - ret = malloc(ret_len); - if (ret) { - memcpy(ret, cwd, cwd_len); - ret[cwd_len] = '/'; - memcpy(ret + cwd_len + 1, rel_path, rel_len); - ret[ret_len - 1] = '\0'; - } - free(buf); - return ret; -} - - -/** - * inc_byte_array - Increment arbitrary length byte array by one - * @counter: Pointer to byte array - * @len: Length of the counter in bytes - * - * This function increments the last byte of the counter by one and continues - * rolling over to more significant bytes if the byte was incremented from - * 0xff to 0x00. - */ -void inc_byte_array(u8 *counter, size_t len) -{ - int pos = len - 1; - while (pos >= 0) { - counter[pos]++; - if (counter[pos] != 0) - break; - pos--; - } -} - - -void print_char(char c) -{ - if (c >= 32 && c < 127) - printf("%c", c); - else - printf("<%02x>", c); -} - - -void fprint_char(FILE *f, char c) -{ - if (c >= 32 && c < 127) - fprintf(f, "%c", c); - else - fprintf(f, "<%02x>", c); -} - - -#ifndef CONFIG_NO_STDOUT_DEBUG - -void wpa_debug_print_timestamp(void) -{ - struct timeval tv; - char buf[16]; - - if (!wpa_debug_timestamp) - return; - - gettimeofday(&tv, NULL); - if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", - localtime((const time_t *) &tv.tv_sec)) <= 0) { - snprintf(buf, sizeof(buf), "%u", (int) tv.tv_sec); - } - printf("%s.%06u: ", buf, (unsigned int) tv.tv_usec); -} - - -/** - * wpa_printf - conditional printf - * @level: priority level (MSG_*) of the message - * @fmt: printf format string, followed by optional arguments - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. - * - * Note: New line '\n' is added to the end of the text when printing to stdout. - */ -void wpa_printf(int level, char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - if (level >= wpa_debug_level) { - wpa_debug_print_timestamp(); - vprintf(fmt, ap); - printf("\n"); - } - va_end(ap); -} - - -static void _wpa_hexdump(int level, const char *title, const u8 *buf, - size_t len, int show) -{ - size_t i; - if (level < wpa_debug_level) - return; - wpa_debug_print_timestamp(); - printf("%s - hexdump(len=%lu):", title, (unsigned long) len); - if (buf == NULL) { - printf(" [NULL]"); - } else if (show) { - for (i = 0; i < len; i++) - printf(" %02x", buf[i]); - } else { - printf(" [REMOVED]"); - } - printf("\n"); -} - -void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len) -{ - _wpa_hexdump(level, title, buf, len, 1); -} - - -void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len) -{ - _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys); -} - - -static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, - size_t len, int show) -{ - int i, llen; - const u8 *pos = buf; - const int line_len = 16; - - if (level < wpa_debug_level) - return; - wpa_debug_print_timestamp(); - if (!show) { - printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n", - title, (unsigned long) len); - return; - } - if (buf == NULL) { - printf("%s - hexdump_ascii(len=%lu): [NULL]\n", - title, (unsigned long) len); - return; - } - printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); - while (len) { - llen = len > line_len ? line_len : len; - printf(" "); - for (i = 0; i < llen; i++) - printf(" %02x", pos[i]); - for (i = llen; i < line_len; i++) - printf(" "); - printf(" "); - for (i = 0; i < llen; i++) { - if (isprint(pos[i])) - printf("%c", pos[i]); - else - printf("_"); - } - for (i = llen; i < line_len; i++) - printf(" "); - printf("\n"); - pos += llen; - len -= llen; - } -} - - -void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) -{ - _wpa_hexdump_ascii(level, title, buf, len, 1); -} - - -void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, - size_t len) -{ - _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); -} - -#endif /* CONFIG_NO_STDOUT_DEBUG */ - - -#ifdef CONFIG_NATIVE_WINDOWS - -#define EPOCHFILETIME (116444736000000000ULL) - -int gettimeofday(struct timeval *tv, struct timezone *tz) -{ - FILETIME ft; - LARGE_INTEGER li; - ULONGLONG t; - - GetSystemTimeAsFileTime(&ft); - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - t = (li.QuadPart - EPOCHFILETIME) / 10; - tv->tv_sec = (long) (t / 1000000); - tv->tv_usec = (long) (t % 1000000); - - return 0; -} -#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd-0.4.9/common.h b/contrib/hostapd-0.4.9/common.h deleted file mode 100644 index 2ce8a5acfc..0000000000 --- a/contrib/hostapd-0.4.9/common.h +++ /dev/null @@ -1,293 +0,0 @@ -/* - * wpa_supplicant/hostapd / common helper functions, etc. - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef COMMON_H -#define COMMON_H - -#ifdef __linux__ -#include -#include -#endif /* __linux__ */ - -#if defined(__FreeBSD__) || defined(__NetBSD__) -#include -#include -#define __BYTE_ORDER _BYTE_ORDER -#define __LITTLE_ENDIAN _LITTLE_ENDIAN -#define __BIG_ENDIAN _BIG_ENDIAN -#define bswap_16 bswap16 -#define bswap_32 bswap32 -#define bswap_64 bswap64 -#endif /* defined(__FreeBSD__) || defined(__NetBSD__) */ - -#ifdef CONFIG_NATIVE_WINDOWS -#include - -static inline int daemon(int nochdir, int noclose) -{ - printf("Windows - daemon() not supported yet\n"); - return -1; -} - -static inline void sleep(int seconds) -{ - Sleep(seconds * 1000); -} - -static inline void usleep(unsigned long usec) -{ - Sleep(usec / 1000); -} - -#ifndef timersub -#define timersub(a, b, res) do { \ - (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ - if ((res)->tv_usec < 0) { \ - (res)->tv_sec--; \ - (res)->tv_usec += 1000000; \ - } \ -} while (0) -#endif - -struct timezone { - int tz_minuteswest; - int tz_dsttime; -}; - -int gettimeofday(struct timeval *tv, struct timezone *tz); - -static inline long int random(void) -{ - return rand(); -} - -typedef int gid_t; -typedef int socklen_t; - -#ifndef MSG_DONTWAIT -#define MSG_DONTWAIT 0 /* not supported */ -#endif - -#endif /* CONFIG_NATIVE_WINDOWS */ - -#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) - -static inline unsigned short wpa_swap_16(unsigned short v) -{ - return ((v & 0xff) << 8) | (v >> 8); -} - -static inline unsigned int wpa_swap_32(unsigned int v) -{ - return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | - ((v & 0xff0000) >> 8) | (v >> 24); -} - -#define le_to_host16(n) (n) -#define host_to_le16(n) (n) -#define be_to_host16(n) wpa_swap_16(n) -#define host_to_be16(n) wpa_swap_16(n) -#define le_to_host32(n) (n) -#define be_to_host32(n) wpa_swap_32(n) -#define host_to_be32(n) wpa_swap_32(n) - -#else /* __CYGWIN__ */ - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define le_to_host16(n) (n) -#define host_to_le16(n) (n) -#define be_to_host16(n) bswap_16(n) -#define host_to_be16(n) bswap_16(n) -#define le_to_host32(n) (n) -#define be_to_host32(n) bswap_32(n) -#define host_to_be32(n) bswap_32(n) -#elif __BYTE_ORDER == __BIG_ENDIAN -#define le_to_host16(n) bswap_16(n) -#define host_to_le16(n) bswap_16(n) -#define be_to_host16(n) (n) -#define host_to_be16(n) (n) -#define le_to_host32(n) bswap_32(n) -#define be_to_host32(n) (n) -#define host_to_be32(n) (n) -#ifndef WORDS_BIGENDIAN -#define WORDS_BIGENDIAN -#endif -#else -#error Could not determine CPU byte order -#endif - -#endif /* __CYGWIN__ */ - -/* Macros for handling unaligned 16-bit variables */ -#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) -#define WPA_PUT_BE16(a, val) \ - do { \ - (a)[0] = ((u16) (val)) >> 8; \ - (a)[1] = ((u16) (val)) & 0xff; \ - } while (0) - -#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) -#define WPA_PUT_LE16(a, val) \ - do { \ - (a)[1] = ((u16) (val)) >> 8; \ - (a)[0] = ((u16) (val)) & 0xff; \ - } while (0) - -#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ - (((u32) (a)[2]) << 8) | ((u32) (a)[3])) - - -#ifndef ETH_ALEN -#define ETH_ALEN 6 -#endif - -#include -typedef uint64_t u64; -typedef uint32_t u32; -typedef uint16_t u16; -typedef uint8_t u8; -typedef int64_t s64; -typedef int32_t s32; -typedef int16_t s16; -typedef int8_t s8; - -int hostapd_get_rand(u8 *buf, size_t len); -void hostapd_hexdump(const char *title, const u8 *buf, size_t len); -int hwaddr_aton(const char *txt, u8 *addr); -int hexstr2bin(const char *hex, u8 *buf, size_t len); -char * rel2abs_path(const char *rel_path); -void inc_byte_array(u8 *counter, size_t len); -void print_char(char c); -void fprint_char(FILE *f, char c); - - -/* Debugging function - conditional printf and hex dump. Driver wrappers can - * use these for debugging purposes. */ - -enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; - -#ifdef CONFIG_NO_STDOUT_DEBUG - -#define wpa_debug_print_timestamp() do { } while (0) -#define wpa_printf(args...) do { } while (0) -#define wpa_hexdump(args...) do { } while (0) -#define wpa_hexdump_key(args...) do { } while (0) -#define wpa_hexdump_ascii(args...) do { } while (0) -#define wpa_hexdump_ascii_key(args...) do { } while (0) - -#else /* CONFIG_NO_STDOUT_DEBUG */ - -/** - * wpa_debug_printf_timestamp - Print timestamp for debug output - * - * This function prints a timestamp in . - * format if debug output has been configured to include timestamps in debug - * messages. - */ -void wpa_debug_print_timestamp(void); - -/** - * wpa_printf - conditional printf - * @level: priority level (MSG_*) of the message - * @fmt: printf format string, followed by optional arguments - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. - * - * Note: New line '\n' is added to the end of the text when printing to stdout. - */ -void wpa_printf(int level, char *fmt, ...) -__attribute__ ((format (printf, 2, 3))); - -/** - * wpa_hexdump - conditional hex dump - * @level: priority level (MSG_*) of the message - * @title: title of for the message - * @buf: data buffer to be dumped - * @len: length of the buf - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of buf is printed out has hex dump. - */ -void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); - -/** - * wpa_hexdump_key - conditional hex dump, hide keys - * @level: priority level (MSG_*) of the message - * @title: title of for the message - * @buf: data buffer to be dumped - * @len: length of the buf - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of buf is printed out has hex dump. This works - * like wpa_hexdump(), but by default, does not include secret keys (passwords, - * etc.) in debug output. - */ -void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); - -/** - * wpa_hexdump_ascii - conditional hex dump - * @level: priority level (MSG_*) of the message - * @title: title of for the message - * @buf: data buffer to be dumped - * @len: length of the buf - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of buf is printed out has hex dump with both - * the hex numbers and ASCII characters (for printable range) are shown. 16 - * bytes per line will be shown. - */ -void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, - size_t len); - -/** - * wpa_hexdump_ascii_key - conditional hex dump, hide keys - * @level: priority level (MSG_*) of the message - * @title: title of for the message - * @buf: data buffer to be dumped - * @len: length of the buf - * - * This function is used to print conditional debugging and error messages. The - * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of buf is printed out has hex dump with both - * the hex numbers and ASCII characters (for printable range) are shown. 16 - * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by - * default, does not include secret keys (passwords, etc.) in debug output. - */ -void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, - size_t len); - -#endif /* CONFIG_NO_STDOUT_DEBUG */ - - -#ifdef EAPOL_TEST -#define WPA_ASSERT(a) \ - do { \ - if (!(a)) { \ - printf("WPA_ASSERT FAILED '" #a "' " \ - "%s %s:%d\n", \ - __FUNCTION__, __FILE__, __LINE__); \ - exit(1); \ - } \ - } while (0) -#else -#define WPA_ASSERT(a) do { } while (0) -#endif - -#endif /* COMMON_H */ diff --git a/contrib/hostapd-0.4.9/config.c b/contrib/hostapd-0.4.9/config.c deleted file mode 100644 index 1c4f84e46a..0000000000 --- a/contrib/hostapd-0.4.9/config.c +++ /dev/null @@ -1,1230 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / Configuration file - * Copyright (c) 2003-2006, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hostapd.h" -#include "driver.h" -#include "sha1.h" -#include "eap.h" -#include "radius_client.h" -#include "ieee802_1x.h" - - -static struct hostapd_config *hostapd_config_defaults(void) -{ - struct hostapd_config *conf; - - conf = malloc(sizeof(*conf) + sizeof(struct hostapd_radius_servers)); - if (conf == NULL) { - printf("Failed to allocate memory for configuration data.\n"); - return NULL; - } - memset(conf, 0, sizeof(*conf) + sizeof(struct hostapd_radius_servers)); - conf->radius = (struct hostapd_radius_servers *) (conf + 1); - - /* set default driver based on configuration */ - conf->driver = driver_lookup("default"); - if (conf->driver == NULL) { - printf("No default driver registered!\n"); - free(conf); - return NULL; - } - - conf->wep_rekeying_period = 300; - conf->eap_reauth_period = 3600; - - conf->logger_syslog_level = HOSTAPD_LEVEL_INFO; - conf->logger_stdout_level = HOSTAPD_LEVEL_INFO; - conf->logger_syslog = (unsigned int) -1; - conf->logger_stdout = (unsigned int) -1; - - conf->auth_algs = HOSTAPD_AUTH_OPEN | HOSTAPD_AUTH_SHARED_KEY; - conf->eapol_version = EAPOL_VERSION; - - conf->wpa_group_rekey = 600; - conf->wpa_gmk_rekey = 86400; - conf->wpa_key_mgmt = WPA_KEY_MGMT_PSK; - conf->wpa_pairwise = WPA_CIPHER_TKIP; - conf->wpa_group = WPA_CIPHER_TKIP; - - conf->radius_server_auth_port = 1812; - - return conf; -} - - -static int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr) -{ - if (inet_aton(txt, &addr->u.v4)) { - addr->af = AF_INET; - return 0; - } - -#ifdef CONFIG_IPV6 - if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) { - addr->af = AF_INET6; - return 0; - } -#endif /* CONFIG_IPV6 */ - - return -1; -} - - -static int mac_comp(const void *a, const void *b) -{ - return memcmp(a, b, sizeof(macaddr)); -} - - -static int hostapd_config_read_maclist(const char *fname, macaddr **acl, - int *num) -{ - FILE *f; - char buf[128], *pos; - int line = 0; - u8 addr[ETH_ALEN]; - macaddr *newacl; - - if (!fname) - return 0; - - f = fopen(fname, "r"); - if (!f) { - printf("MAC list file '%s' not found.\n", fname); - return -1; - } - - while (fgets(buf, sizeof(buf), f)) { - line++; - - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - - if (hwaddr_aton(buf, addr)) { - printf("Invalid MAC address '%s' at line %d in '%s'\n", - buf, line, fname); - fclose(f); - return -1; - } - - newacl = (macaddr *) realloc(*acl, (*num + 1) * ETH_ALEN); - if (newacl == NULL) { - printf("MAC list reallocation failed\n"); - fclose(f); - return -1; - } - - *acl = newacl; - memcpy((*acl)[*num], addr, ETH_ALEN); - (*num)++; - } - - fclose(f); - - qsort(*acl, *num, sizeof(macaddr), mac_comp); - - return 0; -} - - -static int hostapd_config_read_wpa_psk(const char *fname, - struct hostapd_config *conf) -{ - FILE *f; - char buf[128], *pos; - int line = 0, ret = 0, len, ok; - u8 addr[ETH_ALEN]; - struct hostapd_wpa_psk *psk; - - if (!fname) - return 0; - - f = fopen(fname, "r"); - if (!f) { - printf("WPA PSK file '%s' not found.\n", fname); - return -1; - } - - while (fgets(buf, sizeof(buf), f)) { - line++; - - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - - if (hwaddr_aton(buf, addr)) { - printf("Invalid MAC address '%s' on line %d in '%s'\n", - buf, line, fname); - ret = -1; - break; - } - - psk = malloc(sizeof(*psk)); - if (psk == NULL) { - printf("WPA PSK allocation failed\n"); - ret = -1; - break; - } - memset(psk, 0, sizeof(*psk)); - if (memcmp(addr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) - psk->group = 1; - else - memcpy(psk->addr, addr, ETH_ALEN); - - pos = buf + 17; - if (pos == '\0') { - printf("No PSK on line %d in '%s'\n", line, fname); - free(psk); - ret = -1; - break; - } - pos++; - - ok = 0; - len = strlen(pos); - if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0) - ok = 1; - else if (len >= 8 && len < 64) { - pbkdf2_sha1(pos, conf->ssid, conf->ssid_len, - 4096, psk->psk, PMK_LEN); - ok = 1; - } - if (!ok) { - printf("Invalid PSK '%s' on line %d in '%s'\n", - pos, line, fname); - free(psk); - ret = -1; - break; - } - - psk->next = conf->wpa_psk; - conf->wpa_psk = psk; - } - - fclose(f); - - return ret; -} - - -int hostapd_setup_wpa_psk(struct hostapd_config *conf) -{ - if (conf->wpa_passphrase != NULL) { - if (conf->wpa_psk != NULL) { - printf("Warning: both WPA PSK and passphrase set. " - "Using passphrase.\n"); - free(conf->wpa_psk); - } - conf->wpa_psk = malloc(sizeof(struct hostapd_wpa_psk)); - if (conf->wpa_psk == NULL) { - printf("Unable to alloc space for PSK\n"); - return -1; - } - wpa_hexdump_ascii(MSG_DEBUG, "SSID", - (u8 *) conf->ssid, conf->ssid_len); - wpa_hexdump_ascii(MSG_DEBUG, "PSK (ASCII passphrase)", - (u8 *) conf->wpa_passphrase, - strlen(conf->wpa_passphrase)); - memset(conf->wpa_psk, 0, sizeof(struct hostapd_wpa_psk)); - pbkdf2_sha1(conf->wpa_passphrase, - conf->ssid, conf->ssid_len, - 4096, conf->wpa_psk->psk, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "PSK (from passphrase)", - conf->wpa_psk->psk, PMK_LEN); - conf->wpa_psk->group = 1; - - memset(conf->wpa_passphrase, 0, strlen(conf->wpa_passphrase)); - free(conf->wpa_passphrase); - conf->wpa_passphrase = 0; - } - - if (conf->wpa_psk_file) { - if (hostapd_config_read_wpa_psk(conf->wpa_psk_file, conf)) - return -1; - free(conf->wpa_psk_file); - conf->wpa_psk_file = NULL; - } - - return 0; -} - - -#ifdef EAP_SERVER -static int hostapd_config_read_eap_user(const char *fname, - struct hostapd_config *conf) -{ - FILE *f; - char buf[512], *pos, *start, *pos2; - int line = 0, ret = 0, num_methods; - struct hostapd_eap_user *user, *tail = NULL; - - if (!fname) - return 0; - - f = fopen(fname, "r"); - if (!f) { - printf("EAP user file '%s' not found.\n", fname); - return -1; - } - - /* Lines: "user" METHOD,METHOD2 "password" (password optional) */ - while (fgets(buf, sizeof(buf), f)) { - line++; - - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - - user = NULL; - - if (buf[0] != '"' && buf[0] != '*') { - printf("Invalid EAP identity (no \" in start) on " - "line %d in '%s'\n", line, fname); - goto failed; - } - - user = malloc(sizeof(*user)); - if (user == NULL) { - printf("EAP user allocation failed\n"); - goto failed; - } - memset(user, 0, sizeof(*user)); - user->force_version = -1; - - if (buf[0] == '*') { - pos = buf; - } else { - pos = buf + 1; - start = pos; - while (*pos != '"' && *pos != '\0') - pos++; - if (*pos == '\0') { - printf("Invalid EAP identity (no \" in end) on" - " line %d in '%s'\n", line, fname); - goto failed; - } - - user->identity = malloc(pos - start); - if (user->identity == NULL) { - printf("Failed to allocate memory for EAP " - "identity\n"); - goto failed; - } - memcpy(user->identity, start, pos - start); - user->identity_len = pos - start; - } - pos++; - while (*pos == ' ' || *pos == '\t') - pos++; - - if (*pos == '\0') { - printf("No EAP method on line %d in '%s'\n", - line, fname); - goto failed; - } - - start = pos; - while (*pos != ' ' && *pos != '\t' && *pos != '\0') - pos++; - if (*pos == '\0') { - pos = NULL; - } else { - *pos = '\0'; - pos++; - } - num_methods = 0; - while (*start) { - char *pos2 = strchr(start, ','); - if (pos2) { - *pos2++ = '\0'; - } - user->methods[num_methods] = eap_get_type(start); - if (user->methods[num_methods] == EAP_TYPE_NONE) { - printf("Unsupported EAP type '%s' on line %d " - "in '%s'\n", start, line, fname); - goto failed; - } - - num_methods++; - if (num_methods >= EAP_USER_MAX_METHODS) - break; - if (pos2 == NULL) - break; - start = pos2; - } - if (num_methods == 0) { - printf("No EAP types configured on line %d in '%s'\n", - line, fname); - goto failed; - } - - if (pos == NULL) - goto done; - - while (*pos == ' ' || *pos == '\t') - pos++; - if (*pos == '\0') - goto done; - - if (strncmp(pos, "[ver=0]", 7) == 0) { - user->force_version = 0; - goto done; - } - - if (strncmp(pos, "[ver=1]", 7) == 0) { - user->force_version = 1; - goto done; - } - - if (strncmp(pos, "[2]", 3) == 0) { - user->phase2 = 1; - goto done; - } - - if (*pos == '"') { - pos++; - start = pos; - while (*pos != '"' && *pos != '\0') - pos++; - if (*pos == '\0') { - printf("Invalid EAP password (no \" in end) " - "on line %d in '%s'\n", line, fname); - goto failed; - } - - user->password = malloc(pos - start); - if (user->password == NULL) { - printf("Failed to allocate memory for EAP " - "password\n"); - goto failed; - } - memcpy(user->password, start, pos - start); - user->password_len = pos - start; - - pos++; - } else { - pos2 = pos; - while (*pos2 != '\0' && *pos2 != ' ' && - *pos2 != '\t' && *pos2 != '#') - pos2++; - if ((pos2 - pos) & 1) { - printf("Invalid hex password on line %d in " - "'%s'\n", line, fname); - goto failed; - } - user->password = malloc((pos2 - pos) / 2); - if (user->password == NULL) { - printf("Failed to allocate memory for EAP " - "password\n"); - goto failed; - } - if (hexstr2bin(pos, user->password, - (pos2 - pos) / 2) < 0) { - printf("Invalid hex password on line %d in " - "'%s'\n", line, fname); - goto failed; - } - user->password_len = (pos2 - pos) / 2; - pos = pos2; - } - - while (*pos == ' ' || *pos == '\t') - pos++; - if (strncmp(pos, "[2]", 3) == 0) { - user->phase2 = 1; - } - - done: - if (tail == NULL) { - tail = conf->eap_user = user; - } else { - tail->next = user; - tail = user; - } - continue; - - failed: - if (user) { - free(user->identity); - free(user); - } - ret = -1; - break; - } - - fclose(f); - - return ret; -} -#endif /* EAP_SERVER */ - - -static int -hostapd_config_read_radius_addr(struct hostapd_radius_server **server, - int *num_server, const char *val, int def_port, - struct hostapd_radius_server **curr_serv) -{ - struct hostapd_radius_server *nserv; - int ret; - static int server_index = 1; - - nserv = realloc(*server, (*num_server + 1) * sizeof(*nserv)); - if (nserv == NULL) - return -1; - - *server = nserv; - nserv = &nserv[*num_server]; - (*num_server)++; - (*curr_serv) = nserv; - - memset(nserv, 0, sizeof(*nserv)); - nserv->port = def_port; - ret = hostapd_parse_ip_addr(val, &nserv->addr); - nserv->index = server_index++; - - return ret; -} - - -static int hostapd_config_parse_key_mgmt(int line, const char *value) -{ - int val = 0, last; - char *start, *end, *buf; - - buf = strdup(value); - if (buf == NULL) - return -1; - start = buf; - - while (start != '\0') { - while (*start == ' ' || *start == '\t') - start++; - if (*start == '\0') - break; - end = start; - while (*end != ' ' && *end != '\t' && *end != '\0') - end++; - last = *end == '\0'; - *end = '\0'; - if (strcmp(start, "WPA-PSK") == 0) - val |= WPA_KEY_MGMT_PSK; - else if (strcmp(start, "WPA-EAP") == 0) - val |= WPA_KEY_MGMT_IEEE8021X; - else { - printf("Line %d: invalid key_mgmt '%s'", line, start); - free(buf); - return -1; - } - - if (last) - break; - start = end + 1; - } - - free(buf); - if (val == 0) { - printf("Line %d: no key_mgmt values configured.", line); - return -1; - } - - return val; -} - - -static int hostapd_config_parse_cipher(int line, const char *value) -{ - int val = 0, last; - char *start, *end, *buf; - - buf = strdup(value); - if (buf == NULL) - return -1; - start = buf; - - while (start != '\0') { - while (*start == ' ' || *start == '\t') - start++; - if (*start == '\0') - break; - end = start; - while (*end != ' ' && *end != '\t' && *end != '\0') - end++; - last = *end == '\0'; - *end = '\0'; - if (strcmp(start, "CCMP") == 0) - val |= WPA_CIPHER_CCMP; - else if (strcmp(start, "TKIP") == 0) - val |= WPA_CIPHER_TKIP; - else if (strcmp(start, "WEP104") == 0) - val |= WPA_CIPHER_WEP104; - else if (strcmp(start, "WEP40") == 0) - val |= WPA_CIPHER_WEP40; - else if (strcmp(start, "NONE") == 0) - val |= WPA_CIPHER_NONE; - else { - printf("Line %d: invalid cipher '%s'.", line, start); - free(buf); - return -1; - } - - if (last) - break; - start = end + 1; - } - free(buf); - - if (val == 0) { - printf("Line %d: no cipher values configured.", line); - return -1; - } - return val; -} - - -static int hostapd_config_check(struct hostapd_config *conf) -{ - if (conf->ieee802_1x && !conf->eap_server && - !conf->radius->auth_servers) { - printf("Invalid IEEE 802.1X configuration (no EAP " - "authenticator configured).\n"); - return -1; - } - - if (conf->wpa && (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && - conf->wpa_psk == NULL && conf->wpa_passphrase == NULL && - conf->wpa_psk_file == NULL) { - printf("WPA-PSK enabled, but PSK or passphrase is not " - "configured.\n"); - return -1; - } - - return 0; -} - - -struct hostapd_config * hostapd_config_read(const char *fname) -{ - struct hostapd_config *conf; - FILE *f; - char buf[256], *pos; - int line = 0; - int errors = 0; - char *accept_mac_file = NULL, *deny_mac_file = NULL; -#ifdef EAP_SERVER - char *eap_user_file = NULL; -#endif /* EAP_SERVER */ - - f = fopen(fname, "r"); - if (f == NULL) { - printf("Could not open configuration file '%s' for reading.\n", - fname); - return NULL; - } - - conf = hostapd_config_defaults(); - if (conf == NULL) { - fclose(f); - return NULL; - } - - while (fgets(buf, sizeof(buf), f)) { - line++; - - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - - pos = strchr(buf, '='); - if (pos == NULL) { - printf("Line %d: invalid line '%s'\n", line, buf); - errors++; - continue; - } - *pos = '\0'; - pos++; - - if (strcmp(buf, "interface") == 0) { - snprintf(conf->iface, sizeof(conf->iface), "%s", pos); - } else if (strcmp(buf, "bridge") == 0) { - snprintf(conf->bridge, sizeof(conf->bridge), "%s", - pos); - } else if (strcmp(buf, "driver") == 0) { - conf->driver = driver_lookup(pos); - if (conf->driver == NULL) { - printf("Line %d: invalid/unknown driver " - "'%s'\n", line, pos); - errors++; - } - } else if (strcmp(buf, "debug") == 0) { - conf->debug = atoi(pos); - } else if (strcmp(buf, "logger_syslog_level") == 0) { - conf->logger_syslog_level = atoi(pos); - } else if (strcmp(buf, "logger_stdout_level") == 0) { - conf->logger_stdout_level = atoi(pos); - } else if (strcmp(buf, "logger_syslog") == 0) { - conf->logger_syslog = atoi(pos); - } else if (strcmp(buf, "logger_stdout") == 0) { - conf->logger_stdout = atoi(pos); - } else if (strcmp(buf, "dump_file") == 0) { - conf->dump_log_name = strdup(pos); - } else if (strcmp(buf, "ssid") == 0) { - conf->ssid_len = strlen(pos); - if (conf->ssid_len >= HOSTAPD_SSID_LEN || - conf->ssid_len < 1) { - printf("Line %d: invalid SSID '%s'\n", line, - pos); - errors++; - } - memcpy(conf->ssid, pos, conf->ssid_len); - conf->ssid[conf->ssid_len] = '\0'; - conf->ssid_set = 1; - } else if (strcmp(buf, "macaddr_acl") == 0) { - conf->macaddr_acl = atoi(pos); - if (conf->macaddr_acl != ACCEPT_UNLESS_DENIED && - conf->macaddr_acl != DENY_UNLESS_ACCEPTED && - conf->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { - printf("Line %d: unknown macaddr_acl %d\n", - line, conf->macaddr_acl); - } - } else if (strcmp(buf, "accept_mac_file") == 0) { - accept_mac_file = strdup(pos); - if (!accept_mac_file) { - printf("Line %d: allocation failed\n", line); - errors++; - } - } else if (strcmp(buf, "deny_mac_file") == 0) { - deny_mac_file = strdup(pos); - if (!deny_mac_file) { - printf("Line %d: allocation failed\n", line); - errors++; - } - } else if (strcmp(buf, "assoc_ap_addr") == 0) { - if (hwaddr_aton(pos, conf->assoc_ap_addr)) { - printf("Line %d: invalid MAC address '%s'\n", - line, pos); - errors++; - } - conf->assoc_ap = 1; - } else if (strcmp(buf, "ieee8021x") == 0) { - conf->ieee802_1x = atoi(pos); - } else if (strcmp(buf, "eapol_version") == 0) { - conf->eapol_version = atoi(pos); - if (conf->eapol_version < 1 || - conf->eapol_version > 2) { - printf("Line %d: invalid EAPOL " - "version (%d): '%s'.\n", - line, conf->eapol_version, pos); - errors++; - } else - wpa_printf(MSG_DEBUG, "eapol_version=%d", - conf->eapol_version); -#ifdef EAP_SERVER - } else if (strcmp(buf, "eap_authenticator") == 0) { - conf->eap_server = atoi(pos); - printf("Line %d: obsolete eap_authenticator used; " - "this has been renamed to eap_server\n", line); - } else if (strcmp(buf, "eap_server") == 0) { - conf->eap_server = atoi(pos); - } else if (strcmp(buf, "eap_user_file") == 0) { - free(eap_user_file); - eap_user_file = strdup(pos); - if (!eap_user_file) { - printf("Line %d: allocation failed\n", line); - errors++; - } - } else if (strcmp(buf, "ca_cert") == 0) { - free(conf->ca_cert); - conf->ca_cert = strdup(pos); - } else if (strcmp(buf, "server_cert") == 0) { - free(conf->server_cert); - conf->server_cert = strdup(pos); - } else if (strcmp(buf, "private_key") == 0) { - free(conf->private_key); - conf->private_key = strdup(pos); - } else if (strcmp(buf, "private_key_passwd") == 0) { - free(conf->private_key_passwd); - conf->private_key_passwd = strdup(pos); - } else if (strcmp(buf, "check_crl") == 0) { - conf->check_crl = atoi(pos); -#ifdef EAP_SIM - } else if (strcmp(buf, "eap_sim_db") == 0) { - free(conf->eap_sim_db); - conf->eap_sim_db = strdup(pos); -#endif /* EAP_SIM */ -#endif /* EAP_SERVER */ - } else if (strcmp(buf, "eap_message") == 0) { - char *term; - conf->eap_req_id_text = strdup(pos); - if (conf->eap_req_id_text == NULL) { - printf("Line %d: Failed to allocate memory " - "for eap_req_id_text\n", line); - errors++; - continue; - } - conf->eap_req_id_text_len = - strlen(conf->eap_req_id_text); - term = strstr(conf->eap_req_id_text, "\\0"); - if (term) { - *term++ = '\0'; - memmove(term, term + 1, - conf->eap_req_id_text_len - - (term - conf->eap_req_id_text) - 1); - conf->eap_req_id_text_len--; - } - } else if (strcmp(buf, "wep_key_len_broadcast") == 0) { - conf->default_wep_key_len = atoi(pos); - if (conf->default_wep_key_len > 13) { - printf("Line %d: invalid WEP key len %lu " - "(= %lu bits)\n", line, - (unsigned long) - conf->default_wep_key_len, - (unsigned long) - conf->default_wep_key_len * 8); - errors++; - } - } else if (strcmp(buf, "wep_key_len_unicast") == 0) { - conf->individual_wep_key_len = atoi(pos); - if (conf->individual_wep_key_len < 0 || - conf->individual_wep_key_len > 13) { - printf("Line %d: invalid WEP key len %d " - "(= %d bits)\n", line, - conf->individual_wep_key_len, - conf->individual_wep_key_len * 8); - errors++; - } - } else if (strcmp(buf, "wep_rekey_period") == 0) { - conf->wep_rekeying_period = atoi(pos); - if (conf->wep_rekeying_period < 0) { - printf("Line %d: invalid period %d\n", - line, conf->wep_rekeying_period); - errors++; - } - } else if (strcmp(buf, "eap_reauth_period") == 0) { - conf->eap_reauth_period = atoi(pos); - if (conf->eap_reauth_period < 0) { - printf("Line %d: invalid period %d\n", - line, conf->eap_reauth_period); - errors++; - } - } else if (strcmp(buf, "eapol_key_index_workaround") == 0) { - conf->eapol_key_index_workaround = atoi(pos); -#ifdef CONFIG_IAPP - } else if (strcmp(buf, "iapp_interface") == 0) { - conf->ieee802_11f = 1; - snprintf(conf->iapp_iface, sizeof(conf->iapp_iface), - "%s", pos); -#endif /* CONFIG_IAPP */ - } else if (strcmp(buf, "own_ip_addr") == 0) { - if (hostapd_parse_ip_addr(pos, &conf->own_ip_addr)) { - printf("Line %d: invalid IP address '%s'\n", - line, pos); - errors++; - } - } else if (strcmp(buf, "nas_identifier") == 0) { - conf->nas_identifier = strdup(pos); - } else if (strcmp(buf, "auth_server_addr") == 0) { - if (hostapd_config_read_radius_addr( - &conf->radius->auth_servers, - &conf->radius->num_auth_servers, pos, 1812, - &conf->radius->auth_server)) { - printf("Line %d: invalid IP address '%s'\n", - line, pos); - errors++; - } - } else if (conf->radius->auth_server && - strcmp(buf, "auth_server_port") == 0) { - conf->radius->auth_server->port = atoi(pos); - } else if (conf->radius->auth_server && - strcmp(buf, "auth_server_shared_secret") == 0) { - int len = strlen(pos); - if (len == 0) { - /* RFC 2865, Ch. 3 */ - printf("Line %d: empty shared secret is not " - "allowed.\n", line); - errors++; - } - conf->radius->auth_server->shared_secret = - (u8 *) strdup(pos); - conf->radius->auth_server->shared_secret_len = len; - } else if (strcmp(buf, "acct_server_addr") == 0) { - if (hostapd_config_read_radius_addr( - &conf->radius->acct_servers, - &conf->radius->num_acct_servers, pos, 1813, - &conf->radius->acct_server)) { - printf("Line %d: invalid IP address '%s'\n", - line, pos); - errors++; - } - } else if (conf->radius->acct_server && - strcmp(buf, "acct_server_port") == 0) { - conf->radius->acct_server->port = atoi(pos); - } else if (conf->radius->acct_server && - strcmp(buf, "acct_server_shared_secret") == 0) { - int len = strlen(pos); - if (len == 0) { - /* RFC 2865, Ch. 3 */ - printf("Line %d: empty shared secret is not " - "allowed.\n", line); - errors++; - } - conf->radius->acct_server->shared_secret = - (u8 *) strdup(pos); - conf->radius->acct_server->shared_secret_len = len; - } else if (strcmp(buf, "radius_retry_primary_interval") == 0) { - conf->radius->retry_primary_interval = atoi(pos); - } else if (strcmp(buf, "radius_acct_interim_interval") == 0) { - conf->radius->acct_interim_interval = atoi(pos); - } else if (strcmp(buf, "auth_algs") == 0) { - conf->auth_algs = atoi(pos); - if (conf->auth_algs == 0) { - printf("Line %d: no authentication algorithms " - "allowed\n", - line); - errors++; - } - } else if (strcmp(buf, "wpa") == 0) { - conf->wpa = atoi(pos); - } else if (strcmp(buf, "wpa_group_rekey") == 0) { - conf->wpa_group_rekey = atoi(pos); - } else if (strcmp(buf, "wpa_strict_rekey") == 0) { - conf->wpa_strict_rekey = atoi(pos); - } else if (strcmp(buf, "wpa_gmk_rekey") == 0) { - conf->wpa_gmk_rekey = atoi(pos); - } else if (strcmp(buf, "wpa_passphrase") == 0) { - int len = strlen(pos); - if (len < 8 || len > 63) { - printf("Line %d: invalid WPA passphrase length" - " %d (expected 8..63)\n", line, len); - errors++; - } else { - free(conf->wpa_passphrase); - conf->wpa_passphrase = strdup(pos); - } - } else if (strcmp(buf, "wpa_psk") == 0) { - free(conf->wpa_psk); - conf->wpa_psk = malloc(sizeof(struct hostapd_wpa_psk)); - if (conf->wpa_psk) { - memset(conf->wpa_psk, 0, - sizeof(struct hostapd_wpa_psk)); - } - if (conf->wpa_psk == NULL) - errors++; - else if (hexstr2bin(pos, conf->wpa_psk->psk, PMK_LEN) - || pos[PMK_LEN * 2] != '\0') { - printf("Line %d: Invalid PSK '%s'.\n", line, - pos); - errors++; - } else { - conf->wpa_psk->group = 1; - } - } else if (strcmp(buf, "wpa_psk_file") == 0) { - free(conf->wpa_psk_file); - conf->wpa_psk_file = strdup(pos); - if (!conf->wpa_psk_file) { - printf("Line %d: allocation failed\n", line); - errors++; - } - } else if (strcmp(buf, "wpa_key_mgmt") == 0) { - conf->wpa_key_mgmt = - hostapd_config_parse_key_mgmt(line, pos); - if (conf->wpa_key_mgmt == -1) - errors++; - } else if (strcmp(buf, "wpa_pairwise") == 0) { - conf->wpa_pairwise = - hostapd_config_parse_cipher(line, pos); - if (conf->wpa_pairwise == -1 || - conf->wpa_pairwise == 0) - errors++; - else if (conf->wpa_pairwise & - (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | - WPA_CIPHER_WEP104)) { - printf("Line %d: unsupported pairwise " - "cipher suite '%s'\n", - conf->wpa_pairwise, pos); - errors++; - } else { - if (conf->wpa_pairwise & WPA_CIPHER_TKIP) - conf->wpa_group = WPA_CIPHER_TKIP; - else - conf->wpa_group = WPA_CIPHER_CCMP; - } -#ifdef CONFIG_RSN_PREAUTH - } else if (strcmp(buf, "rsn_preauth") == 0) { - conf->rsn_preauth = atoi(pos); - } else if (strcmp(buf, "rsn_preauth_interfaces") == 0) { - conf->rsn_preauth_interfaces = strdup(pos); -#endif /* CONFIG_RSN_PREAUTH */ - } else if (strcmp(buf, "ctrl_interface") == 0) { - free(conf->ctrl_interface); - conf->ctrl_interface = strdup(pos); - } else if (strcmp(buf, "ctrl_interface_group") == 0) { - struct group *grp; - char *endp; - const char *group = pos; - - grp = getgrnam(group); - if (grp) { - conf->ctrl_interface_gid = grp->gr_gid; - conf->ctrl_interface_gid_set = 1; - wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" - " (from group name '%s')", - conf->ctrl_interface_gid, group); - continue; - } - - /* Group name not found - try to parse this as gid */ - conf->ctrl_interface_gid = strtol(group, &endp, 10); - if (*group == '\0' || *endp != '\0') { - wpa_printf(MSG_DEBUG, "Line %d: Invalid group " - "'%s'", line, group); - errors++; - continue; - } - conf->ctrl_interface_gid_set = 1; - wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", - conf->ctrl_interface_gid); -#ifdef RADIUS_SERVER - } else if (strcmp(buf, "radius_server_clients") == 0) { - free(conf->radius_server_clients); - conf->radius_server_clients = strdup(pos); - } else if (strcmp(buf, "radius_server_auth_port") == 0) { - conf->radius_server_auth_port = atoi(pos); - } else if (strcmp(buf, "radius_server_ipv6") == 0) { - conf->radius_server_ipv6 = atoi(pos); -#endif /* RADIUS_SERVER */ - } else if (strcmp(buf, "test_socket") == 0) { - free(conf->test_socket); - conf->test_socket = strdup(pos); - } else if (strcmp(buf, "use_pae_group_addr") == 0) { - conf->use_pae_group_addr = atoi(pos); - } else { - printf("Line %d: unknown configuration item '%s'\n", - line, buf); - errors++; - } - } - - fclose(f); - - if (hostapd_config_read_maclist(accept_mac_file, &conf->accept_mac, - &conf->num_accept_mac)) - errors++; - free(accept_mac_file); - if (hostapd_config_read_maclist(deny_mac_file, &conf->deny_mac, - &conf->num_deny_mac)) - errors++; - free(deny_mac_file); - -#ifdef EAP_SERVER - if (hostapd_config_read_eap_user(eap_user_file, conf)) - errors++; - free(eap_user_file); -#endif /* EAP_SERVER */ - - conf->radius->auth_server = conf->radius->auth_servers; - conf->radius->acct_server = conf->radius->acct_servers; - - if (hostapd_config_check(conf)) - errors++; - - if (errors) { - printf("%d errors found in configuration file '%s'\n", - errors, fname); - hostapd_config_free(conf); - conf = NULL; - } - - return conf; -} - - -static void hostapd_config_free_radius(struct hostapd_radius_server *servers, - int num_servers) -{ - int i; - - for (i = 0; i < num_servers; i++) { - free(servers[i].shared_secret); - } - free(servers); -} - - -static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) -{ - free(user->identity); - free(user->password); - free(user); -} - - -void hostapd_config_free(struct hostapd_config *conf) -{ - struct hostapd_wpa_psk *psk, *prev; - struct hostapd_eap_user *user, *prev_user; - - if (conf == NULL) - return; - - psk = conf->wpa_psk; - while (psk) { - prev = psk; - psk = psk->next; - free(prev); - } - - free(conf->wpa_passphrase); - free(conf->wpa_psk_file); - - user = conf->eap_user; - while (user) { - prev_user = user; - user = user->next; - hostapd_config_free_eap_user(prev_user); - } - - free(conf->dump_log_name); - free(conf->eap_req_id_text); - free(conf->accept_mac); - free(conf->deny_mac); - free(conf->nas_identifier); - hostapd_config_free_radius(conf->radius->auth_servers, - conf->radius->num_auth_servers); - hostapd_config_free_radius(conf->radius->acct_servers, - conf->radius->num_acct_servers); - free(conf->rsn_preauth_interfaces); - free(conf->ctrl_interface); - free(conf->ca_cert); - free(conf->server_cert); - free(conf->private_key); - free(conf->private_key_passwd); - free(conf->eap_sim_db); - free(conf->radius_server_clients); - free(conf->test_socket); - free(conf); -} - - -/* Perform a binary search for given MAC address from a pre-sorted list. - * Returns 1 if address is in the list or 0 if not. */ -int hostapd_maclist_found(macaddr *list, int num_entries, u8 *addr) -{ - int start, end, middle, res; - - start = 0; - end = num_entries - 1; - - while (start <= end) { - middle = (start + end) / 2; - res = memcmp(list[middle], addr, ETH_ALEN); - if (res == 0) - return 1; - if (res < 0) - start = middle + 1; - else - end = middle - 1; - } - - return 0; -} - - -const u8 * hostapd_get_psk(const struct hostapd_config *conf, const u8 *addr, - const u8 *prev_psk) -{ - struct hostapd_wpa_psk *psk; - int next_ok = prev_psk == NULL; - - for (psk = conf->wpa_psk; psk != NULL; psk = psk->next) { - if (next_ok && - (psk->group || memcmp(psk->addr, addr, ETH_ALEN) == 0)) - return psk->psk; - - if (psk->psk == prev_psk) - next_ok = 1; - } - - return NULL; -} - - -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_config *conf, const u8 *identity, - size_t identity_len, int phase2) -{ - struct hostapd_eap_user *user = conf->eap_user; - - while (user) { - if (!phase2 && user->identity == NULL) { - /* Wildcard match */ - break; - } - if (user->phase2 == !!phase2 && - user->identity_len == identity_len && - memcmp(user->identity, identity, identity_len) == 0) - break; - user = user->next; - } - - return user; -} diff --git a/contrib/hostapd-0.4.9/config.h b/contrib/hostapd-0.4.9/config.h deleted file mode 100644 index df411a5621..0000000000 --- a/contrib/hostapd-0.4.9/config.h +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H - -#include "config_types.h" - -typedef u8 macaddr[ETH_ALEN]; - -struct hostapd_radius_servers; - -#define PMK_LEN 32 -struct hostapd_wpa_psk { - struct hostapd_wpa_psk *next; - int group; - u8 psk[PMK_LEN]; - u8 addr[ETH_ALEN]; -}; - -#define EAP_USER_MAX_METHODS 8 -struct hostapd_eap_user { - struct hostapd_eap_user *next; - u8 *identity; - size_t identity_len; - u8 methods[EAP_USER_MAX_METHODS]; - u8 *password; - size_t password_len; - int phase2; - int force_version; -}; - -struct hostapd_config { - char iface[IFNAMSIZ + 1]; - char bridge[IFNAMSIZ + 1]; - - const struct driver_ops *driver; - - enum { - HOSTAPD_LEVEL_DEBUG_VERBOSE = 0, - HOSTAPD_LEVEL_DEBUG = 1, - HOSTAPD_LEVEL_INFO = 2, - HOSTAPD_LEVEL_NOTICE = 3, - HOSTAPD_LEVEL_WARNING = 4 - } logger_syslog_level, logger_stdout_level; - -#define HOSTAPD_MODULE_IEEE80211 BIT(0) -#define HOSTAPD_MODULE_IEEE8021X BIT(1) -#define HOSTAPD_MODULE_RADIUS BIT(2) -#define HOSTAPD_MODULE_WPA BIT(3) -#define HOSTAPD_MODULE_DRIVER BIT(4) -#define HOSTAPD_MODULE_IAPP BIT(5) - unsigned int logger_syslog; /* module bitfield */ - unsigned int logger_stdout; /* module bitfield */ - - enum { HOSTAPD_DEBUG_NO = 0, HOSTAPD_DEBUG_MINIMAL = 1, - HOSTAPD_DEBUG_VERBOSE = 2, - HOSTAPD_DEBUG_MSGDUMPS = 3, - HOSTAPD_DEBUG_EXCESSIVE = 4 } debug; /* debug verbosity level */ - char *dump_log_name; /* file name for state dump (SIGUSR1) */ - - int ieee802_1x; /* use IEEE 802.1X */ - int eapol_version; - int eap_server; /* Use internal EAP server instead of external - * RADIUS server */ - struct hostapd_eap_user *eap_user; - char *eap_sim_db; - struct hostapd_ip_addr own_ip_addr; - char *nas_identifier; - struct hostapd_radius_servers *radius; - -#define HOSTAPD_SSID_LEN 32 - char ssid[HOSTAPD_SSID_LEN + 1]; - size_t ssid_len; - int ssid_set; - char *eap_req_id_text; /* optional displayable message sent with - * EAP Request-Identity */ - size_t eap_req_id_text_len; - int eapol_key_index_workaround; - - size_t default_wep_key_len; - int individual_wep_key_len; - int wep_rekeying_period; - int eap_reauth_period; - - int ieee802_11f; /* use IEEE 802.11f (IAPP) */ - char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast - * frames */ - - u8 assoc_ap_addr[ETH_ALEN]; - int assoc_ap; /* whether assoc_ap_addr is set */ - - enum { - ACCEPT_UNLESS_DENIED = 0, - DENY_UNLESS_ACCEPTED = 1, - USE_EXTERNAL_RADIUS_AUTH = 2 - } macaddr_acl; - macaddr *accept_mac; - int num_accept_mac; - macaddr *deny_mac; - int num_deny_mac; - -#define HOSTAPD_AUTH_OPEN BIT(0) -#define HOSTAPD_AUTH_SHARED_KEY BIT(1) - int auth_algs; /* bitfield of allowed IEEE 802.11 authentication - * algorithms */ - -#define HOSTAPD_WPA_VERSION_WPA BIT(0) -#define HOSTAPD_WPA_VERSION_WPA2 BIT(1) - int wpa; - struct hostapd_wpa_psk *wpa_psk; - char *wpa_passphrase; - char *wpa_psk_file; -#define WPA_KEY_MGMT_IEEE8021X BIT(0) -#define WPA_KEY_MGMT_PSK BIT(1) - int wpa_key_mgmt; -#define WPA_CIPHER_NONE BIT(0) -#define WPA_CIPHER_WEP40 BIT(1) -#define WPA_CIPHER_WEP104 BIT(2) -#define WPA_CIPHER_TKIP BIT(3) -#define WPA_CIPHER_CCMP BIT(4) - int wpa_pairwise; - int wpa_group; - int wpa_group_rekey; - int wpa_strict_rekey; - int wpa_gmk_rekey; - int rsn_preauth; - char *rsn_preauth_interfaces; - - char *ctrl_interface; /* directory for UNIX domain sockets */ - gid_t ctrl_interface_gid; - int ctrl_interface_gid_set; - - char *ca_cert; - char *server_cert; - char *private_key; - char *private_key_passwd; - int check_crl; - - char *radius_server_clients; - int radius_server_auth_port; - int radius_server_ipv6; - - char *test_socket; /* UNIX domain socket path for driver_test */ - - int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group - * address instead of individual address - * (for driver_wired.c). - */ -}; - - -struct hostapd_config * hostapd_config_read(const char *fname); -void hostapd_config_free(struct hostapd_config *conf); -int hostapd_maclist_found(macaddr *list, int num_entries, u8 *addr); -const u8 * hostapd_get_psk(const struct hostapd_config *conf, const u8 *addr, - const u8 *prev_psk); -int hostapd_setup_wpa_psk(struct hostapd_config *conf); -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_config *conf, const u8 *identity, - size_t identity_len, int phase2); - -#endif /* CONFIG_H */ diff --git a/contrib/hostapd-0.4.9/config_types.h b/contrib/hostapd-0.4.9/config_types.h deleted file mode 100644 index 12b57cb4ac..0000000000 --- a/contrib/hostapd-0.4.9/config_types.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef CONFIG_TYPES_H -#define CONFIG_TYPES_H - -struct hostapd_ip_addr { - union { - struct in_addr v4; -#ifdef CONFIG_IPV6 - struct in6_addr v6; -#endif /* CONFIG_IPV6 */ - } u; - int af; /* AF_INET / AF_INET6 */ -}; - -#endif /* CONFIG_TYPES_H */ diff --git a/contrib/hostapd-0.4.9/crypto.c b/contrib/hostapd-0.4.9/crypto.c deleted file mode 100644 index 1b136711d1..0000000000 --- a/contrib/hostapd-0.4.9/crypto.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * WPA Supplicant / wrapper functions for libcrypto - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "crypto.h" - -#if OPENSSL_VERSION_NUMBER < 0x00907000 -#define DES_key_schedule des_key_schedule -#define DES_cblock des_cblock -#define DES_set_key(key, schedule) des_set_key((key), *(schedule)) -#define DES_ecb_encrypt(input, output, ks, enc) \ - des_ecb_encrypt((input), (output), *(ks), (enc)) -#endif /* openssl < 0.9.7 */ - - -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - MD4_CTX ctx; - int i; - - MD4_Init(&ctx); - for (i = 0; i < num_elem; i++) - MD4_Update(&ctx, addr[i], len[i]); - MD4_Final(mac, &ctx); -} - - -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) -{ - u8 pkey[8], next, tmp; - int i; - DES_key_schedule ks; - - /* Add parity bits to the key */ - next = 0; - for (i = 0; i < 7; i++) { - tmp = key[i]; - pkey[i] = (tmp >> i) | next | 1; - next = tmp << (7 - i); - } - pkey[i] = next | 1; - - DES_set_key(&pkey, &ks); - DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, - DES_ENCRYPT); -} - - -#ifdef EAP_TLS_FUNCS -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - MD5_CTX ctx; - int i; - - MD5_Init(&ctx); - for (i = 0; i < num_elem; i++) - MD5_Update(&ctx, addr[i], len[i]); - MD5_Final(mac, &ctx); -} - - -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - SHA_CTX ctx; - int i; - - SHA1_Init(&ctx); - for (i = 0; i < num_elem; i++) - SHA1_Update(&ctx, addr[i], len[i]); - SHA1_Final(mac, &ctx); -} - - -void sha1_transform(u8 *state, const u8 data[64]) -{ - SHA_CTX context; - memset(&context, 0, sizeof(context)); - memcpy(&context.h0, state, 5 * 4); - SHA1_Transform(&context, data); - memcpy(state, &context.h0, 5 * 4); -} - - -void * aes_encrypt_init(const u8 *key, size_t len) -{ - AES_KEY *ak; - ak = malloc(sizeof(*ak)); - if (ak == NULL) - return NULL; - if (AES_set_encrypt_key(key, 8 * len, ak) < 0) { - free(ak); - return NULL; - } - return ak; -} - - -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) -{ - AES_encrypt(plain, crypt, ctx); -} - - -void aes_encrypt_deinit(void *ctx) -{ - free(ctx); -} - - -void * aes_decrypt_init(const u8 *key, size_t len) -{ - AES_KEY *ak; - ak = malloc(sizeof(*ak)); - if (ak == NULL) - return NULL; - if (AES_set_decrypt_key(key, 8 * len, ak) < 0) { - free(ak); - return NULL; - } - return ak; -} - - -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) -{ - AES_decrypt(crypt, plain, ctx); -} - - -void aes_decrypt_deinit(void *ctx) -{ - free(ctx); -} -#endif /* EAP_TLS_FUNCS */ diff --git a/contrib/hostapd-0.4.9/crypto.h b/contrib/hostapd-0.4.9/crypto.h deleted file mode 100644 index e664861afe..0000000000 --- a/contrib/hostapd-0.4.9/crypto.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * WPA Supplicant / wrapper functions for crypto libraries - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * This file defines the cryptographic functions that need to be implemented - * for wpa_supplicant and hostapd. When TLS is not used, internal - * implementation of MD5, SHA1, and AES is used and no external libraries are - * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the - * crypto library used by the TLS implementation is expected to be used for - * non-TLS needs, too, in order to save space by not implementing these - * functions twice. - * - * Wrapper code for using each crypto library is in its own file (crypto*.c) - * and one of these files is build and linked in to provide the functions - * defined here. - */ - -#ifndef CRYPTO_H -#define CRYPTO_H - -/** - * md4_vector - MD4 hash for data vector - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash - */ -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); - -/** - * md5_vector - MD5 hash for data vector - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash - */ -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); - -/** - * sha1_vector - SHA-1 hash for data vector - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash - */ -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac); - -/** - * sha1_transform - Perform one SHA-1 transform step - * @state: SHA-1 state - * @data: Input data for the SHA-1 transform - * - * This function is used to implement random number generation specified in - * NIST FIPS Publication 186-2 for EAP-SIM. This PRF uses a function that is - * similar to SHA-1, but has different message padding and as such, access to - * just part of the SHA-1 is needed. - */ -void sha1_transform(u8 *state, const u8 data[64]); - -/** - * des_encrypt - Encrypt one block with DES - * @clear: 8 octets (in) - * @key: 7 octets (in) (no parity bits included) - * @cypher: 8 octets (out) - */ -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); - -/** - * aes_encrypt_init - Initialize AES for encryption - * @key: Encryption key - * @len: Key length in bytes (usually 16, i.e., 128 bits) - * Returns: Pointer to context data or %NULL on failure - */ -void * aes_encrypt_init(const u8 *key, size_t len); - -/** - * aes_encrypt - Encrypt one AES block - * @ctx: Context pointer from aes_encrypt_init() - * @plain: Plaintext data to be encrypted (16 bytes) - * @crypt: Buffer for the encrypted data (16 bytes) - */ -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); - -/** - * aes_encrypt_deinit - Deinitialize AES encryption - * @ctx: Context pointer from aes_encrypt_init() - */ -void aes_encrypt_deinit(void *ctx); - -/** - * aes_decrypt_init - Initialize AES for decryption - * @key: Decryption key - * @len: Key length in bytes (usually 16, i.e., 128 bits) - * Returns: Pointer to context data or %NULL on failure - */ -void * aes_decrypt_init(const u8 *key, size_t len); - -/** - * aes_decrypt - Decrypt one AES block - * @ctx: Context pointer from aes_encrypt_init() - * @crypt: Encrypted data (16 bytes) - * @plain: Buffer for the decrypted data (16 bytes) - */ -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); - -/** - * aes_decrypt_deinit - Deinitialize AES decryption - * @ctx: Context pointer from aes_encrypt_init() - */ -void aes_decrypt_deinit(void *ctx); - - -#endif /* CRYPTO_H */ diff --git a/contrib/hostapd-0.4.9/ctrl_iface.c b/contrib/hostapd-0.4.9/ctrl_iface.c deleted file mode 100644 index ff730d4a95..0000000000 --- a/contrib/hostapd-0.4.9/ctrl_iface.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / UNIX domain socket -based control interface - * Copyright (c) 2004, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hostapd.h" -#include "eloop.h" -#include "config.h" -#include "eapol_sm.h" -#include "ieee802_1x.h" -#include "wpa.h" -#include "radius_client.h" -#include "ieee802_11.h" -#include "ctrl_iface.h" -#include "sta_info.h" - - -struct wpa_ctrl_dst { - struct wpa_ctrl_dst *next; - struct sockaddr_un addr; - socklen_t addrlen; - int debug_level; - int errors; -}; - - -static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, - struct sockaddr_un *from, - socklen_t fromlen) -{ - struct wpa_ctrl_dst *dst; - - dst = malloc(sizeof(*dst)); - if (dst == NULL) - return -1; - memset(dst, 0, sizeof(*dst)); - memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); - dst->addrlen = fromlen; - dst->debug_level = MSG_INFO; - dst->next = hapd->ctrl_dst; - hapd->ctrl_dst = dst; - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", - (u8 *) from->sun_path, fromlen); - return 0; -} - - -static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, - struct sockaddr_un *from, - socklen_t fromlen) -{ - struct wpa_ctrl_dst *dst, *prev = NULL; - - dst = hapd->ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) { - if (prev == NULL) - hapd->ctrl_dst = dst->next; - else - prev->next = dst->next; - free(dst); - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", - (u8 *) from->sun_path, fromlen); - return 0; - } - prev = dst; - dst = dst->next; - } - return -1; -} - - -static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, - struct sockaddr_un *from, - socklen_t fromlen, - char *level) -{ - struct wpa_ctrl_dst *dst; - - wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); - - dst = hapd->ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - memcmp(from->sun_path, dst->addr.sun_path, fromlen) == 0) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " - "level", (u8 *) from->sun_path, fromlen); - dst->debug_level = atoi(level); - return 0; - } - dst = dst->next; - } - - return -1; -} - - -static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, - struct sta_info *sta, - char *buf, size_t buflen) -{ - int len, res; - - if (sta == NULL) { - return snprintf(buf, buflen, "FAIL\n"); - } - - len = 0; - len += snprintf(buf + len, buflen - len, MACSTR "\n", - MAC2STR(sta->addr)); - - res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); - if (res >= 0) - len += res; - res = wpa_get_mib_sta(hapd, sta, buf + len, buflen - len); - if (res >= 0) - len += res; - res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len); - if (res >= 0) - len += res; - - return len; -} - - -static int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, - char *buf, size_t buflen) -{ - return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen); -} - - -static int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, - const char *txtaddr, - char *buf, size_t buflen) -{ - u8 addr[ETH_ALEN]; - - if (hwaddr_aton(txtaddr, addr)) - return snprintf(buf, buflen, "FAIL\n"); - return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr), - buf, buflen); -} - - -static int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, - const char *txtaddr, - char *buf, size_t buflen) -{ - u8 addr[ETH_ALEN]; - struct sta_info *sta; - - if (hwaddr_aton(txtaddr, addr) || - (sta = ap_get_sta(hapd, addr)) == NULL) - return snprintf(buf, buflen, "FAIL\n"); - return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); -} - - -static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, - void *sock_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - char buf[256]; - int res; - struct sockaddr_un from; - socklen_t fromlen = sizeof(from); - char *reply; - const int reply_size = 4096; - int reply_len; - - res = recvfrom(sock, buf, sizeof(buf) - 1, 0, - (struct sockaddr *) &from, &fromlen); - if (res < 0) { - perror("recvfrom(ctrl_iface)"); - return; - } - buf[res] = '\0'; - wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res); - - reply = malloc(reply_size); - if (reply == NULL) { - sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, - fromlen); - return; - } - - memcpy(reply, "OK\n", 3); - reply_len = 3; - - if (strcmp(buf, "PING") == 0) { - memcpy(reply, "PONG\n", 5); - reply_len = 5; - } else if (strcmp(buf, "MIB") == 0) { - reply_len = ieee802_11_get_mib(hapd, reply, reply_size); - if (reply_len >= 0) { - res = wpa_get_mib(hapd, reply + reply_len, - reply_size - reply_len); - if (res < 0) - reply_len = -1; - else - reply_len += res; - } - if (reply_len >= 0) { - res = ieee802_1x_get_mib(hapd, reply + reply_len, - reply_size - reply_len); - if (res < 0) - reply_len = -1; - else - reply_len += res; - } - if (reply_len >= 0) { - res = radius_client_get_mib(hapd->radius, - reply + reply_len, - reply_size - reply_len); - if (res < 0) - reply_len = -1; - else - reply_len += res; - } - } else if (strcmp(buf, "STA-FIRST") == 0) { - reply_len = hostapd_ctrl_iface_sta_first(hapd, reply, - reply_size); - } else if (strncmp(buf, "STA ", 4) == 0) { - reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply, - reply_size); - } else if (strncmp(buf, "STA-NEXT ", 9) == 0) { - reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, - reply_size); - } else if (strcmp(buf, "ATTACH") == 0) { - if (hostapd_ctrl_iface_attach(hapd, &from, fromlen)) - reply_len = -1; - } else if (strcmp(buf, "DETACH") == 0) { - if (hostapd_ctrl_iface_detach(hapd, &from, fromlen)) - reply_len = -1; - } else if (strncmp(buf, "LEVEL ", 6) == 0) { - if (hostapd_ctrl_iface_level(hapd, &from, fromlen, - buf + 6)) - reply_len = -1; - } else { - memcpy(reply, "UNKNOWN COMMAND\n", 16); - reply_len = 16; - } - - if (reply_len < 0) { - memcpy(reply, "FAIL\n", 5); - reply_len = 5; - } - sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); - free(reply); -} - - -static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) -{ - char *buf; - size_t len; - - if (hapd->conf->ctrl_interface == NULL) - return NULL; - - len = strlen(hapd->conf->ctrl_interface) + strlen(hapd->conf->iface) + - 2; - buf = malloc(len); - if (buf == NULL) - return NULL; - - snprintf(buf, len, "%s/%s", - hapd->conf->ctrl_interface, hapd->conf->iface); - return buf; -} - - -int hostapd_ctrl_iface_init(struct hostapd_data *hapd) -{ - struct sockaddr_un addr; - int s = -1; - char *fname = NULL; - - hapd->ctrl_sock = -1; - - if (hapd->conf->ctrl_interface == NULL) - return 0; - - if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { - if (errno == EEXIST) { - wpa_printf(MSG_DEBUG, "Using existing control " - "interface directory."); - } else { - perror("mkdir[ctrl_interface]"); - goto fail; - } - } - - if (hapd->conf->ctrl_interface_gid_set && - chown(hapd->conf->ctrl_interface, 0, - hapd->conf->ctrl_interface_gid) < 0) { - perror("chown[ctrl_interface]"); - return -1; - } - - if (strlen(hapd->conf->ctrl_interface) + 1 + strlen(hapd->conf->iface) - >= sizeof(addr.sun_path)) - goto fail; - - s = socket(PF_UNIX, SOCK_DGRAM, 0); - if (s < 0) { - perror("socket(PF_UNIX)"); - goto fail; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - fname = hostapd_ctrl_iface_path(hapd); - if (fname == NULL) - goto fail; - strncpy(addr.sun_path, fname, sizeof(addr.sun_path)); - if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); - goto fail; - } - - if (hapd->conf->ctrl_interface_gid_set && - chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) { - perror("chown[ctrl_interface/ifname]"); - goto fail; - } - - if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { - perror("chmod[ctrl_interface/ifname]"); - goto fail; - } - free(fname); - - hapd->ctrl_sock = s; - eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd, - NULL); - - return 0; - -fail: - if (s >= 0) - close(s); - if (fname) { - unlink(fname); - free(fname); - } - return -1; -} - - -void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) -{ - struct wpa_ctrl_dst *dst, *prev; - - if (hapd->ctrl_sock > -1) { - char *fname; - eloop_unregister_read_sock(hapd->ctrl_sock); - close(hapd->ctrl_sock); - hapd->ctrl_sock = -1; - fname = hostapd_ctrl_iface_path(hapd); - if (fname) - unlink(fname); - free(fname); - - if (hapd->conf->ctrl_interface && - rmdir(hapd->conf->ctrl_interface) < 0) { - if (errno == ENOTEMPTY) { - wpa_printf(MSG_DEBUG, "Control interface " - "directory not empty - leaving it " - "behind"); - } else { - perror("rmdir[ctrl_interface]"); - } - } - } - - dst = hapd->ctrl_dst; - while (dst) { - prev = dst; - dst = dst->next; - free(prev); - } -} - - -void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, - char *buf, size_t len) -{ - struct wpa_ctrl_dst *dst, *next; - struct msghdr msg; - int idx; - struct iovec io[2]; - char levelstr[10]; - - dst = hapd->ctrl_dst; - if (hapd->ctrl_sock < 0 || dst == NULL) - return; - - snprintf(levelstr, sizeof(levelstr), "<%d>", level); - io[0].iov_base = levelstr; - io[0].iov_len = strlen(levelstr); - io[1].iov_base = buf; - io[1].iov_len = len; - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = io; - msg.msg_iovlen = 2; - - idx = 0; - while (dst) { - next = dst->next; - if (level >= dst->debug_level) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", - (u8 *) dst->addr.sun_path, dst->addrlen); - msg.msg_name = &dst->addr; - msg.msg_namelen = dst->addrlen; - if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) { - fprintf(stderr, "CTRL_IFACE monitor[%d]: ", - idx); - perror("sendmsg"); - dst->errors++; - if (dst->errors > 10) { - hostapd_ctrl_iface_detach( - hapd, &dst->addr, - dst->addrlen); - } - } else - dst->errors = 0; - } - idx++; - dst = next; - } -} diff --git a/contrib/hostapd-0.4.9/ctrl_iface.h b/contrib/hostapd-0.4.9/ctrl_iface.h deleted file mode 100644 index ef1a541c94..0000000000 --- a/contrib/hostapd-0.4.9/ctrl_iface.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CTRL_IFACE_H -#define CTRL_IFACE_H - -int hostapd_ctrl_iface_init(struct hostapd_data *hapd); -void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd); -void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, - char *buf, size_t len); - -#endif /* CTRL_IFACE_H */ diff --git a/contrib/hostapd-0.4.9/defconfig b/contrib/hostapd-0.4.9/defconfig deleted file mode 100644 index e8f4e4fc53..0000000000 --- a/contrib/hostapd-0.4.9/defconfig +++ /dev/null @@ -1,75 +0,0 @@ -# Example hostapd build time configuration -# -# This file lists the configuration options that are used when building the -# hostapd binary. All lines starting with # are ignored. Configuration option -# lines must be commented out complete, if they are not to be included, i.e., -# just setting VARIABLE=n is not disabling that variable. -# -# This file is included in Makefile, so variables like CFLAGS and LIBS can also -# be modified from here. In most cass, these lines should use += in order not -# to override previous values of the variables. - -# Driver interface for Host AP driver -CONFIG_DRIVER_HOSTAP=y - -# Driver interface for wired authenticator -#CONFIG_DRIVER_WIRED=y - -# Driver interface for madwifi driver -#CONFIG_DRIVER_MADWIFI=y -#CFLAGS += -I../head # change to reflect local setup; directory for madwifi src - -# Driver interface for Prism54 driver -#CONFIG_DRIVER_PRISM54=y - -# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) -#CONFIG_DRIVER_BSD=y -#CFLAGS += -I/usr/local/include -#LIBS += -L/usr/local/lib - -# IEEE 802.11F/IAPP -CONFIG_IAPP=y - -# WPA2/IEEE 802.11i RSN pre-authentication -CONFIG_RSN_PREAUTH=y - -# Integrated EAP server -CONFIG_EAP=y - -# EAP-MD5 for the integrated EAP server -CONFIG_EAP_MD5=y - -# EAP-TLS for the integrated EAP server -CONFIG_EAP_TLS=y - -# EAP-MSCHAPv2 for the integrated EAP server -CONFIG_EAP_MSCHAPV2=y - -# EAP-PEAP for the integrated EAP server -CONFIG_EAP_PEAP=y - -# EAP-GTC for the integrated EAP server -CONFIG_EAP_GTC=y - -# EAP-TTLS for the integrated EAP server -CONFIG_EAP_TTLS=y - -# EAP-SIM for the integrated EAP server -#CONFIG_EAP_SIM=y - -# EAP-PAX for the integrated EAP server -#CONFIG_EAP_PAX=y - -# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK) -#CONFIG_EAP_PSK=y - -# PKCS#12 (PFX) support (used to read private key and certificate file from -# a file that usually has extension .p12 or .pfx) -CONFIG_PKCS12=y - -# RADIUS authentication server. This provides access to the integrated EAP -# server from external hosts using RADIUS. -#CONFIG_RADIUS_SERVER=y - -# Build IPv6 support for RADIUS operations -CONFIG_IPV6=y diff --git a/contrib/hostapd-0.4.9/defs.h b/contrib/hostapd-0.4.9/defs.h deleted file mode 100644 index 6f9881d71d..0000000000 --- a/contrib/hostapd-0.4.9/defs.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * WPA Supplicant - Common definitions - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef DEFS_H -#define DEFS_H - -#ifdef FALSE -#undef FALSE -#endif -#ifdef TRUE -#undef TRUE -#endif -typedef enum { FALSE = 0, TRUE = 1 } Boolean; - - -typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg; -typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, - CIPHER_WEP104 } wpa_cipher; -typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, - KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE } wpa_key_mgmt; - -/** - * enum wpa_states - wpa_supplicant state - * - * These enumeration values are used to indicate the current wpa_supplicant - * state (wpa_s->wpa_state). The current state can be retrieved with - * wpa_supplicant_get_state() function and the state can be changed by calling - * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the - * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used - * to access the state variable. - */ -typedef enum { - /** - * WPA_DISCONNECTED - Disconnected state - * - * This state indicates that client is not associated, but is likely to - * start looking for an access point. This state is entered when a - * connection is lost. - */ - WPA_DISCONNECTED, - - /** - * WPA_INACTIVE - Inactive state (wpa_supplicant disabled) - * - * This state is entered if there are no enabled networks in the - * configuration. wpa_supplicant is not trying to associate with a new - * network and external interaction (e.g., ctrl_iface call to add or - * enable a network) is needed to start association. - */ - WPA_INACTIVE, - - /** - * WPA_SCANNING - Scanning for a network - * - * This state is entered when wpa_supplicant starts scanning for a - * network. - */ - WPA_SCANNING, - - /** - * WPA_ASSOCIATING - Trying to associate with a BSS/SSID - * - * This state is entered when wpa_supplicant has found a suitable BSS - * to associate with and the driver is configured to try to associate - * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this - * state is entered when the driver is configured to try to associate - * with a network using the configured SSID and security policy. - */ - WPA_ASSOCIATING, - - /** - * WPA_ASSOCIATED - Association completed - * - * This state is entered when the driver reports that association has - * been successfully completed with an AP. If IEEE 802.1X is used - * (with or without WPA/WPA2), wpa_supplicant remains in this state - * until the IEEE 802.1X/EAPOL authentication has been completed. - */ - WPA_ASSOCIATED, - - /** - * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress - * - * This state is entered when WPA/WPA2 4-Way Handshake is started. In - * case of WPA-PSK, this happens when receiving the first EAPOL-Key - * frame after association. In case of WPA-EAP, this state is entered - * when the IEEE 802.1X/EAPOL authentication has been completed. - */ - WPA_4WAY_HANDSHAKE, - - /** - * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress - * - * This state is entered when 4-Way Key Handshake has been completed - * (i.e., when the supplicant sends out message 4/4) and when Group - * Key rekeying is started by the AP (i.e., when supplicant receives - * message 1/2). - */ - WPA_GROUP_HANDSHAKE, - - /** - * WPA_COMPLETED - All authentication completed - * - * This state is entered when the full authentication process is - * completed. In case of WPA2, this happens when the 4-Way Handshake is - * successfully completed. With WPA, this state is entered after the - * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is - * completed after dynamic keys are received (or if not used, after - * the EAP authentication has been completed). With static WEP keys and - * plaintext connections, this state is entered when an association - * has been completed. - * - * This state indicates that the supplicant has completed its - * processing for the association phase and that data connection is - * fully configured. - */ - WPA_COMPLETED -} wpa_states; - -#endif /* DEFS_H */ diff --git a/contrib/hostapd-0.4.9/developer.txt b/contrib/hostapd-0.4.9/developer.txt deleted file mode 100644 index e1d3163413..0000000000 --- a/contrib/hostapd-0.4.9/developer.txt +++ /dev/null @@ -1,219 +0,0 @@ -Developer notes for hostapd -=========================== - -hostapd daemon setup, operations, and shutdown ----------------------------------------------- - -Files: hostapd.[ch] - -Externally called functions: - hostapd_new_assoc_sta() is called when a station associates with the AP - -Event loop functions: - handle_term() is called on SIGINT and SIGTERM to terminate hostapd process - handle_reload() is called on SIGHUP to reload configuration - handle_dump_state() is called on SIGUSR1 to dump station state data to a - text file - hostapd_rotate_wep() is called to periodically change WEP keys - - -Configuration parsing ---------------------- - -Configuration file parsing and data structure definition. - -Files: config.[ch] - -Externally called functions: - hostapd_config_read() is called to read and parse a configuration file; - allocates and returns configuration data structure - hostapd_config_free() is called to free configuration data structure - hostapd_maclist_found() is called to check whether a given address is found - in a list of MAC addresses - - -Kernel driver access --------------------- - -Helper functions for configuring the Host AP kernel driver and -accessing data from it. - -Files: driver.[ch] - - -IEEE 802.11 frame handling (netdevice wlan#ap) ----------------------------------------------- - -Receive all incoming IEEE 802.11 frames from the kernel driver via -wlan#ap interface. - -Files: receive.c - -Externally called functions: - hostapd_init_sockets() is called to initialize sockets for receiving and - sending IEEE 802.11 frames via wlan#ap interface - -Event loop functions: - handle_read() is called for each incoming packet from wlan#ap net device - - -Station table -------------- - -Files: sta_info.[ch], ap.h - -Event loop functions: - ap_handle_timer() is called to check station activity and to remove - inactive stations - - -IEEE 802.11 management ----------------------- - -IEEE 802.11 management frame sending and processing (mainly, -authentication and association). IEEE 802.11 station functionality -(authenticate and associate with another AP as an station). - -Files: ieee802_11.[ch] - -Externally called functions: - ieee802_11_mgmt() is called for each received IEEE 802.11 management frame - (from handle_frame() in hostapd.c) - ieee802_11_mgmt_cb() is called for each received TX callback of IEEE 802.11 - management frame (from handle_tx_callback() in hostapd.c) - ieee802_11_send_deauth() is called to send deauthentication frame - ieee802_11_send_disassoc() is called to send disassociation frame - ieee802_11_parse_elems() is used to parse information elements in - IEEE 802.11 management frames - -Event loop functions: - ieee802_11_sta_authenticate() called to retry authentication (with another - AP) - ieee802_11_sta_associate() called to retry association (with another AP) - - -IEEE 802.11 authentication --------------------------- - -Access control list for IEEE 802.11 authentication. Uses staticly -configured ACL from configuration files or an external RADIUS -server. Results from external RADIUS queries are cached to allow -faster authentication frame processing. - -Files: ieee802_11_auth.[ch] - -Externally called functions: - hostapd_acl_init() called once during hostapd startup - hostapd_acl_deinit() called once during hostapd shutdown - hostapd_acl_recv_radius() called by IEEE 802.1X code for incoming RADIUS - Authentication messages (returns 0 if message was processed) - hostapd_allowed_address() called to check whether a specified station can be - authenticated - -Event loop functions: - hostapd_acl_expire() is called to expire ACL cache entries - - -IEEE 802.1X Authenticator -------------------------- - -Files: ieee802_1x.[ch] - - -Externally called functions: - ieee802_1x_receive() is called for each incoming EAPOL frame from the - wireless interface - ieee802_1x_new_station() is called to start IEEE 802.1X authentication when - a new station completes IEEE 802.11 association - -Event loop functions: - ieee802_1x_receive_auth() called for each incoming RADIUS Authentication - message - - -EAPOL state machine -------------------- - -IEEE 802.1X state machine for EAPOL. - -Files: eapol_sm.[ch] - -Externally called functions: - eapol_sm_step() is called to advance EAPOL state machines after any change - that could affect their state - -Event loop functions: - eapol_port_timers_tick() called once per second to advance Port Timers state - machine - - -IEEE 802.11f (IAPP) -------------------- - -Files: iapp.[ch] - -Externally called functions: - iapp_new_station() is called to start accounting session when a new station - completes IEEE 802.11 association or IEEE 802.1X authentication - -Event loop functions: - iapp_receive_udp() is called for incoming IAPP frames over UDP - - -Per station accounting ----------------------- - -Send RADIUS Accounting start and stop messages to a RADIUS Accounting -server. Process incoming RADIUS Accounting messages. - -Files: accounting.[ch] - -Externally called functions: - accounting_init() called once during hostapd startup - accounting_deinit() called once during hostapd shutdown - accounting_sta_start() called when a station starts new session - accounting_sta_stop() called when a station session is terminated - -Event loop functions: - accounting_receive() called for each incoming RADIUS Accounting message - accounting_list_timer() called to retransmit accounting messages and to - remove expired entries - - -RADIUS messages ---------------- - -RADIUS message generation and parsing functions. - -Files: radius.[ch] - - -Event loop ----------- - -Event loop for registering timeout calls, signal handlers, and socket -read events. - -Files: eloop.[ch] - - -RC4 ---- - -RC4 encryption - -Files: rc4.[ch] - - -MD5 ---- - -MD5 hash and HMAC-MD5. - -Files: md5.[ch] - - -Miscellaneous helper functions ------------------------------- - -Files: common.[ch] diff --git a/contrib/hostapd-0.4.9/driver.h b/contrib/hostapd-0.4.9/driver.h deleted file mode 100644 index ed9ecbfdbc..0000000000 --- a/contrib/hostapd-0.4.9/driver.h +++ /dev/null @@ -1,271 +0,0 @@ -#ifndef DRIVER_H -#define DRIVER_H - -struct driver_ops { - const char *name; /* as appears in the config file */ - - int (*init)(struct hostapd_data *hapd); - void (*deinit)(void *priv); - - int (*wireless_event_init)(void *priv); - void (*wireless_event_deinit)(void *priv); - - /** - * set_8021x - enable/disable IEEE 802.1X support - * @priv: driver private data - * @enabled: 1 = enable, 0 = disable - * - * Returns: 0 on success, -1 on failure - * - * Configure the kernel driver to enable/disable 802.1X support. - * This may be an empty function if 802.1X support is always enabled. - */ - int (*set_ieee8021x)(void *priv, int enabled); - - /** - * set_privacy - enable/disable privacy - * @priv: driver private data - * @enabled: 1 = privacy enabled, 0 = disabled - * - * Return: 0 on success, -1 on failure - * - * Configure privacy. - */ - int (*set_privacy)(void *priv, int enabled); - - int (*set_encryption)(void *priv, const char *alg, u8 *addr, - int idx, u8 *key, size_t key_len); - int (*get_seqnum)(void *priv, u8 *addr, int idx, u8 *seq); - int (*flush)(void *priv); - int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); - - int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data, - u8 *addr); - int (*send_eapol)(void *priv, u8 *addr, u8 *data, size_t data_len, - int encrypt); - int (*set_sta_authorized)(void *driver, u8 *addr, int authorized); - int (*sta_deauth)(void *priv, u8 *addr, int reason); - int (*sta_disassoc)(void *priv, u8 *addr, int reason); - int (*sta_remove)(void *priv, u8 *addr); - int (*get_ssid)(void *priv, u8 *buf, int len); - int (*set_ssid)(void *priv, u8 *buf, int len); - int (*set_countermeasures)(void *priv, int enabled); - int (*send_mgmt_frame)(void *priv, const void *msg, size_t len, - int flags); - int (*set_assoc_ap)(void *priv, u8 *addr); - int (*sta_add)(void *priv, u8 *addr, u16 aid, u16 capability, - u8 tx_supp_rates); - int (*get_inact_sec)(void *priv, u8 *addr); - int (*sta_clear_stats)(void *priv, u8 *addr); -}; - -static inline int -hostapd_driver_init(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || hapd->driver->init == NULL) - return -1; - return hapd->driver->init(hapd); -} - -static inline void -hostapd_driver_deinit(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || hapd->driver->deinit == NULL) - return; - hapd->driver->deinit(hapd->driver); -} - -static inline int -hostapd_wireless_event_init(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || - hapd->driver->wireless_event_init == NULL) - return 0; - return hapd->driver->wireless_event_init(hapd->driver); -} - -static inline void -hostapd_wireless_event_deinit(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || - hapd->driver->wireless_event_deinit == NULL) - return; - hapd->driver->wireless_event_deinit(hapd->driver); -} - -static inline int -hostapd_set_ieee8021x(struct hostapd_data *hapd, int enabled) -{ - if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL) - return 0; - return hapd->driver->set_ieee8021x(hapd->driver, enabled); -} - -static inline int -hostapd_set_privacy(struct hostapd_data *hapd, int enabled) -{ - if (hapd->driver == NULL || hapd->driver->set_privacy == NULL) - return 0; - return hapd->driver->set_privacy(hapd->driver, enabled); -} - -static inline int -hostapd_set_encryption(struct hostapd_data *hapd, const char *alg, u8 *addr, - int idx, u8 *key, size_t key_len) -{ - if (hapd->driver == NULL || hapd->driver->set_encryption == NULL) - return 0; - return hapd->driver->set_encryption(hapd->driver, alg, addr, idx, key, - key_len); -} - -static inline int -hostapd_get_seqnum(struct hostapd_data *hapd, u8 *addr, int idx, u8 *seq) -{ - if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL) - return 0; - return hapd->driver->get_seqnum(hapd->driver, addr, idx, seq); -} - -static inline int -hostapd_flush(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || hapd->driver->flush == NULL) - return 0; - return hapd->driver->flush(hapd->driver); -} - -static inline int -hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, - size_t elem_len) -{ - if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL) - return 0; - return hapd->driver->set_generic_elem(hapd->driver, elem, elem_len); -} - -static inline int -hostapd_read_sta_data(struct hostapd_data *hapd, - struct hostap_sta_driver_data *data, u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) - return -1; - return hapd->driver->read_sta_data(hapd->driver, data, addr); -} - -static inline int -hostapd_send_eapol(struct hostapd_data *hapd, u8 *addr, u8 *data, - size_t data_len, int encrypt) -{ - if (hapd->driver == NULL || hapd->driver->send_eapol == NULL) - return 0; - return hapd->driver->send_eapol(hapd->driver, addr, data, data_len, - encrypt); -} - -static inline int -hostapd_set_sta_authorized(struct hostapd_data *hapd, u8 *addr, int authorized) -{ - if (hapd->driver == NULL || hapd->driver->set_sta_authorized == NULL) - return 0; - return hapd->driver->set_sta_authorized(hapd->driver, addr, - authorized); -} - -static inline int -hostapd_sta_deauth(struct hostapd_data *hapd, u8 *addr, int reason) -{ - if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) - return 0; - return hapd->driver->sta_deauth(hapd->driver, addr, reason); -} - -static inline int -hostapd_sta_disassoc(struct hostapd_data *hapd, u8 *addr, int reason) -{ - if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) - return 0; - return hapd->driver->sta_disassoc(hapd->driver, addr, reason); -} - -static inline int -hostapd_sta_remove(struct hostapd_data *hapd, u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) - return 0; - return hapd->driver->sta_remove(hapd->driver, addr); -} - -static inline int -hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len) -{ - if (hapd->driver == NULL || hapd->driver->get_ssid == NULL) - return 0; - return hapd->driver->get_ssid(hapd->driver, buf, len); -} - -static inline int -hostapd_set_ssid(struct hostapd_data *hapd, u8 *buf, size_t len) -{ - if (hapd->driver == NULL || hapd->driver->set_ssid == NULL) - return 0; - return hapd->driver->set_ssid(hapd->driver, buf, len); -} - -static inline int -hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg, size_t len, - int flags) -{ - if (hapd->driver == NULL || hapd->driver->send_mgmt_frame == NULL) - return 0; - return hapd->driver->send_mgmt_frame(hapd->driver, msg, len, flags); -} - -static inline int -hostapd_set_assoc_ap(struct hostapd_data *hapd, u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->set_assoc_ap == NULL) - return 0; - return hapd->driver->set_assoc_ap(hapd->driver, addr); -} - -static inline int -hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) -{ - if (hapd->driver == NULL || hapd->driver->set_countermeasures == NULL) - return 0; - return hapd->driver->set_countermeasures(hapd->driver, enabled); -} - -static inline int -hostapd_sta_add(struct hostapd_data *hapd, u8 *addr, u16 aid, u16 capability, - u8 tx_supp_rates) -{ - if (hapd->driver == NULL || hapd->driver->sta_add == NULL) - return 0; - return hapd->driver->sta_add(hapd->driver, addr, aid, capability, - tx_supp_rates); -} - -static inline int -hostapd_get_inact_sec(struct hostapd_data *hapd, u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) - return 0; - return hapd->driver->get_inact_sec(hapd->driver, addr); -} - - -void driver_register(const char *name, const struct driver_ops *ops); -void driver_unregister(const char *name); -const struct driver_ops *driver_lookup(const char *name); - -static inline int -hostapd_sta_clear_stats(struct hostapd_data *hapd, u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) - return 0; - return hapd->driver->sta_clear_stats(hapd->driver, addr); -} - -#endif /* DRIVER_H */ diff --git a/contrib/hostapd-0.4.9/driver_wired.c b/contrib/hostapd-0.4.9/driver_wired.c deleted file mode 100644 index 09eb31990c..0000000000 --- a/contrib/hostapd-0.4.9/driver_wired.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / Kernel driver communication - * Copyright (c) 2002-2005, Jouni Malinen - * Copyright (c) 2004, Gunter Burchardt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef USE_KERNEL_HEADERS -#include -#include -#include /* The L2 protocols */ -#include -#include -#else /* USE_KERNEL_HEADERS */ -#include -#include -#include -#endif /* USE_KERNEL_HEADERS */ - -#include "hostapd.h" -#include "ieee802_1x.h" -#include "eloop.h" -#include "sta_info.h" -#include "driver.h" -#include "accounting.h" - - -struct wired_driver_data { - struct driver_ops ops; - struct hostapd_data *hapd; - - int sock; /* raw packet socket for driver access */ - int dhcp_sock; /* socket for dhcp packets */ - int use_pae_group_addr; -}; - -static const struct driver_ops wired_driver_ops; - - -#define WIRED_EAPOL_MULTICAST_GROUP {0x01,0x80,0xc2,0x00,0x00,0x03} - - -/* TODO: detecting new devices should eventually be changed from using DHCP - * snooping to trigger on any packet from a new layer 2 MAC address, e.g., - * based on ebtables, etc. */ - -struct dhcp_message { - u_int8_t op; - u_int8_t htype; - u_int8_t hlen; - u_int8_t hops; - u_int32_t xid; - u_int16_t secs; - u_int16_t flags; - u_int32_t ciaddr; - u_int32_t yiaddr; - u_int32_t siaddr; - u_int32_t giaddr; - u_int8_t chaddr[16]; - u_int8_t sname[64]; - u_int8_t file[128]; - u_int32_t cookie; - u_int8_t options[308]; /* 312 - cookie */ -}; - - -static void wired_possible_new_sta(struct hostapd_data *hapd, u8 *addr) -{ - struct sta_info *sta; - - sta = ap_get_sta(hapd, addr); - if (sta) - return; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Data frame from unknown STA " - MACSTR " - adding a new STA\n", MAC2STR(addr)); - sta = ap_sta_add(hapd, addr); - if (sta) { - hostapd_new_assoc_sta(hapd, sta, 0); - accounting_sta_get_id(hapd, sta); - } else { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Failed to add STA entry " - "for " MACSTR "\n", MAC2STR(addr)); - } -} - - -static void handle_data(struct hostapd_data *hapd, unsigned char *buf, - size_t len) -{ - struct ieee8023_hdr *hdr; - u8 *pos, *sa; - size_t left; - - /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest, - * 2 byte ethertype */ - if (len < 14) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, "handle_data: too short " - "(%lu)\n", (unsigned long) len); - return; - } - - hdr = (struct ieee8023_hdr *) buf; - - switch (ntohs(hdr->ethertype)) { - case ETH_P_PAE: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, - "Received EAPOL packet\n"); - sa = hdr->src; - wired_possible_new_sta(hapd, sa); - - pos = (u8 *) (hdr + 1); - left = len - sizeof(*hdr); - - ieee802_1x_receive(hapd, sa, pos, left); - break; - - default: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Unknown ethertype 0x%04x in data frame\n", - ntohs(hdr->ethertype)); - break; - } -} - - -static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; - int len; - unsigned char buf[3000]; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - perror("recv"); - return; - } - - handle_data(hapd, buf, len); -} - - -static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; - int len; - unsigned char buf[3000]; - struct dhcp_message *msg; - u8 *mac_address; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - perror("recv"); - return; - } - - /* must contain at least dhcp_message->chaddr */ - if (len < 44) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, "handle_dhcp: too short " - "(%d)\n", len); - return; - } - - msg = (struct dhcp_message *) buf; - mac_address = (u8 *) &(msg->chaddr); - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, - "Got DHCP broadcast packet from " MACSTR "\n", - MAC2STR(mac_address)); - - wired_possible_new_sta(hapd, mac_address); -} - - -static int wired_init_sockets(struct wired_driver_data *drv) -{ - struct hostapd_data *hapd = drv->hapd; - struct ifreq ifr; - struct sockaddr_ll addr; - struct sockaddr_in addr2; - struct packet_mreq mreq; - u8 multicastgroup_eapol[6] = WIRED_EAPOL_MULTICAST_GROUP; - int n = 1; - - drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); - if (drv->sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); - return -1; - } - - if (eloop_register_read_sock(drv->sock, handle_read, hapd, NULL)) { - printf("Could not register read socket\n"); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", - hapd->conf->iface); - if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); - return -1; - } - - - memset(&addr, 0, sizeof(addr)); - addr.sll_family = AF_PACKET; - addr.sll_ifindex = ifr.ifr_ifindex; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Opening raw packet socket for ifindex %d\n", - addr.sll_ifindex); - - if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); - return -1; - } - - /* filter multicast address */ - memset(&mreq, 0, sizeof(mreq)); - mreq.mr_ifindex = ifr.ifr_ifindex; - mreq.mr_type = PACKET_MR_MULTICAST; - mreq.mr_alen = 6; - memcpy(mreq.mr_address, multicastgroup_eapol, mreq.mr_alen); - - if (setsockopt(drv->sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, - sizeof(mreq)) < 0) { - perror("setsockopt[SOL_SOCKET,PACKET_ADD_MEMBERSHIP]"); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", hapd->conf->iface); - if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFHWADDR)"); - return -1; - } - - if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { - printf("Invalid HW-addr family 0x%04x\n", - ifr.ifr_hwaddr.sa_family); - return -1; - } - memcpy(hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - - /* setup dhcp listen socket for sta detection */ - if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - perror("socket call failed for dhcp"); - return -1; - } - - if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, hapd, NULL)) - { - printf("Could not register read socket\n"); - return -1; - } - - memset(&addr2, 0, sizeof(addr2)); - addr2.sin_family = AF_INET; - addr2.sin_port = htons(67); - addr2.sin_addr.s_addr = INADDR_ANY; - - if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n, - sizeof(n)) == -1) { - perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]"); - return -1; - } - if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n, - sizeof(n)) == -1) { - perror("setsockopt[SOL_SOCKET,SO_BROADCAST]"); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_ifrn.ifrn_name, hapd->conf->iface, IFNAMSIZ); - if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, - (char *) &ifr, sizeof(ifr)) < 0) { - perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]"); - return -1; - } - - if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2, - sizeof(struct sockaddr)) == -1) { - perror("bind"); - return -1; - } - - return 0; -} - - -static int wired_send_eapol(void *priv, u8 *addr, - u8 *data, size_t data_len, int encrypt) -{ - struct wired_driver_data *drv = priv; - u8 pae_group_addr[ETH_ALEN] = WIRED_EAPOL_MULTICAST_GROUP; - struct ieee8023_hdr *hdr; - size_t len; - u8 *pos; - int res; - - len = sizeof(*hdr) + data_len; - hdr = malloc(len); - if (hdr == NULL) { - printf("malloc() failed for wired_send_eapol(len=%lu)\n", - (unsigned long) len); - return -1; - } - - memset(hdr, 0, len); - memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, - ETH_ALEN); - memcpy(hdr->src, drv->hapd->own_addr, ETH_ALEN); - hdr->ethertype = htons(ETH_P_PAE); - - pos = (u8 *) (hdr + 1); - memcpy(pos, data, data_len); - - res = send(drv->sock, (u8 *) hdr, len, 0); - free(hdr); - - if (res < 0) { - perror("wired_send_eapol: send"); - printf("wired_send_eapol - packet len: %lu - failed\n", - (unsigned long) len); - } - - return res; -} - - -static int wired_driver_init(struct hostapd_data *hapd) -{ - struct wired_driver_data *drv; - - drv = malloc(sizeof(struct wired_driver_data)); - if (drv == NULL) { - printf("Could not allocate memory for wired driver data\n"); - return -1; - } - - memset(drv, 0, sizeof(*drv)); - drv->ops = wired_driver_ops; - drv->hapd = hapd; - drv->use_pae_group_addr = hapd->conf->use_pae_group_addr; - - if (wired_init_sockets(drv)) - return -1; - - hapd->driver = &drv->ops; - return 0; -} - - -static void wired_driver_deinit(void *priv) -{ - struct wired_driver_data *drv = priv; - - drv->hapd->driver = NULL; - - if (drv->sock >= 0) - close(drv->sock); - - if (drv->dhcp_sock >= 0) - close(drv->dhcp_sock); - - free(drv); -} - - -static const struct driver_ops wired_driver_ops = { - .name = "wired", - .init = wired_driver_init, - .deinit = wired_driver_deinit, - .send_eapol = wired_send_eapol, -}; - -void wired_driver_register(void) -{ - driver_register(wired_driver_ops.name, &wired_driver_ops); -} diff --git a/contrib/hostapd-0.4.9/eap.c b/contrib/hostapd-0.4.9/eap.c deleted file mode 100644 index a20147e79e..0000000000 --- a/contrib/hostapd-0.4.9/eap.c +++ /dev/null @@ -1,944 +0,0 @@ -/* - * hostapd / EAP Standalone Authenticator state machine - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include "hostapd.h" -#include "eloop.h" -#include "sta_info.h" -#include "eap_i.h" - -#define EAP_MAX_AUTH_ROUNDS 50 - -extern const struct eap_method eap_method_identity; -#ifdef EAP_MD5 -extern const struct eap_method eap_method_md5; -#endif /* EAP_MD5 */ -#ifdef EAP_TLS -extern const struct eap_method eap_method_tls; -#endif /* EAP_TLS */ -#ifdef EAP_MSCHAPv2 -extern const struct eap_method eap_method_mschapv2; -#endif /* EAP_MSCHAPv2 */ -#ifdef EAP_PEAP -extern const struct eap_method eap_method_peap; -#endif /* EAP_PEAP */ -#ifdef EAP_TLV -extern const struct eap_method eap_method_tlv; -#endif /* EAP_TLV */ -#ifdef EAP_GTC -extern const struct eap_method eap_method_gtc; -#endif /* EAP_GTC */ -#ifdef EAP_TTLS -extern const struct eap_method eap_method_ttls; -#endif /* EAP_TTLS */ -#ifdef EAP_SIM -extern const struct eap_method eap_method_sim; -#endif /* EAP_SIM */ -#ifdef EAP_PAX -extern const struct eap_method eap_method_pax; -#endif /* EAP_PAX */ -#ifdef EAP_PSK -extern const struct eap_method eap_method_psk; -#endif /* EAP_PSK */ - -static const struct eap_method *eap_methods[] = -{ - &eap_method_identity, -#ifdef EAP_MD5 - &eap_method_md5, -#endif /* EAP_MD5 */ -#ifdef EAP_TLS - &eap_method_tls, -#endif /* EAP_TLS */ -#ifdef EAP_MSCHAPv2 - &eap_method_mschapv2, -#endif /* EAP_MSCHAPv2 */ -#ifdef EAP_PEAP - &eap_method_peap, -#endif /* EAP_PEAP */ -#ifdef EAP_TTLS - &eap_method_ttls, -#endif /* EAP_TTLS */ -#ifdef EAP_TLV - &eap_method_tlv, -#endif /* EAP_TLV */ -#ifdef EAP_GTC - &eap_method_gtc, -#endif /* EAP_GTC */ -#ifdef EAP_SIM - &eap_method_sim, -#endif /* EAP_SIM */ -#ifdef EAP_PAX - &eap_method_pax, -#endif /* EAP_PAX */ -#ifdef EAP_PSK - &eap_method_psk, -#endif /* EAP_PSK */ -}; -#define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0])) - - -const struct eap_method * eap_sm_get_eap_methods(int method) -{ - int i; - for (i = 0; i < NUM_EAP_METHODS; i++) { - if (eap_methods[i]->method == method) - return eap_methods[i]; - } - return NULL; -} - -static void eap_user_free(struct eap_user *user); - - -/* EAP state machines are described in draft-ietf-eap-statemachine-05.txt */ - -static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, - int eapSRTT, int eapRTTVAR, - int methodTimeout); -static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len); -static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len); -static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len); -static int eap_sm_nextId(struct eap_sm *sm, int id); -static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len); -static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm); -static int eap_sm_Policy_getDecision(struct eap_sm *sm); -static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method); - - -/* Definitions for clarifying state machine implementation */ -#define SM_STATE(machine, state) \ -static void sm_ ## machine ## _ ## state ## _Enter(struct eap_sm *sm, \ - int global) - -#define SM_ENTRY(machine, state) \ -if (!global || sm->machine ## _state != machine ## _ ## state) { \ - sm->changed = TRUE; \ - wpa_printf(MSG_DEBUG, "EAP: " #machine " entering state " #state); \ -} \ -sm->machine ## _state = machine ## _ ## state; - -#define SM_ENTER(machine, state) \ -sm_ ## machine ## _ ## state ## _Enter(sm, 0) -#define SM_ENTER_GLOBAL(machine, state) \ -sm_ ## machine ## _ ## state ## _Enter(sm, 1) - -#define SM_STEP(machine) \ -static void sm_ ## machine ## _Step(struct eap_sm *sm) - -#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) - - -static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) -{ - return sm->eapol_cb->get_bool(sm->eapol_ctx, var); -} - - -static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, - Boolean value) -{ - sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); -} - - -static void eapol_set_eapReqData(struct eap_sm *sm, - const u8 *eapReqData, size_t eapReqDataLen) -{ - wpa_hexdump(MSG_MSGDUMP, "EAP: eapReqData -> EAPOL", - sm->eapReqData, sm->eapReqDataLen); - sm->eapol_cb->set_eapReqData(sm->eapol_ctx, eapReqData, eapReqDataLen); -} - - -static void eapol_set_eapKeyData(struct eap_sm *sm, - const u8 *eapKeyData, size_t eapKeyDataLen) -{ - wpa_hexdump(MSG_MSGDUMP, "EAP: eapKeyData -> EAPOL", - sm->eapKeyData, sm->eapKeyDataLen); - sm->eapol_cb->set_eapKeyData(sm->eapol_ctx, eapKeyData, eapKeyDataLen); -} - - -int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, - int phase2) -{ - struct eap_user *user; - - if (sm == NULL || sm->eapol_cb == NULL || - sm->eapol_cb->get_eap_user == NULL) - return -1; - - eap_user_free(sm->user); - sm->user = NULL; - - user = malloc(sizeof(*user)); - if (user == NULL) - return -1; - memset(user, 0, sizeof(*user)); - - if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity, - identity_len, phase2, user) != 0) { - eap_user_free(user); - return -1; - } - - sm->user = user; - sm->user_eap_method_index = 0; - - return 0; -} - - -SM_STATE(EAP, DISABLED) -{ - SM_ENTRY(EAP, DISABLED); - sm->num_rounds = 0; -} - - -SM_STATE(EAP, INITIALIZE) -{ - SM_ENTRY(EAP, INITIALIZE); - - sm->currentId = -1; - eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); - eapol_set_bool(sm, EAPOL_eapFail, FALSE); - eapol_set_bool(sm, EAPOL_eapTimeout, FALSE); - free(sm->eapKeyData); - sm->eapKeyData = NULL; - sm->eapKeyDataLen = 0; - /* eapKeyAvailable = FALSE */ - eapol_set_bool(sm, EAPOL_eapRestart, FALSE); - - /* This is not defined in draft-ietf-eap-statemachine-05.txt, but - * method state needs to be reseted here so that it does not remain in - * success state when re-authentication starts. */ - if (sm->m && sm->eap_method_priv) { - sm->m->reset(sm, sm->eap_method_priv); - sm->eap_method_priv = NULL; - } - sm->m = NULL; - sm->user_eap_method_index = 0; - - if (sm->backend_auth) { - sm->currentMethod = EAP_TYPE_NONE; - /* parse rxResp, respId, respMethod */ - eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen); - if (sm->rxResp) { - sm->currentId = sm->respId; - } - } - sm->num_rounds = 0; -} - - -SM_STATE(EAP, PICK_UP_METHOD) -{ - SM_ENTRY(EAP, PICK_UP_METHOD); - - if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) { - sm->currentMethod = sm->respMethod; - if (sm->m && sm->eap_method_priv) { - sm->m->reset(sm, sm->eap_method_priv); - sm->eap_method_priv = NULL; - } - sm->m = eap_sm_get_eap_methods(sm->currentMethod); - if (sm->m && sm->m->initPickUp) { - sm->eap_method_priv = sm->m->initPickUp(sm); - if (sm->eap_method_priv == NULL) { - wpa_printf(MSG_DEBUG, "EAP: Failed to " - "initialize EAP method %d", - sm->currentMethod); - sm->m = NULL; - sm->currentMethod = EAP_TYPE_NONE; - } - } else { - sm->m = NULL; - sm->currentMethod = EAP_TYPE_NONE; - } - } -} - - -SM_STATE(EAP, IDLE) -{ - SM_ENTRY(EAP, IDLE); - - sm->retransWhile = eap_sm_calculateTimeout(sm, sm->retransCount, - sm->eapSRTT, sm->eapRTTVAR, - sm->methodTimeout); -} - - -SM_STATE(EAP, RETRANSMIT) -{ - SM_ENTRY(EAP, RETRANSMIT); - - /* TODO: Is this needed since EAPOL state machines take care of - * retransmit? */ -} - - -SM_STATE(EAP, RECEIVED) -{ - SM_ENTRY(EAP, RECEIVED); - - /* parse rxResp, respId, respMethod */ - eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen); - sm->num_rounds++; -} - - -SM_STATE(EAP, DISCARD) -{ - SM_ENTRY(EAP, DISCARD); - eapol_set_bool(sm, EAPOL_eapResp, FALSE); - eapol_set_bool(sm, EAPOL_eapNoReq, TRUE); -} - - -SM_STATE(EAP, SEND_REQUEST) -{ - SM_ENTRY(EAP, SEND_REQUEST); - - sm->retransCount = 0; - if (sm->eapReqData) { - eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); - free(sm->lastReqData); - sm->lastReqData = sm->eapReqData; - sm->lastReqDataLen = sm->eapReqDataLen; - sm->eapReqData = NULL; - sm->eapReqDataLen = 0; - eapol_set_bool(sm, EAPOL_eapResp, FALSE); - eapol_set_bool(sm, EAPOL_eapReq, TRUE); - } else { - wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData"); - eapol_set_bool(sm, EAPOL_eapResp, FALSE); - eapol_set_bool(sm, EAPOL_eapReq, FALSE); - eapol_set_bool(sm, EAPOL_eapNoReq, TRUE); - } -} - - -SM_STATE(EAP, INTEGRITY_CHECK) -{ - SM_ENTRY(EAP, INTEGRITY_CHECK); - - if (sm->m->check) { - sm->ignore = sm->m->check(sm, sm->eap_method_priv, - sm->eapRespData, sm->eapRespDataLen); - } -} - - -SM_STATE(EAP, METHOD_REQUEST) -{ - SM_ENTRY(EAP, METHOD_REQUEST); - - if (sm->m == NULL) { - wpa_printf(MSG_DEBUG, "EAP: method not initialized"); - return; - } - - sm->currentId = eap_sm_nextId(sm, sm->currentId); - wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d", - sm->currentId); - sm->lastId = sm->currentId; - free(sm->eapReqData); - sm->eapReqData = sm->m->buildReq(sm, sm->eap_method_priv, - sm->currentId, &sm->eapReqDataLen); - if (sm->m->getTimeout) - sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv); - else - sm->methodTimeout = 0; -} - - -SM_STATE(EAP, METHOD_RESPONSE) -{ - SM_ENTRY(EAP, METHOD_RESPONSE); - - sm->m->process(sm, sm->eap_method_priv, sm->eapRespData, - sm->eapRespDataLen); - if (sm->m->isDone(sm, sm->eap_method_priv)) { - eap_sm_Policy_update(sm, NULL, 0); - free(sm->eapKeyData); - if (sm->m->getKey) { - sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, - &sm->eapKeyDataLen); - } else { - sm->eapKeyData = NULL; - sm->eapKeyDataLen = 0; - } - sm->methodState = METHOD_END; - } else { - sm->methodState = METHOD_CONTINUE; - } -} - - -SM_STATE(EAP, PROPOSE_METHOD) -{ - SM_ENTRY(EAP, PROPOSE_METHOD); - - sm->currentMethod = eap_sm_Policy_getNextMethod(sm); - if (sm->m && sm->eap_method_priv) { - sm->m->reset(sm, sm->eap_method_priv); - sm->eap_method_priv = NULL; - } - sm->m = eap_sm_get_eap_methods(sm->currentMethod); - if (sm->m) { - sm->eap_method_priv = sm->m->init(sm); - if (sm->eap_method_priv == NULL) { - wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP " - "method %d", sm->currentMethod); - sm->m = NULL; - sm->currentMethod = EAP_TYPE_NONE; - } - } - if (sm->currentMethod == EAP_TYPE_IDENTITY || - sm->currentMethod == EAP_TYPE_NOTIFICATION) - sm->methodState = METHOD_CONTINUE; - else - sm->methodState = METHOD_PROPOSED; -} - - -SM_STATE(EAP, NAK) -{ - struct eap_hdr *nak; - size_t len = 0; - u8 *pos, *nak_list = NULL; - - SM_ENTRY(EAP, NAK); - - if (sm->eap_method_priv) { - sm->m->reset(sm, sm->eap_method_priv); - sm->eap_method_priv = NULL; - } - sm->m = NULL; - - nak = (struct eap_hdr *) sm->eapRespData; - if (nak && sm->eapRespDataLen > sizeof(*nak)) { - len = ntohs(nak->length); - if (len > sm->eapRespDataLen) - len = sm->eapRespDataLen; - pos = (u8 *) (nak + 1); - len -= sizeof(*nak); - if (*pos == EAP_TYPE_NAK) { - pos++; - len--; - nak_list = pos; - } - } - eap_sm_Policy_update(sm, nak_list, len); -} - - -SM_STATE(EAP, SELECT_ACTION) -{ - SM_ENTRY(EAP, SELECT_ACTION); - - sm->decision = eap_sm_Policy_getDecision(sm); -} - - -SM_STATE(EAP, TIMEOUT_FAILURE) -{ - SM_ENTRY(EAP, TIMEOUT_FAILURE); - - eapol_set_bool(sm, EAPOL_eapTimeout, TRUE); -} - - -SM_STATE(EAP, FAILURE) -{ - SM_ENTRY(EAP, FAILURE); - - free(sm->eapReqData); - sm->eapReqData = eap_sm_buildFailure(sm, sm->currentId, - &sm->eapReqDataLen); - if (sm->eapReqData) { - eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); - free(sm->eapReqData); - sm->eapReqData = NULL; - sm->eapReqDataLen = 0; - } - free(sm->lastReqData); - sm->lastReqData = NULL; - sm->lastReqDataLen = 0; - eapol_set_bool(sm, EAPOL_eapFail, TRUE); -} - - -SM_STATE(EAP, SUCCESS) -{ - SM_ENTRY(EAP, SUCCESS); - - free(sm->eapReqData); - sm->eapReqData = eap_sm_buildSuccess(sm, sm->currentId, - &sm->eapReqDataLen); - if (sm->eapReqData) { - eapol_set_eapReqData(sm, sm->eapReqData, sm->eapReqDataLen); - free(sm->eapReqData); - sm->eapReqData = NULL; - sm->eapReqDataLen = 0; - } - free(sm->lastReqData); - sm->lastReqData = NULL; - sm->lastReqDataLen = 0; - if (sm->eapKeyData) { - eapol_set_eapKeyData(sm, sm->eapKeyData, sm->eapKeyDataLen); - } - eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); -} - - -SM_STEP(EAP) -{ - if (eapol_get_bool(sm, EAPOL_eapRestart) && - eapol_get_bool(sm, EAPOL_portEnabled)) - SM_ENTER_GLOBAL(EAP, INITIALIZE); - else if (!eapol_get_bool(sm, EAPOL_portEnabled)) - SM_ENTER_GLOBAL(EAP, DISABLED); - else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { - if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { - wpa_printf(MSG_DEBUG, "EAP: more than %d " - "authentication rounds - abort", - EAP_MAX_AUTH_ROUNDS); - sm->num_rounds++; - SM_ENTER_GLOBAL(EAP, FAILURE); - } - } else switch (sm->EAP_state) { - case EAP_INITIALIZE: - if (sm->backend_auth) { - if (!sm->rxResp) - SM_ENTER(EAP, SELECT_ACTION); - else if (sm->rxResp && - (sm->respMethod == EAP_TYPE_NAK || - sm->respMethod == EAP_TYPE_EXPANDED_NAK)) - SM_ENTER(EAP, NAK); - else - SM_ENTER(EAP, PICK_UP_METHOD); - } else { - SM_ENTER(EAP, SELECT_ACTION); - } - break; - case EAP_PICK_UP_METHOD: - if (sm->currentMethod == EAP_TYPE_NONE) { - SM_ENTER(EAP, SELECT_ACTION); - } else { - SM_ENTER(EAP, METHOD_RESPONSE); - } - break; - case EAP_DISABLED: - if (eapol_get_bool(sm, EAPOL_portEnabled)) - SM_ENTER(EAP, INITIALIZE); - break; - case EAP_IDLE: - if (sm->retransWhile == 0) - SM_ENTER(EAP, RETRANSMIT); - else if (eapol_get_bool(sm, EAPOL_eapResp)) - SM_ENTER(EAP, RECEIVED); - break; - case EAP_RETRANSMIT: - if (sm->retransCount > sm->MaxRetrans) - SM_ENTER(EAP, TIMEOUT_FAILURE); - else - SM_ENTER(EAP, IDLE); - break; - case EAP_RECEIVED: - if (sm->rxResp && (sm->respId == sm->currentId) && - (sm->respMethod == EAP_TYPE_NAK || - sm->respMethod == EAP_TYPE_EXPANDED_NAK) - && (sm->methodState == METHOD_PROPOSED)) - SM_ENTER(EAP, NAK); - else if (sm->rxResp && (sm->respId == sm->currentId) && - (sm->respMethod == sm->currentMethod)) - SM_ENTER(EAP, INTEGRITY_CHECK); - else - SM_ENTER(EAP, DISCARD); - break; - case EAP_DISCARD: - SM_ENTER(EAP, IDLE); - break; - case EAP_SEND_REQUEST: - SM_ENTER(EAP, IDLE); - break; - case EAP_INTEGRITY_CHECK: - if (sm->ignore) - SM_ENTER(EAP, DISCARD); - else - SM_ENTER(EAP, METHOD_RESPONSE); - break; - case EAP_METHOD_REQUEST: - SM_ENTER(EAP, SEND_REQUEST); - break; - case EAP_METHOD_RESPONSE: - if (sm->methodState == METHOD_END) - SM_ENTER(EAP, SELECT_ACTION); - else - SM_ENTER(EAP, METHOD_REQUEST); - break; - case EAP_PROPOSE_METHOD: - SM_ENTER(EAP, METHOD_REQUEST); - break; - case EAP_NAK: - SM_ENTER(EAP, SELECT_ACTION); - break; - case EAP_SELECT_ACTION: - if (sm->decision == DECISION_FAILURE) - SM_ENTER(EAP, FAILURE); - else if (sm->decision == DECISION_SUCCESS) - SM_ENTER(EAP, SUCCESS); - else - SM_ENTER(EAP, PROPOSE_METHOD); - break; - case EAP_TIMEOUT_FAILURE: - break; - case EAP_FAILURE: - break; - case EAP_SUCCESS: - break; - } -} - - -static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, - int eapSRTT, int eapRTTVAR, - int methodTimeout) -{ - /* For now, retransmission is done in EAPOL state machines, so make - * sure EAP state machine does not end up trying to retransmit packets. - */ - return 1; -} - - -static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len) -{ - struct eap_hdr *hdr; - size_t plen; - - /* parse rxResp, respId, respMethod */ - sm->rxResp = FALSE; - sm->respId = -1; - sm->respMethod = EAP_TYPE_NONE; - - if (resp == NULL || len < sizeof(*hdr)) - return; - - hdr = (struct eap_hdr *) resp; - plen = ntohs(hdr->length); - if (plen > len) { - wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " - "(len=%lu plen=%lu)", (unsigned long) len, - (unsigned long) plen); - return; - } - - sm->respId = hdr->identifier; - - if (hdr->code == EAP_CODE_RESPONSE) - sm->rxResp = TRUE; - - if (len > sizeof(*hdr)) - sm->respMethod = *((u8 *) (hdr + 1)); - - wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d " - "respMethod=%d", sm->rxResp, sm->respId, sm->respMethod); -} - - -static u8 * eap_sm_buildSuccess(struct eap_sm *sm, int id, size_t *len) -{ - struct eap_hdr *resp; - wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id); - - *len = sizeof(*resp); - resp = malloc(*len); - if (resp == NULL) - return NULL; - resp->code = EAP_CODE_SUCCESS; - resp->identifier = id; - resp->length = htons(*len); - - return (u8 *) resp; -} - - -static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len) -{ - struct eap_hdr *resp; - wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id); - - *len = sizeof(*resp); - resp = malloc(*len); - if (resp == NULL) - return NULL; - resp->code = EAP_CODE_FAILURE; - resp->identifier = id; - resp->length = htons(*len); - - return (u8 *) resp; -} - - -static int eap_sm_nextId(struct eap_sm *sm, int id) -{ - if (id < 0) { - /* RFC 3748 Ch 4.1: recommended to initalize Identifier with a - * random number */ - id = rand() & 0xff; - if (id != sm->lastId) - return id; - } - return (id + 1) & 0xff; -} - - -void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len) -{ - int i, j; - - wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method " - "index %d)", sm->user_eap_method_index); - - wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods", - sm->user->methods, EAP_MAX_METHODS); - wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer", - nak_list, len); - - i = sm->user_eap_method_index; - while (i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE) { - for (j = 0; j < len; j++) { - if (nak_list[j] == sm->user->methods[i]) { - break; - } - } - - if (j < len) { - /* found */ - i++; - continue; - } - - /* not found - remove from the list */ - memmove(&sm->user->methods[i], &sm->user->methods[i + 1], - EAP_MAX_METHODS - i - 1); - sm->user->methods[EAP_MAX_METHODS - 1] = EAP_TYPE_NONE; - } - - wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods", - sm->user->methods, EAP_MAX_METHODS); -} - - -static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len) -{ - if (nak_list == NULL || sm == NULL || sm->user == NULL) - return; - - if (sm->user->phase2) { - wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user" - " info was selected - reject"); - sm->decision = DECISION_FAILURE; - return; - } - - eap_sm_process_nak(sm, nak_list, len); -} - - -static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm) -{ - EapType next; - - /* In theory, there should be no problems with starting - * re-authentication with something else than EAP-Request/Identity and - * this does indeed work with wpa_supplicant. However, at least Funk - * Supplicant seemed to ignore re-auth if it skipped - * EAP-Request/Identity. - * Re-auth sets currentId == -1, so that can be used here to select - * whether Identity needs to be requested again. */ - if (sm->identity == NULL || sm->currentId == -1) { - next = EAP_TYPE_IDENTITY; - sm->update_user = TRUE; - } else if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index] != - EAP_TYPE_NONE) { - next = sm->user->methods[sm->user_eap_method_index++]; - } else { - next = EAP_TYPE_NONE; - } - wpa_printf(MSG_DEBUG, "EAP: getNextMethod: type %d", next); - return next; -} - - -static int eap_sm_Policy_getDecision(struct eap_sm *sm) -{ - if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY && - sm->m->isSuccess(sm, sm->eap_method_priv)) { - wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> " - "SUCCESS"); - sm->update_user = TRUE; - return DECISION_SUCCESS; - } - - if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) && - !sm->m->isSuccess(sm, sm->eap_method_priv)) { - wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> " - "FAILURE"); - sm->update_user = TRUE; - return DECISION_FAILURE; - } - - if ((sm->user == NULL || sm->update_user) && sm->identity) { - if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) { - wpa_printf(MSG_DEBUG, "EAP: getDecision: user not " - "found from database -> FAILURE"); - return DECISION_FAILURE; - } - sm->update_user = FALSE; - } - - if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index] != EAP_TYPE_NONE) { - wpa_printf(MSG_DEBUG, "EAP: getDecision: another method " - "available -> CONTINUE"); - return DECISION_CONTINUE; - } - - if (sm->identity == NULL || sm->currentId == -1) { - wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known " - "yet -> CONTINUE"); - return DECISION_CONTINUE; - } - - wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> " - "FAILURE"); - return DECISION_FAILURE; -} - - -static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method) -{ - return method == EAP_TYPE_IDENTITY ? TRUE : FALSE; -} - - -int eap_sm_step(struct eap_sm *sm) -{ - int res = 0; - do { - sm->changed = FALSE; - SM_STEP_RUN(EAP); - if (sm->changed) - res = 1; - } while (sm->changed); - return res; -} - - -u8 eap_get_type(const char *name) -{ - int i; - for (i = 0; i < NUM_EAP_METHODS; i++) { - if (strcmp(eap_methods[i]->name, name) == 0) - return eap_methods[i]->method; - } - return EAP_TYPE_NONE; -} - - -void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData, - size_t eapRespDataLen) -{ - if (sm == NULL) - return; - free(sm->eapRespData); - sm->eapRespData = malloc(eapRespDataLen); - if (sm->eapRespData == NULL) - return; - memcpy(sm->eapRespData, eapRespData, eapRespDataLen); - sm->eapRespDataLen = eapRespDataLen; - wpa_hexdump(MSG_MSGDUMP, "EAP: EAP-Response received", - eapRespData, eapRespDataLen); -} - - -static void eap_user_free(struct eap_user *user) -{ - if (user == NULL) - return; - free(user->password); - user->password = NULL; - free(user); -} - - -struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, - struct eap_config *eap_conf) -{ - struct eap_sm *sm; - - sm = malloc(sizeof(*sm)); - if (sm == NULL) - return NULL; - memset(sm, 0, sizeof(*sm)); - sm->eapol_ctx = eapol_ctx; - sm->eapol_cb = eapol_cb; - sm->MaxRetrans = 10; - sm->ssl_ctx = eap_conf->ssl_ctx; - sm->eap_sim_db_priv = eap_conf->eap_sim_db_priv; - sm->backend_auth = eap_conf->backend_auth; - - wpa_printf(MSG_DEBUG, "EAP: State machine created"); - - return sm; -} - - -void eap_sm_deinit(struct eap_sm *sm) -{ - if (sm == NULL) - return; - wpa_printf(MSG_DEBUG, "EAP: State machine removed"); - if (sm->m && sm->eap_method_priv) - sm->m->reset(sm, sm->eap_method_priv); - free(sm->eapReqData); - free(sm->eapKeyData); - free(sm->lastReqData); - free(sm->eapRespData); - free(sm->identity); - eap_user_free(sm->user); - free(sm); -} - - -void eap_sm_notify_cached(struct eap_sm *sm) -{ - if (sm == NULL) - return; - - sm->EAP_state = EAP_SUCCESS; -} diff --git a/contrib/hostapd-0.4.9/eap.h b/contrib/hostapd-0.4.9/eap.h deleted file mode 100644 index c5c62eb57c..0000000000 --- a/contrib/hostapd-0.4.9/eap.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef EAP_H -#define EAP_H - -#include "defs.h" -#include "eap_defs.h" - -struct eap_sm; - -#define EAP_MAX_METHODS 8 -struct eap_user { - u8 methods[EAP_MAX_METHODS]; - u8 *password; - size_t password_len; - int phase2; - int force_version; -}; - -enum eapol_bool_var { - EAPOL_eapSuccess, EAPOL_eapRestart, EAPOL_eapFail, EAPOL_eapResp, - EAPOL_eapReq, EAPOL_eapNoReq, EAPOL_portEnabled, EAPOL_eapTimeout -}; - -struct eapol_callbacks { - Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable); - void (*set_bool)(void *ctx, enum eapol_bool_var variable, - Boolean value); - void (*set_eapReqData)(void *ctx, const u8 *eapReqData, - size_t eapReqDataLen); - void (*set_eapKeyData)(void *ctx, const u8 *eapKeyData, - size_t eapKeyDataLen); - int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, - int phase2, struct eap_user *user); - const char * (*get_eap_req_id_text)(void *ctx, size_t *len); -}; - -struct eap_config { - void *ssl_ctx; - void *eap_sim_db_priv; - Boolean backend_auth; -}; - - -#ifdef EAP_SERVER - -struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, - struct eap_config *eap_conf); -void eap_sm_deinit(struct eap_sm *sm); -int eap_sm_step(struct eap_sm *sm); -u8 eap_get_type(const char *name); -void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData, - size_t eapRespDataLen); -void eap_sm_notify_cached(struct eap_sm *sm); - -#else /* EAP_SERVER */ - -static inline struct eap_sm * eap_sm_init(void *eapol_ctx, - struct eapol_callbacks *eapol_cb, - struct eap_config *eap_conf) -{ - return NULL; -} - -static inline void eap_sm_deinit(struct eap_sm *sm) -{ -} - -static inline int eap_sm_step(struct eap_sm *sm) -{ - return 0; -} - -static inline u8 eap_get_type(const char *name) -{ - return EAP_TYPE_NONE; -} - -static inline void eap_set_eapRespData(struct eap_sm *sm, - const u8 *eapRespData, - size_t eapRespDataLen) -{ -} - -static inline void eap_sm_notify_cached(struct eap_sm *sm) -{ -} - -#endif /* EAP_SERVER */ - -#endif /* EAP_H */ diff --git a/contrib/hostapd-0.4.9/eap_defs.h b/contrib/hostapd-0.4.9/eap_defs.h deleted file mode 100644 index 9cd4490515..0000000000 --- a/contrib/hostapd-0.4.9/eap_defs.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * WPA Supplicant/hostapd / Shared EAP definitions - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef EAP_DEFS_H -#define EAP_DEFS_H - -/* RFC 3748 - Extensible Authentication Protocol (EAP) */ - -struct eap_hdr { - u8 code; - u8 identifier; - u16 length; /* including code and identifier; network byte order */ - /* followed by length-4 octets of data */ -} __attribute__ ((packed)); - -enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, - EAP_CODE_FAILURE = 4 }; - -/* EAP Request and Response data begins with one octet Type. Success and - * Failure do not have additional data. */ - -typedef enum { - EAP_TYPE_NONE = 0, - EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, - EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, - EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */, - EAP_TYPE_MD5 = 4, /* RFC 3748 */ - EAP_TYPE_OTP = 5 /* RFC 3748 */, - EAP_TYPE_GTC = 6, /* RFC 3748 */ - EAP_TYPE_TLS = 13 /* RFC 2716 */, - EAP_TYPE_LEAP = 17 /* Cisco proprietary */, - EAP_TYPE_SIM = 18 /* draft-haverinen-pppext-eap-sim-12.txt */, - EAP_TYPE_TTLS = 21 /* draft-ietf-pppext-eap-ttls-02.txt */, - EAP_TYPE_AKA = 23 /* draft-arkko-pppext-eap-aka-12.txt */, - EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */, - EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, - EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, - EAP_TYPE_FAST = 43 /* draft-cam-winget-eap-fast-00.txt */, - EAP_TYPE_PAX = 46, /* draft-clancy-eap-pax-04.txt */ - EAP_TYPE_EXPANDED_NAK = 254 /* RFC 3748 */, - EAP_TYPE_PSK = 255 /* EXPERIMENTAL - type not yet allocated - * draft-bersani-eap-psk-09 */ -} EapType; - -#endif /* EAP_DEFS_H */ diff --git a/contrib/hostapd-0.4.9/eap_gtc.c b/contrib/hostapd-0.4.9/eap_gtc.c deleted file mode 100644 index 674f83735f..0000000000 --- a/contrib/hostapd-0.4.9/eap_gtc.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * hostapd / EAP-GTC (RFC 3748) - * Copyright (c) 2004, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" - - -struct eap_gtc_data { - enum { CONTINUE, SUCCESS, FAILURE } state; -}; - - -static void * eap_gtc_init(struct eap_sm *sm) -{ - struct eap_gtc_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->state = CONTINUE; - - return data; -} - - -static void eap_gtc_reset(struct eap_sm *sm, void *priv) -{ - struct eap_gtc_data *data = priv; - free(data); -} - - -static u8 * eap_gtc_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_gtc_data *data = priv; - struct eap_hdr *req; - u8 *pos; - char *msg = "Password"; - size_t msg_len; - - msg_len = strlen(msg); - *reqDataLen = sizeof(*req) + 1 + msg_len; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for " - "request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_GTC; - memcpy(pos, msg, msg_len); - - data->state = CONTINUE; - - return (u8 *) req; -} - - -static Boolean eap_gtc_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_hdr *resp; - u8 *pos; - size_t len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_GTC || - (len = ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame"); - return TRUE; - } - - return FALSE; -} - - -static void eap_gtc_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_gtc_data *data = priv; - struct eap_hdr *resp; - u8 *pos; - size_t rlen; - - if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-GTC: Password not configured"); - data->state = FAILURE; - return; - } - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos++; - rlen = ntohs(resp->length) - sizeof(*resp) - 1; - wpa_hexdump_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen); - - if (rlen != sm->user->password_len || - memcmp(pos, sm->user->password, rlen) != 0) { - wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure"); - data->state = FAILURE; - } else { - wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Success"); - data->state = SUCCESS; - } -} - - -static Boolean eap_gtc_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_gtc_data *data = priv; - return data->state != CONTINUE; -} - - -static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_gtc_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_gtc = -{ - .method = EAP_TYPE_GTC, - .name = "GTC", - .init = eap_gtc_init, - .reset = eap_gtc_reset, - .buildReq = eap_gtc_buildReq, - .check = eap_gtc_check, - .process = eap_gtc_process, - .isDone = eap_gtc_isDone, - .isSuccess = eap_gtc_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_i.h b/contrib/hostapd-0.4.9/eap_i.h deleted file mode 100644 index 4e803f905d..0000000000 --- a/contrib/hostapd-0.4.9/eap_i.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef EAP_I_H -#define EAP_I_H - -#include "eap.h" - -/* draft-ietf-eap-statemachine-05.pdf - EAP Standalone Authenticator */ - -struct eap_method { - EapType method; - const char *name; - - void * (*init)(struct eap_sm *sm); - void * (*initPickUp)(struct eap_sm *sm); - void (*reset)(struct eap_sm *sm, void *priv); - - u8 * (*buildReq)(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen); - int (*getTimeout)(struct eap_sm *sm, void *priv); - Boolean (*check)(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen); - void (*process)(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen); - Boolean (*isDone)(struct eap_sm *sm, void *priv); - u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); - /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt, - * but it is useful in implementing Policy.getDecision() */ - Boolean (*isSuccess)(struct eap_sm *sm, void *priv); -}; - -struct eap_sm { - enum { - EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED, - EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST, - EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST, - EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE, - EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD - } EAP_state; - - /* Constants */ - int MaxRetrans; - - /* Lower layer to standalone authenticator variables */ - /* eapResp: eapol_sm->be_auth.eapResp */ - /* portEnabled: eapol_sm->portEnabled */ - /* eapRestart: eapol_sm->auth_pae.eapRestart */ - u8 *eapRespData; - size_t eapRespDataLen; - int retransWhile; - int eapSRTT; - int eapRTTVAR; - - /* Standalone authenticator to lower layer variables */ - /* eapReq: eapol_sm->be_auth.eapReq */ - /* eapNoReq: eapol_sm->be_auth.eapNoReq */ - /* eapSuccess: eapol_sm->eapSuccess */ - /* eapFail: eapol_sm->eapFail */ - /* eapTimeout: eapol_sm->eapTimeout */ - u8 *eapReqData; - size_t eapReqDataLen; - u8 *eapKeyData; /* also eapKeyAvailable (boolean) */ - size_t eapKeyDataLen; - - /* Standalone authenticator state machine local variables */ - - /* Long-term (maintained betwen packets) */ - EapType currentMethod; - int currentId; - enum { - METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END - } methodState; - int retransCount; - u8 *lastReqData; - size_t lastReqDataLen; - int methodTimeout; - - /* Short-term (not maintained between packets) */ - Boolean rxResp; - int respId; - EapType respMethod; - Boolean ignore; - enum { - DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE - } decision; - - /* Miscellaneous variables */ - const struct eap_method *m; /* selected EAP method */ - /* not defined in draft-ietf-eap-statemachine-02 */ - Boolean changed; - void *eapol_ctx, *msg_ctx; - struct eapol_callbacks *eapol_cb; - void *eap_method_priv; - u8 *identity; - size_t identity_len; - int lastId; /* Identifier used in the last EAP-Packet */ - struct eap_user *user; - int user_eap_method_index; - int init_phase2; - void *ssl_ctx; - enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request; - void *eap_sim_db_priv; - Boolean backend_auth; - Boolean update_user; - - int num_rounds; -}; - -const struct eap_method * eap_sm_get_eap_methods(int method); -int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, - int phase2); -void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len); - -#endif /* EAP_I_H */ diff --git a/contrib/hostapd-0.4.9/eap_identity.c b/contrib/hostapd-0.4.9/eap_identity.c deleted file mode 100644 index 54efc47961..0000000000 --- a/contrib/hostapd-0.4.9/eap_identity.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * hostapd / EAP-Identity - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" - - -struct eap_identity_data { - enum { CONTINUE, SUCCESS, FAILURE } state; - int pick_up; -}; - - -static void * eap_identity_init(struct eap_sm *sm) -{ - struct eap_identity_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->state = CONTINUE; - - return data; -} - - -static void * eap_identity_initPickUp(struct eap_sm *sm) -{ - struct eap_identity_data *data; - data = eap_identity_init(sm); - if (data) { - data->pick_up = 1; - } - return data; -} - - -static void eap_identity_reset(struct eap_sm *sm, void *priv) -{ - struct eap_identity_data *data = priv; - free(data); -} - - -static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_identity_data *data = priv; - struct eap_hdr *req; - u8 *pos; - const char *req_data; - size_t req_data_len; - - if (sm->eapol_cb->get_eap_req_id_text) { - req_data = sm->eapol_cb->get_eap_req_id_text(sm->eapol_ctx, - &req_data_len); - } else { - req_data = NULL; - req_data_len = 0; - } - *reqDataLen = sizeof(*req) + 1 + req_data_len; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate " - "memory for request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_IDENTITY; - if (req_data) - memcpy(pos, req_data, req_data_len); - - return (u8 *) req; -} - - -static Boolean eap_identity_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_hdr *resp; - u8 *pos; - size_t len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 1 || *pos != EAP_TYPE_IDENTITY || - (len = ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame"); - return TRUE; - } - - return FALSE; -} - - -static void eap_identity_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_identity_data *data = priv; - struct eap_hdr *resp; - u8 *pos; - int len; - - if (data->pick_up) { - if (eap_identity_check(sm, data, respData, respDataLen)) { - wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick " - "up already started negotiation"); - data->state = FAILURE; - return; - } - data->pick_up = 0; - } - - resp = (struct eap_hdr *) respData; - len = ntohs(resp->length); - pos = (u8 *) (resp + 1); - pos++; - len -= sizeof(*resp) + 1; - if (len < 0) { - data->state = FAILURE; - return; - } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len); - free(sm->identity); - sm->identity = malloc(len); - if (sm->identity == NULL) { - data->state = FAILURE; - } else { - memcpy(sm->identity, pos, len); - sm->identity_len = len; - data->state = SUCCESS; - } -} - - -static Boolean eap_identity_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_identity_data *data = priv; - return data->state != CONTINUE; -} - - -static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_identity_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_identity = -{ - .method = EAP_TYPE_IDENTITY, - .name = "Identity", - .init = eap_identity_init, - .initPickUp = eap_identity_initPickUp, - .reset = eap_identity_reset, - .buildReq = eap_identity_buildReq, - .check = eap_identity_check, - .process = eap_identity_process, - .isDone = eap_identity_isDone, - .isSuccess = eap_identity_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_md5.c b/contrib/hostapd-0.4.9/eap_md5.c deleted file mode 100644 index d776c8c827..0000000000 --- a/contrib/hostapd-0.4.9/eap_md5.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * hostapd / EAP-MD5 server - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" -#include "md5.h" -#include "crypto.h" - - -#define CHALLENGE_LEN 16 - -struct eap_md5_data { - u8 challenge[CHALLENGE_LEN]; - enum { CONTINUE, SUCCESS, FAILURE } state; -}; - - -static void * eap_md5_init(struct eap_sm *sm) -{ - struct eap_md5_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->state = CONTINUE; - - return data; -} - - -static void eap_md5_reset(struct eap_sm *sm, void *priv) -{ - struct eap_md5_data *data = priv; - free(data); -} - - -static u8 * eap_md5_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_md5_data *data = priv; - struct eap_hdr *req; - u8 *pos; - - if (hostapd_get_rand(data->challenge, CHALLENGE_LEN)) { - wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data"); - data->state = FAILURE; - return NULL; - } - - *reqDataLen = sizeof(*req) + 2 + CHALLENGE_LEN; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-MD5: Failed to allocate memory for " - "request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_MD5; - *pos++ = CHALLENGE_LEN; - memcpy(pos, data->challenge, CHALLENGE_LEN); - wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", pos, CHALLENGE_LEN); - - data->state = CONTINUE; - - return (u8 *) req; -} - - -static Boolean eap_md5_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_hdr *resp; - u8 *pos; - size_t len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_MD5 || - (len = ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame"); - return TRUE; - } - pos++; - if (*pos != MD5_MAC_LEN || - sizeof(*resp) + 2 + MD5_MAC_LEN > len) { - wpa_printf(MSG_INFO, "EAP-MD5: Invalid response " - "(response_len=%d respDataLen=%lu", - *pos, (unsigned long) respDataLen); - return TRUE; - } - - return FALSE; -} - - -static void eap_md5_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_md5_data *data = priv; - struct eap_hdr *resp; - u8 *pos; - const u8 *addr[3]; - size_t len[3]; - u8 hash[MD5_MAC_LEN]; - - if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-MD5: Password not configured"); - data->state = FAILURE; - return; - } - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos += 2; /* Skip type and len */ - wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, MD5_MAC_LEN); - - addr[0] = &resp->identifier; - len[0] = 1; - addr[1] = sm->user->password; - len[1] = sm->user->password_len; - addr[2] = data->challenge; - len[2] = CHALLENGE_LEN; - md5_vector(3, addr, len, hash); - - if (memcmp(hash, pos, MD5_MAC_LEN) == 0) { - wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success"); - data->state = SUCCESS; - } else { - wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Failure"); - data->state = FAILURE; - } -} - - -static Boolean eap_md5_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_md5_data *data = priv; - return data->state != CONTINUE; -} - - -static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_md5_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_md5 = -{ - .method = EAP_TYPE_MD5, - .name = "MD5", - .init = eap_md5_init, - .reset = eap_md5_reset, - .buildReq = eap_md5_buildReq, - .check = eap_md5_check, - .process = eap_md5_process, - .isDone = eap_md5_isDone, - .isSuccess = eap_md5_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_mschapv2.c b/contrib/hostapd-0.4.9/eap_mschapv2.c deleted file mode 100644 index 097da25417..0000000000 --- a/contrib/hostapd-0.4.9/eap_mschapv2.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" -#include "ms_funcs.h" - - -struct eap_mschapv2_hdr { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_MSCHAPV2 */ - u8 op_code; /* MSCHAPV2_OP_* */ - u8 mschapv2_id; /* must be changed for challenges, but not for - * success/failure */ - u8 ms_length[2]; /* Note: misaligned; length - 5 */ - /* followed by data */ -} __attribute__ ((packed)); - -#define MSCHAPV2_OP_CHALLENGE 1 -#define MSCHAPV2_OP_RESPONSE 2 -#define MSCHAPV2_OP_SUCCESS 3 -#define MSCHAPV2_OP_FAILURE 4 -#define MSCHAPV2_OP_CHANGE_PASSWORD 7 - -#define MSCHAPV2_RESP_LEN 49 - -#define ERROR_RESTRICTED_LOGON_HOURS 646 -#define ERROR_ACCT_DISABLED 647 -#define ERROR_PASSWD_EXPIRED 648 -#define ERROR_NO_DIALIN_PERMISSION 649 -#define ERROR_AUTHENTICATION_FAILURE 691 -#define ERROR_CHANGING_PASSWORD 709 - -#define PASSWD_CHANGE_CHAL_LEN 16 - - -#define CHALLENGE_LEN 16 - -struct eap_mschapv2_data { - u8 auth_challenge[CHALLENGE_LEN]; - u8 auth_response[20]; - enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; - u8 resp_mschapv2_id; -}; - - -static void * eap_mschapv2_init(struct eap_sm *sm) -{ - struct eap_mschapv2_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->state = CHALLENGE; - - return data; -} - - -static void eap_mschapv2_reset(struct eap_sm *sm, void *priv) -{ - struct eap_mschapv2_data *data = priv; - free(data); -} - - -static u8 * eap_mschapv2_build_challenge(struct eap_sm *sm, - struct eap_mschapv2_data *data, - int id, size_t *reqDataLen) -{ - struct eap_mschapv2_hdr *req; - u8 *pos; - char *name = "hostapd"; /* TODO: make this configurable */ - - if (hostapd_get_rand(data->auth_challenge, CHALLENGE_LEN)) { - wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " - "data"); - data->state = FAILURE; - return NULL; - } - - *reqDataLen = sizeof(*req) + 1 + CHALLENGE_LEN + strlen(name); - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" - " for request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_MSCHAPV2; - req->op_code = MSCHAPV2_OP_CHALLENGE; - req->mschapv2_id = id; - req->ms_length[0] = (*reqDataLen - 5) >> 8; - req->ms_length[1] = (*reqDataLen - 5) & 0xff; - pos = (u8 *) (req + 1); - *pos++ = CHALLENGE_LEN; - memcpy(pos, data->auth_challenge, CHALLENGE_LEN); - wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", pos, - CHALLENGE_LEN); - pos += CHALLENGE_LEN; - memcpy(pos, name, strlen(name)); - - return (u8 *) req; -} - - -static u8 * eap_mschapv2_build_success_req(struct eap_sm *sm, - struct eap_mschapv2_data *data, - int id, size_t *reqDataLen) -{ - struct eap_mschapv2_hdr *req; - u8 *pos, *msg, *end; - char *message = "OK"; - size_t msg_len; - int i; - - msg_len = 2 + 2 * sizeof(data->auth_response) + 3 + strlen(message); - *reqDataLen = sizeof(*req) + msg_len; - req = malloc(*reqDataLen + 1); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" - " for request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_MSCHAPV2; - req->op_code = MSCHAPV2_OP_SUCCESS; - req->mschapv2_id = data->resp_mschapv2_id; - req->ms_length[0] = (*reqDataLen - 5) >> 8; - req->ms_length[1] = (*reqDataLen - 5) & 0xff; - - msg = pos = (u8 *) (req + 1); - end = ((u8 *) req) + *reqDataLen + 1; - - pos += snprintf((char *) pos, end - pos, "S="); - for (i = 0; i < sizeof(data->auth_response); i++) { - pos += snprintf((char *) pos, end - pos, "%02X", - data->auth_response[i]); - } - pos += snprintf((char *) pos, end - pos, " M=%s", message); - - wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", - msg, msg_len); - - return (u8 *) req; -} - - -static u8 * eap_mschapv2_build_failure_req(struct eap_sm *sm, - struct eap_mschapv2_data *data, - int id, size_t *reqDataLen) -{ - struct eap_mschapv2_hdr *req; - u8 *pos; - char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " - "M=FAILED"; - size_t msg_len; - - msg_len = strlen(message); - *reqDataLen = sizeof(*req) + msg_len; - req = malloc(*reqDataLen + 1); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" - " for request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_MSCHAPV2; - req->op_code = MSCHAPV2_OP_FAILURE; - req->mschapv2_id = data->resp_mschapv2_id; - req->ms_length[0] = (*reqDataLen - 5) >> 8; - req->ms_length[1] = (*reqDataLen - 5) & 0xff; - - pos = (u8 *) (req + 1); - memcpy(pos, message, msg_len); - - wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", - (u8 *) message, msg_len); - - return (u8 *) req; -} - - -static u8 * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_mschapv2_data *data = priv; - - switch (data->state) { - case CHALLENGE: - return eap_mschapv2_build_challenge(sm, data, id, reqDataLen); - case SUCCESS_REQ: - return eap_mschapv2_build_success_req(sm, data, id, - reqDataLen); - case FAILURE_REQ: - return eap_mschapv2_build_failure_req(sm, data, id, - reqDataLen); - default: - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " - "buildReq", data->state); - break; - } - return NULL; -} - - -static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_mschapv2_data *data = priv; - struct eap_mschapv2_hdr *resp; - u8 *pos; - size_t len; - - resp = (struct eap_mschapv2_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < 6 || resp->type != EAP_TYPE_MSCHAPV2 || - (len = ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); - return TRUE; - } - - if (data->state == CHALLENGE && - resp->op_code != MSCHAPV2_OP_RESPONSE) { - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " - "ignore op %d", resp->op_code); - return TRUE; - } - - if (data->state == SUCCESS_REQ && - resp->op_code != MSCHAPV2_OP_SUCCESS && - resp->op_code != MSCHAPV2_OP_FAILURE) { - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or " - "Failure - ignore op %d", resp->op_code); - return TRUE; - } - - if (data->state == FAILURE_REQ && - resp->op_code != MSCHAPV2_OP_FAILURE) { - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure " - "- ignore op %d", resp->op_code); - return TRUE; - } - - return FALSE; -} - - -static void eap_mschapv2_process_response(struct eap_sm *sm, - struct eap_mschapv2_data *data, - u8 *respData, size_t respDataLen) -{ - struct eap_mschapv2_hdr *resp; - u8 *pos; - u8 *peer_challenge, *nt_response, flags, *name; - size_t name_len; - u8 expected[24]; - int i; - u8 *username, *user; - size_t username_len, user_len; - - resp = (struct eap_mschapv2_hdr *) respData; - pos = (u8 *) (resp + 1); - - if (respDataLen < sizeof(*resp) + 1 + 49 || - resp->op_code != MSCHAPV2_OP_RESPONSE || - pos[0] != 49) { - wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", - respData, respDataLen); - data->state = FAILURE; - return; - } - data->resp_mschapv2_id = resp->mschapv2_id; - pos++; - peer_challenge = pos; - pos += 16 + 8; - nt_response = pos; - pos += 24; - flags = *pos++; - name = pos; - name_len = respData + respDataLen - name; - - wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", - peer_challenge, 16); - wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); - wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); - wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); - - /* MSCHAPv2 does not include optional domain name in the - * challenge-response calculation, so remove domain prefix - * (if present). */ - username = sm->identity; - username_len = sm->identity_len; - for (i = 0; i < username_len; i++) { - if (username[i] == '\\') { - username_len -= i + 1; - username += i + 1; - break; - } - } - - user = name; - user_len = name_len; - for (i = 0; i < user_len; i++) { - if (user[i] == '\\') { - user_len -= i + 1; - user += i + 1; - break; - } - } - - if (username_len != user_len || - memcmp(username, user, username_len) != 0) { - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); - wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " - "name", username, username_len); - wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " - "name", user, user_len); - data->state = FAILURE; - return; - } - - wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", - username, username_len); - - generate_nt_response(data->auth_challenge, peer_challenge, - username, username_len, - sm->user->password, sm->user->password_len, - expected); - - if (memcmp(nt_response, expected, 24) == 0) { - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); - data->state = SUCCESS_REQ; - - - /* Authenticator response is not really needed yet, but - * calculate it here so that peer_challenge and username need - * not be saved. */ - generate_authenticator_response(sm->user->password, - sm->user->password_len, - peer_challenge, - data->auth_challenge, - username, username_len, - nt_response, - data->auth_response); - } else { - wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", - expected, 24); - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); - data->state = FAILURE_REQ; - } -} - - -static void eap_mschapv2_process_success_resp(struct eap_sm *sm, - struct eap_mschapv2_data *data, - u8 *respData, size_t respDataLen) -{ - struct eap_mschapv2_hdr *resp; - - resp = (struct eap_mschapv2_hdr *) respData; - - if (resp->op_code == MSCHAPV2_OP_SUCCESS) { - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" - " - authentication completed successfully"); - data->state = SUCCESS; - } else { - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " - "Response - peer rejected authentication"); - data->state = FAILURE; - } -} - - -static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, - struct eap_mschapv2_data *data, - u8 *respData, size_t respDataLen) -{ - struct eap_mschapv2_hdr *resp; - - resp = (struct eap_mschapv2_hdr *) respData; - - if (resp->op_code == MSCHAPV2_OP_FAILURE) { - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" - " - authentication failed"); - } else { - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " - "Response - authentication failed"); - } - - data->state = FAILURE; -} - - -static void eap_mschapv2_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_mschapv2_data *data = priv; - - if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); - data->state = FAILURE; - return; - } - - switch (data->state) { - case CHALLENGE: - eap_mschapv2_process_response(sm, data, respData, respDataLen); - break; - case SUCCESS_REQ: - eap_mschapv2_process_success_resp(sm, data, respData, - respDataLen); - break; - case FAILURE_REQ: - eap_mschapv2_process_failure_resp(sm, data, respData, - respDataLen); - break; - default: - wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " - "process", data->state); - break; - } -} - - -static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_mschapv2_data *data = priv; - return data->state == SUCCESS || data->state == FAILURE; -} - - -static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_mschapv2_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_mschapv2 = -{ - .method = EAP_TYPE_MSCHAPV2, - .name = "MSCHAPV2", - .init = eap_mschapv2_init, - .reset = eap_mschapv2_reset, - .buildReq = eap_mschapv2_buildReq, - .check = eap_mschapv2_check, - .process = eap_mschapv2_process, - .isDone = eap_mschapv2_isDone, - .isSuccess = eap_mschapv2_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_pax.c b/contrib/hostapd-0.4.9/eap_pax.c deleted file mode 100644 index 2645ba58f2..0000000000 --- a/contrib/hostapd-0.4.9/eap_pax.c +++ /dev/null @@ -1,519 +0,0 @@ -/* - * hostapd / EAP-PAX (draft-clancy-eap-pax-04.txt) server - * Copyright (c) 2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" -#include "eap_pax_common.h" - -/* - * Note: only PAX_STD subprotocol is currently supported - */ - -struct eap_pax_data { - enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state; - u8 mac_id; - union { - u8 e[2 * EAP_PAX_RAND_LEN]; - struct { - u8 x[EAP_PAX_RAND_LEN]; /* server rand */ - u8 y[EAP_PAX_RAND_LEN]; /* client rand */ - } r; - } rand; - u8 ak[EAP_PAX_AK_LEN]; - u8 mk[EAP_PAX_MK_LEN]; - u8 ck[EAP_PAX_CK_LEN]; - u8 ick[EAP_PAX_ICK_LEN]; - int keys_set; - char *cid; - size_t cid_len; -}; - - -static void * eap_pax_init(struct eap_sm *sm) -{ - struct eap_pax_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->state = PAX_STD_1; - /* - * TODO: make this configurable once EAP_PAX_MAC_AES_CBC_MAC_128 is - * supported - */ - data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128; - - return data; -} - - -static void eap_pax_reset(struct eap_sm *sm, void *priv) -{ - struct eap_pax_data *data = priv; - free(data->cid); - free(data); -} - - -static u8 * eap_pax_build_std_1(struct eap_sm *sm, - struct eap_pax_data *data, - int id, size_t *reqDataLen) -{ - struct eap_pax_hdr *req; - u8 *pos; - - wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); - - if (hostapd_get_rand(data->rand.r.x, EAP_PAX_RAND_LEN)) { - wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); - data->state = FAILURE; - return NULL; - } - - *reqDataLen = sizeof(*req) + 2 + EAP_PAX_RAND_LEN + EAP_PAX_ICV_LEN; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " - "request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_PAX; - req->op_code = EAP_PAX_OP_STD_1; - req->flags = 0; - req->mac_id = data->mac_id; - req->dh_group_id = EAP_PAX_DH_GROUP_NONE; - req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; - pos = (u8 *) (req + 1); - *pos++ = 0; - *pos++ = EAP_PAX_RAND_LEN; - memcpy(pos, data->rand.r.x, EAP_PAX_RAND_LEN); - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)", - pos, EAP_PAX_RAND_LEN); - pos += EAP_PAX_RAND_LEN; - - eap_pax_mac(data->mac_id, (u8 *) "", 0, - (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, pos); - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); - pos += EAP_PAX_ICV_LEN; - - return (u8 *) req; -} - - -static u8 * eap_pax_build_std_3(struct eap_sm *sm, - struct eap_pax_data *data, - int id, size_t *reqDataLen) -{ - struct eap_pax_hdr *req; - u8 *pos; - - wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)"); - - *reqDataLen = sizeof(*req) + 2 + EAP_PAX_MAC_LEN + EAP_PAX_ICV_LEN; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " - "request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_PAX; - req->op_code = EAP_PAX_OP_STD_3; - req->flags = 0; - req->mac_id = data->mac_id; - req->dh_group_id = EAP_PAX_DH_GROUP_NONE; - req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; - pos = (u8 *) (req + 1); - *pos++ = 0; - *pos++ = EAP_PAX_MAC_LEN; - eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, - data->rand.r.y, EAP_PAX_RAND_LEN, - (u8 *) data->cid, data->cid_len, NULL, 0, pos); - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", - pos, EAP_PAX_MAC_LEN); - pos += EAP_PAX_MAC_LEN; - - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN, - NULL, 0, NULL, 0, pos); - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); - pos += EAP_PAX_ICV_LEN; - - return (u8 *) req; -} - - -static u8 * eap_pax_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_pax_data *data = priv; - - switch (data->state) { - case PAX_STD_1: - return eap_pax_build_std_1(sm, data, id, reqDataLen); - case PAX_STD_3: - return eap_pax_build_std_3(sm, data, id, reqDataLen); - default: - wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq", - data->state); - break; - } - return NULL; -} - - -static Boolean eap_pax_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_pax_data *data = priv; - struct eap_pax_hdr *resp; - size_t len; - u8 icvbuf[EAP_PAX_ICV_LEN], *icv; - - resp = (struct eap_pax_hdr *) respData; - if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PAX || - (len = ntohs(resp->length)) > respDataLen || - len < sizeof(*resp) + EAP_PAX_ICV_LEN) { - wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); - return TRUE; - } - - wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " - "flags 0x%x mac_id 0x%x dh_group_id 0x%x " - "public_key_id 0x%x", - resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id, - resp->public_key_id); - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", - (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN); - - if (data->state == PAX_STD_1 && - resp->op_code != EAP_PAX_OP_STD_2) { - wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - " - "ignore op %d", resp->op_code); - return TRUE; - } - - if (data->state == PAX_STD_3 && - resp->op_code != EAP_PAX_OP_ACK) { - wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - " - "ignore op %d", resp->op_code); - return TRUE; - } - - if (resp->op_code != EAP_PAX_OP_STD_2 && - resp->op_code != EAP_PAX_OP_ACK) { - wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x", - resp->op_code); - } - - if (data->mac_id != resp->mac_id) { - wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, " - "received 0x%x", data->mac_id, resp->mac_id); - return TRUE; - } - - if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) { - wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, " - "received 0x%x", EAP_PAX_DH_GROUP_NONE, - resp->dh_group_id); - return TRUE; - } - - if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { - wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, " - "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE, - resp->public_key_id); - return TRUE; - } - - if (resp->flags & EAP_PAX_FLAGS_MF) { - /* TODO: add support for reassembling fragments */ - wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported"); - return TRUE; - } - - if (resp->flags & EAP_PAX_FLAGS_CE) { - wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag"); - return TRUE; - } - - if (data->keys_set) { - if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) { - wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet"); - return TRUE; - } - icv = respData + len - EAP_PAX_ICV_LEN; - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, - icvbuf); - if (memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { - wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", - icvbuf, EAP_PAX_ICV_LEN); - return TRUE; - } - } - - return FALSE; -} - - -static void eap_pax_process_std_2(struct eap_sm *sm, - struct eap_pax_data *data, - u8 *respData, size_t respDataLen) -{ - struct eap_pax_hdr *resp; - u8 *pos, mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; - size_t len, left; - int i; - - if (data->state != PAX_STD_1) - return; - - wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2"); - - resp = (struct eap_pax_hdr *) respData; - len = ntohs(resp->length); - pos = (u8 *) (resp + 1); - left = len - sizeof(*resp); - - if (left < 2 + EAP_PAX_RAND_LEN || - ((pos[0] << 8) | pos[1]) != EAP_PAX_RAND_LEN) { - wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)"); - return; - } - pos += 2; - left -= 2; - memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN); - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", - data->rand.r.y, EAP_PAX_RAND_LEN); - pos += EAP_PAX_RAND_LEN; - left -= EAP_PAX_RAND_LEN; - - if (left < 2 || 2 + ((pos[0] << 8) | pos[1]) > left) { - wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); - return; - } - data->cid_len = (pos[0] << 8) | pos[1]; - free(data->cid); - data->cid = malloc(data->cid_len); - if (data->cid == NULL) { - wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " - "CID"); - return; - } - memcpy (data->cid, pos + 2, data->cid_len); - pos += 2 + data->cid_len; - left -= 2 + data->cid_len; - wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", - (u8 *) data->cid, data->cid_len); - - if (left < 2 + EAP_PAX_MAC_LEN || - ((pos[0] << 8) | pos[1]) != EAP_PAX_MAC_LEN) { - wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)"); - return; - } - pos += 2; - left -= 2; - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", - pos, EAP_PAX_MAC_LEN); - - if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID", - (u8 *) data->cid, data->cid_len); - data->state = FAILURE; - return; - } - - for (i = 0; - i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE; - i++) { - if (sm->user->methods[i] == EAP_TYPE_PAX) - break; - } - - if (sm->user->methods[i] != EAP_TYPE_PAX) { - wpa_hexdump_ascii(MSG_DEBUG, - "EAP-PAX: EAP-PAX not enabled for CID", - (u8 *) data->cid, data->cid_len); - data->state = FAILURE; - return; - } - - if (sm->user->password == NULL || - sm->user->password_len != EAP_PAX_AK_LEN) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in " - "user database for CID", - (u8 *) data->cid, data->cid_len); - data->state = FAILURE; - return; - } - memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN); - - if (eap_pax_initial_key_derivation(data->mac_id, data->ak, - data->rand.e, data->mk, data->ck, - data->ick) < 0) { - wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial " - "key derivation"); - data->state = FAILURE; - return; - } - data->keys_set = 1; - - eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, - data->rand.r.x, EAP_PAX_RAND_LEN, - data->rand.r.y, EAP_PAX_RAND_LEN, - (u8 *) data->cid, data->cid_len, mac); - if (memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) { - wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " - "PAX_STD-2"); - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", - mac, EAP_PAX_MAC_LEN); - data->state = FAILURE; - return; - } - - pos += EAP_PAX_MAC_LEN; - left -= EAP_PAX_MAC_LEN; - - if (left < EAP_PAX_ICV_LEN) { - wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in " - "PAX_STD-2", (unsigned long) left); - return; - } - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); - eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, - respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf); - if (memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { - wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", - icvbuf, EAP_PAX_ICV_LEN); - return; - } - pos += EAP_PAX_ICV_LEN; - left -= EAP_PAX_ICV_LEN; - - if (left > 0) { - wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", - pos, left); - } - - data->state = PAX_STD_3; -} - - -static void eap_pax_process_ack(struct eap_sm *sm, - struct eap_pax_data *data, - u8 *respData, size_t respDataLen) -{ - if (data->state != PAX_STD_3) - return; - - wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication " - "completed successfully"); - data->state = SUCCESS; -} - - -static void eap_pax_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_pax_data *data = priv; - struct eap_pax_hdr *resp; - - if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-PAX: Password not configured"); - data->state = FAILURE; - return; - } - - resp = (struct eap_pax_hdr *) respData; - - switch (resp->op_code) { - case EAP_PAX_OP_STD_2: - eap_pax_process_std_2(sm, data, respData, respDataLen); - break; - case EAP_PAX_OP_ACK: - eap_pax_process_ack(sm, data, respData, respDataLen); - break; - } -} - - -static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_pax_data *data = priv; - return data->state == SUCCESS || data->state == FAILURE; -} - - -static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_pax_data *data = priv; - u8 *key; - - if (data->state != SUCCESS) - return NULL; - - key = malloc(EAP_PAX_MSK_LEN); - if (key == NULL) - return NULL; - - *len = EAP_PAX_MSK_LEN; - eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, - "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, - EAP_PAX_MSK_LEN, key); - - return key; -} - - -static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_pax_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_pax = -{ - .method = EAP_TYPE_PAX, - .name = "PAX", - .init = eap_pax_init, - .reset = eap_pax_reset, - .buildReq = eap_pax_buildReq, - .check = eap_pax_check, - .process = eap_pax_process, - .isDone = eap_pax_isDone, - .getKey = eap_pax_getKey, - .isSuccess = eap_pax_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_pax_common.c b/contrib/hostapd-0.4.9/eap_pax_common.c deleted file mode 100644 index d8f4016a6a..0000000000 --- a/contrib/hostapd-0.4.9/eap_pax_common.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * WPA Supplicant / EAP-PAX shared routines - * Copyright (c) 2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include - -#include "common.h" -#include "sha1.h" -#include "eap_pax_common.h" - - -/** - * eap_pax_kdf - PAX Key Derivation Function - * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported - * @key: Secret key (X) - * @key_len: Length of the secret key in bytes - * @identifier: Public identifier for the key (Y) - * @entropy: Exchanged entropy to seed the KDF (Z) - * @entropy_len: Length of the entropy in bytes - * @output_len: Output len in bytes (W) - * @output: Buffer for the derived key - * Returns: 0 on success, -1 failed - * - * draft-clancy-eap-pax-04.txt, chap. 2.5: PAX-KDF-W(X, Y, Z) - */ -int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, - const char *identifier, - const u8 *entropy, size_t entropy_len, - size_t output_len, u8 *output) -{ - u8 mac[SHA1_MAC_LEN]; - u8 counter, *pos; - const u8 *addr[3]; - size_t len[3]; - size_t num_blocks, left; - - num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN; - if (identifier == NULL || num_blocks >= 255) - return -1; - - /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ - if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) - return -1; - - addr[0] = (const u8 *) identifier; - len[0] = strlen(identifier); - addr[1] = entropy; - len[1] = entropy_len; - addr[2] = &counter; - len[2] = 1; - - pos = output; - left = output_len; - for (counter = 1; counter <= (u8) num_blocks; counter++) { - size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left; - hmac_sha1_vector(key, key_len, 3, addr, len, mac); - memcpy(pos, mac, clen); - pos += clen; - left -= clen; - } - - return 0; -} - - -/** - * eap_pax_mac - EAP-PAX MAC - * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported - * @key: Secret key - * @key_len: Length of the secret key in bytes - * @data1: Optional data, first block; %NULL if not used - * @data1_len: Length of data1 in bytes - * @data2: Optional data, second block; %NULL if not used - * @data2_len: Length of data2 in bytes - * @data3: Optional data, third block; %NULL if not used - * @data3_len: Length of data3 in bytes - * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes) - * Returns: 0 on success, -1 on failure - * - * Wrapper function to calculate EAP-PAX MAC. - */ -int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, - const u8 *data1, size_t data1_len, - const u8 *data2, size_t data2_len, - const u8 *data3, size_t data3_len, - u8 *mac) -{ - u8 hash[SHA1_MAC_LEN]; - const u8 *addr[3]; - size_t len[3]; - size_t count; - - /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ - if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) - return -1; - - addr[0] = data1; - len[0] = data1_len; - addr[1] = data2; - len[1] = data2_len; - addr[2] = data3; - len[2] = data3_len; - - count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0); - hmac_sha1_vector(key, key_len, count, addr, len, hash); - memcpy(mac, hash, EAP_PAX_MAC_LEN); - - return 0; -} - - -/** - * eap_pax_initial_key_derivation - EAP-PAX initial key derivation - * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported - * @ak: Authentication Key - * @e: Entropy - * @mk: Buffer for the derived Master Key - * @ck: Buffer for the derived Confirmation Key - * @ick: Buffer for the derived Integrity Check Key - * Returns: 0 on success, -1 on failure - */ -int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, - u8 *mk, u8 *ck, u8 *ick) -{ - wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation"); - if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key", - e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MK_LEN, mk) || - eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key", - e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) || - eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key", - e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick)) - return -1; - - wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN); - wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN); - wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN); - wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN); - - return 0; -} diff --git a/contrib/hostapd-0.4.9/eap_pax_common.h b/contrib/hostapd-0.4.9/eap_pax_common.h deleted file mode 100644 index b5ad6af1f2..0000000000 --- a/contrib/hostapd-0.4.9/eap_pax_common.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * WPA Supplicant / EAP-PAX shared routines - * Copyright (c) 2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef EAP_PAX_COMMON_H -#define EAP_PAX_COMMON_H - -struct eap_pax_hdr { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_PAX */ - u8 op_code; - u8 flags; - u8 mac_id; - u8 dh_group_id; - u8 public_key_id; - /* Followed by variable length payload and ICV */ -} __attribute__ ((packed)); - - -/* op_code: */ -enum { - EAP_PAX_OP_STD_1 = 0x01, - EAP_PAX_OP_STD_2 = 0x02, - EAP_PAX_OP_STD_3 = 0x03, - EAP_PAX_OP_SEC_1 = 0x11, - EAP_PAX_OP_SEC_2 = 0x12, - EAP_PAX_OP_SEC_3 = 0x13, - EAP_PAX_OP_SEC_4 = 0x14, - EAP_PAX_OP_SEC_5 = 0x15, - EAP_PAX_OP_ACK = 0x21 -}; - -/* flags: */ -#define EAP_PAX_FLAGS_MF 0x01 -#define EAP_PAX_FLAGS_CE 0x02 - -/* mac_id: */ -#define EAP_PAX_MAC_HMAC_SHA1_128 0x01 -#define EAP_PAX_MAC_AES_CBC_MAC_128 0x02 - -/* dh_group_id: */ -#define EAP_PAX_DH_GROUP_NONE 0x00 -#define EAP_PAX_DH_GROUP_3072_MODP 0x01 - -/* public_key_id: */ -#define EAP_PAX_PUBLIC_KEY_NONE 0x00 -#define EAP_PAX_PUBLIC_KEY_RSA_OAEP_2048 0x01 - - -#define EAP_PAX_RAND_LEN 32 -#define EAP_PAX_MSK_LEN 64 -#define EAP_PAX_MAC_LEN 16 -#define EAP_PAX_ICV_LEN 16 -#define EAP_PAX_AK_LEN 16 -#define EAP_PAX_MK_LEN 16 -#define EAP_PAX_CK_LEN 16 -#define EAP_PAX_ICK_LEN 16 - - -int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, - const char *identifier, - const u8 *entropy, size_t entropy_len, - size_t output_len, u8 *output); -int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, - const u8 *data1, size_t data1_len, - const u8 *data2, size_t data2_len, - const u8 *data3, size_t data3_len, - u8 *mac); -int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, - u8 *mk, u8 *ck, u8 *ick); - -#endif /* EAP_PAX_COMMON_H */ diff --git a/contrib/hostapd-0.4.9/eap_peap.c b/contrib/hostapd-0.4.9/eap_peap.c deleted file mode 100644 index 9eb61a6b13..0000000000 --- a/contrib/hostapd-0.4.9/eap_peap.c +++ /dev/null @@ -1,726 +0,0 @@ -/* - * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt) - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" -#include "eap_tls_common.h" -#include "tls.h" - - -/* Maximum supported PEAP version - * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt - * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt - * 2 = draft-josefsson-ppext-eap-tls-eap-07.txt - */ -#define EAP_PEAP_VERSION 1 - - -static void eap_peap_reset(struct eap_sm *sm, void *priv); - - -struct eap_peap_data { - struct eap_ssl_data ssl; - enum { - START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD, - PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE - } state; - - int peap_version; - const struct eap_method *phase2_method; - void *phase2_priv; - int force_version; -}; - - -static const char * eap_peap_state_txt(int state) -{ - switch (state) { - case START: - return "START"; - case PHASE1: - return "PHASE1"; - case PHASE2_START: - return "PHASE2_START"; - case PHASE2_ID: - return "PHASE2_ID"; - case PHASE2_METHOD: - return "PHASE2_METHOD"; - case PHASE2_TLV: - return "PHASE2_TLV"; - case SUCCESS_REQ: - return "SUCCESS_REQ"; - case FAILURE_REQ: - return "FAILURE_REQ"; - case SUCCESS: - return "SUCCESS"; - case FAILURE: - return "FAILURE"; - default: - return "Unknown?!"; - } -} - - -static void eap_peap_state(struct eap_peap_data *data, int state) -{ - wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s", - eap_peap_state_txt(data->state), - eap_peap_state_txt(state)); - data->state = state; -} - - -static EapType eap_peap_req_success(struct eap_sm *sm, - struct eap_peap_data *data) -{ - if (data->state == FAILURE || data->state == FAILURE_REQ) { - eap_peap_state(data, FAILURE); - return EAP_TYPE_NONE; - } - - if (data->peap_version == 0) { - sm->tlv_request = TLV_REQ_SUCCESS; - eap_peap_state(data, PHASE2_TLV); - return EAP_TYPE_TLV; - } else { - eap_peap_state(data, SUCCESS_REQ); - return EAP_TYPE_NONE; - } -} - - -static EapType eap_peap_req_failure(struct eap_sm *sm, - struct eap_peap_data *data) -{ - if (data->state == FAILURE || data->state == FAILURE_REQ || - data->state == SUCCESS_REQ || - (data->phase2_method && - data->phase2_method->method == EAP_TYPE_TLV)) { - eap_peap_state(data, FAILURE); - return EAP_TYPE_NONE; - } - - if (data->peap_version == 0) { - sm->tlv_request = TLV_REQ_FAILURE; - eap_peap_state(data, PHASE2_TLV); - return EAP_TYPE_TLV; - } else { - eap_peap_state(data, FAILURE_REQ); - return EAP_TYPE_NONE; - } -} - - -static void * eap_peap_init(struct eap_sm *sm) -{ - struct eap_peap_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->peap_version = EAP_PEAP_VERSION; - data->force_version = -1; - if (sm->user && sm->user->force_version >= 0) { - data->force_version = sm->user->force_version; - wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d", - data->force_version); - data->peap_version = data->force_version; - } - data->state = START; - - if (eap_tls_ssl_init(sm, &data->ssl, 0)) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); - eap_peap_reset(sm, data); - return NULL; - } - - return data; -} - - -static void eap_peap_reset(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - if (data == NULL) - return; - if (data->phase2_priv && data->phase2_method) - data->phase2_method->reset(sm, data->phase2_priv); - eap_tls_ssl_deinit(sm, &data->ssl); - free(data); -} - - -static u8 * eap_peap_build_start(struct eap_sm *sm, struct eap_peap_data *data, - int id, size_t *reqDataLen) -{ - struct eap_hdr *req; - u8 *pos; - - *reqDataLen = sizeof(*req) + 2; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for" - " request"); - eap_peap_state(data, FAILURE); - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_PEAP; - *pos = EAP_TLS_FLAGS_START | data->peap_version; - - eap_peap_state(data, PHASE1); - - return (u8 *) req; -} - - -static u8 * eap_peap_build_req(struct eap_sm *sm, struct eap_peap_data *data, - int id, size_t *reqDataLen) -{ - int res; - u8 *req; - - res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_PEAP, - data->peap_version, id, &req, - reqDataLen); - - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, starting " - "Phase2"); - eap_peap_state(data, PHASE2_START); - } - - if (res == 1) - return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_PEAP, - data->peap_version); - return req; -} - - -static u8 * eap_peap_encrypt(struct eap_sm *sm, struct eap_peap_data *data, - int id, u8 *plain, size_t plain_len, - size_t *out_len) -{ - int res; - u8 *pos; - struct eap_hdr *req; - - /* TODO: add support for fragmentation, if needed. This will need to - * add TLS Message Length field, if the frame is fragmented. */ - req = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); - if (req == NULL) - return NULL; - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_PEAP; - *pos++ = data->peap_version; - - res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - plain, plain_len, - pos, data->ssl.tls_out_limit); - if (res < 0) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt Phase 2 " - "data"); - free(req); - return NULL; - } - - *out_len = sizeof(struct eap_hdr) + 2 + res; - req->length = host_to_be16(*out_len); - return (u8 *) req; -} - - -static u8 * eap_peap_build_phase2_req(struct eap_sm *sm, - struct eap_peap_data *data, - int id, size_t *reqDataLen) -{ - u8 *req, *buf, *encr_req; - size_t req_len; - - buf = req = data->phase2_method->buildReq(sm, data->phase2_priv, id, - &req_len); - if (req == NULL) - return NULL; - - wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", - req, req_len); - - if (data->peap_version == 0 && - data->phase2_method->method != EAP_TYPE_TLV) { - req += sizeof(struct eap_hdr); - req_len -= sizeof(struct eap_hdr); - } - - encr_req = eap_peap_encrypt(sm, data, id, req, req_len, reqDataLen); - free(buf); - - return encr_req; -} - - -static u8 * eap_peap_build_phase2_term(struct eap_sm *sm, - struct eap_peap_data *data, - int id, size_t *reqDataLen, int success) -{ - u8 *encr_req; - size_t req_len; - struct eap_hdr *hdr; - - req_len = sizeof(*hdr); - hdr = malloc(req_len); - if (hdr == NULL) { - return NULL; - } - - memset(hdr, 0, req_len); - hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; - hdr->identifier = id; - hdr->length = htons(req_len); - - wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", - (u8 *) hdr, req_len); - - encr_req = eap_peap_encrypt(sm, data, id, (u8 *) hdr, req_len, - reqDataLen); - free(hdr); - - return encr_req; -} - - -static u8 * eap_peap_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_peap_data *data = priv; - - switch (data->state) { - case START: - return eap_peap_build_start(sm, data, id, reqDataLen); - case PHASE1: - return eap_peap_build_req(sm, data, id, reqDataLen); - case PHASE2_ID: - case PHASE2_METHOD: - case PHASE2_TLV: - return eap_peap_build_phase2_req(sm, data, id, reqDataLen); - case SUCCESS_REQ: - return eap_peap_build_phase2_term(sm, data, id, reqDataLen, 1); - case FAILURE_REQ: - return eap_peap_build_phase2_term(sm, data, id, reqDataLen, 0); - default: - wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", - __func__, data->state); - return NULL; - } -} - - -static Boolean eap_peap_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_hdr *resp; - u8 *pos; - size_t len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_PEAP || - (len = ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); - return TRUE; - } - - return FALSE; -} - - -static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, - u8 eap_type) -{ - if (data->phase2_priv && data->phase2_method) { - data->phase2_method->reset(sm, data->phase2_priv); - data->phase2_method = NULL; - data->phase2_priv = NULL; - } - data->phase2_method = eap_sm_get_eap_methods(eap_type); - if (!data->phase2_method) - return -1; - - sm->init_phase2 = 1; - data->phase2_priv = data->phase2_method->init(sm); - sm->init_phase2 = 0; - return 0; -} - - -static void eap_peap_process_phase2_response(struct eap_sm *sm, - struct eap_peap_data *data, - u8 *in_data, size_t in_len) -{ - u8 next_type = EAP_TYPE_NONE; - struct eap_hdr *hdr; - u8 *pos; - size_t left; - - if (data->phase2_priv == NULL) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not " - "initialized?!", __func__); - return; - } - - hdr = (struct eap_hdr *) in_data; - pos = (u8 *) (hdr + 1); - left = in_len - sizeof(*hdr); - - if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { - wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; " - "allowed types", pos + 1, left - 1); - eap_sm_process_nak(sm, pos + 1, left - 1); - if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index] != - EAP_TYPE_NONE) { - next_type = - sm->user->methods[sm->user_eap_method_index++]; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", - next_type); - } else { - next_type = eap_peap_req_failure(sm, data); - } - eap_peap_phase2_init(sm, data, next_type); - return; - } - - if (data->phase2_method->check(sm, data->phase2_priv, in_data, - in_len)) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to " - "ignore the packet"); - return; - } - - data->phase2_method->process(sm, data->phase2_priv, in_data, in_len); - - if (!data->phase2_method->isDone(sm, data->phase2_priv)) - return; - - - if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed"); - next_type = eap_peap_req_failure(sm, data); - eap_peap_phase2_init(sm, data, next_type); - return; - } - - switch (data->state) { - case PHASE2_ID: - if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 " - "Identity not found in the user " - "database", - sm->identity, sm->identity_len); - next_type = eap_peap_req_failure(sm, data); - break; - } - - eap_peap_state(data, PHASE2_METHOD); - next_type = sm->user->methods[0]; - sm->user_eap_method_index = 1; - wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); - break; - case PHASE2_METHOD: - next_type = eap_peap_req_success(sm, data); - break; - case PHASE2_TLV: - if (sm->tlv_request == TLV_REQ_SUCCESS || - data->state == SUCCESS_REQ) { - eap_peap_state(data, SUCCESS); - } else { - eap_peap_state(data, FAILURE); - } - break; - case FAILURE: - break; - default: - wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", - __func__, data->state); - break; - } - - eap_peap_phase2_init(sm, data, next_type); -} - - -static void eap_peap_process_phase2(struct eap_sm *sm, - struct eap_peap_data *data, - struct eap_hdr *resp, - u8 *in_data, size_t in_len) -{ - u8 *in_decrypted; - int buf_len, len_decrypted, len, res; - struct eap_hdr *hdr; - - wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" - " Phase 2", (unsigned long) in_len); - - res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); - if (res < 0 || res == 1) - return; - - buf_len = in_len; - if (data->ssl.tls_in_total > buf_len) - buf_len = data->ssl.tls_in_total; - in_decrypted = malloc(buf_len); - if (in_decrypted == NULL) { - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory " - "for decryption"); - return; - } - - len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, - in_decrypted, buf_len); - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - if (len_decrypted < 0) { - wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " - "data"); - free(in_decrypted); - eap_peap_state(data, FAILURE); - return; - } - - wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", - in_decrypted, len_decrypted); - - hdr = (struct eap_hdr *) in_decrypted; - - if (data->peap_version == 0 && data->state != PHASE2_TLV) { - struct eap_hdr *nhdr = malloc(sizeof(struct eap_hdr) + - len_decrypted); - if (nhdr == NULL) { - free(in_decrypted); - return; - } - memcpy((u8 *) (nhdr + 1), in_decrypted, len_decrypted); - free(in_decrypted); - nhdr->code = resp->code; - nhdr->identifier = resp->identifier; - nhdr->length = host_to_be16(sizeof(struct eap_hdr) + - len_decrypted); - - len_decrypted += sizeof(struct eap_hdr); - in_decrypted = (u8 *) nhdr; - } - hdr = (struct eap_hdr *) in_decrypted; - if (len_decrypted < sizeof(*hdr)) { - free(in_decrypted); - wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " - "EAP frame (len=%d)", len_decrypted); - eap_peap_req_failure(sm, data); - return; - } - len = be_to_host16(hdr->length); - if (len > len_decrypted) { - free(in_decrypted); - wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " - "Phase 2 EAP frame (len=%d hdr->length=%d)", - len_decrypted, len); - eap_peap_req_failure(sm, data); - return; - } - wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " - "identifier=%d length=%d", hdr->code, hdr->identifier, len); - switch (hdr->code) { - case EAP_CODE_RESPONSE: - eap_peap_process_phase2_response(sm, data, (u8 *) hdr, len); - break; - case EAP_CODE_SUCCESS: - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); - if (data->state == SUCCESS_REQ) { - eap_peap_state(data, SUCCESS); - } - break; - case EAP_CODE_FAILURE: - wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); - eap_peap_state(data, FAILURE); - break; - default: - wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " - "Phase 2 EAP header", hdr->code); - break; - } - - free(in_decrypted); - } - - -static void eap_peap_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_peap_data *data = priv; - struct eap_hdr *resp; - u8 *pos, flags; - int left; - unsigned int tls_msg_len; - int peer_version; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos++; - flags = *pos++; - left = htons(resp->length) - sizeof(struct eap_hdr) - 2; - wpa_printf(MSG_DEBUG, "EAP-PEAP: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) respDataLen, flags); - peer_version = flags & EAP_PEAP_VERSION_MASK; - if (data->force_version >= 0 && peer_version != data->force_version) { - wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced" - " version (forced=%d peer=%d) - reject", - data->force_version, peer_version); - eap_peap_state(data, FAILURE); - return; - } - if (peer_version < data->peap_version) { - wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; " - "use version %d", - peer_version, data->peap_version, peer_version); - data->peap_version = peer_version; - - } - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-PEAP: Short frame with TLS " - "length"); - eap_peap_state(data, FAILURE); - return; - } - tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | - pos[3]; - wpa_printf(MSG_DEBUG, "EAP-PEAP: TLS Message Length: %d", - tls_msg_len); - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - pos += 4; - left -= 4; - } - - switch (data->state) { - case PHASE1: - if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) { - wpa_printf(MSG_INFO, "EAP-PEAP: TLS processing " - "failed"); - eap_peap_state(data, FAILURE); - } - break; - case PHASE2_START: - eap_peap_state(data, PHASE2_ID); - eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY); - break; - case PHASE2_ID: - case PHASE2_METHOD: - case PHASE2_TLV: - eap_peap_process_phase2(sm, data, resp, pos, left); - break; - case SUCCESS_REQ: - eap_peap_state(data, SUCCESS); - break; - case FAILURE_REQ: - eap_peap_state(data, FAILURE); - break; - default: - wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s", - data->state, __func__); - break; - } - - if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { - wpa_printf(MSG_INFO, "EAP-PEAP: Locally detected fatal error " - "in TLS processing"); - eap_peap_state(data, FAILURE); - } -} - - -static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - return data->state == SUCCESS || data->state == FAILURE; -} - - -static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_peap_data *data = priv; - u8 *eapKeyData; - - if (data->state != SUCCESS) - return NULL; - - /* TODO: PEAPv1 - different label in some cases */ - eapKeyData = eap_tls_derive_key(sm, &data->ssl, - "client EAP encryption", - EAP_TLS_KEY_LEN); - if (eapKeyData) { - *len = EAP_TLS_KEY_LEN; - wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", - eapKeyData, EAP_TLS_KEY_LEN); - } else { - wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key"); - } - - return eapKeyData; -} - - -static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_peap_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_peap = -{ - .method = EAP_TYPE_PEAP, - .name = "PEAP", - .init = eap_peap_init, - .reset = eap_peap_reset, - .buildReq = eap_peap_buildReq, - .check = eap_peap_check, - .process = eap_peap_process, - .isDone = eap_peap_isDone, - .getKey = eap_peap_getKey, - .isSuccess = eap_peap_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_psk.c b/contrib/hostapd-0.4.9/eap_psk.c deleted file mode 100644 index f688e61d10..0000000000 --- a/contrib/hostapd-0.4.9/eap_psk.c +++ /dev/null @@ -1,458 +0,0 @@ -/* - * hostapd / EAP-PSK (draft-bersani-eap-psk-09.txt) server - * Copyright (c) 2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * Note: EAP-PSK is an EAP authentication method and as such, completely - * different from WPA-PSK. This file is not needed for WPA-PSK functionality. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" -#include "aes_wrap.h" -#include "eap_psk_common.h" - - -struct eap_psk_data { - enum { PSK_1, PSK_3, SUCCESS, FAILURE } state; - u8 rand_s[EAP_PSK_RAND_LEN]; - u8 rand_p[EAP_PSK_RAND_LEN]; - u8 *id_p, *id_s; - size_t id_p_len, id_s_len; - u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; - u8 msk[EAP_PSK_MSK_LEN]; -}; - - -static void * eap_psk_init(struct eap_sm *sm) -{ - struct eap_psk_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->state = PSK_1; - data->id_s = "hostapd"; - data->id_s_len = 7; - - return data; -} - - -static void eap_psk_reset(struct eap_sm *sm, void *priv) -{ - struct eap_psk_data *data = priv; - free(data->id_p); - free(data); -} - - -static u8 * eap_psk_build_1(struct eap_sm *sm, struct eap_psk_data *data, - int id, size_t *reqDataLen) -{ - struct eap_psk_hdr_1 *req; - - wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)"); - - if (hostapd_get_rand(data->rand_s, EAP_PSK_RAND_LEN)) { - wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); - data->state = FAILURE; - return NULL; - } - wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_S (server rand)", - data->rand_s, EAP_PSK_RAND_LEN); - - *reqDataLen = sizeof(*req) + data->id_s_len; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " - "request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_PSK; - req->flags = 0; /* T=0 */ - memcpy(req->rand_s, data->rand_s, EAP_PSK_RAND_LEN); - memcpy((u8 *) (req + 1), data->id_s, data->id_s_len); - - return (u8 *) req; -} - - -static u8 * eap_psk_build_3(struct eap_sm *sm, struct eap_psk_data *data, - int id, size_t *reqDataLen) -{ - struct eap_psk_hdr_3 *req; - u8 *buf, *pchannel, nonce[16]; - size_t buflen; - - wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-3 (sending)"); - - *reqDataLen = sizeof(*req) + 4 + 16 + 1; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " - "request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_PSK; - req->flags = 2; /* T=2 */ - memcpy(req->rand_s, data->rand_s, EAP_PSK_RAND_LEN); - - /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ - buflen = data->id_s_len + EAP_PSK_RAND_LEN; - buf = malloc(buflen); - if (buf == NULL) { - data->state = FAILURE; - return NULL; - } - memcpy(buf, data->id_s, data->id_s_len); - memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); - omac1_aes_128(data->ak, buf, buflen, req->mac_s); - free(buf); - - eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk); - wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_PSK_MSK_LEN); - - memset(nonce, 0, sizeof(nonce)); - pchannel = (u8 *) (req + 1); - memcpy(pchannel, nonce + 12, 4); - memset(pchannel + 4, 0, 16); /* Tag */ - pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; - wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (plaintext)", - pchannel, 4 + 16 + 1); - aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), (u8 *) req, 22, - pchannel + 4 + 16, 1, pchannel + 4); - wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (encrypted)", - pchannel, 4 + 16 + 1); - - return (u8 *) req; -} - - -static u8 * eap_psk_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_psk_data *data = priv; - - switch (data->state) { - case PSK_1: - return eap_psk_build_1(sm, data, id, reqDataLen); - case PSK_3: - return eap_psk_build_3(sm, data, id, reqDataLen); - default: - wpa_printf(MSG_DEBUG, "EAP-PSK: Unknown state %d in buildReq", - data->state); - break; - } - return NULL; -} - - -static Boolean eap_psk_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_psk_data *data = priv; - struct eap_psk_hdr *resp; - size_t len; - u8 t; - - resp = (struct eap_psk_hdr *) respData; - if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PSK || - (len = ntohs(resp->length)) > respDataLen || - len < sizeof(*resp)) { - wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); - return TRUE; - } - t = resp->flags & 0x03; - - wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t); - - if (data->state == PSK_1 && t != 1) { - wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-2 - " - "ignore T=%d", t); - return TRUE; - } - - if (data->state == PSK_3 && t != 3) { - wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-4 - " - "ignore T=%d", t); - return TRUE; - } - - if ((t == 1 && len < sizeof(struct eap_psk_hdr_2)) || - (t == 3 && len < sizeof(struct eap_psk_hdr_4))) { - wpa_printf(MSG_DEBUG, "EAP-PSK: Too short frame"); - return TRUE; - } - - return FALSE; -} - - -static void eap_psk_process_2(struct eap_sm *sm, - struct eap_psk_data *data, - u8 *respData, size_t respDataLen) -{ - struct eap_psk_hdr_2 *resp; - u8 *pos, mac[EAP_PSK_MAC_LEN], *buf; - size_t len, left, buflen; - int i; - - if (data->state != PSK_1) - return; - - wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-2"); - - resp = (struct eap_psk_hdr_2 *) respData; - len = ntohs(resp->length); - pos = (u8 *) (resp + 1); - left = len - sizeof(*resp); - - free(data->id_p); - data->id_p = malloc(left); - if (data->id_p == NULL) { - wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for " - "ID_P"); - return; - } - memcpy(data->id_p, pos, left); - data->id_p_len = left; - wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P", - data->id_p, data->id_p_len); - - if (eap_user_get(sm, data->id_p, data->id_p_len, 0) < 0) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: unknown ID_P", - data->id_p, data->id_p_len); - data->state = FAILURE; - return; - } - - for (i = 0; - i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE; - i++) { - if (sm->user->methods[i] == EAP_TYPE_PSK) - break; - } - - if (sm->user->methods[i] != EAP_TYPE_PSK) { - wpa_hexdump_ascii(MSG_DEBUG, - "EAP-PSK: EAP-PSK not enabled for ID_P", - data->id_p, data->id_p_len); - data->state = FAILURE; - return; - } - - if (sm->user->password == NULL || - sm->user->password_len != EAP_PSK_PSK_LEN) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: invalid password in " - "user database for ID_P", - data->id_p, data->id_p_len); - data->state = FAILURE; - return; - } - eap_psk_key_setup(sm->user->password, data->ak, data->kdk); - wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); - - wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_P (client rand)", - resp->rand_p, EAP_PSK_RAND_LEN); - memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN); - - /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ - buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; - buf = malloc(buflen); - if (buf == NULL) { - data->state = FAILURE; - return; - } - memcpy(buf, data->id_p, data->id_p_len); - pos = buf + data->id_p_len; - memcpy(pos, data->id_s, data->id_s_len); - pos += data->id_s_len; - memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN); - pos += EAP_PSK_RAND_LEN; - memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); - omac1_aes_128(data->ak, buf, buflen, mac); - free(buf); - wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN); - if (memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) { - wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P"); - wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P", - mac, EAP_PSK_MAC_LEN); - data->state = FAILURE; - return; - } - - data->state = PSK_3; -} - - -static void eap_psk_process_4(struct eap_sm *sm, - struct eap_psk_data *data, - u8 *respData, size_t respDataLen) -{ - struct eap_psk_hdr_4 *resp; - u8 *pos, *decrypted, nonce[16], *tag; - size_t left; - - if (data->state != PSK_3) - return; - - wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-4"); - - resp = (struct eap_psk_hdr_4 *) respData; - pos = (u8 *) (resp + 1); - left = ntohs(resp->length) - sizeof(*resp); - - wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Encrypted PCHANNEL", pos, left); - - if (left < 4 + 16 + 1) { - wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " - "PSK-4 (len=%lu, expected 21)", - (unsigned long) left); - return; - } - - if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0 && pos[3] == 0) { - wpa_printf(MSG_DEBUG, "EAP-PSK: Nonce did not increase"); - return; - } - - memset(nonce, 0, 12); - memcpy(nonce + 12, pos, 4); - pos += 4; - left -= 4; - tag = pos; - pos += 16; - left -= 16; - - decrypted = malloc(left); - if (decrypted == NULL) - return; - memcpy(decrypted, pos, left); - - if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), - respData, 22, decrypted, left, tag)) { - wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); - free(decrypted); - data->state = FAILURE; - return; - } - wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", - decrypted, left); - - /* Verify R flag */ - switch (decrypted[0] >> 6) { - case EAP_PSK_R_FLAG_CONT: - wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); - data->state = FAILURE; - break; - case EAP_PSK_R_FLAG_DONE_SUCCESS: - wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); - data->state = SUCCESS; - break; - case EAP_PSK_R_FLAG_DONE_FAILURE: - wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); - data->state = FAILURE; - break; - } - free(decrypted); -} - - -static void eap_psk_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_psk_data *data = priv; - struct eap_psk_hdr *resp; - - if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-PSK: Password not configured"); - data->state = FAILURE; - return; - } - - resp = (struct eap_psk_hdr *) respData; - - switch (resp->flags & 0x03) { - case 1: - eap_psk_process_2(sm, data, respData, respDataLen); - break; - case 3: - eap_psk_process_4(sm, data, respData, respDataLen); - break; - } -} - - -static Boolean eap_psk_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_psk_data *data = priv; - return data->state == SUCCESS || data->state == FAILURE; -} - - -static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_psk_data *data = priv; - u8 *key; - - if (data->state != SUCCESS) - return NULL; - - key = malloc(EAP_PSK_MSK_LEN); - if (key == NULL) - return NULL; - memcpy(key, data->msk, EAP_PSK_MSK_LEN); - *len = EAP_PSK_MSK_LEN; - - return key; -} - - -static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_psk_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_psk = -{ - .method = EAP_TYPE_PSK, - .name = "PSK", - .init = eap_psk_init, - .reset = eap_psk_reset, - .buildReq = eap_psk_buildReq, - .check = eap_psk_check, - .process = eap_psk_process, - .isDone = eap_psk_isDone, - .getKey = eap_psk_getKey, - .isSuccess = eap_psk_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_psk_common.c b/contrib/hostapd-0.4.9/eap_psk_common.c deleted file mode 100644 index 24de66cf0c..0000000000 --- a/contrib/hostapd-0.4.9/eap_psk_common.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * WPA Supplicant / EAP-PSK shared routines - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include - -#include "common.h" -#include "aes_wrap.h" -#include "eap_psk_common.h" - -#define aes_block_size 16 - - -void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) -{ - memset(ak, 0, aes_block_size); - aes_128_encrypt_block(psk, ak, ak); - memcpy(kdk, ak, aes_block_size); - ak[aes_block_size - 1] ^= 0x01; - kdk[aes_block_size - 1] ^= 0x02; - aes_128_encrypt_block(psk, ak, ak); - aes_128_encrypt_block(psk, kdk, kdk); -} - - -void eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk) -{ - u8 hash[aes_block_size]; - u8 counter = 1; - int i; - - aes_128_encrypt_block(kdk, rand_p, hash); - - hash[aes_block_size - 1] ^= counter; - aes_128_encrypt_block(kdk, hash, tek); - hash[aes_block_size - 1] ^= counter; - counter++; - - for (i = 0; i < EAP_PSK_MSK_LEN / aes_block_size; i++) { - hash[aes_block_size - 1] ^= counter; - aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]); - hash[aes_block_size - 1] ^= counter; - counter++; - } -} diff --git a/contrib/hostapd-0.4.9/eap_psk_common.h b/contrib/hostapd-0.4.9/eap_psk_common.h deleted file mode 100644 index 5dd3a1042d..0000000000 --- a/contrib/hostapd-0.4.9/eap_psk_common.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * WPA Supplicant / EAP-PSK shared routines - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef EAP_PSK_COMMON_H -#define EAP_PSK_COMMON_H - - -#define EAP_PSK_RAND_LEN 16 -#define EAP_PSK_MAC_LEN 16 -#define EAP_PSK_TEK_LEN 16 -#define EAP_PSK_MSK_LEN 64 -#define EAP_PSK_PSK_LEN 16 -#define EAP_PSK_AK_LEN 16 -#define EAP_PSK_KDK_LEN 16 - -#define EAP_PSK_R_FLAG_CONT 1 -#define EAP_PSK_R_FLAG_DONE_SUCCESS 2 -#define EAP_PSK_R_FLAG_DONE_FAILURE 3 -#define EAP_PSK_E_FLAG 0x20 - -/* Shared prefix for all EAP-PSK frames */ -struct eap_psk_hdr { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_PSK */ - u8 flags; -} __attribute__ ((packed)); - -/* EAP-PSK First Message (AS -> Supplicant) */ -struct eap_psk_hdr_1 { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_PSK */ - u8 flags; - u8 rand_s[EAP_PSK_RAND_LEN]; - /* Followed by variable length ID_S */ -} __attribute__ ((packed)); - -/* EAP-PSK Second Message (Supplicant -> AS) */ -struct eap_psk_hdr_2 { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_PSK */ - u8 flags; - u8 rand_s[EAP_PSK_RAND_LEN]; - u8 rand_p[EAP_PSK_RAND_LEN]; - u8 mac_p[EAP_PSK_MAC_LEN]; - /* Followed by variable length ID_P */ -} __attribute__ ((packed)); - -/* EAP-PSK Third Message (AS -> Supplicant) */ -struct eap_psk_hdr_3 { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_PSK */ - u8 flags; - u8 rand_s[EAP_PSK_RAND_LEN]; - u8 mac_s[EAP_PSK_MAC_LEN]; - /* Followed by variable length PCHANNEL */ -} __attribute__ ((packed)); - -/* EAP-PSK Fourth Message (Supplicant -> AS) */ -struct eap_psk_hdr_4 { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_PSK */ - u8 flags; - u8 rand_s[EAP_PSK_RAND_LEN]; - /* Followed by variable length PCHANNEL */ -} __attribute__ ((packed)); - - -void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk); -void eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk); - -#endif /* EAP_PSK_COMMON_H */ diff --git a/contrib/hostapd-0.4.9/eap_sim.c b/contrib/hostapd-0.4.9/eap_sim.c deleted file mode 100644 index fa60cf54d1..0000000000 --- a/contrib/hostapd-0.4.9/eap_sim.c +++ /dev/null @@ -1,431 +0,0 @@ -/* - * hostapd / EAP-SIM (draft-haverinen-pppext-eap-sim-15.txt) - * Copyright (c) 2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "crypto.h" -#include "eap_i.h" -#include "eap_sim_common.h" -#include "eap_sim_db.h" - - -#define EAP_SIM_VERSION 1 - -/* EAP-SIM Subtypes */ -#define EAP_SIM_SUBTYPE_START 10 -#define EAP_SIM_SUBTYPE_CHALLENGE 11 -#define EAP_SIM_SUBTYPE_NOTIFICATION 12 -#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13 -#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14 - -/* AT_CLIENT_ERROR_CODE error codes */ -#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0 -#define EAP_SIM_UNSUPPORTED_VERSION 1 -#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2 -#define EAP_SIM_RAND_NOT_FRESH 3 - -#define KC_LEN 8 -#define SRES_LEN 4 -#define EAP_SIM_MAX_FAST_REAUTHS 1000 - -#define EAP_SIM_MAX_CHAL 3 - -struct eap_sim_data { - u8 mk[EAP_SIM_MK_LEN]; - u8 nonce_mt[EAP_SIM_NONCE_MT_LEN]; - u8 k_aut[EAP_SIM_K_AUT_LEN]; - u8 k_encr[EAP_SIM_K_ENCR_LEN]; - u8 msk[EAP_SIM_KEYING_DATA_LEN]; - u8 kc[EAP_SIM_MAX_CHAL][KC_LEN]; - u8 sres[EAP_SIM_MAX_CHAL][SRES_LEN]; - u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; - int num_chal; - enum { START, CHALLENGE, SUCCESS, FAILURE } state; -}; - - -static const char * eap_sim_state_txt(int state) -{ - switch (state) { - case START: - return "START"; - case CHALLENGE: - return "CHALLENGE"; - case SUCCESS: - return "SUCCESS"; - case FAILURE: - return "FAILURE"; - default: - return "Unknown?!"; - } -} - - -static void eap_sim_state(struct eap_sim_data *data, int state) -{ - wpa_printf(MSG_DEBUG, "EAP-SIM %s -> %s", - eap_sim_state_txt(data->state), - eap_sim_state_txt(state)); - data->state = state; -} - - -static void * eap_sim_init(struct eap_sm *sm) -{ - struct eap_sim_data *data; - - if (sm->eap_sim_db_priv == NULL) { - wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured"); - return NULL; - } - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->state = START; - - return data; -} - - -static void eap_sim_reset(struct eap_sm *sm, void *priv) -{ - struct eap_sim_data *data = priv; - free(data); -} - - -static u8 * eap_sim_build_start(struct eap_sm *sm, struct eap_sim_data *data, - int id, size_t *reqDataLen) -{ - struct eap_sim_msg *msg; - u8 ver[2]; - - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, - EAP_SIM_SUBTYPE_START); - if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, - sm->identity_len)) { - eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); - } - ver[0] = 0; - ver[1] = EAP_SIM_VERSION; - eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver), - ver, sizeof(ver)); - return eap_sim_msg_finish(msg, reqDataLen, NULL, NULL, 0); -} - - -static u8 * eap_sim_build_challenge(struct eap_sm *sm, - struct eap_sim_data *data, - int id, size_t *reqDataLen) -{ - struct eap_sim_msg *msg; - - msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, - EAP_SIM_SUBTYPE_CHALLENGE); - eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand, - data->num_chal * GSM_RAND_LEN); - eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); - return eap_sim_msg_finish(msg, reqDataLen, data->k_aut, data->nonce_mt, - EAP_SIM_NONCE_MT_LEN); -} - - -static u8 * eap_sim_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_sim_data *data = priv; - - switch (data->state) { - case START: - return eap_sim_build_start(sm, data, id, reqDataLen); - case CHALLENGE: - return eap_sim_build_challenge(sm, data, id, reqDataLen); - default: - wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " - "buildReq", data->state); - break; - } - return NULL; -} - - -static Boolean eap_sim_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_sim_data *data = priv; - struct eap_hdr *resp; - u8 *pos, subtype; - size_t len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_SIM || - (len = ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); - return TRUE; - } - subtype = pos[1]; - - if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) - return FALSE; - - switch (data->state) { - case START: - if (subtype != EAP_SIM_SUBTYPE_START) { - wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " - "subtype %d", subtype); - return TRUE; - } - break; - case CHALLENGE: - if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) { - wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " - "subtype %d", subtype); - return TRUE; - } - break; - default: - wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for " - "processing a response", data->state); - return TRUE; - } - - return FALSE; -} - - -static int eap_sim_supported_ver(struct eap_sim_data *data, int version) -{ - return version == EAP_SIM_VERSION; -} - - -static void eap_sim_derive_mk(struct eap_sim_data *data, - const u8 *identity, size_t identity_len, - const u8 *nonce_mt, int selected_version, - int num_chal, const u8 *kc) -{ - u8 sel_ver[2], ver_list[2]; - const unsigned char *addr[5]; - size_t len[5]; - - addr[0] = identity; - addr[1] = kc; - addr[2] = nonce_mt; - addr[3] = ver_list; - addr[4] = sel_ver; - - len[0] = identity_len; - len[1] = num_chal * KC_LEN; - len[2] = EAP_SIM_NONCE_MT_LEN; - len[3] = sizeof(ver_list); - len[4] = sizeof(sel_ver); - - ver_list[0] = 0; - ver_list[1] = EAP_SIM_VERSION; - sel_ver[0] = selected_version >> 8; - sel_ver[1] = selected_version & 0xff; - - /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ - sha1_vector(5, addr, len, data->mk); - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", data->mk, EAP_SIM_MK_LEN); -} - - -static void eap_sim_process_start(struct eap_sm *sm, - struct eap_sim_data *data, - u8 *respData, size_t respDataLen, - struct eap_sim_attrs *attr) -{ - wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response"); - - if (attr->nonce_mt == NULL || attr->selected_version < 0) { - wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing " - "required attributes"); - eap_sim_state(data, FAILURE); - return; - } - - if (!eap_sim_supported_ver(data, attr->selected_version)) { - wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported " - "version %d", attr->selected_version); - eap_sim_state(data, FAILURE); - return; - } - - if (attr->identity) { - free(sm->identity); - sm->identity = malloc(attr->identity_len); - if (sm->identity) { - memcpy(sm->identity, attr->identity, - attr->identity_len); - sm->identity_len = attr->identity_len; - } - } - - if (sm->identity == NULL || sm->identity_len < 1 || - sm->identity[0] != '1') { - wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent" - " user name"); - eap_sim_state(data, FAILURE); - return; - } - - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", - sm->identity, sm->identity_len); - - data->num_chal = eap_sim_db_get_gsm_triplets( - sm->eap_sim_db_priv, sm->identity, sm->identity_len, - EAP_SIM_MAX_CHAL, - (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres); - if (data->num_chal < 2) { - wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM " - "authentication triplets for the peer"); - eap_sim_state(data, FAILURE); - return; - } - - memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN); - eap_sim_derive_mk(data, sm->identity, sm->identity_len, attr->nonce_mt, - attr->selected_version, data->num_chal, - (u8 *) data->kc); - eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk); - - eap_sim_state(data, CHALLENGE); -} - - -static void eap_sim_process_challenge(struct eap_sm *sm, - struct eap_sim_data *data, - u8 *respData, size_t respDataLen, - struct eap_sim_attrs *attr) -{ - if (attr->mac == NULL || - eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac, - (u8 *) data->sres, data->num_chal * SRES_LEN)) { - wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " - "did not include valid AT_MAC"); - eap_sim_state(data, FAILURE); - return; - } - - wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the " - "correct AT_MAC"); - eap_sim_state(data, SUCCESS); -} - - -static void eap_sim_process_client_error(struct eap_sm *sm, - struct eap_sim_data *data, - u8 *respData, size_t respDataLen, - struct eap_sim_attrs *attr) -{ - wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d", - attr->client_error_code); - eap_sim_state(data, FAILURE); -} - - -static void eap_sim_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_sim_data *data = priv; - struct eap_hdr *resp; - u8 *pos, subtype; - size_t len; - struct eap_sim_attrs attr; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - subtype = pos[1]; - len = ntohs(resp->length); - pos += 4; - - if (eap_sim_parse_attr(pos, respData + len, &attr, 0, 0)) { - wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes"); - eap_sim_state(data, FAILURE); - return; - } - - if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) { - eap_sim_process_client_error(sm, data, respData, len, &attr); - return; - } - - switch (data->state) { - case START: - eap_sim_process_start(sm, data, respData, len, &attr); - break; - case CHALLENGE: - eap_sim_process_challenge(sm, data, respData, len, &attr); - break; - default: - wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " - "process", data->state); - break; - } -} - - -static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_sim_data *data = priv; - return data->state == SUCCESS || data->state == FAILURE; -} - - -static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_sim_data *data = priv; - u8 *key; - - if (data->state != SUCCESS) - return NULL; - - key = malloc(EAP_SIM_KEYING_DATA_LEN); - if (key == NULL) - return NULL; - memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); - *len = EAP_SIM_KEYING_DATA_LEN; - return key; -} - - -static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_sim_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_sim = -{ - .method = EAP_TYPE_SIM, - .name = "SIM", - .init = eap_sim_init, - .reset = eap_sim_reset, - .buildReq = eap_sim_buildReq, - .check = eap_sim_check, - .process = eap_sim_process, - .isDone = eap_sim_isDone, - .getKey = eap_sim_getKey, - .isSuccess = eap_sim_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_sim_common.c b/contrib/hostapd-0.4.9/eap_sim_common.c deleted file mode 100644 index 75947b7995..0000000000 --- a/contrib/hostapd-0.4.9/eap_sim_common.c +++ /dev/null @@ -1,794 +0,0 @@ -/* - * WPA Supplicant / EAP-SIM/AKA shared routines - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include - -#include "common.h" -#include "eap_i.h" -#include "sha1.h" -#include "crypto.h" -#include "aes_wrap.h" -#include "eap_sim_common.h" - - -static void eap_sim_prf(const u8 *key, u8 *x, size_t xlen) -{ - u8 xkey[64]; - u32 t[5], _t[5]; - int i, j, m, k; - u8 *xpos = x; - u32 carry; - - /* FIPS 186-2 + change notice 1 */ - - memcpy(xkey, key, EAP_SIM_MK_LEN); - memset(xkey + EAP_SIM_MK_LEN, 0, 64 - EAP_SIM_MK_LEN); - t[0] = 0x67452301; - t[1] = 0xEFCDAB89; - t[2] = 0x98BADCFE; - t[3] = 0x10325476; - t[4] = 0xC3D2E1F0; - - m = xlen / 40; - for (j = 0; j < m; j++) { - /* XSEED_j = 0 */ - for (i = 0; i < 2; i++) { - /* XVAL = (XKEY + XSEED_j) mod 2^b */ - - /* w_i = G(t, XVAL) */ - memcpy(_t, t, 20); - sha1_transform((u8 *) _t, xkey); - _t[0] = host_to_be32(_t[0]); - _t[1] = host_to_be32(_t[1]); - _t[2] = host_to_be32(_t[2]); - _t[3] = host_to_be32(_t[3]); - _t[4] = host_to_be32(_t[4]); - memcpy(xpos, _t, 20); - - /* XKEY = (1 + XKEY + w_i) mod 2^b */ - carry = 1; - for (k = 19; k >= 0; k--) { - carry += xkey[k] + xpos[k]; - xkey[k] = carry & 0xff; - carry >>= 8; - } - - xpos += SHA1_MAC_LEN; - } - /* x_j = w_0|w_1 */ - } -} - - -void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk) -{ - u8 buf[120], *pos; - eap_sim_prf(mk, buf, 120); - pos = buf; - memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); - pos += EAP_SIM_K_ENCR_LEN; - memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN); - pos += EAP_SIM_K_AUT_LEN; - memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN); - - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr", - k_encr, EAP_SIM_K_ENCR_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut", - k_aut, EAP_SIM_K_ENCR_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material", - msk, EAP_SIM_KEYING_DATA_LEN); -} - - -void eap_sim_derive_keys_reauth(u16 _counter, - const u8 *identity, size_t identity_len, - const u8 *nonce_s, const u8 *mk, u8 *msk) -{ - u8 xkey[SHA1_MAC_LEN]; - u8 counter[2]; - const u8 *addr[4]; - size_t len[4]; - - addr[0] = identity; - len[0] = identity_len; - addr[1] = counter; - len[1] = 2; - addr[2] = nonce_s; - len[2] = EAP_SIM_NONCE_S_LEN; - addr[3] = mk; - len[3] = EAP_SIM_MK_LEN; - - WPA_PUT_BE16(counter, _counter); - - wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth"); - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", - identity, identity_len); - wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2); - wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s, - EAP_SIM_NONCE_S_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN); - - /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */ - sha1_vector(4, addr, len, xkey); - wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN); - - eap_sim_prf(xkey, msk, EAP_SIM_KEYING_DATA_LEN); - wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material", - msk, EAP_SIM_KEYING_DATA_LEN); -} - - -int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, - const u8 *mac, const u8 *extra, size_t extra_len) -{ - unsigned char hmac[SHA1_MAC_LEN]; - const u8 *addr[2]; - size_t len[2]; - u8 *tmp; - - if (mac == NULL || req_len < EAP_SIM_MAC_LEN || mac < req || - mac > req + req_len - EAP_SIM_MAC_LEN) - return -1; - - tmp = malloc(req_len); - if (tmp == NULL) - return -1; - - addr[0] = tmp; - len[0] = req_len; - addr[1] = extra; - len[1] = extra_len; - - /* HMAC-SHA1-128 */ - memcpy(tmp, req, req_len); - memset(tmp + (mac - req), 0, EAP_SIM_MAC_LEN); - hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); - free(tmp); - - return (memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; -} - - -void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, - const u8 *extra, size_t extra_len) -{ - unsigned char hmac[SHA1_MAC_LEN]; - const u8 *addr[2]; - size_t len[2]; - - addr[0] = msg; - len[0] = msg_len; - addr[1] = extra; - len[1] = extra_len; - - /* HMAC-SHA1-128 */ - memset(mac, 0, EAP_SIM_MAC_LEN); - hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); - memcpy(mac, hmac, EAP_SIM_MAC_LEN); -} - - -int eap_sim_parse_attr(const u8 *start, const u8 *end, - struct eap_sim_attrs *attr, int aka, int encr) -{ - const u8 *pos = start, *apos; - size_t alen, plen; - int list_len, i; - - memset(attr, 0, sizeof(*attr)); - attr->id_req = NO_ID_REQ; - attr->notification = -1; - attr->counter = -1; - attr->selected_version = -1; - attr->client_error_code = -1; - - while (pos < end) { - if (pos + 2 > end) { - wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)"); - return -1; - } - wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d", - pos[0], pos[1] * 4); - if (pos + pos[1] * 4 > end) { - wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow " - "(pos=%p len=%d end=%p)", - pos, pos[1] * 4, end); - return -1; - } - apos = pos + 2; - alen = pos[1] * 4 - 2; - wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data", - apos, alen); - - switch (pos[0]) { - case EAP_SIM_AT_RAND: - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND"); - apos += 2; - alen -= 2; - if ((!aka && (alen % GSM_RAND_LEN)) || - (aka && alen != AKA_RAND_LEN)) { - wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND" - " (len %lu)", - (unsigned long) alen); - return -1; - } - attr->rand = apos; - attr->num_chal = alen / GSM_RAND_LEN; - break; - case EAP_SIM_AT_AUTN: - wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN"); - if (!aka) { - wpa_printf(MSG_DEBUG, "EAP-SIM: " - "Unexpected AT_AUTN"); - return -1; - } - apos += 2; - alen -= 2; - if (alen != AKA_AUTN_LEN) { - wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN" - " (len %lu)", - (unsigned long) alen); - return -1; - } - attr->autn = apos; - break; - case EAP_SIM_AT_PADDING: - if (!encr) { - wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " - "AT_PADDING"); - return -1; - } - wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING"); - for (i = 2; i < alen; i++) { - if (apos[i] != 0) { - wpa_printf(MSG_INFO, "EAP-SIM: (encr) " - "AT_PADDING used a non-zero" - " padding byte"); - wpa_hexdump(MSG_DEBUG, "EAP-SIM: " - "(encr) padding bytes", - apos + 2, alen - 2); - return -1; - } - } - break; - case EAP_SIM_AT_NONCE_MT: - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT"); - if (alen != 2 + EAP_SIM_NONCE_MT_LEN) { - wpa_printf(MSG_INFO, "EAP-SIM: Invalid " - "AT_NONCE_MT length"); - return -1; - } - attr->nonce_mt = apos + 2; - break; - case EAP_SIM_AT_PERMANENT_ID_REQ: - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ"); - attr->id_req = PERMANENT_ID; - break; - case EAP_SIM_AT_MAC: - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC"); - if (alen != 2 + EAP_SIM_MAC_LEN) { - wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC " - "length"); - return -1; - } - attr->mac = apos + 2; - break; - case EAP_SIM_AT_NOTIFICATION: - if (alen != 2) { - wpa_printf(MSG_INFO, "EAP-SIM: Invalid " - "AT_NOTIFICATION length %lu", - (unsigned long) alen); - return -1; - } - attr->notification = apos[0] * 256 + apos[1]; - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d", - attr->notification); - break; - case EAP_SIM_AT_ANY_ID_REQ: - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ"); - attr->id_req = ANY_ID; - break; - case EAP_SIM_AT_IDENTITY: - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY"); - attr->identity = apos + 2; - attr->identity_len = alen - 2; - break; - case EAP_SIM_AT_VERSION_LIST: - if (aka) { - wpa_printf(MSG_DEBUG, "EAP-AKA: " - "Unexpected AT_VERSION_LIST"); - return -1; - } - list_len = apos[0] * 256 + apos[1]; - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST"); - if (list_len < 2 || list_len > alen - 2) { - wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " - "AT_VERSION_LIST (list_len=%d " - "attr_len=%lu)", list_len, - (unsigned long) alen); - return -1; - } - attr->version_list = apos + 2; - attr->version_list_len = list_len; - break; - case EAP_SIM_AT_SELECTED_VERSION: - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION"); - if (alen != 2) { - wpa_printf(MSG_INFO, "EAP-SIM: Invalid " - "AT_SELECTED_VERSION length %lu", - (unsigned long) alen); - return -1; - } - attr->selected_version = apos[0] * 256 + apos[1]; - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION " - "%d", attr->selected_version); - break; - case EAP_SIM_AT_FULLAUTH_ID_REQ: - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ"); - attr->id_req = FULLAUTH_ID; - break; - case EAP_SIM_AT_COUNTER: - if (!encr) { - wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " - "AT_COUNTER"); - return -1; - } - if (alen != 2) { - wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " - "AT_COUNTER (alen=%lu)", - (unsigned long) alen); - return -1; - } - attr->counter = apos[0] * 256 + apos[1]; - wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d", - attr->counter); - break; - case EAP_SIM_AT_NONCE_S: - if (!encr) { - wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " - "AT_NONCE_S"); - return -1; - } - wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " - "AT_NONCE_S"); - if (alen != 2 + EAP_SIM_NONCE_S_LEN) { - wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " - "AT_NONCE_S (alen=%lu)", - (unsigned long) alen); - return -1; - } - attr->nonce_s = apos + 2; - break; - case EAP_SIM_AT_CLIENT_ERROR_CODE: - if (alen != 2) { - wpa_printf(MSG_INFO, "EAP-SIM: Invalid " - "AT_CLIENT_ERROR_CODE length %lu", - (unsigned long) alen); - return -1; - } - attr->client_error_code = apos[0] * 256 + apos[1]; - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE " - "%d", attr->client_error_code); - break; - case EAP_SIM_AT_IV: - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV"); - if (alen != 2 + EAP_SIM_MAC_LEN) { - wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV " - "length %lu", (unsigned long) alen); - return -1; - } - attr->iv = apos + 2; - break; - case EAP_SIM_AT_ENCR_DATA: - wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA"); - attr->encr_data = apos + 2; - attr->encr_data_len = alen - 2; - if (attr->encr_data_len % 16) { - wpa_printf(MSG_INFO, "EAP-SIM: Invalid " - "AT_ENCR_DATA length %lu", - (unsigned long) - attr->encr_data_len); - return -1; - } - break; - case EAP_SIM_AT_NEXT_PSEUDONYM: - if (!encr) { - wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " - "AT_NEXT_PSEUDONYM"); - return -1; - } - wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " - "AT_NEXT_PSEUDONYM"); - plen = apos[0] * 256 + apos[1]; - if (plen > alen - 2) { - wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" - " AT_NEXT_PSEUDONYM (actual" - " len %lu, attr len %lu)", - (unsigned long) plen, - (unsigned long) alen); - return -1; - } - attr->next_pseudonym = pos + 4; - attr->next_pseudonym_len = plen; - break; - case EAP_SIM_AT_NEXT_REAUTH_ID: - if (!encr) { - wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " - "AT_NEXT_REAUTH_ID"); - return -1; - } - wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " - "AT_NEXT_REAUTH_ID"); - plen = apos[0] * 256 + apos[1]; - if (plen > alen - 2) { - wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" - " AT_NEXT_REAUTH_ID (actual" - " len %lu, attr len %lu)", - (unsigned long) plen, - (unsigned long) alen); - return -1; - } - attr->next_reauth_id = pos + 4; - attr->next_reauth_id_len = plen; - break; - default: - if (pos[0] < 128) { - wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " - "non-skippable attribute %d", - pos[0]); - return -1; - } - - wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable" - " attribute %d ignored", pos[0]); - break; - } - - pos += pos[1] * 4; - } - - wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully " - "(aka=%d encr=%d)", aka, encr); - - return 0; -} - - -u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, - size_t encr_data_len, const u8 *iv, - struct eap_sim_attrs *attr, int aka) -{ - u8 *decrypted; - - if (!iv) { - wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV"); - return NULL; - } - - decrypted = malloc(encr_data_len); - if (decrypted == NULL) - return NULL; - memcpy(decrypted, encr_data, encr_data_len); - - aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len); - wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", - decrypted, encr_data_len); - - if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr, - aka, 1)) { - wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse " - "decrypted AT_ENCR_DATA"); - free(decrypted); - return NULL; - } - - return decrypted; -} - - -#define EAP_SIM_INIT_LEN 128 - -struct eap_sim_msg { - u8 *buf; - size_t buf_len, used; - size_t mac, iv, encr; /* index from buf */ -}; - - -struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype) -{ - struct eap_sim_msg *msg; - struct eap_hdr *eap; - u8 *pos; - - msg = malloc(sizeof(*msg)); - if (msg == NULL) - return NULL; - memset(msg, 0, sizeof(*msg)); - - msg->buf = malloc(EAP_SIM_INIT_LEN); - if (msg->buf == NULL) { - free(msg); - return NULL; - } - memset(msg->buf, 0, EAP_SIM_INIT_LEN); - msg->buf_len = EAP_SIM_INIT_LEN; - eap = (struct eap_hdr *) msg->buf; - eap->code = code; - eap->identifier = id; - msg->used = sizeof(*eap); - - pos = (u8 *) (eap + 1); - *pos++ = type; - *pos++ = subtype; - *pos++ = 0; /* Reserved */ - *pos++ = 0; /* Reserved */ - msg->used += 4; - - return msg; -} - - -u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, - const u8 *extra, size_t extra_len) -{ - struct eap_hdr *eap; - u8 *buf; - - if (msg == NULL) - return NULL; - - eap = (struct eap_hdr *) msg->buf; - eap->length = host_to_be16(msg->used); - - if (k_aut && msg->mac) { - eap_sim_add_mac(k_aut, msg->buf, msg->used, - msg->buf + msg->mac, extra, extra_len); - } - - *len = msg->used; - buf = msg->buf; - free(msg); - return buf; -} - - -void eap_sim_msg_free(struct eap_sim_msg *msg) -{ - if (msg) { - free(msg->buf); - free(msg); - } -} - - -static int eap_sim_msg_resize(struct eap_sim_msg *msg, size_t add_len) -{ - if (msg->used + add_len > msg->buf_len) { - u8 *nbuf = realloc(msg->buf, msg->used + add_len); - if (nbuf == NULL) - return -1; - msg->buf = nbuf; - msg->buf_len = msg->used + add_len; - } - return 0; -} - - -u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, - const u8 *data, size_t len) -{ - int attr_len = 2 + len; - int pad_len; - u8 *start, *pos; - - if (msg == NULL) - return NULL; - - pad_len = (4 - attr_len % 4) % 4; - attr_len += pad_len; - if (eap_sim_msg_resize(msg, attr_len)) - return NULL; - start = pos = msg->buf + msg->used; - *pos++ = attr; - *pos++ = attr_len / 4; - memcpy(pos, data, len); - if (pad_len) { - pos += len; - memset(pos, 0, pad_len); - } - msg->used += attr_len; - return start; -} - - -u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, - const u8 *data, size_t len) -{ - int attr_len = 4 + len; - int pad_len; - u8 *start, *pos; - - if (msg == NULL) - return NULL; - - pad_len = (4 - attr_len % 4) % 4; - attr_len += pad_len; - if (eap_sim_msg_resize(msg, attr_len)) - return NULL; - start = pos = msg->buf + msg->used; - *pos++ = attr; - *pos++ = attr_len / 4; - WPA_PUT_BE16(pos, value); - pos += 2; - if (data) - memcpy(pos, data, len); - if (pad_len) { - pos += len; - memset(pos, 0, pad_len); - } - msg->used += attr_len; - return start; -} - - -u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr) -{ - u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN); - if (pos) - msg->mac = (pos - msg->buf) + 4; - return pos; -} - - -int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, - u8 attr_encr) -{ - u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN); - if (pos == NULL) - return -1; - msg->iv = (pos - msg->buf) + 4; - if (hostapd_get_rand(msg->buf + msg->iv, EAP_SIM_IV_LEN)) { - msg->iv = 0; - return -1; - } - - pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0); - if (pos == NULL) { - msg->iv = 0; - return -1; - } - msg->encr = pos - msg->buf; - - return 0; -} - - -int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) -{ - size_t encr_len; - - if (k_encr == NULL || msg->iv == 0 || msg->encr == 0) - return -1; - - encr_len = msg->used - msg->encr - 4; - if (encr_len % 16) { - u8 *pos; - int pad_len = 16 - (encr_len % 16); - if (pad_len < 4) { - wpa_printf(MSG_WARNING, "EAP-SIM: " - "eap_sim_msg_add_encr_end - invalid pad_len" - " %d", pad_len); - return -1; - } - wpa_printf(MSG_DEBUG, " *AT_PADDING"); - pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4); - if (pos == NULL) - return -1; - memset(pos + 4, 0, pad_len - 4); - encr_len += pad_len; - } - wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)", - (unsigned long) encr_len); - msg->buf[msg->encr + 1] = encr_len / 4 + 1; - aes_128_cbc_encrypt(k_encr, msg->buf + msg->iv, - msg->buf + msg->encr + 4, encr_len); - - return 0; -} - - -void eap_sim_report_notification(void *msg_ctx, int notification, int aka) -{ -#ifndef CONFIG_NO_STDOUT_DEBUG - const char *type = aka ? "AKA" : "SIM"; -#endif /* CONFIG_NO_STDOUT_DEBUG */ - - switch (notification) { - case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH: - wpa_printf(MSG_WARNING, "EAP-%s: General failure " - "notification (after authentication)", type); - break; - case EAP_SIM_TEMPORARILY_DENIED: - wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " - "User has been temporarily denied access to the " - "requested service", type); - break; - case EAP_SIM_NOT_SUBSCRIBED: - wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " - "User has not subscribed to the requested service", - type); - break; - case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH: - wpa_printf(MSG_WARNING, "EAP-%s: General failure " - "notification (before authentication)", type); - break; - case EAP_SIM_SUCCESS: - wpa_printf(MSG_INFO, "EAP-%s: Successful authentication " - "notification", type); - break; - default: - if (notification >= 32768) { - wpa_printf(MSG_INFO, "EAP-%s: Unrecognized " - "non-failure notification %d", - type, notification); - } else { - wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized " - "failure notification %d", - type, notification); - } - } -} - - -#ifdef TEST_MAIN_EAP_SIM_COMMON -static int test_eap_sim_prf(void) -{ - /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ - u8 xkey[] = { - 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b, - 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f, - 0xeb, 0x5a, 0x38, 0xb6 - }; - u8 w[] = { - 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f, - 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49, - 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba, - 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78, - 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16 - }; - u8 buf[40]; - - printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n"); - eap_sim_prf(xkey, buf, sizeof(buf)); - if (memcmp(w, buf, sizeof(w) != 0)) { - printf("eap_sim_prf failed\n"); - return 1; - } - - return 0; -} - - -int main(int argc, char *argv[]) -{ - int errors = 0; - - errors += test_eap_sim_prf(); - - return errors; -} -#endif /* TEST_MAIN_EAP_SIM_COMMON */ diff --git a/contrib/hostapd-0.4.9/eap_sim_common.h b/contrib/hostapd-0.4.9/eap_sim_common.h deleted file mode 100644 index 6715c369c0..0000000000 --- a/contrib/hostapd-0.4.9/eap_sim_common.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * WPA Supplicant / EAP-SIM/AKA shared routines - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef EAP_SIM_COMMON_H -#define EAP_SIM_COMMON_H - -#define EAP_SIM_NONCE_S_LEN 16 -#define EAP_SIM_NONCE_MT_LEN 16 -#define EAP_SIM_MAC_LEN 16 -#define EAP_SIM_MK_LEN 20 -#define EAP_SIM_K_AUT_LEN 16 -#define EAP_SIM_K_ENCR_LEN 16 -#define EAP_SIM_KEYING_DATA_LEN 64 -#define EAP_SIM_IV_LEN 16 - -#define GSM_RAND_LEN 16 - -#define AKA_RAND_LEN 16 -#define AKA_AUTN_LEN 16 - -void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk); -void eap_sim_derive_keys_reauth(u16 _counter, - const u8 *identity, size_t identity_len, - const u8 *nonce_s, const u8 *mk, u8 *msk); -int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, - const u8 *mac, const u8 *extra, size_t extra_len); -void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, - const u8 *extra, size_t extra_len); - - -/* EAP-SIM/AKA Attributes (0..127 non-skippable) */ -#define EAP_SIM_AT_RAND 1 -#define EAP_SIM_AT_AUTN 2 /* only AKA */ -#define EAP_SIM_AT_RES 3 /* only AKA, only send */ -#define EAP_SIM_AT_AUTS 4 /* only AKA, only send */ -#define EAP_SIM_AT_PADDING 6 /* only encrypted */ -#define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */ -#define EAP_SIM_AT_PERMANENT_ID_REQ 10 -#define EAP_SIM_AT_MAC 11 -#define EAP_SIM_AT_NOTIFICATION 12 -#define EAP_SIM_AT_ANY_ID_REQ 13 -#define EAP_SIM_AT_IDENTITY 14 /* only send */ -#define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */ -#define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */ -#define EAP_SIM_AT_FULLAUTH_ID_REQ 17 -#define EAP_SIM_AT_COUNTER 19 /* only encrypted */ -#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */ -#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */ -#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */ -#define EAP_SIM_AT_IV 129 -#define EAP_SIM_AT_ENCR_DATA 130 -#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */ -#define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */ -#define EAP_SIM_AT_CHECKCODE 134 /* only AKA */ -#define EAP_SIM_AT_RESULT_IND 135 - -/* AT_NOTIFICATION notification code values */ -#define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0 -#define EAP_SIM_TEMPORARILY_DENIED 1026 -#define EAP_SIM_NOT_SUBSCRIBED 1031 -#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384 -#define EAP_SIM_SUCCESS 32768 - - -enum eap_sim_id_req { - NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID -}; - - -struct eap_sim_attrs { - const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; - const u8 *next_pseudonym, *next_reauth_id; - const u8 *nonce_mt, *identity; - size_t num_chal, version_list_len, encr_data_len; - size_t next_pseudonym_len, next_reauth_id_len, identity_len; - enum eap_sim_id_req id_req; - int notification, counter, selected_version, client_error_code; -}; - -int eap_sim_parse_attr(const u8 *start, const u8 *end, - struct eap_sim_attrs *attr, int aka, int encr); -u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, - size_t encr_data_len, const u8 *iv, - struct eap_sim_attrs *attr, int aka); - - -struct eap_sim_msg; - -struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype); -u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, - const u8 *extra, size_t extra_len); -void eap_sim_msg_free(struct eap_sim_msg *msg); -u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, - const u8 *data, size_t len); -u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, - u16 value, const u8 *data, size_t len); -u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr); -int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, - u8 attr_encr); -int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, - int attr_pad); - -void eap_sim_report_notification(void *msg_ctx, int notification, int aka); - -#endif /* EAP_SIM_COMMON_H */ diff --git a/contrib/hostapd-0.4.9/eap_sim_db.c b/contrib/hostapd-0.4.9/eap_sim_db.c deleted file mode 100644 index a965fa461d..0000000000 --- a/contrib/hostapd-0.4.9/eap_sim_db.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * hostapd / EAP-SIM database/authenticator gateway - * Copyright (c) 2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -/* This is an example implementation of the EAP-SIM database/authentication - * gateway interface that is expected to be replaced with an implementation of - * SS7 gateway to GSM authentication center (HLR/AuC) or a local - * implementation of SIM triplet generator. - * - * The example implementation here reads triplets from a text file in - * IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex strings. This - * is used to simulate an HLR/AuC. As such, it is not very useful for real life - * authentication, but it is useful both as an example implementation and for - * EAP-SIM testing. - */ - -#include -#include -#include - -#include "common.h" -#include "eap_sim_common.h" -#include "eap_sim_db.h" - - -/* TODO: add an alternative callback based version of the interface. This is - * needed to work better with the single threaded design of hostapd. For this, - * the EAP data has to be stored somewhere and eap_sim_db is given a context - * pointer for this and a callback function. The callback function will re-send - * the EAP data through normal operations which will eventually end up calling - * eap_sim_db_get_gsm_triplets() again for the same user. This time, eap_sim_db - * should have the triplets available immediately. */ - - -struct eap_sim_db_data { - char *fname; -}; - -#define KC_LEN 8 -#define SRES_LEN 4 -#define RAND_LEN 16 - - -/* Initialize EAP-SIM database/authentication gateway interface. - * Returns pointer to a private data structure. */ -void * eap_sim_db_init(const char *config) -{ - struct eap_sim_db_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) { - return NULL; - } - - memset(data, 0, sizeof(*data)); - data->fname = strdup(config); - if (data->fname == NULL) { - free(data); - return NULL; - } - - return data; -} - -/* Deinitialize EAP-SIM database/authentication gateway interface. - * priv is the pointer from eap_sim_db_init(). */ -void eap_sim_db_deinit(void *priv) -{ - struct eap_sim_db_data *data = priv; - free(data->fname); - free(data); -} - - -/* Get GSM triplets for user name identity (identity_len bytes). In most cases, - * the user name is '1' | IMSI, i.e., 1 followed by the IMSI in ASCII format. - * The identity may also include NAI realm (@realm). - * priv is the pointer from eap_sim_db_init(). - * Returns the number of triplets received (has to be less than or equal to - * max_chal) or -1 on error (e.g., user not found). rand, kc, and sres are - * pointers to data areas for the triplets. */ -int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, - size_t identity_len, int max_chal, - u8 *rand, u8 *kc, u8 *sres) -{ - struct eap_sim_db_data *data = priv; - FILE *f; - int count, i; - char buf[80], *pos, *next; - - f = fopen(data->fname, "r"); - if (f == NULL) { - wpa_printf(MSG_DEBUG, "EAP-SIM DB: could not open triplet " - "file '%s'", data->fname); - return -1; - } - - if (identity_len < 2 || identity[0] != '1') { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); - fclose(f); - return -1; - } - identity++; - identity_len--; - for (i = 0; i < identity_len; i++) { - if (identity[i] == '@') { - identity_len = i; - break; - } - } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: get triplets for IMSI", - identity, identity_len); - - count = 0; - while (count < max_chal && fgets(buf, sizeof(buf), f)) { - /* Parse IMSI:Kc:SRES:RAND and match IMSI with identity. */ - buf[sizeof(buf) - 1] = '\0'; - pos = buf; - while (*pos != '\0' && *pos != '\n') - pos++; - if (*pos == '\n') - *pos = '\0'; - if (pos - buf < 60 || pos[0] == '#') - continue; - - pos = strchr(buf, ':'); - if (pos == NULL) - continue; - *pos++ = '\0'; - if (strlen(buf) != identity_len || - memcmp(buf, identity, identity_len) != 0) - continue; - - next = strchr(pos, ':'); - if (next == NULL) - continue; - *next++ = '\0'; - if (hexstr2bin(pos, &kc[count * KC_LEN], KC_LEN) < 0) - continue; - - pos = next; - next = strchr(pos, ':'); - if (next == NULL) - continue; - *next++ = '\0'; - if (hexstr2bin(pos, &sres[count * SRES_LEN], SRES_LEN) < 0) - continue; - - if (hexstr2bin(next, &rand[count * RAND_LEN], RAND_LEN) < 0) - continue; - - count++; - } - - fclose(f); - - if (count == 0) { - wpa_printf(MSG_DEBUG, "EAP-SIM DB: no triplets found"); - count = -1; - } - - return count; -} - - -/* Verify whether the given user identity (identity_len bytes) is known. In - * most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in - * ASCII format. - * priv is the pointer from eap_sim_db_init(). - * Returns 0 if the user is found and GSM triplets would be available for it or - * -1 on error (e.g., user not found or no triplets available). */ -int eap_sim_db_identity_known(void *priv, const u8 *identity, - size_t identity_len) -{ - struct eap_sim_db_data *data = priv; - FILE *f; - char buf[80], *pos; - int i; - - if (identity_len < 1 || identity[0] != '1') { - return -1; - } - - f = fopen(data->fname, "r"); - if (f == NULL) { - wpa_printf(MSG_DEBUG, "EAP-SIM DB: could not open triplet " - "file '%s'", data->fname); - return -1; - } - - if (identity_len < 2 || identity[0] != '1') { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", - identity, identity_len); - return -1; - } - identity++; - identity_len--; - for (i = 0; i < identity_len; i++) { - if (identity[i] == '@') { - identity_len = i; - break; - } - } - - while (fgets(buf, sizeof(buf), f)) { - /* Parse IMSI:Kc:SRES:RAND and match IMSI with identity. */ - buf[sizeof(buf) - 1] = '\0'; - pos = buf; - while (*pos != '\0' && *pos != '\n') - pos++; - if (*pos == '\n') - *pos = '\0'; - if (pos - buf < 60 || pos[0] == '#') - continue; - - pos = strchr(buf, ':'); - if (pos == NULL) - continue; - *pos++ = '\0'; - if (strlen(buf) != identity_len || - memcmp(buf, identity, identity_len) != 0) - continue; - - fclose(f); - return 0; - } - - /* IMSI not found */ - - fclose(f); - return -1; -} diff --git a/contrib/hostapd-0.4.9/eap_sim_db.h b/contrib/hostapd-0.4.9/eap_sim_db.h deleted file mode 100644 index 57a9871073..0000000000 --- a/contrib/hostapd-0.4.9/eap_sim_db.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef EAP_SIM_DB_H -#define EAP_SIM_DB_H - -#ifdef EAP_SIM - -/* Initialize EAP-SIM database/authentication gateway interface. - * Returns pointer to a private data structure. */ -void * eap_sim_db_init(const char *config); - -/* Deinitialize EAP-SIM database/authentication gateway interface. - * priv is the pointer from eap_sim_db_init(). */ -void eap_sim_db_deinit(void *priv); - -/* Get GSM triplets for user name identity (identity_len bytes). In most cases, - * the user name is '1' | IMSI, i.e., 1 followed by the IMSI in ASCII format. - * priv is the pointer from eap_sim_db_init(). - * Returns the number of triplets received (has to be less than or equal to - * max_chal) or -1 on error (e.g., user not found). rand, kc, and sres are - * pointers to data areas for the triplets. */ -int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, - size_t identity_len, int max_chal, - u8 *rand, u8 *kc, u8 *sres); - -/* Verify whether the given user identity (identity_len bytes) is known. In - * most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in - * ASCII format. - * priv is the pointer from eap_sim_db_init(). - * Returns 0 if the user is found and GSM triplets would be available for it or - * -1 on error (e.g., user not found or no triplets available). */ -int eap_sim_db_identity_known(void *priv, const u8 *identity, - size_t identity_len); - -#else /* EAP_SIM */ -static inline void * eap_sim_db_init(const char *config) -{ - return NULL; -} - -static inline void eap_sim_db_deinit(void *priv) -{ -} -#endif /* EAP_SIM */ - -#endif /* EAP_SIM_DB_H */ diff --git a/contrib/hostapd-0.4.9/eap_tls.c b/contrib/hostapd-0.4.9/eap_tls.c deleted file mode 100644 index bf76f5a504..0000000000 --- a/contrib/hostapd-0.4.9/eap_tls.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * hostapd / EAP-TLS (RFC 2716) - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" -#include "eap_tls_common.h" -#include "tls.h" - - -static void eap_tls_reset(struct eap_sm *sm, void *priv); - - -struct eap_tls_data { - struct eap_ssl_data ssl; - enum { START, CONTINUE, SUCCESS, FAILURE } state; -}; - - -static void * eap_tls_init(struct eap_sm *sm) -{ - struct eap_tls_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->state = START; - - if (eap_tls_ssl_init(sm, &data->ssl, 1)) { - wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); - eap_tls_reset(sm, data); - return NULL; - } - - return data; -} - - -static void eap_tls_reset(struct eap_sm *sm, void *priv) -{ - struct eap_tls_data *data = priv; - if (data == NULL) - return; - eap_tls_ssl_deinit(sm, &data->ssl); - free(data); -} - - -static u8 * eap_tls_build_start(struct eap_sm *sm, struct eap_tls_data *data, - int id, size_t *reqDataLen) -{ - struct eap_hdr *req; - u8 *pos; - - *reqDataLen = sizeof(*req) + 2; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for " - "request"); - data->state = FAILURE; - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_TLS; - *pos = EAP_TLS_FLAGS_START; - - data->state = CONTINUE; - - return (u8 *) req; -} - - -static u8 * eap_tls_build_req(struct eap_sm *sm, struct eap_tls_data *data, - int id, size_t *reqDataLen) -{ - int res; - u8 *req; - - res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, - &req, reqDataLen); - - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); - data->state = SUCCESS; - } - - if (res == 1) - return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_TLS, 0); - return req; -} - - -static u8 * eap_tls_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_tls_data *data = priv; - - switch (data->state) { - case START: - return eap_tls_build_start(sm, data, id, reqDataLen); - case CONTINUE: - return eap_tls_build_req(sm, data, id, reqDataLen); - default: - wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d", - __func__, data->state); - return NULL; - } -} - - -static Boolean eap_tls_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_hdr *resp; - u8 *pos; - size_t len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TLS || - (len = ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); - return TRUE; - } - - return FALSE; -} - - -static void eap_tls_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_tls_data *data = priv; - struct eap_hdr *resp; - u8 *pos, flags; - int left; - unsigned int tls_msg_len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos++; - flags = *pos++; - left = htons(resp->length) - sizeof(struct eap_hdr) - 2; - wpa_printf(MSG_DEBUG, "EAP-TLS: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) respDataLen, flags); - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-TLS: Short frame with TLS " - "length"); - data->state = FAILURE; - return; - } - tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | - pos[3]; - wpa_printf(MSG_DEBUG, "EAP-TLS: TLS Message Length: %d", - tls_msg_len); - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - pos += 4; - left -= 4; - } - - if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) { - wpa_printf(MSG_INFO, "EAP-TLS: TLS processing failed"); - data->state = FAILURE; - return; - } - - if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { - wpa_printf(MSG_INFO, "EAP-TLS: Locally detected fatal error " - "in TLS processing"); - data->state = FAILURE; - return; - } -} - - -static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_tls_data *data = priv; - return data->state == SUCCESS || data->state == FAILURE; -} - - -static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_tls_data *data = priv; - u8 *eapKeyData; - - if (data->state != SUCCESS) - return NULL; - - eapKeyData = eap_tls_derive_key(sm, &data->ssl, - "client EAP encryption", - EAP_TLS_KEY_LEN); - if (eapKeyData) { - *len = EAP_TLS_KEY_LEN; - wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key", - eapKeyData, EAP_TLS_KEY_LEN); - } else { - wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key"); - } - - return eapKeyData; -} - - -static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_tls_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_tls = -{ - .method = EAP_TYPE_TLS, - .name = "TLS", - .init = eap_tls_init, - .reset = eap_tls_reset, - .buildReq = eap_tls_buildReq, - .check = eap_tls_check, - .process = eap_tls_process, - .isDone = eap_tls_isDone, - .getKey = eap_tls_getKey, - .isSuccess = eap_tls_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_tls_common.c b/contrib/hostapd-0.4.9/eap_tls_common.c deleted file mode 100644 index d573064b61..0000000000 --- a/contrib/hostapd-0.4.9/eap_tls_common.c +++ /dev/null @@ -1,278 +0,0 @@ -/* - * hostapd / EAP-TLS/PEAP/TTLS common functions - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" -#include "eap_tls_common.h" -#include "sha1.h" -#include "tls.h" - - -int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - int verify_peer) -{ - data->eap = sm; - data->phase2 = sm->init_phase2; - - data->conn = tls_connection_init(sm->ssl_ctx); - if (data->conn == NULL) { - wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS " - "connection"); - return -1; - } - - if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { - wpa_printf(MSG_INFO, "SSL: Failed to configure verification " - "of TLS peer certificate"); - tls_connection_deinit(sm->ssl_ctx, data->conn); - data->conn = NULL; - return -1; - } - - /* TODO: make this configurable */ - data->tls_out_limit = 1398; - if (data->phase2) { - /* Limit the fragment size in the inner TLS authentication - * since the outer authentication with EAP-PEAP does not yet - * support fragmentation */ - if (data->tls_out_limit > 100) - data->tls_out_limit -= 100; - } - return 0; -} - - -void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) -{ - tls_connection_deinit(sm->ssl_ctx, data->conn); - free(data->tls_in); - free(data->tls_out); -} - - -u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - char *label, size_t len) -{ - struct tls_keys keys; - u8 *random; - u8 *out; - - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) - return NULL; - out = malloc(len); - random = malloc(keys.client_random_len + keys.server_random_len); - if (out == NULL || random == NULL) { - free(out); - free(random); - return NULL; - } - memcpy(random, keys.client_random, keys.client_random_len); - memcpy(random + keys.client_random_len, keys.server_random, - keys.server_random_len); - - if (tls_prf(keys.master_key, keys.master_key_len, - label, random, keys.client_random_len + - keys.server_random_len, out, len)) { - free(random); - free(out); - return NULL; - } - free(random); - return out; -} - - -int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, - u8 **in_data, size_t *in_len) -{ - u8 *buf; - - if (data->tls_in_left > *in_len || data->tls_in) { - buf = realloc(data->tls_in, data->tls_in_len + *in_len); - if (buf == NULL) { - free(data->tls_in); - data->tls_in = NULL; - data->tls_in_len = 0; - wpa_printf(MSG_INFO, "SSL: Could not allocate memory " - "for TLS data"); - return -1; - } - memcpy(buf + data->tls_in_len, *in_data, *in_len); - data->tls_in = buf; - data->tls_in_len += *in_len; - if (*in_len > data->tls_in_left) { - wpa_printf(MSG_INFO, "SSL: more data than TLS message " - "length indicated"); - data->tls_in_left = 0; - return -1; - } - data->tls_in_left -= *in_len; - if (data->tls_in_left > 0) { - wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input " - "data", (unsigned long) data->tls_in_left); - return 1; - } - - *in_data = data->tls_in; - *in_len = data->tls_in_len; - } else - data->tls_in_left = 0; - - return 0; -} - - -int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, - u8 *in_data, size_t in_len) -{ - WPA_ASSERT(data->tls_out_len == 0 || in_len == 0); - - if (data->tls_out_len == 0) { - /* No more data to send out - expect to receive more data from - * the peer. */ - int res = eap_tls_data_reassemble(sm, data, &in_data, &in_len); - if (res < 0 || res == 1) { - wpa_printf(MSG_DEBUG, "SSL: data reassembly failed"); - return res; - } - /* Full TLS message reassembled - continue handshake processing - */ - if (data->tls_out) { - /* This should not happen.. */ - wpa_printf(MSG_INFO, "SSL: eap_tls_process_helper - " - "pending tls_out data even though " - "tls_out_len = 0"); - free(data->tls_out); - WPA_ASSERT(data->tls_out == NULL); - } - data->tls_out = tls_connection_server_handshake( - sm->ssl_ctx, data->conn, in_data, in_len, - &data->tls_out_len); - - /* Clear reassembled input data (if the buffer was needed). */ - data->tls_in_left = data->tls_in_total = data->tls_in_len = 0; - free(data->tls_in); - data->tls_in = NULL; - } - - if (data->tls_out == NULL) { - wpa_printf(MSG_DEBUG, "SSL: failed to generate output data"); - data->tls_out_len = 0; - return -1; - } - if (data->tls_out_len == 0) { - /* TLS negotiation should now be complete since all other cases - * needing more that should have been catched above based on - * the TLS Message Length field. */ - wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); - free(data->tls_out); - data->tls_out = NULL; - - if (tls_connection_get_read_alerts(sm->ssl_ctx, data->conn)) { - wpa_printf(MSG_DEBUG, "SSL: Remote end sent a fatal " - "alert - abort handshake"); - return -1; - } - - return 1; - } - - wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " - "%lu bytes)", - (unsigned long) data->tls_out_len - data->tls_out_pos, - (unsigned long) data->tls_out_len); - - return 0; -} - - -int eap_tls_buildReq_helper(struct eap_sm *sm, struct eap_ssl_data *data, - int eap_type, int peap_version, u8 id, - u8 **out_data, size_t *out_len) -{ - size_t len; - u8 *pos, *flags; - struct eap_hdr *req; - - *out_len = 0; - - req = malloc(sizeof(struct eap_hdr) + 2 + 4 + data->tls_out_limit); - if (req == NULL) { - *out_data = NULL; - return -1; - } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - pos = (u8 *) (req + 1); - *pos++ = eap_type; - flags = pos++; - *flags = peap_version; - if (data->tls_out_pos == 0 && - data->tls_out_len > data->tls_out_limit) { - *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; - *pos++ = (data->tls_out_len >> 24) & 0xff; - *pos++ = (data->tls_out_len >> 16) & 0xff; - *pos++ = (data->tls_out_len >> 8) & 0xff; - *pos++ = data->tls_out_len & 0xff; - } - - len = data->tls_out_len - data->tls_out_pos; - if (len > data->tls_out_limit) { - *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; - len = data->tls_out_limit; - wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " - "will follow", (unsigned long) len); - } - memcpy(pos, &data->tls_out[data->tls_out_pos], len); - data->tls_out_pos += len; - *out_len = (pos - (u8 *) req) + len; - req->length = htons(*out_len); - *out_data = (u8 *) req; - - if (!(*flags & EAP_TLS_FLAGS_MORE_FRAGMENTS)) { - data->tls_out_len = 0; - data->tls_out_pos = 0; - free(data->tls_out); - data->tls_out = NULL; - } - - return 0; -} - - -u8 * eap_tls_build_ack(size_t *reqDataLen, u8 id, int eap_type, - int peap_version) -{ - struct eap_hdr *req; - u8 *pos; - - *reqDataLen = sizeof(struct eap_hdr) + 2; - req = malloc(*reqDataLen); - if (req == NULL) - return NULL; - wpa_printf(MSG_DEBUG, "SSL: Building ACK"); - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = eap_type; /* Type */ - *pos = peap_version; /* Flags */ - return (u8 *) req; -} diff --git a/contrib/hostapd-0.4.9/eap_tls_common.h b/contrib/hostapd-0.4.9/eap_tls_common.h deleted file mode 100644 index 659ee849f7..0000000000 --- a/contrib/hostapd-0.4.9/eap_tls_common.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef EAP_TLS_COMMON_H -#define EAP_TLS_COMMON_H - -struct eap_ssl_data { - struct tls_connection *conn; - - u8 *tls_out; - size_t tls_out_len; - size_t tls_out_pos; - size_t tls_out_limit; - u8 *tls_in; - size_t tls_in_len; - size_t tls_in_left; - size_t tls_in_total; - - int phase2; - - struct eap_sm *eap; -}; - - -/* EAP TLS Flags */ -#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 -#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 -#define EAP_TLS_FLAGS_START 0x20 -#define EAP_PEAP_VERSION_MASK 0x07 - - /* could be up to 128 bytes, but only the first 64 bytes are used */ -#define EAP_TLS_KEY_LEN 64 - - -int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - int verify_peer); -void eap_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); -u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - char *label, size_t len); -int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, - u8 **in_data, size_t *in_len); -int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, - u8 *in_data, size_t in_len); -int eap_tls_buildReq_helper(struct eap_sm *sm, struct eap_ssl_data *data, - int eap_type, int peap_version, u8 id, - u8 **out_data, size_t *out_len); -u8 * eap_tls_build_ack(size_t *reqDataLen, u8 id, int eap_type, - int peap_version); - -#endif /* EAP_TLS_COMMON_H */ diff --git a/contrib/hostapd-0.4.9/eap_tlv.c b/contrib/hostapd-0.4.9/eap_tlv.c deleted file mode 100644 index b7609dcf2e..0000000000 --- a/contrib/hostapd-0.4.9/eap_tlv.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * hostapd / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt) - * Copyright (c) 2004, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" - - -/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-07.txt) */ -#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */ -#define EAP_TLV_NAK_TLV 4 -#define EAP_TLV_CRYPTO_BINDING_TLV 5 -#define EAP_TLV_CONNECTION_BINDING_TLV 6 -#define EAP_TLV_VENDOR_SPECIFIC_TLV 7 -#define EAP_TLV_URI_TLV 8 -#define EAP_TLV_EAP_PAYLOAD_TLV 9 -#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 - -#define EAP_TLV_RESULT_SUCCESS 1 -#define EAP_TLV_RESULT_FAILURE 2 - - -struct eap_tlv_data { - enum { CONTINUE, SUCCESS, FAILURE } state; -}; - - -static void * eap_tlv_init(struct eap_sm *sm) -{ - struct eap_tlv_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->state = CONTINUE; - - return data; -} - - -static void eap_tlv_reset(struct eap_sm *sm, void *priv) -{ - struct eap_tlv_data *data = priv; - free(data); -} - - -static u8 * eap_tlv_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_hdr *req; - u8 *pos; - u16 status; - - if (sm->tlv_request == TLV_REQ_SUCCESS) { - status = EAP_TLV_RESULT_SUCCESS; - } else { - status = EAP_TLV_RESULT_FAILURE; - } - - *reqDataLen = sizeof(struct eap_hdr) + 1 + 6; - req = malloc(*reqDataLen); - if (req == NULL) - return NULL; - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = host_to_be16(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_TLV; - *pos++ = 0x80; /* Mandatory */ - *pos++ = EAP_TLV_RESULT_TLV; - /* Length */ - *pos++ = 0; - *pos++ = 2; - /* Status */ - *pos++ = status >> 8; - *pos++ = status & 0xff; - - return (u8 *) req; -} - - -static Boolean eap_tlv_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_hdr *resp; - u8 *pos; - size_t len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 1 || *pos != EAP_TYPE_TLV || - (len = ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-TLV: Invalid frame"); - return TRUE; - } - - return FALSE; -} - - -static void eap_tlv_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_tlv_data *data = priv; - struct eap_hdr *resp; - u8 *pos; - int len; - size_t left; - u8 *result_tlv = NULL; - size_t result_tlv_len = 0; - int tlv_type, mandatory, tlv_len; - - resp = (struct eap_hdr *) respData; - len = ntohs(resp->length); - pos = (u8 *) (resp + 1); - - /* Parse TLVs */ - left = be_to_host16(resp->length) - sizeof(struct eap_hdr) - 1; - pos = (u8 *) (resp + 1); - pos++; - wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); - while (left >= 4) { - mandatory = !!(pos[0] & 0x80); - tlv_type = pos[0] & 0x3f; - tlv_type = (tlv_type << 8) | pos[1]; - tlv_len = ((int) pos[2] << 8) | pos[3]; - pos += 4; - left -= 4; - if (tlv_len > left) { - wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " - "(tlv_len=%d left=%lu)", tlv_len, - (unsigned long) left); - data->state = FAILURE; - return; - } - switch (tlv_type) { - case EAP_TLV_RESULT_TLV: - result_tlv = pos; - result_tlv_len = tlv_len; - break; - default: - wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " - "%d%s", tlv_type, - mandatory ? " (mandatory)" : ""); - if (mandatory) { - data->state = FAILURE; - return; - } - /* Ignore this TLV, but process other TLVs */ - break; - } - - pos += tlv_len; - left -= tlv_len; - } - if (left) { - wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " - "Request (left=%lu)", (unsigned long) left); - data->state = FAILURE; - return; - } - - /* Process supported TLVs */ - if (result_tlv) { - int status; - const char *requested; - - wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", - result_tlv, result_tlv_len); - if (result_tlv_len < 2) { - wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " - "(len=%lu)", - (unsigned long) result_tlv_len); - data->state = FAILURE; - return; - } - requested = sm->tlv_request == TLV_REQ_SUCCESS ? "Success" : - "Failure"; - status = ((int) result_tlv[0] << 8) | result_tlv[1]; - if (status == EAP_TLV_RESULT_SUCCESS) { - wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " - "- requested %s", requested); - if (sm->tlv_request == TLV_REQ_SUCCESS) - data->state = SUCCESS; - else - data->state = FAILURE; - - } else if (status == EAP_TLV_RESULT_FAILURE) { - wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure - " - "requested %s", requested); - if (sm->tlv_request == TLV_REQ_FAILURE) - data->state = SUCCESS; - else - data->state = FAILURE; - } else { - wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " - "Status %d", status); - data->state = FAILURE; - } - } -} - - -static Boolean eap_tlv_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_tlv_data *data = priv; - return data->state != CONTINUE; -} - - -static Boolean eap_tlv_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_tlv_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_tlv = -{ - .method = EAP_TYPE_TLV, - .name = "TLV", - .init = eap_tlv_init, - .reset = eap_tlv_reset, - .buildReq = eap_tlv_buildReq, - .check = eap_tlv_check, - .process = eap_tlv_process, - .isDone = eap_tlv_isDone, - .isSuccess = eap_tlv_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_ttls.c b/contrib/hostapd-0.4.9/eap_ttls.c deleted file mode 100644 index 569b1c3cc2..0000000000 --- a/contrib/hostapd-0.4.9/eap_ttls.c +++ /dev/null @@ -1,1193 +0,0 @@ -/* - * hostapd / EAP-TTLS (draft-ietf-pppext-eap-ttls-05.txt) - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include - -#include "hostapd.h" -#include "common.h" -#include "eap_i.h" -#include "eap_tls_common.h" -#include "ms_funcs.h" -#include "md5.h" -#include "crypto.h" -#include "tls.h" -#include "eap_ttls.h" - -#define EAP_TTLS_VERSION 0 - - -static void eap_ttls_reset(struct eap_sm *sm, void *priv); - - -struct eap_ttls_data { - struct eap_ssl_data ssl; - enum { - START, PHASE1, PHASE2_START, PHASE2_METHOD, - PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE - } state; - - int ttls_version; - const struct eap_method *phase2_method; - void *phase2_priv; - int mschapv2_resp_ok; - u8 mschapv2_auth_response[20]; - u8 mschapv2_ident; -}; - - -static const char * eap_ttls_state_txt(int state) -{ - switch (state) { - case START: - return "START"; - case PHASE1: - return "PHASE1"; - case PHASE2_START: - return "PHASE2_START"; - case PHASE2_METHOD: - return "PHASE2_METHOD"; - case PHASE2_MSCHAPV2_RESP: - return "PHASE2_MSCHAPV2_RESP"; - case SUCCESS: - return "SUCCESS"; - case FAILURE: - return "FAILURE"; - default: - return "Unknown?!"; - } -} - - -static void eap_ttls_state(struct eap_ttls_data *data, int state) -{ - wpa_printf(MSG_DEBUG, "EAP-TTLS: %s -> %s", - eap_ttls_state_txt(data->state), - eap_ttls_state_txt(state)); - data->state = state; -} - - -static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, - int mandatory, size_t len) -{ - struct ttls_avp_vendor *avp; - u8 flags; - size_t hdrlen; - - avp = (struct ttls_avp_vendor *) avphdr; - flags = mandatory ? AVP_FLAGS_MANDATORY : 0; - if (vendor_id) { - flags |= AVP_FLAGS_VENDOR; - hdrlen = sizeof(*avp); - avp->vendor_id = host_to_be32(vendor_id); - } else { - hdrlen = sizeof(struct ttls_avp); - } - - avp->avp_code = host_to_be32(avp_code); - avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); - - return avphdr + hdrlen; -} - - -static int eap_ttls_avp_encapsulate(u8 **resp, size_t *resp_len, u32 avp_code, - int mandatory) -{ - u8 *avp, *pos; - - avp = malloc(sizeof(struct ttls_avp) + *resp_len + 4); - if (avp == NULL) { - free(*resp); - *resp_len = 0; - return -1; - } - - pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, *resp_len); - memcpy(pos, *resp, *resp_len); - pos += *resp_len; - AVP_PAD(avp, pos); - free(*resp); - *resp = avp; - *resp_len = pos - avp; - return 0; -} - - -struct eap_ttls_avp { - /* Note: eap is allocated memory; caller is responsible for freeing - * it. All the other pointers are pointing to the packet data, i.e., - * they must not be freed separately. */ - u8 *eap; - size_t eap_len; - u8 *user_name; - size_t user_name_len; - u8 *user_password; - size_t user_password_len; - u8 *chap_challenge; - size_t chap_challenge_len; - u8 *chap_password; - size_t chap_password_len; - u8 *mschap_challenge; - size_t mschap_challenge_len; - u8 *mschap_response; - size_t mschap_response_len; - u8 *mschap2_response; - size_t mschap2_response_len; -}; - - -static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) -{ - struct ttls_avp *avp; - u8 *pos; - int left; - - pos = buf; - left = len; - memset(parse, 0, sizeof(*parse)); - - while (left > 0) { - u32 avp_code, avp_length, vendor_id = 0; - u8 avp_flags, *dpos; - size_t pad, dlen; - avp = (struct ttls_avp *) pos; - avp_code = be_to_host32(avp->avp_code); - avp_length = be_to_host32(avp->avp_length); - avp_flags = (avp_length >> 24) & 0xff; - avp_length &= 0xffffff; - wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " - "length=%d", (int) avp_code, avp_flags, - (int) avp_length); - if (avp_length > left) { - wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " - "(len=%d, left=%d) - dropped", - (int) avp_length, left); - return -1; - } - dpos = (u8 *) (avp + 1); - dlen = avp_length - sizeof(*avp); - if (avp_flags & AVP_FLAGS_VENDOR) { - if (dlen < 4) { - wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP " - "underflow"); - return -1; - } - vendor_id = be_to_host32(* (u32 *) dpos); - wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", - (int) vendor_id); - dpos += 4; - dlen -= 4; - } - - wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); - - if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); - if (parse->eap == NULL) { - parse->eap = malloc(dlen); - if (parse->eap == NULL) { - wpa_printf(MSG_WARNING, "EAP-TTLS: " - "failed to allocate memory " - "for Phase 2 EAP data"); - return -1; - } - memcpy(parse->eap, dpos, dlen); - parse->eap_len = dlen; - } else { - u8 *neweap = realloc(parse->eap, - parse->eap_len + dlen); - if (neweap == NULL) { - wpa_printf(MSG_WARNING, "EAP-TTLS: " - "failed to allocate memory " - "for Phase 2 EAP data"); - free(parse->eap); - parse->eap = NULL; - return -1; - } - memcpy(neweap + parse->eap_len, dpos, dlen); - parse->eap = neweap; - parse->eap_len += dlen; - } - } else if (vendor_id == 0 && - avp_code == RADIUS_ATTR_USER_NAME) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: User-Name", - dpos, dlen); - parse->user_name = dpos; - parse->user_name_len = dlen; - } else if (vendor_id == 0 && - avp_code == RADIUS_ATTR_USER_PASSWORD) { - u8 *password = dpos; - size_t password_len = dlen; - while (password_len > 0 && - password[password_len - 1] == '\0') { - password_len--; - } - wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: " - "User-Password (PAP)", - password, password_len); - parse->user_password = password; - parse->user_password_len = password_len; - } else if (vendor_id == 0 && - avp_code == RADIUS_ATTR_CHAP_CHALLENGE) { - wpa_hexdump(MSG_DEBUG, - "EAP-TTLS: CHAP-Challenge (CHAP)", - dpos, dlen); - parse->chap_challenge = dpos; - parse->chap_challenge_len = dlen; - } else if (vendor_id == 0 && - avp_code == RADIUS_ATTR_CHAP_PASSWORD) { - wpa_hexdump(MSG_DEBUG, - "EAP-TTLS: CHAP-Password (CHAP)", - dpos, dlen); - parse->chap_password = dpos; - parse->chap_password_len = dlen; - } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && - avp_code == RADIUS_ATTR_MS_CHAP_CHALLENGE) { - wpa_hexdump(MSG_DEBUG, - "EAP-TTLS: MS-CHAP-Challenge", - dpos, dlen); - parse->mschap_challenge = dpos; - parse->mschap_challenge_len = dlen; - } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && - avp_code == RADIUS_ATTR_MS_CHAP_RESPONSE) { - wpa_hexdump(MSG_DEBUG, - "EAP-TTLS: MS-CHAP-Response (MSCHAP)", - dpos, dlen); - parse->mschap_response = dpos; - parse->mschap_response_len = dlen; - } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && - avp_code == RADIUS_ATTR_MS_CHAP2_RESPONSE) { - wpa_hexdump(MSG_DEBUG, - "EAP-TTLS: MS-CHAP2-Response (MSCHAPV2)", - dpos, dlen); - parse->mschap2_response = dpos; - parse->mschap2_response_len = dlen; - } else if (avp_flags & AVP_FLAGS_MANDATORY) { - wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported " - "mandatory AVP code %d vendor_id %d - " - "dropped", (int) avp_code, (int) vendor_id); - return -1; - } else { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported " - "AVP code %d vendor_id %d", - (int) avp_code, (int) vendor_id); - } - - pad = (4 - (avp_length & 3)) & 3; - pos += avp_length + pad; - left -= avp_length + pad; - } - - return 0; -} - - -static void * eap_ttls_init(struct eap_sm *sm) -{ - struct eap_ttls_data *data; - - data = malloc(sizeof(*data)); - if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); - data->ttls_version = EAP_TTLS_VERSION; - data->state = START; - - if (eap_tls_ssl_init(sm, &data->ssl, 0)) { - wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); - eap_ttls_reset(sm, data); - return NULL; - } - - return data; -} - - -static void eap_ttls_reset(struct eap_sm *sm, void *priv) -{ - struct eap_ttls_data *data = priv; - if (data == NULL) - return; - if (data->phase2_priv && data->phase2_method) - data->phase2_method->reset(sm, data->phase2_priv); - eap_tls_ssl_deinit(sm, &data->ssl); - free(data); -} - - -static u8 * eap_ttls_build_start(struct eap_sm *sm, struct eap_ttls_data *data, - int id, size_t *reqDataLen) -{ - struct eap_hdr *req; - u8 *pos; - - *reqDataLen = sizeof(*req) + 2; - req = malloc(*reqDataLen); - if (req == NULL) { - wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for" - " request"); - eap_ttls_state(data, FAILURE); - return NULL; - } - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_TTLS; - *pos = EAP_TLS_FLAGS_START | data->ttls_version; - - eap_ttls_state(data, PHASE1); - - return (u8 *) req; -} - - -static u8 * eap_ttls_build_req(struct eap_sm *sm, struct eap_ttls_data *data, - int id, size_t *reqDataLen) -{ - int res; - u8 *req; - - res = eap_tls_buildReq_helper(sm, &data->ssl, EAP_TYPE_TTLS, - data->ttls_version, id, &req, - reqDataLen); - - if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, starting " - "Phase2"); - eap_ttls_state(data, PHASE2_START); - } - - if (res == 1) - return eap_tls_build_ack(reqDataLen, id, EAP_TYPE_TTLS, - data->ttls_version); - return req; -} - - -static u8 * eap_ttls_encrypt(struct eap_sm *sm, struct eap_ttls_data *data, - int id, u8 *plain, size_t plain_len, - size_t *out_len) -{ - int res; - u8 *pos; - struct eap_hdr *req; - - /* TODO: add support for fragmentation, if needed. This will need to - * add TLS Message Length field, if the frame is fragmented. */ - req = malloc(sizeof(struct eap_hdr) + 2 + data->ssl.tls_out_limit); - if (req == NULL) - return NULL; - - req->code = EAP_CODE_REQUEST; - req->identifier = id; - - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_TTLS; - *pos++ = data->ttls_version; - - res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - plain, plain_len, - pos, data->ssl.tls_out_limit); - if (res < 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt Phase 2 " - "data"); - free(req); - return NULL; - } - - *out_len = sizeof(struct eap_hdr) + 2 + res; - req->length = host_to_be16(*out_len); - return (u8 *) req; -} - - -static u8 * eap_ttls_build_phase2_eap_req(struct eap_sm *sm, - struct eap_ttls_data *data, - int id, size_t *reqDataLen) -{ - u8 *req, *encr_req; - size_t req_len; - - - req = data->phase2_method->buildReq(sm, data->phase2_priv, id, - &req_len); - if (req == NULL) - return NULL; - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encapsulate Phase 2 data", - req, req_len); - - if (eap_ttls_avp_encapsulate(&req, &req_len, RADIUS_ATTR_EAP_MESSAGE, - 1) < 0) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate " - "packet"); - return NULL; - } - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated Phase " - "2 data", req, req_len); - - encr_req = eap_ttls_encrypt(sm, data, id, req, req_len, reqDataLen); - free(req); - - return encr_req; -} - - -static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm, - struct eap_ttls_data *data, - int id, size_t *reqDataLen) -{ - u8 *req, *encr_req, *pos, *end; - size_t req_len; - int i; - - pos = req = malloc(100); - if (req == NULL) - return NULL; - end = req + 200; - - if (data->mschapv2_resp_ok) { - pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS, - RADIUS_VENDOR_ID_MICROSOFT, 1, 43); - *pos++ = data->mschapv2_ident; - pos += snprintf((char *) pos, end - pos, "S="); - for (i = 0; i < sizeof(data->mschapv2_auth_response); i++) { - pos += snprintf((char *) pos, end - pos, "%02X", - data->mschapv2_auth_response[i]); - } - } else { - pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR, - RADIUS_VENDOR_ID_MICROSOFT, 1, 6); - memcpy(pos, "Failed", 6); - pos += 6; - AVP_PAD(req, pos); - } - - req_len = pos - req; - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 " - "data", req, req_len); - - encr_req = eap_ttls_encrypt(sm, data, id, req, req_len, reqDataLen); - free(req); - - return encr_req; -} - - -static u8 * eap_ttls_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) -{ - struct eap_ttls_data *data = priv; - - switch (data->state) { - case START: - return eap_ttls_build_start(sm, data, id, reqDataLen); - case PHASE1: - return eap_ttls_build_req(sm, data, id, reqDataLen); - case PHASE2_METHOD: - return eap_ttls_build_phase2_eap_req(sm, data, id, reqDataLen); - case PHASE2_MSCHAPV2_RESP: - return eap_ttls_build_phase2_mschapv2(sm, data, id, - reqDataLen); - default: - wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", - __func__, data->state); - return NULL; - } -} - - -static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_hdr *resp; - u8 *pos; - size_t len; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TTLS || - (len = ntohs(resp->length)) > respDataLen) { - wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame"); - return TRUE; - } - - return FALSE; -} - - -static void eap_ttls_process_phase2_pap(struct eap_sm *sm, - struct eap_ttls_data *data, - const u8 *user_password, - size_t user_password_len) -{ - /* TODO: add support for verifying that the user entry accepts - * EAP-TTLS/PAP. */ - if (!sm->user || !sm->user->password) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No user password " - "configured"); - eap_ttls_state(data, FAILURE); - return; - } - - if (sm->user->password_len != user_password_len || - memcmp(sm->user->password, user_password, user_password_len) != 0) - { - wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password"); - eap_ttls_state(data, FAILURE); - return; - } - - wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); - eap_ttls_state(data, SUCCESS); -} - - -static void eap_ttls_process_phase2_chap(struct eap_sm *sm, - struct eap_ttls_data *data, - const u8 *challenge, - size_t challenge_len, - const u8 *password, - size_t password_len) -{ - u8 *chal, hash[MD5_MAC_LEN]; - const u8 *addr[3]; - size_t len[3]; - - if (challenge == NULL || password == NULL || - challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN || - password_len != 1 + EAP_TTLS_CHAP_PASSWORD_LEN) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid CHAP attributes " - "(challenge len %lu password len %lu)", - (unsigned long) challenge_len, - (unsigned long) password_len); - eap_ttls_state(data, FAILURE); - return; - } - - /* TODO: add support for verifying that the user entry accepts - * EAP-TTLS/CHAP. */ - if (!sm->user || !sm->user->password) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No user password " - "configured"); - eap_ttls_state(data, FAILURE); - return; - } - - chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", - EAP_TTLS_CHAP_CHALLENGE_LEN + 1); - if (chal == NULL) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate " - "challenge from TLS data"); - eap_ttls_state(data, FAILURE); - return; - } - - if (memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 || - password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch"); - free(chal); - eap_ttls_state(data, FAILURE); - return; - } - free(chal); - - /* MD5(Ident + Password + Challenge) */ - addr[0] = password; - len[0] = 1; - addr[1] = sm->user->password; - len[1] = sm->user->password_len; - addr[2] = challenge; - len[2] = challenge_len; - md5_vector(3, addr, len, hash); - - if (memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); - eap_ttls_state(data, SUCCESS); - } else { - wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); - eap_ttls_state(data, FAILURE); - } -} - - -static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, - struct eap_ttls_data *data, - u8 *challenge, size_t challenge_len, - u8 *response, size_t response_len) -{ - u8 *chal, nt_response[24]; - - if (challenge == NULL || response == NULL || - challenge_len != EAP_TTLS_MSCHAP_CHALLENGE_LEN || - response_len != EAP_TTLS_MSCHAP_RESPONSE_LEN) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid MS-CHAP " - "attributes (challenge len %lu response len %lu)", - (unsigned long) challenge_len, - (unsigned long) response_len); - eap_ttls_state(data, FAILURE); - return; - } - - /* TODO: add support for verifying that the user entry accepts - * EAP-TTLS/MSCHAP. */ - if (!sm->user || !sm->user->password) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password " - "configured"); - eap_ttls_state(data, FAILURE); - return; - } - - chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", - EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); - if (chal == NULL) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate " - "challenge from TLS data"); - eap_ttls_state(data, FAILURE); - return; - } - - if (memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 || - response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch"); - free(chal); - eap_ttls_state(data, FAILURE); - return; - } - free(chal); - - nt_challenge_response(challenge, sm->user->password, - sm->user->password_len, nt_response); - - if (memcmp(nt_response, response + 2 + 24, 24) == 0) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); - eap_ttls_state(data, SUCCESS); - } else { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); - wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", - response + 2 + 24, 24); - wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Expected", - nt_response, 24); - eap_ttls_state(data, FAILURE); - } -} - - -static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, - struct eap_ttls_data *data, - u8 *challenge, - size_t challenge_len, - u8 *response, size_t response_len) -{ - u8 *chal, *username, nt_response[24], *pos, *rx_resp, *peer_challenge, - *auth_challenge; - size_t username_len; - int i; - - if (challenge == NULL || response == NULL || - challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN || - response_len != EAP_TTLS_MSCHAPV2_RESPONSE_LEN) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid MS-CHAP2 " - "attributes (challenge len %lu response len %lu)", - (unsigned long) challenge_len, - (unsigned long) response_len); - eap_ttls_state(data, FAILURE); - return; - } - - /* TODO: add support for verifying that the user entry accepts - * EAP-TTLS/MSCHAPV2. */ - if (!sm->user || !sm->user->password) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password " - "configured"); - eap_ttls_state(data, FAILURE); - return; - } - - /* MSCHAPv2 does not include optional domain name in the - * challenge-response calculation, so remove domain prefix - * (if present). */ - username = sm->identity; - username_len = sm->identity_len; - pos = username; - for (i = 0; i < username_len; i++) { - if (username[i] == '\\') { - username_len -= i + 1; - username += i + 1; - break; - } - } - - chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", - EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); - if (chal == NULL) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate " - "challenge from TLS data"); - eap_ttls_state(data, FAILURE); - return; - } - - if (memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 || - response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch"); - free(chal); - eap_ttls_state(data, FAILURE); - return; - } - free(chal); - - auth_challenge = challenge; - peer_challenge = response + 2; - - wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: User", - username, username_len); - wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: auth_challenge", - auth_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); - wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge", - peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); - - generate_nt_response(auth_challenge, peer_challenge, - username, username_len, - sm->user->password, sm->user->password_len, - nt_response); - - rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; - if (memcmp(nt_response, rx_resp, 24) == 0) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " - "NT-Response"); - data->mschapv2_resp_ok = 1; - - generate_authenticator_response(sm->user->password, - sm->user->password_len, - peer_challenge, - auth_challenge, - username, username_len, - nt_response, - data->mschapv2_auth_response); - - } else { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid " - "NT-Response"); - wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Received", - rx_resp, 24); - wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Expected", - nt_response, 24); - data->mschapv2_resp_ok = 0; - } - eap_ttls_state(data, PHASE2_MSCHAPV2_RESP); - data->mschapv2_ident = response[0]; -} - - -static int eap_ttls_phase2_eap_init(struct eap_sm *sm, - struct eap_ttls_data *data, u8 eap_type) -{ - if (data->phase2_priv && data->phase2_method) { - data->phase2_method->reset(sm, data->phase2_priv); - data->phase2_method = NULL; - data->phase2_priv = NULL; - } - data->phase2_method = eap_sm_get_eap_methods(eap_type); - if (!data->phase2_method) - return -1; - - sm->init_phase2 = 1; - data->phase2_priv = data->phase2_method->init(sm); - sm->init_phase2 = 0; - return 0; -} - - -static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, - struct eap_ttls_data *data, - u8 *in_data, size_t in_len) -{ - u8 next_type = EAP_TYPE_NONE; - struct eap_hdr *hdr; - u8 *pos; - size_t left; - - if (data->phase2_priv == NULL) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not " - "initialized?!", __func__); - return; - } - - hdr = (struct eap_hdr *) in_data; - pos = (u8 *) (hdr + 1); - left = in_len - sizeof(*hdr); - - if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { - wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; " - "allowed types", pos + 1, left - 1); - eap_sm_process_nak(sm, pos + 1, left - 1); - if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index] != - EAP_TYPE_NONE) { - next_type = - sm->user->methods[sm->user_eap_method_index++]; - wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", - next_type); - eap_ttls_phase2_eap_init(sm, data, next_type); - } else { - eap_ttls_state(data, FAILURE); - } - return; - } - - if (data->phase2_method->check(sm, data->phase2_priv, in_data, - in_len)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to " - "ignore the packet"); - return; - } - - data->phase2_method->process(sm, data->phase2_priv, in_data, in_len); - - if (!data->phase2_method->isDone(sm, data->phase2_priv)) - return; - - if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed"); - eap_ttls_state(data, FAILURE); - return; - } - - switch (data->state) { - case PHASE2_START: - if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { - wpa_hexdump_ascii(MSG_DEBUG, "EAP_TTLS: Phase2 " - "Identity not found in the user " - "database", - sm->identity, sm->identity_len); - eap_ttls_state(data, FAILURE); - break; - } - - eap_ttls_state(data, PHASE2_METHOD); - next_type = sm->user->methods[0]; - sm->user_eap_method_index = 1; - wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); - break; - case PHASE2_METHOD: - eap_ttls_state(data, SUCCESS); - break; - case FAILURE: - break; - default: - wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", - __func__, data->state); - break; - } - - eap_ttls_phase2_eap_init(sm, data, next_type); -} - - -static void eap_ttls_process_phase2_eap(struct eap_sm *sm, - struct eap_ttls_data *data, - const u8 *eap, size_t eap_len) -{ - struct eap_hdr *hdr; - size_t len; - - if (data->state == PHASE2_START) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2"); - if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0) - { - wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to " - "initialize EAP-Identity"); - return; - } - } - - if (eap_len < sizeof(*hdr)) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: too short Phase 2 EAP " - "packet (len=%lu)", (unsigned long) eap_len); - return; - } - - hdr = (struct eap_hdr *) eap; - len = be_to_host16(hdr->length); - wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: received Phase 2 EAP: code=%d " - "identifier=%d length=%lu", hdr->code, hdr->identifier, - (unsigned long) len); - if (len > eap_len) { - wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Length mismatch in Phase 2" - " EAP frame (hdr len=%lu, data len in AVP=%lu)", - (unsigned long) len, (unsigned long) eap_len); - return; - } - - switch (hdr->code) { - case EAP_CODE_RESPONSE: - eap_ttls_process_phase2_eap_response(sm, data, (u8 *) hdr, - len); - break; - default: - wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Unexpected code=%d in " - "Phase 2 EAP header", hdr->code); - break; - } -} - - -static void eap_ttls_process_phase2(struct eap_sm *sm, - struct eap_ttls_data *data, - struct eap_hdr *resp, - u8 *in_data, size_t in_len) -{ - u8 *in_decrypted; - int buf_len, len_decrypted, res; - struct eap_ttls_avp parse; - - wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" - " Phase 2", (unsigned long) in_len); - - res = eap_tls_data_reassemble(sm, &data->ssl, &in_data, &in_len); - if (res < 0 || res == 1) - return; - - buf_len = in_len; - if (data->ssl.tls_in_total > buf_len) - buf_len = data->ssl.tls_in_total; - in_decrypted = malloc(buf_len); - if (in_decrypted == NULL) { - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate memory " - "for decryption"); - return; - } - - len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, - in_decrypted, buf_len); - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - if (len_decrypted < 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " - "data"); - free(in_decrypted); - eap_ttls_state(data, FAILURE); - return; - } - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", - in_decrypted, len_decrypted); - - if (eap_ttls_avp_parse(in_decrypted, len_decrypted, &parse) < 0) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs"); - free(in_decrypted); - eap_ttls_state(data, FAILURE); - return; - } - - if (parse.user_name) { - free(sm->identity); - sm->identity = malloc(parse.user_name_len); - if (sm->identity) { - memcpy(sm->identity, parse.user_name, - parse.user_name_len); - sm->identity_len = parse.user_name_len; - } - if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1) - != 0) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not " - "found in the user database"); - eap_ttls_state(data, FAILURE); - goto done; - } - } - - if (parse.eap) { - eap_ttls_process_phase2_eap(sm, data, parse.eap, - parse.eap_len); - } else if (parse.user_password) { - eap_ttls_process_phase2_pap(sm, data, parse.user_password, - parse.user_password_len); - } else if (parse.chap_password) { - eap_ttls_process_phase2_chap(sm, data, - parse.chap_challenge, - parse.chap_challenge_len, - parse.chap_password, - parse.chap_password_len); - } else if (parse.mschap_response) { - eap_ttls_process_phase2_mschap(sm, data, - parse.mschap_challenge, - parse.mschap_challenge_len, - parse.mschap_response, - parse.mschap_response_len); - } else if (parse.mschap2_response) { - eap_ttls_process_phase2_mschapv2(sm, data, - parse.mschap_challenge, - parse.mschap_challenge_len, - parse.mschap2_response, - parse.mschap2_response_len); - } - -done: - free(in_decrypted); - free(parse.eap); - } - - -static void eap_ttls_process(struct eap_sm *sm, void *priv, - u8 *respData, size_t respDataLen) -{ - struct eap_ttls_data *data = priv; - struct eap_hdr *resp; - u8 *pos, flags; - int left; - unsigned int tls_msg_len; - int peer_version; - - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos++; - flags = *pos++; - left = htons(resp->length) - sizeof(struct eap_hdr) - 2; - wpa_printf(MSG_DEBUG, "EAP-TTLS: Received packet(len=%lu) - " - "Flags 0x%02x", (unsigned long) respDataLen, flags); - peer_version = flags & EAP_PEAP_VERSION_MASK; - if (peer_version < data->ttls_version) { - wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; " - "use version %d", - peer_version, data->ttls_version, peer_version); - data->ttls_version = peer_version; - - } - if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { - if (left < 4) { - wpa_printf(MSG_INFO, "EAP-TTLS: Short frame with TLS " - "length"); - eap_ttls_state(data, FAILURE); - return; - } - tls_msg_len = (pos[0] << 24) | (pos[1] << 16) | (pos[2] << 8) | - pos[3]; - wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS Message Length: %d", - tls_msg_len); - if (data->ssl.tls_in_left == 0) { - data->ssl.tls_in_total = tls_msg_len; - data->ssl.tls_in_left = tls_msg_len; - free(data->ssl.tls_in); - data->ssl.tls_in = NULL; - data->ssl.tls_in_len = 0; - } - pos += 4; - left -= 4; - } - - switch (data->state) { - case PHASE1: - if (eap_tls_process_helper(sm, &data->ssl, pos, left) < 0) { - wpa_printf(MSG_INFO, "EAP-TTLS: TLS processing " - "failed"); - eap_ttls_state(data, FAILURE); - } - break; - case PHASE2_START: - case PHASE2_METHOD: - eap_ttls_process_phase2(sm, data, resp, pos, left); - break; - case PHASE2_MSCHAPV2_RESP: - if (data->mschapv2_resp_ok && left == 0) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " - "acknowledged response"); - eap_ttls_state(data, SUCCESS); - } else if (!data->mschapv2_resp_ok) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " - "acknowledged error"); - eap_ttls_state(data, FAILURE); - } else { - wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected " - "frame from peer (payload len %d, expected " - "empty frame)", left); - eap_ttls_state(data, FAILURE); - } - break; - default: - wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s", - data->state, __func__); - break; - } - - if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { - wpa_printf(MSG_INFO, "EAP-TTLS: Locally detected fatal error " - "in TLS processing"); - eap_ttls_state(data, FAILURE); - } -} - - -static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv) -{ - struct eap_ttls_data *data = priv; - return data->state == SUCCESS || data->state == FAILURE; -} - - -static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) -{ - struct eap_ttls_data *data = priv; - u8 *eapKeyData; - - if (data->state != SUCCESS) - return NULL; - - eapKeyData = eap_tls_derive_key(sm, &data->ssl, - "ttls keying material", - EAP_TLS_KEY_LEN); - if (eapKeyData) { - *len = EAP_TLS_KEY_LEN; - wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived key", - eapKeyData, EAP_TLS_KEY_LEN); - } else { - wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); - } - - return eapKeyData; -} - - -static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv) -{ - struct eap_ttls_data *data = priv; - return data->state == SUCCESS; -} - - -const struct eap_method eap_method_ttls = -{ - .method = EAP_TYPE_TTLS, - .name = "TTLS", - .init = eap_ttls_init, - .reset = eap_ttls_reset, - .buildReq = eap_ttls_buildReq, - .check = eap_ttls_check, - .process = eap_ttls_process, - .isDone = eap_ttls_isDone, - .getKey = eap_ttls_getKey, - .isSuccess = eap_ttls_isSuccess, -}; diff --git a/contrib/hostapd-0.4.9/eap_ttls.h b/contrib/hostapd-0.4.9/eap_ttls.h deleted file mode 100644 index f35f5a9059..0000000000 --- a/contrib/hostapd-0.4.9/eap_ttls.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * WPA Supplicant / EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt) - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef EAP_TTLS_H -#define EAP_TTLS_H - -struct ttls_avp { - u32 avp_code; - u32 avp_length; /* 8-bit flags, 24-bit length; - * length includes AVP header */ - /* optional 32-bit Vendor-ID */ - /* Data */ -}; - -struct ttls_avp_vendor { - u32 avp_code; - u32 avp_length; /* 8-bit flags, 24-bit length; - * length includes AVP header */ - u32 vendor_id; - /* Data */ -}; - -#define AVP_FLAGS_VENDOR 0x80 -#define AVP_FLAGS_MANDATORY 0x40 - -#define AVP_PAD(start, pos) \ -do { \ - int pad; \ - pad = (4 - (((pos) - (start)) & 3)) & 3; \ - memset((pos), 0, pad); \ - pos += pad; \ -} while(0) - - -/* RFC 2865 */ -#define RADIUS_ATTR_USER_NAME 1 -#define RADIUS_ATTR_USER_PASSWORD 2 -#define RADIUS_ATTR_CHAP_PASSWORD 3 -#define RADIUS_ATTR_REPLY_MESSAGE 18 -#define RADIUS_ATTR_CHAP_CHALLENGE 60 -#define RADIUS_ATTR_EAP_MESSAGE 79 - -/* RFC 2548 */ -#define RADIUS_VENDOR_ID_MICROSOFT 311 -#define RADIUS_ATTR_MS_CHAP_RESPONSE 1 -#define RADIUS_ATTR_MS_CHAP_ERROR 2 -#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6 -#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11 -#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25 -#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26 -#define RADIUS_ATTR_MS_CHAP2_CPW 27 - -#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16 -#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50 -#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8 -#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50 -#define EAP_TTLS_CHAP_CHALLENGE_LEN 16 -#define EAP_TTLS_CHAP_PASSWORD_LEN 16 - -#endif /* EAP_TTLS_H */ diff --git a/contrib/hostapd-0.4.9/eapol_sm.c b/contrib/hostapd-0.4.9/eapol_sm.c deleted file mode 100644 index f2d5ec7c52..0000000000 --- a/contrib/hostapd-0.4.9/eapol_sm.c +++ /dev/null @@ -1,1259 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.1X Authenticator - EAPOL state machine - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include "hostapd.h" -#include "ieee802_1x.h" -#include "eapol_sm.h" -#include "eloop.h" -#include "wpa.h" -#include "sta_info.h" -#include "eap.h" - -static struct eapol_callbacks eapol_cb; - -/* EAPOL state machines are described in IEEE Std 802.1X-REV-d11, Chap. 8.2 */ - -#define setPortAuthorized() \ -ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1) -#define setPortUnauthorized() \ -ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0) - -/* procedures */ -#define txCannedFail() ieee802_1x_tx_canned_eap(sm->hapd, sm->sta, 0) -#define txCannedSuccess() ieee802_1x_tx_canned_eap(sm->hapd, sm->sta, 1) -#define txReq() ieee802_1x_tx_req(sm->hapd, sm->sta) -#define sendRespToServer() ieee802_1x_send_resp_to_server(sm->hapd, sm->sta) -#define abortAuth() ieee802_1x_abort_auth(sm->hapd, sm->sta) -#define txKey() ieee802_1x_tx_key(sm->hapd, sm->sta) -#define processKey() do { } while (0) - - -/* Definitions for clarifying state machine implementation */ -#define SM_STATE(machine, state) \ -static void sm_ ## machine ## _ ## state ## _Enter(struct eapol_state_machine \ -*sm) - -#define SM_ENTRY(machine, _state, _data) \ -sm->_data.state = machine ## _ ## _state; \ -if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \ - printf("IEEE 802.1X: " MACSTR " " #machine " entering state " #_state \ - "\n", MAC2STR(sm->addr)); - -#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm) - -#define SM_STEP(machine) \ -static void sm_ ## machine ## _Step(struct eapol_state_machine *sm) - -#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) - - -static void eapol_sm_step_run(struct eapol_state_machine *sm); -static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); - - -/* Port Timers state machine - implemented as a function that will be called - * once a second as a registered event loop timeout */ - -static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) -{ - struct eapol_state_machine *state = timeout_ctx; - - if (state->aWhile > 0) { - state->aWhile--; - if (state->aWhile == 0) { - wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR - " - aWhile --> 0", - MAC2STR(state->addr)); - } - } - - if (state->quietWhile > 0) { - state->quietWhile--; - if (state->quietWhile == 0) { - wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR - " - quietWhile --> 0", - MAC2STR(state->addr)); - } - } - - if (state->reAuthWhen > 0) { - state->reAuthWhen--; - if (state->reAuthWhen == 0) { - wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR - " - reAuthWhen --> 0", - MAC2STR(state->addr)); - } - } - - eapol_sm_step_run(state); - - eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); -} - - - -/* Authenticator PAE state machine */ - -SM_STATE(AUTH_PAE, INITIALIZE) -{ - SM_ENTRY(AUTH_PAE, INITIALIZE, auth_pae); - sm->auth_pae.portMode = Auto; - - sm->currentId = 255; -} - - -SM_STATE(AUTH_PAE, DISCONNECTED) -{ - int from_initialize = sm->auth_pae.state == AUTH_PAE_INITIALIZE; - - if (sm->auth_pae.eapolLogoff) { - if (sm->auth_pae.state == AUTH_PAE_CONNECTING) - sm->auth_pae.authEapLogoffsWhileConnecting++; - else if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED) - sm->auth_pae.authAuthEapLogoffWhileAuthenticated++; - } - - SM_ENTRY(AUTH_PAE, DISCONNECTED, auth_pae); - - sm->authPortStatus = Unauthorized; - setPortUnauthorized(); - sm->auth_pae.reAuthCount = 0; - sm->auth_pae.eapolLogoff = FALSE; - if (!from_initialize) { - if (sm->flags & EAPOL_SM_PREAUTH) - rsn_preauth_finished(sm->hapd, sm->sta, 0); - else - ieee802_1x_finished(sm->hapd, sm->sta, 0); - } -} - - -SM_STATE(AUTH_PAE, RESTART) -{ - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED) { - if (sm->reAuthenticate) - sm->auth_pae.authAuthReauthsWhileAuthenticated++; - if (sm->auth_pae.eapolStart) - sm->auth_pae.authAuthEapStartsWhileAuthenticated++; - if (sm->auth_pae.eapolLogoff) - sm->auth_pae.authAuthEapLogoffWhileAuthenticated++; - } - - SM_ENTRY(AUTH_PAE, RESTART, auth_pae); - - sm->auth_pae.eapRestart = TRUE; - ieee802_1x_request_identity(sm->hapd, sm->sta); -} - - -SM_STATE(AUTH_PAE, CONNECTING) -{ - if (sm->auth_pae.state != AUTH_PAE_CONNECTING) - sm->auth_pae.authEntersConnecting++; - - SM_ENTRY(AUTH_PAE, CONNECTING, auth_pae); - - sm->reAuthenticate = FALSE; - sm->auth_pae.reAuthCount++; -} - - -SM_STATE(AUTH_PAE, HELD) -{ - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authFail) - sm->auth_pae.authAuthFailWhileAuthenticating++; - - SM_ENTRY(AUTH_PAE, HELD, auth_pae); - - sm->authPortStatus = Unauthorized; - setPortUnauthorized(); - sm->quietWhile = sm->auth_pae.quietPeriod; - sm->auth_pae.eapolLogoff = FALSE; - - hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_WARNING, "authentication failed"); - if (sm->flags & EAPOL_SM_PREAUTH) - rsn_preauth_finished(sm->hapd, sm->sta, 0); - else - ieee802_1x_finished(sm->hapd, sm->sta, 0); -} - - -SM_STATE(AUTH_PAE, AUTHENTICATED) -{ - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) - sm->auth_pae.authAuthSuccessesWhileAuthenticating++; - - SM_ENTRY(AUTH_PAE, AUTHENTICATED, auth_pae); - - sm->authPortStatus = Authorized; - setPortAuthorized(); - sm->auth_pae.reAuthCount = 0; - hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_INFO, "authenticated"); - if (sm->flags & EAPOL_SM_PREAUTH) - rsn_preauth_finished(sm->hapd, sm->sta, 1); - else - ieee802_1x_finished(sm->hapd, sm->sta, 1); -} - - -SM_STATE(AUTH_PAE, AUTHENTICATING) -{ - if (sm->auth_pae.state == AUTH_PAE_CONNECTING && sm->rx_identity) { - sm->auth_pae.authEntersAuthenticating++; - sm->rx_identity = FALSE; - } - - SM_ENTRY(AUTH_PAE, AUTHENTICATING, auth_pae); - - sm->auth_pae.eapolStart = FALSE; - sm->authSuccess = FALSE; - sm->authFail = FALSE; - sm->authTimeout = FALSE; - sm->authStart = TRUE; - sm->keyRun = FALSE; - sm->keyDone = FALSE; -} - - -SM_STATE(AUTH_PAE, ABORTING) -{ - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING) { - if (sm->authTimeout) - sm->auth_pae.authAuthTimeoutsWhileAuthenticating++; - if (sm->auth_pae.eapolStart) - sm->auth_pae.authAuthEapStartsWhileAuthenticating++; - if (sm->auth_pae.eapolLogoff) - sm->auth_pae.authAuthEapLogoffWhileAuthenticating++; - } - - SM_ENTRY(AUTH_PAE, ABORTING, auth_pae); - - sm->authAbort = TRUE; - sm->keyRun = FALSE; - sm->keyDone = FALSE; -} - - -SM_STATE(AUTH_PAE, FORCE_AUTH) -{ - SM_ENTRY(AUTH_PAE, FORCE_AUTH, auth_pae); - - sm->authPortStatus = Authorized; - setPortAuthorized(); - sm->auth_pae.portMode = ForceAuthorized; - sm->auth_pae.eapolStart = FALSE; - txCannedSuccess(); -} - - -SM_STATE(AUTH_PAE, FORCE_UNAUTH) -{ - SM_ENTRY(AUTH_PAE, FORCE_UNAUTH, auth_pae); - - sm->authPortStatus = Unauthorized; - setPortUnauthorized(); - sm->auth_pae.portMode = ForceUnauthorized; - sm->auth_pae.eapolStart = FALSE; - txCannedFail(); -} - - -SM_STEP(AUTH_PAE) -{ - if ((sm->portControl == Auto && - sm->auth_pae.portMode != sm->portControl) || - sm->initialize || !sm->portEnabled) - SM_ENTER(AUTH_PAE, INITIALIZE); - else if (sm->portControl == ForceAuthorized && - sm->auth_pae.portMode != sm->portControl && - !(sm->initialize || !sm->portEnabled)) - SM_ENTER(AUTH_PAE, FORCE_AUTH); - else if (sm->portControl == ForceUnauthorized && - sm->auth_pae.portMode != sm->portControl && - !(sm->initialize || !sm->portEnabled)) - SM_ENTER(AUTH_PAE, FORCE_UNAUTH); - else { - switch (sm->auth_pae.state) { - case AUTH_PAE_INITIALIZE: - SM_ENTER(AUTH_PAE, DISCONNECTED); - break; - case AUTH_PAE_DISCONNECTED: - SM_ENTER(AUTH_PAE, RESTART); - break; - case AUTH_PAE_RESTART: - if (!sm->auth_pae.eapRestart) - SM_ENTER(AUTH_PAE, CONNECTING); - break; - case AUTH_PAE_HELD: - if (sm->quietWhile == 0) - SM_ENTER(AUTH_PAE, RESTART); - break; - case AUTH_PAE_CONNECTING: - if (sm->auth_pae.eapolLogoff || - sm->auth_pae.reAuthCount > sm->auth_pae.reAuthMax) - SM_ENTER(AUTH_PAE, DISCONNECTED); - else if ((sm->be_auth.eapReq && - sm->auth_pae.reAuthCount <= - sm->auth_pae.reAuthMax) || - sm->eapSuccess || sm->eapFail) - SM_ENTER(AUTH_PAE, AUTHENTICATING); - break; - case AUTH_PAE_AUTHENTICATED: - if (sm->auth_pae.eapolStart || sm->reAuthenticate) - SM_ENTER(AUTH_PAE, RESTART); - else if (sm->auth_pae.eapolLogoff || !sm->portValid) - SM_ENTER(AUTH_PAE, DISCONNECTED); - break; - case AUTH_PAE_AUTHENTICATING: - if (sm->authSuccess && sm->portValid) - SM_ENTER(AUTH_PAE, AUTHENTICATED); - else if (sm->authFail || - (sm->keyDone && !sm->portValid)) - SM_ENTER(AUTH_PAE, HELD); - else if (sm->auth_pae.eapolStart || - sm->auth_pae.eapolLogoff || sm->authTimeout) - SM_ENTER(AUTH_PAE, ABORTING); - break; - case AUTH_PAE_ABORTING: - if (sm->auth_pae.eapolLogoff && !sm->authAbort) - SM_ENTER(AUTH_PAE, DISCONNECTED); - else if (!sm->auth_pae.eapolLogoff && !sm->authAbort) - SM_ENTER(AUTH_PAE, RESTART); - break; - case AUTH_PAE_FORCE_AUTH: - if (sm->auth_pae.eapolStart) - SM_ENTER(AUTH_PAE, FORCE_AUTH); - break; - case AUTH_PAE_FORCE_UNAUTH: - if (sm->auth_pae.eapolStart) - SM_ENTER(AUTH_PAE, FORCE_UNAUTH); - break; - } - } -} - - - -/* Backend Authentication state machine */ - -SM_STATE(BE_AUTH, INITIALIZE) -{ - SM_ENTRY(BE_AUTH, INITIALIZE, be_auth); - - abortAuth(); - sm->be_auth.eapNoReq = FALSE; - sm->authAbort = FALSE; -} - - -SM_STATE(BE_AUTH, REQUEST) -{ - SM_ENTRY(BE_AUTH, REQUEST, be_auth); - - txReq(); - sm->be_auth.eapReq = FALSE; - sm->be_auth.backendOtherRequestsToSupplicant++; - - /* - * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but - * it looks like this would be logical thing to do there since the old - * EAP response would not be valid anymore after the new EAP request - * was sent out. - * - * A race condition has been reported, in which hostapd ended up - * sending out EAP-Response/Identity as a response to the first - * EAP-Request from the main EAP method. This can be avoided by - * clearing eapolEap here. - */ - sm->eapolEap = FALSE; -} - - -SM_STATE(BE_AUTH, RESPONSE) -{ - SM_ENTRY(BE_AUTH, RESPONSE, be_auth); - - sm->authTimeout = FALSE; - sm->eapolEap = FALSE; - sm->be_auth.eapNoReq = FALSE; - sm->aWhile = sm->be_auth.serverTimeout; - sm->be_auth.eapResp = TRUE; - sendRespToServer(); - sm->be_auth.backendResponses++; -} - - -SM_STATE(BE_AUTH, SUCCESS) -{ - SM_ENTRY(BE_AUTH, SUCCESS, be_auth); - - txReq(); - sm->authSuccess = TRUE; - sm->keyRun = TRUE; -} - - -SM_STATE(BE_AUTH, FAIL) -{ - SM_ENTRY(BE_AUTH, FAIL, be_auth); - - /* Note: IEEE 802.1X-REV-d11 has unconditional txReq() here. - * txCannelFail() is used as a workaround for the case where - * authentication server does not include EAP-Message with - * Access-Reject. */ - if (sm->last_eap_radius == NULL) - txCannedFail(); - else - txReq(); - sm->authFail = TRUE; -} - - -SM_STATE(BE_AUTH, TIMEOUT) -{ - SM_ENTRY(BE_AUTH, TIMEOUT, be_auth); - - sm->authTimeout = TRUE; -} - - -SM_STATE(BE_AUTH, IDLE) -{ - SM_ENTRY(BE_AUTH, IDLE, be_auth); - - sm->authStart = FALSE; -} - - -SM_STATE(BE_AUTH, IGNORE) -{ - SM_ENTRY(BE_AUTH, IGNORE, be_auth); - - sm->be_auth.eapNoReq = FALSE; -} - - -SM_STEP(BE_AUTH) -{ - if (sm->portControl != Auto || sm->initialize || sm->authAbort) { - SM_ENTER(BE_AUTH, INITIALIZE); - return; - } - - switch (sm->be_auth.state) { - case BE_AUTH_INITIALIZE: - SM_ENTER(BE_AUTH, IDLE); - break; - case BE_AUTH_REQUEST: - if (sm->eapolEap) - SM_ENTER(BE_AUTH, RESPONSE); - else if (sm->be_auth.eapReq) - SM_ENTER(BE_AUTH, REQUEST); - else if (sm->eapTimeout) - SM_ENTER(BE_AUTH, TIMEOUT); - break; - case BE_AUTH_RESPONSE: - if (sm->be_auth.eapNoReq) - SM_ENTER(BE_AUTH, IGNORE); - if (sm->be_auth.eapReq) { - sm->be_auth.backendAccessChallenges++; - SM_ENTER(BE_AUTH, REQUEST); - } else if (sm->aWhile == 0) - SM_ENTER(BE_AUTH, TIMEOUT); - else if (sm->eapFail) { - sm->be_auth.backendAuthFails++; - SM_ENTER(BE_AUTH, FAIL); - } else if (sm->eapSuccess) { - sm->be_auth.backendAuthSuccesses++; - SM_ENTER(BE_AUTH, SUCCESS); - } - break; - case BE_AUTH_SUCCESS: - SM_ENTER(BE_AUTH, IDLE); - break; - case BE_AUTH_FAIL: - SM_ENTER(BE_AUTH, IDLE); - break; - case BE_AUTH_TIMEOUT: - SM_ENTER(BE_AUTH, IDLE); - break; - case BE_AUTH_IDLE: - if (sm->eapFail && sm->authStart) - SM_ENTER(BE_AUTH, FAIL); - else if (sm->be_auth.eapReq && sm->authStart) - SM_ENTER(BE_AUTH, REQUEST); - else if (sm->eapSuccess && sm->authStart) - SM_ENTER(BE_AUTH, SUCCESS); - break; - case BE_AUTH_IGNORE: - if (sm->eapolEap) - SM_ENTER(BE_AUTH, RESPONSE); - else if (sm->be_auth.eapReq) - SM_ENTER(BE_AUTH, REQUEST); - else if (sm->eapTimeout) - SM_ENTER(BE_AUTH, TIMEOUT); - break; - } -} - - - -/* Reauthentication Timer state machine */ - -SM_STATE(REAUTH_TIMER, INITIALIZE) -{ - SM_ENTRY(REAUTH_TIMER, INITIALIZE, reauth_timer); - - sm->reAuthWhen = sm->reauth_timer.reAuthPeriod; -} - - -SM_STATE(REAUTH_TIMER, REAUTHENTICATE) -{ - SM_ENTRY(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); - - sm->reAuthenticate = TRUE; - wpa_sm_event(sm->hapd, sm->sta, WPA_REAUTH_EAPOL); -} - - -SM_STEP(REAUTH_TIMER) -{ - if (sm->portControl != Auto || sm->initialize || - sm->authPortStatus == Unauthorized || - !sm->reauth_timer.reAuthEnabled) { - SM_ENTER(REAUTH_TIMER, INITIALIZE); - return; - } - - switch (sm->reauth_timer.state) { - case REAUTH_TIMER_INITIALIZE: - if (sm->reAuthWhen == 0) - SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); - break; - case REAUTH_TIMER_REAUTHENTICATE: - SM_ENTER(REAUTH_TIMER, INITIALIZE); - break; - } -} - - - -/* Authenticator Key Transmit state machine */ - -SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT) -{ - SM_ENTRY(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); -} - - -SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) -{ - SM_ENTRY(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); - - txKey(); - sm->keyAvailable = FALSE; - sm->keyDone = TRUE; -} - - -SM_STEP(AUTH_KEY_TX) -{ - if (sm->initialize || sm->portControl != Auto) { - SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); - return; - } - - switch (sm->auth_key_tx.state) { - case AUTH_KEY_TX_NO_KEY_TRANSMIT: - if (sm->keyTxEnabled && sm->keyAvailable && sm->keyRun && - !sm->sta->wpa) - SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); - break; - case AUTH_KEY_TX_KEY_TRANSMIT: - if (!sm->keyTxEnabled || !sm->keyRun) - SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); - else if (sm->keyAvailable) - SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); - break; - } -} - - - -/* Key Receive state machine */ - -SM_STATE(KEY_RX, NO_KEY_RECEIVE) -{ - SM_ENTRY(KEY_RX, NO_KEY_RECEIVE, key_rx); -} - - -SM_STATE(KEY_RX, KEY_RECEIVE) -{ - SM_ENTRY(KEY_RX, KEY_RECEIVE, key_rx); - - processKey(); - sm->key_rx.rxKey = FALSE; -} - - -SM_STEP(KEY_RX) -{ - if (sm->initialize || !sm->portEnabled) { - SM_ENTER(KEY_RX, NO_KEY_RECEIVE); - return; - } - - switch (sm->key_rx.state) { - case KEY_RX_NO_KEY_RECEIVE: - if (sm->key_rx.rxKey) - SM_ENTER(KEY_RX, KEY_RECEIVE); - break; - case KEY_RX_KEY_RECEIVE: - if (sm->key_rx.rxKey) - SM_ENTER(KEY_RX, KEY_RECEIVE); - break; - } -} - - - -/* Controlled Directions state machine */ - -SM_STATE(CTRL_DIR, FORCE_BOTH) -{ - SM_ENTRY(CTRL_DIR, FORCE_BOTH, ctrl_dir); - sm->ctrl_dir.operControlledDirections = Both; -} - - -SM_STATE(CTRL_DIR, IN_OR_BOTH) -{ - SM_ENTRY(CTRL_DIR, IN_OR_BOTH, ctrl_dir); - sm->ctrl_dir.operControlledDirections = - sm->ctrl_dir.adminControlledDirections; -} - - -SM_STEP(CTRL_DIR) -{ - if (sm->initialize) { - SM_ENTER(CTRL_DIR, IN_OR_BOTH); - return; - } - - switch (sm->ctrl_dir.state) { - case CTRL_DIR_FORCE_BOTH: - if (sm->portEnabled && sm->ctrl_dir.operEdge) - SM_ENTER(CTRL_DIR, IN_OR_BOTH); - break; - case CTRL_DIR_IN_OR_BOTH: - if (sm->ctrl_dir.operControlledDirections != - sm->ctrl_dir.adminControlledDirections) - SM_ENTER(CTRL_DIR, IN_OR_BOTH); - if (!sm->portEnabled || !sm->ctrl_dir.operEdge) - SM_ENTER(CTRL_DIR, FORCE_BOTH); - break; - } -} - - - -struct eapol_state_machine * -eapol_sm_alloc(hostapd *hapd, struct sta_info *sta) -{ - struct eapol_state_machine *sm; - - sm = (struct eapol_state_machine *) malloc(sizeof(*sm)); - if (sm == NULL) { - printf("IEEE 802.1X port state allocation failed\n"); - return NULL; - } - memset(sm, 0, sizeof(*sm)); - sm->radius_identifier = -1; - memcpy(sm->addr, sta->addr, ETH_ALEN); - if (sta->flags & WLAN_STA_PREAUTH) - sm->flags |= EAPOL_SM_PREAUTH; - - sm->hapd = hapd; - sm->sta = sta; - - /* Set default values for state machine constants */ - sm->auth_pae.state = AUTH_PAE_INITIALIZE; - sm->auth_pae.quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; - sm->auth_pae.reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; - - sm->be_auth.state = BE_AUTH_INITIALIZE; - sm->be_auth.serverTimeout = BE_AUTH_DEFAULT_serverTimeout; - - sm->reauth_timer.state = REAUTH_TIMER_INITIALIZE; - sm->reauth_timer.reAuthPeriod = hapd->conf->eap_reauth_period; - sm->reauth_timer.reAuthEnabled = hapd->conf->eap_reauth_period > 0 ? - TRUE : FALSE; - - sm->auth_key_tx.state = AUTH_KEY_TX_NO_KEY_TRANSMIT; - - sm->key_rx.state = KEY_RX_NO_KEY_RECEIVE; - - sm->ctrl_dir.state = CTRL_DIR_IN_OR_BOTH; - - sm->portEnabled = FALSE; - sm->portControl = Auto; - - sm->keyAvailable = FALSE; - if (!hapd->conf->wpa && - (hapd->default_wep_key || hapd->conf->individual_wep_key_len > 0)) - sm->keyTxEnabled = TRUE; - else - sm->keyTxEnabled = FALSE; - if (hapd->conf->wpa) - sm->portValid = FALSE; - else - sm->portValid = TRUE; - - if (hapd->conf->eap_server) { - struct eap_config eap_conf; - memset(&eap_conf, 0, sizeof(eap_conf)); - eap_conf.ssl_ctx = hapd->ssl_ctx; - eap_conf.eap_sim_db_priv = hapd->eap_sim_db_priv; - sm->eap = eap_sm_init(sm, &eapol_cb, &eap_conf); - if (sm->eap == NULL) { - eapol_sm_free(sm); - return NULL; - } - } - - eapol_sm_initialize(sm); - - return sm; -} - - -void eapol_sm_free(struct eapol_state_machine *sm) -{ - if (sm == NULL) - return; - - eloop_cancel_timeout(eapol_port_timers_tick, sm->hapd, sm); - eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); - if (sm->eap) - eap_sm_deinit(sm->eap); - free(sm); -} - - -static int eapol_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr) -{ - struct sta_info *sta; - sta = ap_get_sta(hapd, addr); - if (sta == NULL || sta->eapol_sm == NULL) - return 0; - return 1; -} - - -static void eapol_sm_step_run(struct eapol_state_machine *sm) -{ - struct hostapd_data *hapd = sm->hapd; - u8 addr[ETH_ALEN]; - int prev_auth_pae, prev_be_auth, prev_reauth_timer, prev_auth_key_tx, - prev_key_rx, prev_ctrl_dir; - int max_steps = 100; - - memcpy(addr, sm->sta->addr, ETH_ALEN); - - /* - * Allow EAPOL state machines to run as long as there are state - * changes, but exit and return here through event loop if more than - * 100 steps is needed as a precaution against infinite loops inside - * eloop callback. - */ -restart: - prev_auth_pae = sm->auth_pae.state; - prev_be_auth = sm->be_auth.state; - prev_reauth_timer = sm->reauth_timer.state; - prev_auth_key_tx = sm->auth_key_tx.state; - prev_key_rx = sm->key_rx.state; - prev_ctrl_dir = sm->ctrl_dir.state; - - SM_STEP_RUN(AUTH_PAE); - if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) - SM_STEP_RUN(BE_AUTH); - if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) - SM_STEP_RUN(REAUTH_TIMER); - if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) - SM_STEP_RUN(AUTH_KEY_TX); - if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) - SM_STEP_RUN(KEY_RX); - if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) - SM_STEP_RUN(CTRL_DIR); - - if (prev_auth_pae != sm->auth_pae.state || - prev_be_auth != sm->be_auth.state || - prev_reauth_timer != sm->reauth_timer.state || - prev_auth_key_tx != sm->auth_key_tx.state || - prev_key_rx != sm->key_rx.state || - prev_ctrl_dir != sm->ctrl_dir.state) { - if (--max_steps > 0) - goto restart; - /* Re-run from eloop timeout */ - eapol_sm_step(sm); - return; - } - - if (eapol_sm_sta_entry_alive(hapd, addr) && sm->eap) { - if (eap_sm_step(sm->eap)) { - if (--max_steps > 0) - goto restart; - /* Re-run from eloop timeout */ - eapol_sm_step(sm); - return; - } - } - - if (eapol_sm_sta_entry_alive(hapd, addr)) - wpa_sm_notify(sm->hapd, sm->sta); -} - - -static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) -{ - struct eapol_state_machine *sm = eloop_ctx; - eapol_sm_step_run(sm); -} - - -void eapol_sm_step(struct eapol_state_machine *sm) -{ - /* - * Run eapol_sm_step_run from a registered timeout to make sure that - * other possible timeouts/events are processed and to avoid long - * function call chains. - */ - - eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL); -} - - -void eapol_sm_initialize(struct eapol_state_machine *sm) -{ - sm->initializing = TRUE; - /* Initialize the state machines by asserting initialize and then - * deasserting it after one step */ - sm->initialize = TRUE; - eapol_sm_step_run(sm); - sm->initialize = FALSE; - eapol_sm_step_run(sm); - sm->initializing = FALSE; - - /* Start one second tick for port timers state machine */ - eloop_cancel_timeout(eapol_port_timers_tick, sm->hapd, sm); - eloop_register_timeout(1, 0, eapol_port_timers_tick, sm->hapd, sm); -} - - -#ifdef HOSTAPD_DUMP_STATE -static inline const char * port_type_txt(PortTypes pt) -{ - switch (pt) { - case ForceUnauthorized: return "ForceUnauthorized"; - case ForceAuthorized: return "ForceAuthorized"; - case Auto: return "Auto"; - default: return "Unknown"; - } -} - - -static inline const char * port_state_txt(PortState ps) -{ - switch (ps) { - case Unauthorized: return "Unauthorized"; - case Authorized: return "Authorized"; - default: return "Unknown"; - } -} - - -static inline const char * ctrl_dir_txt(ControlledDirection dir) -{ - switch (dir) { - case Both: return "Both"; - case In: return "In"; - default: return "Unknown"; - } -} - - -static inline const char * auth_pae_state_txt(int s) -{ - switch (s) { - case AUTH_PAE_INITIALIZE: return "INITIALIZE"; - case AUTH_PAE_DISCONNECTED: return "DISCONNECTED"; - case AUTH_PAE_CONNECTING: return "CONNECTING"; - case AUTH_PAE_AUTHENTICATING: return "AUTHENTICATING"; - case AUTH_PAE_AUTHENTICATED: return "AUTHENTICATED"; - case AUTH_PAE_ABORTING: return "ABORTING"; - case AUTH_PAE_HELD: return "HELD"; - case AUTH_PAE_FORCE_AUTH: return "FORCE_AUTH"; - case AUTH_PAE_FORCE_UNAUTH: return "FORCE_UNAUTH"; - case AUTH_PAE_RESTART: return "RESTART"; - default: return "Unknown"; - } -} - - -static inline const char * be_auth_state_txt(int s) -{ - switch (s) { - case BE_AUTH_REQUEST: return "REQUEST"; - case BE_AUTH_RESPONSE: return "RESPONSE"; - case BE_AUTH_SUCCESS: return "SUCCESS"; - case BE_AUTH_FAIL: return "FAIL"; - case BE_AUTH_TIMEOUT: return "TIMEOUT"; - case BE_AUTH_IDLE: return "IDLE"; - case BE_AUTH_INITIALIZE: return "INITIALIZE"; - case BE_AUTH_IGNORE: return "IGNORE"; - default: return "Unknown"; - } -} - - -static inline const char * reauth_timer_state_txt(int s) -{ - switch (s) { - case REAUTH_TIMER_INITIALIZE: return "INITIALIZE"; - case REAUTH_TIMER_REAUTHENTICATE: return "REAUTHENTICATE"; - default: return "Unknown"; - } -} - - -static inline const char * auth_key_tx_state_txt(int s) -{ - switch (s) { - case AUTH_KEY_TX_NO_KEY_TRANSMIT: return "NO_KEY_TRANSMIT"; - case AUTH_KEY_TX_KEY_TRANSMIT: return "KEY_TRANSMIT"; - default: return "Unknown"; - } -} - - -static inline const char * key_rx_state_txt(int s) -{ - switch (s) { - case KEY_RX_NO_KEY_RECEIVE: return "NO_KEY_RECEIVE"; - case KEY_RX_KEY_RECEIVE: return "KEY_RECEIVE"; - default: return "Unknown"; - } -} - - -static inline const char * ctrl_dir_state_txt(int s) -{ - switch (s) { - case CTRL_DIR_FORCE_BOTH: return "FORCE_BOTH"; - case CTRL_DIR_IN_OR_BOTH: return "IN_OR_BOTH"; - default: return "Unknown"; - } -} - - -void eapol_sm_dump_state(FILE *f, const char *prefix, - struct eapol_state_machine *sm) -{ - fprintf(f, "%sEAPOL state machine:\n", prefix); - fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix, - sm->aWhile, sm->quietWhile, sm->reAuthWhen); -#define _SB(b) ((b) ? "TRUE" : "FALSE") - fprintf(f, - "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n" - "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n" - "%s eapSuccess=%s eapTimeout=%s initialize=%s " - "keyAvailable=%s\n" - "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n" - "%s portEnabled=%s portValid=%s reAuthenticate=%s\n", - prefix, _SB(sm->authAbort), _SB(sm->authFail), - port_state_txt(sm->authPortStatus), _SB(sm->authStart), - prefix, _SB(sm->authTimeout), _SB(sm->authSuccess), - _SB(sm->eapFail), _SB(sm->eapolEap), - prefix, _SB(sm->eapSuccess), _SB(sm->eapTimeout), - _SB(sm->initialize), _SB(sm->keyAvailable), - prefix, _SB(sm->keyDone), _SB(sm->keyRun), - _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), - prefix, _SB(sm->portEnabled), _SB(sm->portValid), - _SB(sm->reAuthenticate)); - - fprintf(f, "%s Authenticator PAE:\n" - "%s state=%s\n" - "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n" - "%s portMode=%s reAuthCount=%d\n" - "%s quietPeriod=%d reAuthMax=%d\n" - "%s authEntersConnecting=%d\n" - "%s authEapLogoffsWhileConnecting=%d\n" - "%s authEntersAuthenticating=%d\n" - "%s authAuthSuccessesWhileAuthenticating=%d\n" - "%s authAuthTimeoutsWhileAuthenticating=%d\n" - "%s authAuthFailWhileAuthenticating=%d\n" - "%s authAuthEapStartsWhileAuthenticating=%d\n" - "%s authAuthEapLogoffWhileAuthenticating=%d\n" - "%s authAuthReauthsWhileAuthenticated=%d\n" - "%s authAuthEapStartsWhileAuthenticated=%d\n" - "%s authAuthEapLogoffWhileAuthenticated=%d\n", - prefix, prefix, auth_pae_state_txt(sm->auth_pae.state), prefix, - _SB(sm->auth_pae.eapolLogoff), _SB(sm->auth_pae.eapolStart), - _SB(sm->auth_pae.eapRestart), prefix, - port_type_txt(sm->auth_pae.portMode), sm->auth_pae.reAuthCount, - prefix, sm->auth_pae.quietPeriod, sm->auth_pae.reAuthMax, - prefix, sm->auth_pae.authEntersConnecting, - prefix, sm->auth_pae.authEapLogoffsWhileConnecting, - prefix, sm->auth_pae.authEntersAuthenticating, - prefix, sm->auth_pae.authAuthSuccessesWhileAuthenticating, - prefix, sm->auth_pae.authAuthTimeoutsWhileAuthenticating, - prefix, sm->auth_pae.authAuthFailWhileAuthenticating, - prefix, sm->auth_pae.authAuthEapStartsWhileAuthenticating, - prefix, sm->auth_pae.authAuthEapLogoffWhileAuthenticating, - prefix, sm->auth_pae.authAuthReauthsWhileAuthenticated, - prefix, sm->auth_pae.authAuthEapStartsWhileAuthenticated, - prefix, sm->auth_pae.authAuthEapLogoffWhileAuthenticated); - - fprintf(f, "%s Backend Authentication:\n" - "%s state=%s\n" - "%s eapNoReq=%s eapReq=%s eapResp=%s\n" - "%s serverTimeout=%d\n" - "%s backendResponses=%d\n" - "%s backendAccessChallenges=%d\n" - "%s backendOtherRequestsToSupplicant=%d\n" - "%s backendAuthSuccesses=%d\n" - "%s backendAuthFails=%d\n", - prefix, prefix, - be_auth_state_txt(sm->be_auth.state), - prefix, _SB(sm->be_auth.eapNoReq), _SB(sm->be_auth.eapReq), - _SB(sm->be_auth.eapResp), - prefix, sm->be_auth.serverTimeout, - prefix, sm->be_auth.backendResponses, - prefix, sm->be_auth.backendAccessChallenges, - prefix, sm->be_auth.backendOtherRequestsToSupplicant, - prefix, sm->be_auth.backendAuthSuccesses, - prefix, sm->be_auth.backendAuthFails); - - fprintf(f, "%s Reauthentication Timer:\n" - "%s state=%s\n" - "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix, - reauth_timer_state_txt(sm->reauth_timer.state), prefix, - sm->reauth_timer.reAuthPeriod, - _SB(sm->reauth_timer.reAuthEnabled)); - - fprintf(f, "%s Authenticator Key Transmit:\n" - "%s state=%s\n", prefix, prefix, - auth_key_tx_state_txt(sm->auth_key_tx.state)); - - fprintf(f, "%s Key Receive:\n" - "%s state=%s\n" - "%s rxKey=%s\n", prefix, prefix, - key_rx_state_txt(sm->key_rx.state), - prefix, _SB(sm->key_rx.rxKey)); - - fprintf(f, "%s Controlled Directions:\n" - "%s state=%s\n" - "%s adminControlledDirections=%s " - "operControlledDirections=%s\n" - "%s operEdge=%s\n", prefix, prefix, - ctrl_dir_state_txt(sm->ctrl_dir.state), - prefix, ctrl_dir_txt(sm->ctrl_dir.adminControlledDirections), - ctrl_dir_txt(sm->ctrl_dir.operControlledDirections), - prefix, _SB(sm->ctrl_dir.operEdge)); -#undef _SB -} -#endif /* HOSTAPD_DUMP_STATE */ - - -static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) -{ - struct eapol_state_machine *sm = ctx; - if (sm == NULL) - return FALSE; - switch (variable) { - case EAPOL_eapSuccess: - return sm->eapSuccess; - case EAPOL_eapRestart: - return sm->auth_pae.eapRestart; - case EAPOL_eapFail: - return sm->eapFail; - case EAPOL_eapResp: - return sm->be_auth.eapResp; - case EAPOL_eapReq: - return sm->be_auth.eapReq; - case EAPOL_eapNoReq: - return sm->be_auth.eapNoReq; - case EAPOL_portEnabled: - return sm->portEnabled; - case EAPOL_eapTimeout: - return sm->eapTimeout; - } - return FALSE; -} - - -static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, - Boolean value) -{ - struct eapol_state_machine *sm = ctx; - if (sm == NULL) - return; - switch (variable) { - case EAPOL_eapSuccess: - sm->eapSuccess = value; - break; - case EAPOL_eapRestart: - sm->auth_pae.eapRestart = value; - break; - case EAPOL_eapFail: - sm->eapFail = value; - break; - case EAPOL_eapResp: - sm->be_auth.eapResp = value; - break; - case EAPOL_eapReq: - sm->be_auth.eapReq = value; - break; - case EAPOL_eapNoReq: - sm->be_auth.eapNoReq = value; - break; - case EAPOL_portEnabled: - sm->portEnabled = value; - break; - case EAPOL_eapTimeout: - sm->eapTimeout = value; - break; - } -} - - -static void eapol_sm_set_eapReqData(void *ctx, const u8 *eapReqData, - size_t eapReqDataLen) -{ - struct eapol_state_machine *sm = ctx; - if (sm == NULL) - return; - - free(sm->last_eap_radius); - sm->last_eap_radius = malloc(eapReqDataLen); - if (sm->last_eap_radius == NULL) - return; - memcpy(sm->last_eap_radius, eapReqData, eapReqDataLen); - sm->last_eap_radius_len = eapReqDataLen; -} - - -static void eapol_sm_set_eapKeyData(void *ctx, const u8 *eapKeyData, - size_t eapKeyDataLen) -{ - struct eapol_state_machine *sm = ctx; - struct hostapd_data *hapd; - - if (sm == NULL) - return; - - hapd = sm->hapd; - - if (eapKeyData && eapKeyDataLen >= 64) { - free(sm->eapol_key_sign); - free(sm->eapol_key_crypt); - sm->eapol_key_crypt = malloc(32); - if (sm->eapol_key_crypt) { - memcpy(sm->eapol_key_crypt, eapKeyData, 32); - sm->eapol_key_crypt_len = 32; - } - sm->eapol_key_sign = malloc(32); - if (sm->eapol_key_sign) { - memcpy(sm->eapol_key_sign, eapKeyData + 32, 32); - sm->eapol_key_sign_len = 32; - } - if (hapd->default_wep_key || - hapd->conf->individual_wep_key_len > 0 || - hapd->conf->wpa) - sm->keyAvailable = TRUE; - } else { - free(sm->eapol_key_sign); - free(sm->eapol_key_crypt); - sm->eapol_key_sign = NULL; - sm->eapol_key_crypt = NULL; - sm->eapol_key_sign_len = 0; - sm->eapol_key_crypt_len = 0; - sm->keyAvailable = FALSE; - } -} - - -static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, - size_t identity_len, int phase2, - struct eap_user *user) -{ - struct eapol_state_machine *sm = ctx; - const struct hostapd_eap_user *eap_user; - - eap_user = hostapd_get_eap_user(sm->hapd->conf, identity, - identity_len, phase2); - if (eap_user == NULL) - return -1; - - memset(user, 0, sizeof(*user)); - user->phase2 = phase2; - memcpy(user->methods, eap_user->methods, - EAP_USER_MAX_METHODS > EAP_MAX_METHODS ? - EAP_USER_MAX_METHODS : EAP_MAX_METHODS); - - if (eap_user->password) { - user->password = malloc(eap_user->password_len); - if (user->password == NULL) - return -1; - memcpy(user->password, eap_user->password, - eap_user->password_len); - user->password_len = eap_user->password_len; - } - user->force_version = eap_user->force_version; - - return 0; -} - - -static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) -{ - struct eapol_state_machine *sm = ctx; - *len = sm->hapd->conf->eap_req_id_text_len; - return sm->hapd->conf->eap_req_id_text; -} - - -static struct eapol_callbacks eapol_cb = -{ - .get_bool = eapol_sm_get_bool, - .set_bool = eapol_sm_set_bool, - .set_eapReqData = eapol_sm_set_eapReqData, - .set_eapKeyData = eapol_sm_set_eapKeyData, - .get_eap_user = eapol_sm_get_eap_user, - .get_eap_req_id_text = eapol_sm_get_eap_req_id_text, -}; diff --git a/contrib/hostapd-0.4.9/eapol_sm.h b/contrib/hostapd-0.4.9/eapol_sm.h deleted file mode 100644 index 0c34b4fcc2..0000000000 --- a/contrib/hostapd-0.4.9/eapol_sm.h +++ /dev/null @@ -1,224 +0,0 @@ -#ifndef EAPOL_SM_H -#define EAPOL_SM_H - -#include "defs.h" - -/* IEEE Std 802.1X-REV-d11, Ch. 8.2 */ - -typedef enum { ForceUnauthorized = 1, ForceAuthorized = 3, Auto = 2 } - PortTypes; -typedef enum { Unauthorized = 2, Authorized = 1 } PortState; -typedef enum { Both = 0, In = 1 } ControlledDirection; -typedef unsigned int Counter; - - -/* Authenticator PAE state machine */ -struct eapol_auth_pae_sm { - /* variables */ - Boolean eapolLogoff; - Boolean eapolStart; - Boolean eapRestart; - PortTypes portMode; - unsigned int reAuthCount; - - /* constants */ - unsigned int quietPeriod; /* default 60; 0..65535 */ -#define AUTH_PAE_DEFAULT_quietPeriod 60 - unsigned int reAuthMax; /* default 2 */ -#define AUTH_PAE_DEFAULT_reAuthMax 2 - - /* counters */ - Counter authEntersConnecting; - Counter authEapLogoffsWhileConnecting; - Counter authEntersAuthenticating; - Counter authAuthSuccessesWhileAuthenticating; - Counter authAuthTimeoutsWhileAuthenticating; - Counter authAuthFailWhileAuthenticating; - Counter authAuthEapStartsWhileAuthenticating; - Counter authAuthEapLogoffWhileAuthenticating; - Counter authAuthReauthsWhileAuthenticated; - Counter authAuthEapStartsWhileAuthenticated; - Counter authAuthEapLogoffWhileAuthenticated; - - enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING, - AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED, - AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH, - AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } state; -}; - - -/* Backend Authentication state machine */ -struct eapol_backend_auth_sm { - /* variables */ - Boolean eapNoReq; - Boolean eapReq; - Boolean eapResp; - - /* constants */ - unsigned int serverTimeout; /* default 30; 1..X */ -#define BE_AUTH_DEFAULT_serverTimeout 30 - - /* counters */ - Counter backendResponses; - Counter backendAccessChallenges; - Counter backendOtherRequestsToSupplicant; - Counter backendAuthSuccesses; - Counter backendAuthFails; - - enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS, - BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE, - BE_AUTH_IGNORE - } state; -}; - - -/* Reauthentication Timer state machine */ -struct eapol_reauth_timer_sm { - /* constants */ - unsigned int reAuthPeriod; /* default 3600 s */ - Boolean reAuthEnabled; - - enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE } state; -}; - - -/* Authenticator Key Transmit state machine */ -struct eapol_auth_key_tx { - enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT } state; -}; - - -/* Key Receive state machine */ -struct eapol_key_rx { - /* variables */ - Boolean rxKey; - - enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } state; -}; - - -/* Controlled Directions state machine */ -struct eapol_ctrl_dir { - /* variables */ - ControlledDirection adminControlledDirections; - ControlledDirection operControlledDirections; - Boolean operEdge; - - enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } state; -}; - - -struct eap_sm; - -struct radius_attr_data { - u8 *data; - size_t len; -}; - -struct radius_class_data { - struct radius_attr_data *attr; - size_t count; -}; - -struct eapol_state_machine { - /* timers */ - int aWhile; - int quietWhile; - int reAuthWhen; - - /* global variables */ - Boolean authAbort; - Boolean authFail; - PortState authPortStatus; - Boolean authStart; - Boolean authTimeout; - Boolean authSuccess; - Boolean eapFail; - Boolean eapolEap; - Boolean eapSuccess; - Boolean eapTimeout; - Boolean initialize; - Boolean keyAvailable; - Boolean keyDone; - Boolean keyRun; - Boolean keyTxEnabled; - PortTypes portControl; - Boolean portEnabled; - Boolean portValid; - Boolean reAuthenticate; - - /* Port Timers state machine */ - /* 'Boolean tick' implicitly handled as registered timeout */ - - struct eapol_auth_pae_sm auth_pae; - struct eapol_backend_auth_sm be_auth; - struct eapol_reauth_timer_sm reauth_timer; - struct eapol_auth_key_tx auth_key_tx; - struct eapol_key_rx key_rx; - struct eapol_ctrl_dir ctrl_dir; - - /* Authenticator Statistics Table */ - Counter dot1xAuthEapolFramesRx; - Counter dot1xAuthEapolFramesTx; - Counter dot1xAuthEapolStartFramesRx; - Counter dot1xAuthEapolLogoffFramesRx; - Counter dot1xAuthEapolRespIdFramesRx; - Counter dot1xAuthEapolRespFramesRx; - Counter dot1xAuthEapolReqIdFramesTx; - Counter dot1xAuthEapolReqFramesTx; - Counter dot1xAuthInvalidEapolFramesRx; - Counter dot1xAuthEapLengthErrorFramesRx; - Counter dot1xAuthLastEapolFrameVersion; - - /* Other variables - not defined in IEEE 802.1X */ - u8 addr[ETH_ALEN]; /* Supplicant address */ -#define EAPOL_SM_PREAUTH BIT(0) - int flags; /* EAPOL_SM_* */ - - int radius_identifier; - /* TODO: check when the last messages can be released */ - struct radius_msg *last_recv_radius; - u8 *last_eap_supp; /* last received EAP Response from Supplicant */ - size_t last_eap_supp_len; - u8 *last_eap_radius; /* last received EAP Response from Authentication - * Server */ - size_t last_eap_radius_len; - u8 *identity; - size_t identity_len; - struct radius_class_data radius_class; - - /* Keys for encrypting and signing EAPOL-Key frames */ - u8 *eapol_key_sign; - size_t eapol_key_sign_len; - u8 *eapol_key_crypt; - size_t eapol_key_crypt_len; - - Boolean rx_identity; /* set to TRUE on reception of - * EAP-Response/Identity */ - - struct eap_sm *eap; - - /* currentId was removed in IEEE 802.1X-REV, but it is needed to filter - * out EAP-Responses to old packets (e.g., to two EAP-Request/Identity - * packets that are often sent in the beginning of the authentication). - */ - u8 currentId; - - Boolean initializing; /* in process of initializing state machines */ - - /* Somewhat nasty pointers to global hostapd and STA data to avoid - * passing these to every function */ - struct hostapd_data *hapd; - struct sta_info *sta; -}; - - -struct eapol_state_machine *eapol_sm_alloc(hostapd *hapd, - struct sta_info *sta); -void eapol_sm_free(struct eapol_state_machine *sm); -void eapol_sm_step(struct eapol_state_machine *sm); -void eapol_sm_initialize(struct eapol_state_machine *sm); -void eapol_sm_dump_state(FILE *f, const char *prefix, - struct eapol_state_machine *sm); - -#endif /* EAPOL_SM_H */ diff --git a/contrib/hostapd-0.4.9/eloop.c b/contrib/hostapd-0.4.9/eloop.c deleted file mode 100644 index b482f81cce..0000000000 --- a/contrib/hostapd-0.4.9/eloop.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Event loop based on select() loop - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_NATIVE_WINDOWS -#include "common.h" -#endif /* CONFIG_NATIVE_WINDOWS */ - -#include "eloop.h" - - -struct eloop_sock { - int sock; - void *eloop_data; - void *user_data; - void (*handler)(int sock, void *eloop_ctx, void *sock_ctx); -}; - -struct eloop_timeout { - struct timeval time; - void *eloop_data; - void *user_data; - void (*handler)(void *eloop_ctx, void *sock_ctx); - struct eloop_timeout *next; -}; - -struct eloop_signal { - int sig; - void *user_data; - void (*handler)(int sig, void *eloop_ctx, void *signal_ctx); - int signaled; -}; - -struct eloop_data { - void *user_data; - - int max_sock, reader_count; - struct eloop_sock *readers; - - struct eloop_timeout *timeout; - - int signal_count; - struct eloop_signal *signals; - int signaled; - int pending_terminate; - - int terminate; - int reader_table_changed; -}; - -static struct eloop_data eloop; - - -void eloop_init(void *user_data) -{ - memset(&eloop, 0, sizeof(eloop)); - eloop.user_data = user_data; -} - - -int eloop_register_read_sock(int sock, - void (*handler)(int sock, void *eloop_ctx, - void *sock_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_sock *tmp; - - tmp = (struct eloop_sock *) - realloc(eloop.readers, - (eloop.reader_count + 1) * sizeof(struct eloop_sock)); - if (tmp == NULL) - return -1; - - tmp[eloop.reader_count].sock = sock; - tmp[eloop.reader_count].eloop_data = eloop_data; - tmp[eloop.reader_count].user_data = user_data; - tmp[eloop.reader_count].handler = handler; - eloop.reader_count++; - eloop.readers = tmp; - if (sock > eloop.max_sock) - eloop.max_sock = sock; - eloop.reader_table_changed = 1; - - return 0; -} - - -void eloop_unregister_read_sock(int sock) -{ - int i; - - if (eloop.readers == NULL || eloop.reader_count == 0) - return; - - for (i = 0; i < eloop.reader_count; i++) { - if (eloop.readers[i].sock == sock) - break; - } - if (i == eloop.reader_count) - return; - if (i != eloop.reader_count - 1) { - memmove(&eloop.readers[i], &eloop.readers[i + 1], - (eloop.reader_count - i - 1) * - sizeof(struct eloop_sock)); - } - eloop.reader_count--; - eloop.reader_table_changed = 1; -} - - -int eloop_register_timeout(unsigned int secs, unsigned int usecs, - void (*handler)(void *eloop_ctx, void *timeout_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_timeout *timeout, *tmp, *prev; - - timeout = (struct eloop_timeout *) malloc(sizeof(*timeout)); - if (timeout == NULL) - return -1; - gettimeofday(&timeout->time, NULL); - timeout->time.tv_sec += secs; - timeout->time.tv_usec += usecs; - while (timeout->time.tv_usec >= 1000000) { - timeout->time.tv_sec++; - timeout->time.tv_usec -= 1000000; - } - timeout->eloop_data = eloop_data; - timeout->user_data = user_data; - timeout->handler = handler; - timeout->next = NULL; - - if (eloop.timeout == NULL) { - eloop.timeout = timeout; - return 0; - } - - prev = NULL; - tmp = eloop.timeout; - while (tmp != NULL) { - if (timercmp(&timeout->time, &tmp->time, <)) - break; - prev = tmp; - tmp = tmp->next; - } - - if (prev == NULL) { - timeout->next = eloop.timeout; - eloop.timeout = timeout; - } else { - timeout->next = prev->next; - prev->next = timeout; - } - - return 0; -} - - -int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), - void *eloop_data, void *user_data) -{ - struct eloop_timeout *timeout, *prev, *next; - int removed = 0; - - prev = NULL; - timeout = eloop.timeout; - while (timeout != NULL) { - next = timeout->next; - - if (timeout->handler == handler && - (timeout->eloop_data == eloop_data || - eloop_data == ELOOP_ALL_CTX) && - (timeout->user_data == user_data || - user_data == ELOOP_ALL_CTX)) { - if (prev == NULL) - eloop.timeout = next; - else - prev->next = next; - free(timeout); - removed++; - } else - prev = timeout; - - timeout = next; - } - - return removed; -} - - -#ifndef CONFIG_NATIVE_WINDOWS -static void eloop_handle_alarm(int sig) -{ - fprintf(stderr, "eloop: could not process SIGINT or SIGTERM in two " - "seconds. Looks like there\n" - "is a bug that ends up in a busy loop that " - "prevents clean shutdown.\n" - "Killing program forcefully.\n"); - exit(1); -} -#endif /* CONFIG_NATIVE_WINDOWS */ - - -static void eloop_handle_signal(int sig) -{ - int i; - -#ifndef CONFIG_NATIVE_WINDOWS - if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) { - /* Use SIGALRM to break out from potential busy loops that - * would not allow the program to be killed. */ - eloop.pending_terminate = 1; - signal(SIGALRM, eloop_handle_alarm); - alarm(2); - } -#endif /* CONFIG_NATIVE_WINDOWS */ - - eloop.signaled++; - for (i = 0; i < eloop.signal_count; i++) { - if (eloop.signals[i].sig == sig) { - eloop.signals[i].signaled++; - break; - } - } -} - - -static void eloop_process_pending_signals(void) -{ - int i; - - if (eloop.signaled == 0) - return; - eloop.signaled = 0; - - if (eloop.pending_terminate) { -#ifndef CONFIG_NATIVE_WINDOWS - alarm(0); -#endif /* CONFIG_NATIVE_WINDOWS */ - eloop.pending_terminate = 0; - } - - for (i = 0; i < eloop.signal_count; i++) { - if (eloop.signals[i].signaled) { - eloop.signals[i].signaled = 0; - eloop.signals[i].handler(eloop.signals[i].sig, - eloop.user_data, - eloop.signals[i].user_data); - } - } -} - - -int eloop_register_signal(int sig, - void (*handler)(int sig, void *eloop_ctx, - void *signal_ctx), - void *user_data) -{ - struct eloop_signal *tmp; - - tmp = (struct eloop_signal *) - realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); - if (tmp == NULL) - return -1; - - tmp[eloop.signal_count].sig = sig; - tmp[eloop.signal_count].user_data = user_data; - tmp[eloop.signal_count].handler = handler; - tmp[eloop.signal_count].signaled = 0; - eloop.signal_count++; - eloop.signals = tmp; - signal(sig, eloop_handle_signal); - - return 0; -} - - -void eloop_run(void) -{ - fd_set *rfds; - int i, res; - struct timeval tv, now; - - rfds = malloc(sizeof(*rfds)); - if (rfds == NULL) { - printf("eloop_run - malloc failed\n"); - return; - } - - while (!eloop.terminate && - (eloop.timeout || eloop.reader_count > 0)) { - if (eloop.timeout) { - gettimeofday(&now, NULL); - if (timercmp(&now, &eloop.timeout->time, <)) - timersub(&eloop.timeout->time, &now, &tv); - else - tv.tv_sec = tv.tv_usec = 0; -#if 0 - printf("next timeout in %lu.%06lu sec\n", - tv.tv_sec, tv.tv_usec); -#endif - } - - FD_ZERO(rfds); - for (i = 0; i < eloop.reader_count; i++) - FD_SET(eloop.readers[i].sock, rfds); - res = select(eloop.max_sock + 1, rfds, NULL, NULL, - eloop.timeout ? &tv : NULL); - if (res < 0 && errno != EINTR) { - perror("select"); - free(rfds); - return; - } - eloop_process_pending_signals(); - - /* check if some registered timeouts have occurred */ - if (eloop.timeout) { - struct eloop_timeout *tmp; - - gettimeofday(&now, NULL); - if (!timercmp(&now, &eloop.timeout->time, <)) { - tmp = eloop.timeout; - eloop.timeout = eloop.timeout->next; - tmp->handler(tmp->eloop_data, - tmp->user_data); - free(tmp); - } - - } - - if (res <= 0) - continue; - - eloop.reader_table_changed = 0; - for (i = 0; i < eloop.reader_count; i++) { - if (FD_ISSET(eloop.readers[i].sock, rfds)) { - eloop.readers[i].handler( - eloop.readers[i].sock, - eloop.readers[i].eloop_data, - eloop.readers[i].user_data); - if (eloop.reader_table_changed) - break; - } - } - } - - free(rfds); -} - - -void eloop_terminate(void) -{ - eloop.terminate = 1; -} - - -void eloop_destroy(void) -{ - struct eloop_timeout *timeout, *prev; - - timeout = eloop.timeout; - while (timeout != NULL) { - prev = timeout; - timeout = timeout->next; - free(prev); - } - free(eloop.readers); - free(eloop.signals); -} - - -int eloop_terminated(void) -{ - return eloop.terminate; -} diff --git a/contrib/hostapd-0.4.9/eloop.h b/contrib/hostapd-0.4.9/eloop.h deleted file mode 100644 index 884d4e1b2c..0000000000 --- a/contrib/hostapd-0.4.9/eloop.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Event loop - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * This file defines an event loop interface that supports processing events - * from registered timeouts (i.e., do something after N seconds), sockets - * (e.g., a new packet available for reading), and signals. eloop.c is an - * implementation of this interface using select() and sockets. This is - * suitable for most UNIX/POSIX systems. When porting to other operating - * systems, it may be necessary to replace that implementation with OS specific - * mechanisms. - */ - -#ifndef ELOOP_H -#define ELOOP_H - -/** - * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts - */ -#define ELOOP_ALL_CTX (void *) -1 - -/** - * eloop_init() - Initialize global event loop data - * @user_data: Pointer to global data passed as eloop_ctx to signal handlers - * - * This function must be called before any other eloop_* function. user_data - * can be used to configure a global (to the process) pointer that will be - * passed as eloop_ctx parameter to signal handlers. - */ -void eloop_init(void *user_data); - -/** - * eloop_register_read_sock - Register handler for read events - * @sock: File descriptor number for the socket - * @handler: Callback function to be called when data is available for reading - * @eloop_data: Callback context data (eloop_ctx) - * @user_data: Callback context data (sock_ctx) - * Returns: 0 on success, -1 on failure - * - * Register a read socket notifier for the given file descriptor. The handler - * function will be called whenever data is available for reading from the - * socket. - */ -int eloop_register_read_sock(int sock, - void (*handler)(int sock, void *eloop_ctx, - void *sock_ctx), - void *eloop_data, void *user_data); - -/** - * eloop_unregister_read_sock - Unregister handler for read events - * @sock: File descriptor number for the socket - * - * Unregister a read socket notifier that was previously registered with - * eloop_register_read_sock(). - */ -void eloop_unregister_read_sock(int sock); - -/** - * eloop_register_timeout - Register timeout - * @secs: Number of seconds to the timeout - * @usecs: Number of microseconds to the timeout - * @handler: Callback function to be called when timeout occurs - * @eloop_data: Callback context data (eloop_ctx) - * @user_data: Callback context data (sock_ctx) - * Returns: 0 on success, -1 on failure - * - * Register a timeout that will cause the handler function to be called after - * given time. - */ -int eloop_register_timeout(unsigned int secs, unsigned int usecs, - void (*handler)(void *eloop_ctx, void *timeout_ctx), - void *eloop_data, void *user_data); - -/** - * eloop_cancel_timeout - Cancel timeouts - * @handler: Matching callback function - * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all - * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all - * Returns: Number of cancelled timeouts - * - * Cancel matching timeouts registered with - * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for - * cancelling all timeouts regardless of eloop_data/user_data. - */ -int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), - void *eloop_data, void *user_data); - -/** - * eloop_register_signal - Register handler for signals - * @sig: Signal number (e.g., SIGHUP) - * @handler: Callback function to be called when the signal is received - * @user_data: Callback context data (signal_ctx) - * Returns: 0 on success, -1 on failure - * - * Register a callback function that will be called when a signal is received. - * The calback function is actually called only after the system signal handler - * has returned. This means that the normal limits for sighandlers (i.e., only - * "safe functions" allowed) do not apply for the registered callback. - * - * Signals are 'global' events and there is no local eloop_data pointer like - * with other handlers. The global user_data pointer registered with - * eloop_init() will be used as eloop_ctx for signal handlers. - */ -int eloop_register_signal(int sig, - void (*handler)(int sig, void *eloop_ctx, - void *signal_ctx), - void *user_data); - -/** - * eloop_run - Start the event loop - * - * Start the event loop and continue running as long as there are any - * registered event handlers. This function is run after event loop has been - * initialized with event_init() and one or more events have been registered. - */ -void eloop_run(void); - -/** - * eloop_terminate - Terminate event loop - * - * Terminate event loop even if there are registered events. This can be used - * to request the program to be terminated cleanly. - */ -void eloop_terminate(void); - -/** - * eloop_destroy - Free any resources allocated for the event loop - * - * After calling eloop_destroy(), other eloop_* functions must not be called - * before re-running eloop_init(). - */ -void eloop_destroy(void); - -/** - * eloop_terminated - Check whether event loop has been terminated - * Returns: 1 = event loop terminate, 0 = event loop still running - * - * This function can be used to check whether eloop_terminate() has been called - * to request termination of the event loop. This is normally used to abort - * operations that may still be queued to be run when eloop_terminate() was - * called. - */ -int eloop_terminated(void); - -#endif /* ELOOP_H */ diff --git a/contrib/hostapd-0.4.9/hostap_common.h b/contrib/hostapd-0.4.9/hostap_common.h deleted file mode 100644 index 78af287c3c..0000000000 --- a/contrib/hostapd-0.4.9/hostap_common.h +++ /dev/null @@ -1,558 +0,0 @@ -#ifndef HOSTAP_COMMON_H -#define HOSTAP_COMMON_H - -#define BIT(x) (1 << (x)) - -#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] -#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" - - -#ifndef ETH_P_PAE -#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ -#endif /* ETH_P_PAE */ - -#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ - - - -/* IEEE 802.11 defines */ - -#define WLAN_FC_PVER (BIT(1) | BIT(0)) -#define WLAN_FC_TODS BIT(8) -#define WLAN_FC_FROMDS BIT(9) -#define WLAN_FC_MOREFRAG BIT(10) -#define WLAN_FC_RETRY BIT(11) -#define WLAN_FC_PWRMGT BIT(12) -#define WLAN_FC_MOREDATA BIT(13) -#define WLAN_FC_ISWEP BIT(14) -#define WLAN_FC_ORDER BIT(15) - -#define WLAN_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2) -#define WLAN_FC_GET_STYPE(fc) \ - (((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4) - -#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) -#define WLAN_GET_SEQ_SEQ(seq) \ - (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) - -#define WLAN_FC_TYPE_MGMT 0 -#define WLAN_FC_TYPE_CTRL 1 -#define WLAN_FC_TYPE_DATA 2 - -/* management */ -#define WLAN_FC_STYPE_ASSOC_REQ 0 -#define WLAN_FC_STYPE_ASSOC_RESP 1 -#define WLAN_FC_STYPE_REASSOC_REQ 2 -#define WLAN_FC_STYPE_REASSOC_RESP 3 -#define WLAN_FC_STYPE_PROBE_REQ 4 -#define WLAN_FC_STYPE_PROBE_RESP 5 -#define WLAN_FC_STYPE_BEACON 8 -#define WLAN_FC_STYPE_ATIM 9 -#define WLAN_FC_STYPE_DISASSOC 10 -#define WLAN_FC_STYPE_AUTH 11 -#define WLAN_FC_STYPE_DEAUTH 12 - -/* control */ -#define WLAN_FC_STYPE_PSPOLL 10 -#define WLAN_FC_STYPE_RTS 11 -#define WLAN_FC_STYPE_CTS 12 -#define WLAN_FC_STYPE_ACK 13 -#define WLAN_FC_STYPE_CFEND 14 -#define WLAN_FC_STYPE_CFENDACK 15 - -/* data */ -#define WLAN_FC_STYPE_DATA 0 -#define WLAN_FC_STYPE_DATA_CFACK 1 -#define WLAN_FC_STYPE_DATA_CFPOLL 2 -#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 -#define WLAN_FC_STYPE_NULLFUNC 4 -#define WLAN_FC_STYPE_CFACK 5 -#define WLAN_FC_STYPE_CFPOLL 6 -#define WLAN_FC_STYPE_CFACKPOLL 7 - -/* Authentication algorithms */ -#define WLAN_AUTH_OPEN 0 -#define WLAN_AUTH_SHARED_KEY 1 - -#define WLAN_AUTH_CHALLENGE_LEN 128 - -#define WLAN_CAPABILITY_ESS BIT(0) -#define WLAN_CAPABILITY_IBSS BIT(1) -#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) -#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) -#define WLAN_CAPABILITY_PRIVACY BIT(4) - -/* Status codes */ -#define WLAN_STATUS_SUCCESS 0 -#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 -#define WLAN_STATUS_CAPS_UNSUPPORTED 10 -#define WLAN_STATUS_REASSOC_NO_ASSOC 11 -#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 -#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 -#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 -#define WLAN_STATUS_CHALLENGE_FAIL 15 -#define WLAN_STATUS_AUTH_TIMEOUT 16 -#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 -#define WLAN_STATUS_ASSOC_DENIED_RATES 18 -/* 802.11b */ -#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 -#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 -#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 -/* IEEE 802.11i */ -#define WLAN_STATUS_INVALID_IE 40 -#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 -#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 -#define WLAN_STATUS_AKMP_NOT_VALID 43 -#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 -#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 -#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 - -/* Reason codes */ -#define WLAN_REASON_UNSPECIFIED 1 -#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 -#define WLAN_REASON_DEAUTH_LEAVING 3 -#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 -#define WLAN_REASON_DISASSOC_AP_BUSY 5 -#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 -#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 -#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 -#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 -/* IEEE 802.11i */ -#define WLAN_REASON_INVALID_IE 13 -#define WLAN_REASON_MICHAEL_MIC_FAILURE 14 -#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 -#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 -#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 -#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 -#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 -#define WLAN_REASON_AKMP_NOT_VALID 20 -#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 -#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 -#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 -#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 - - -/* Information Element IDs */ -#define WLAN_EID_SSID 0 -#define WLAN_EID_SUPP_RATES 1 -#define WLAN_EID_FH_PARAMS 2 -#define WLAN_EID_DS_PARAMS 3 -#define WLAN_EID_CF_PARAMS 4 -#define WLAN_EID_TIM 5 -#define WLAN_EID_IBSS_PARAMS 6 -#define WLAN_EID_CHALLENGE 16 -#define WLAN_EID_RSN 48 -#define WLAN_EID_GENERIC 221 - - -/* HFA384X Configuration RIDs */ -#define HFA384X_RID_CNFPORTTYPE 0xFC00 -#define HFA384X_RID_CNFOWNMACADDR 0xFC01 -#define HFA384X_RID_CNFDESIREDSSID 0xFC02 -#define HFA384X_RID_CNFOWNCHANNEL 0xFC03 -#define HFA384X_RID_CNFOWNSSID 0xFC04 -#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05 -#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06 -#define HFA384X_RID_CNFMAXDATALEN 0xFC07 -#define HFA384X_RID_CNFWDSADDRESS 0xFC08 -#define HFA384X_RID_CNFPMENABLED 0xFC09 -#define HFA384X_RID_CNFPMEPS 0xFC0A -#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B -#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C -#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D -#define HFA384X_RID_CNFOWNNAME 0xFC0E -#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10 -#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */ -#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */ -#define HFA384X_RID_UNKNOWN1 0xFC20 -#define HFA384X_RID_UNKNOWN2 0xFC21 -#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23 -#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24 -#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25 -#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26 -#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27 -#define HFA384X_RID_CNFWEPFLAGS 0xFC28 -#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 -#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A -#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */ -#define HFA384X_RID_CNFTXCONTROL 0xFC2C -#define HFA384X_RID_CNFROAMINGMODE 0xFC2D -#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */ -#define HFA384X_RID_CNFRCVCRCERROR 0xFC30 -#define HFA384X_RID_CNFMMLIFE 0xFC31 -#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32 -#define HFA384X_RID_CNFBEACONINT 0xFC33 -#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */ -#define HFA384X_RID_CNFSTAPCFINFO 0xFC35 -#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37 -#define HFA384X_RID_CNFTIMCTRL 0xFC40 -#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */ -#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */ -#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */ -#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0; - * write only */ -#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */ -#define HFA384X_RID_GROUPADDRESSES 0xFC80 -#define HFA384X_RID_CREATEIBSS 0xFC81 -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82 -#define HFA384X_RID_RTSTHRESHOLD 0xFC83 -#define HFA384X_RID_TXRATECONTROL 0xFC84 -#define HFA384X_RID_PROMISCUOUSMODE 0xFC85 -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */ -#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0 -#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 -#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 -#define HFA384X_RID_CNFBASICRATES 0xFCB3 -#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4 -#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */ -#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */ -#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */ -#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */ -#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */ -#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */ -#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */ -#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */ -#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */ -#define HFA384X_RID_TICKTIME 0xFCE0 -#define HFA384X_RID_SCANREQUEST 0xFCE1 -#define HFA384X_RID_JOINREQUEST 0xFCE2 -#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */ -#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */ -#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */ - -/* HFA384X Information RIDs */ -#define HFA384X_RID_MAXLOADTIME 0xFD00 -#define HFA384X_RID_DOWNLOADBUFFER 0xFD01 -#define HFA384X_RID_PRIID 0xFD02 -#define HFA384X_RID_PRISUPRANGE 0xFD03 -#define HFA384X_RID_CFIACTRANGES 0xFD04 -#define HFA384X_RID_NICSERNUM 0xFD0A -#define HFA384X_RID_NICID 0xFD0B -#define HFA384X_RID_MFISUPRANGE 0xFD0C -#define HFA384X_RID_CFISUPRANGE 0xFD0D -#define HFA384X_RID_CHANNELLIST 0xFD10 -#define HFA384X_RID_REGULATORYDOMAINS 0xFD11 -#define HFA384X_RID_TEMPTYPE 0xFD12 -#define HFA384X_RID_CIS 0xFD13 -#define HFA384X_RID_STAID 0xFD20 -#define HFA384X_RID_STASUPRANGE 0xFD21 -#define HFA384X_RID_MFIACTRANGES 0xFD22 -#define HFA384X_RID_CFIACTRANGES2 0xFD23 -#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1; - * only Prism2.5(?) */ -#define HFA384X_RID_PORTSTATUS 0xFD40 -#define HFA384X_RID_CURRENTSSID 0xFD41 -#define HFA384X_RID_CURRENTBSSID 0xFD42 -#define HFA384X_RID_COMMSQUALITY 0xFD43 -#define HFA384X_RID_CURRENTTXRATE 0xFD44 -#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45 -#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46 -#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47 -#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48 -#define HFA384X_RID_LONGRETRYLIMIT 0xFD49 -#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A -#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B -#define HFA384X_RID_CFPOLLABLE 0xFD4C -#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D -#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F -#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */ -#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */ -#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */ -#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */ -#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */ -#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */ -#define HFA384X_RID_PHYTYPE 0xFDC0 -#define HFA384X_RID_CURRENTCHANNEL 0xFDC1 -#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2 -#define HFA384X_RID_CCAMODE 0xFDC3 -#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6 -#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */ -#define HFA384X_RID_BUILDSEQ 0xFFFE -#define HFA384X_RID_FWID 0xFFFF - - -struct hfa384x_comp_ident -{ - u16 id; - u16 variant; - u16 major; - u16 minor; -} __attribute__ ((packed)); - -#define HFA384X_COMP_ID_PRI 0x15 -#define HFA384X_COMP_ID_STA 0x1f -#define HFA384X_COMP_ID_FW_AP 0x14b - -struct hfa384x_sup_range -{ - u16 role; - u16 id; - u16 variant; - u16 bottom; - u16 top; -} __attribute__ ((packed)); - - -struct hfa384x_build_id -{ - u16 pri_seq; - u16 sec_seq; -} __attribute__ ((packed)); - -/* FD01 - Download Buffer */ -struct hfa384x_rid_download_buffer -{ - u16 page; - u16 offset; - u16 length; -} __attribute__ ((packed)); - -/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */ -struct hfa384x_comms_quality { - u16 comm_qual; /* 0 .. 92 */ - u16 signal_level; /* 27 .. 154 */ - u16 noise_level; /* 27 .. 154 */ -} __attribute__ ((packed)); - - -/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ - -/* New wireless extensions API - SET/GET convention (even ioctl numbers are - * root only) - */ -#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) -#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) -#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) -#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) -#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) -#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) -#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) -#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) -#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) -#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) -#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) -#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) -#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) -#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) - -/* following are not in SIOCGIWPRIV list; check permission in the driver code - */ -#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) -#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) - - -/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ -enum { - /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ - PRISM2_PARAM_TXRATECTRL = 2, - PRISM2_PARAM_BEACON_INT = 3, - PRISM2_PARAM_PSEUDO_IBSS = 4, - PRISM2_PARAM_ALC = 5, - /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ - PRISM2_PARAM_DUMP = 7, - PRISM2_PARAM_OTHER_AP_POLICY = 8, - PRISM2_PARAM_AP_MAX_INACTIVITY = 9, - PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, - PRISM2_PARAM_DTIM_PERIOD = 11, - PRISM2_PARAM_AP_NULLFUNC_ACK = 12, - PRISM2_PARAM_MAX_WDS = 13, - PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, - PRISM2_PARAM_AP_AUTH_ALGS = 15, - PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, - PRISM2_PARAM_HOST_ENCRYPT = 17, - PRISM2_PARAM_HOST_DECRYPT = 18, - PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, - PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, - PRISM2_PARAM_HOST_ROAMING = 21, - PRISM2_PARAM_BCRX_STA_KEY = 22, - PRISM2_PARAM_IEEE_802_1X = 23, - PRISM2_PARAM_ANTSEL_TX = 24, - PRISM2_PARAM_ANTSEL_RX = 25, - PRISM2_PARAM_MONITOR_TYPE = 26, - PRISM2_PARAM_WDS_TYPE = 27, - PRISM2_PARAM_HOSTSCAN = 28, - PRISM2_PARAM_AP_SCAN = 29, - PRISM2_PARAM_ENH_SEC = 30, - PRISM2_PARAM_IO_DEBUG = 31, - PRISM2_PARAM_BASIC_RATES = 32, - PRISM2_PARAM_OPER_RATES = 33, - PRISM2_PARAM_HOSTAPD = 34, - PRISM2_PARAM_HOSTAPD_STA = 35, - PRISM2_PARAM_WPA = 36, - PRISM2_PARAM_PRIVACY_INVOKED = 37, - PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, - PRISM2_PARAM_DROP_UNENCRYPTED = 39, - PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, -}; - -enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, - HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; - - -/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ -enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, - AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, - AP_MAC_CMD_KICKALL = 4 }; - - -/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ -enum { - PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, - /* Note! Old versions of prism2_srec have a fatal error in CRC-16 - * calculation, which will corrupt all non-volatile downloads. - * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to - * prevent use of old versions of prism2_srec for non-volatile - * download. */ - PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, - PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, - /* Persistent versions of volatile download commands (keep firmware - * data in memory and automatically re-download after hw_reset */ - PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, - PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, -}; - -struct prism2_download_param { - u32 dl_cmd; - u32 start_addr; - u32 num_areas; - struct prism2_download_area { - u32 addr; /* wlan card address */ - u32 len; - caddr_t ptr; /* pointer to data in user space */ - } data[0]; -}; - -#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 -#define PRISM2_MAX_DOWNLOAD_LEN 262144 - - -/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ -enum { - PRISM2_HOSTAPD_FLUSH = 1, - PRISM2_HOSTAPD_ADD_STA = 2, - PRISM2_HOSTAPD_REMOVE_STA = 3, - PRISM2_HOSTAPD_GET_INFO_STA = 4, - /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ - PRISM2_SET_ENCRYPTION = 6, - PRISM2_GET_ENCRYPTION = 7, - PRISM2_HOSTAPD_SET_FLAGS_STA = 8, - PRISM2_HOSTAPD_GET_RID = 9, - PRISM2_HOSTAPD_SET_RID = 10, - PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, - PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, - PRISM2_HOSTAPD_MLME = 13, - PRISM2_HOSTAPD_SCAN_REQ = 14, - PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, -}; - -#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 -#define PRISM2_HOSTAPD_RID_HDR_LEN \ -((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) -#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ -((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) - -/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() - */ -#define HOSTAP_CRYPT_ALG_NAME_LEN 16 - - -struct prism2_hostapd_param { - u32 cmd; - u8 sta_addr[ETH_ALEN]; - union { - struct { - u16 aid; - u16 capability; - u8 tx_supp_rates; - } add_sta; - struct { - u32 inactive_sec; - } get_info_sta; - struct { - u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; - u32 flags; - u32 err; - u8 idx; - u8 seq[8]; /* sequence counter (set: RX, get: TX) */ - u16 key_len; - u8 key[0]; - } crypt; - struct { - u32 flags_and; - u32 flags_or; - } set_flags_sta; - struct { - u16 rid; - u16 len; - u8 data[0]; - } rid; - struct { - u8 len; - u8 data[0]; - } generic_elem; - struct { -#define MLME_STA_DEAUTH 0 -#define MLME_STA_DISASSOC 1 - u16 cmd; - u16 reason_code; - } mlme; - struct { - u8 ssid_len; - u8 ssid[32]; - } scan_req; - } u; -}; - -#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) -#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) - -#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 -#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 -#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 -#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 -#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 -#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 - - -#endif /* HOSTAP_COMMON_H */ diff --git a/contrib/hostapd-0.4.9/hostapd.8 b/contrib/hostapd-0.4.9/hostapd.8 deleted file mode 100644 index 8b02497803..0000000000 --- a/contrib/hostapd-0.4.9/hostapd.8 +++ /dev/null @@ -1,56 +0,0 @@ -.TH HOSTAPD 8 "April 7, 2005" hostapd hostapd -.SH NAME -hostapd \- IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator -.SH SYNOPSIS -.B hostapd -[-hdBKt] -.SH DESCRIPTION -This manual page documents briefly the -.B hostapd -daemon. -.PP -.B hostapd -is a user space daemon for access point and authentication servers. -It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server. -The current version supports Linux (Host AP, madwifi, Prism54 drivers) and FreeBSD (net80211). - -.B hostapd -is designed to be a "daemon" program that runs in the background and acts as the backend component controlling authentication. -.B hostapd -supports separate frontend programs and an example text-based frontend, -.BR hostapd_cli , -is included with -.BR hostapd . -.SH OPTIONS -A summary of options is included below. -For a complete description, run -.BR hostapd -from the command line. -.TP -.B \-h -Show usage. -.TP -.B \-d -Show more debug messages. -.TP -.B \-dd -Show even more debug messages. -.TP -.B \-B -Run daemon in the background. -.TP -.B \-K -Include key data in debug messages. -.TP -.B \-t -Include timestamps in some debug messages. -.TP -.B \-v -Show hostapd version. -.SH SEE ALSO -.BR hostapd_cli (1). -.SH AUTHOR -hostapd was written by Jouni Malinen . -.PP -This manual page was written by Faidon Liambotis , -for the Debian project (but may be used by others). diff --git a/contrib/hostapd-0.4.9/hostapd.accept b/contrib/hostapd-0.4.9/hostapd.accept deleted file mode 100644 index 57122b6634..0000000000 --- a/contrib/hostapd-0.4.9/hostapd.accept +++ /dev/null @@ -1,5 +0,0 @@ -# List of MAC addresses that are allowed to authenticate (IEEE 802.11) -# with the AP. -00:11:22:33:44:55 -00:66:77:88:99:aa -00:00:22:33:44:55 diff --git a/contrib/hostapd-0.4.9/hostapd.c b/contrib/hostapd-0.4.9/hostapd.c deleted file mode 100644 index 086af6291b..0000000000 --- a/contrib/hostapd-0.4.9/hostapd.c +++ /dev/null @@ -1,846 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "eloop.h" -#include "hostapd.h" -#include "ieee802_1x.h" -#include "ieee802_11.h" -#include "accounting.h" -#include "eapol_sm.h" -#include "iapp.h" -#include "ap.h" -#include "ieee802_11_auth.h" -#include "sta_info.h" -#include "driver.h" -#include "radius_client.h" -#include "radius_server.h" -#include "wpa.h" -#include "ctrl_iface.h" -#include "tls.h" -#include "eap_sim_db.h" -#include "version.h" -#include "hostap_common.h" - - -struct hapd_interfaces { - int count; - hostapd **hapd; -}; - -unsigned char rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; - - -extern int wpa_debug_level; -extern int wpa_debug_show_keys; -extern int wpa_debug_timestamp; - - -void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, - unsigned int module, int level, const char *fmt, ...) -{ - char *format, *module_str; - int maxlen; - va_list ap; - int conf_syslog_level, conf_stdout_level; - unsigned int conf_syslog, conf_stdout; - - maxlen = strlen(fmt) + 100; - format = malloc(maxlen); - if (!format) - return; - - if (hapd && hapd->conf) { - conf_syslog_level = hapd->conf->logger_syslog_level; - conf_stdout_level = hapd->conf->logger_stdout_level; - conf_syslog = hapd->conf->logger_syslog; - conf_stdout = hapd->conf->logger_stdout; - } else { - conf_syslog_level = conf_stdout_level = 0; - conf_syslog = conf_stdout = (unsigned int) -1; - } - - switch (module) { - case HOSTAPD_MODULE_IEEE80211: - module_str = "IEEE 802.11"; - break; - case HOSTAPD_MODULE_IEEE8021X: - module_str = "IEEE 802.1X"; - break; - case HOSTAPD_MODULE_RADIUS: - module_str = "RADIUS"; - break; - case HOSTAPD_MODULE_WPA: - module_str = "WPA"; - break; - case HOSTAPD_MODULE_DRIVER: - module_str = "DRIVER"; - break; - case HOSTAPD_MODULE_IAPP: - module_str = "IAPP"; - break; - default: - module_str = NULL; - break; - } - - if (hapd && hapd->conf && addr) - snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s", - hapd->conf->iface, MAC2STR(addr), - module_str ? " " : "", module_str, fmt); - else if (hapd && hapd->conf) - snprintf(format, maxlen, "%s:%s%s %s", - hapd->conf->iface, module_str ? " " : "", - module_str, fmt); - else if (addr) - snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", - MAC2STR(addr), module_str ? " " : "", - module_str, fmt); - else - snprintf(format, maxlen, "%s%s%s", - module_str, module_str ? ": " : "", fmt); - - if ((conf_stdout & module) && level >= conf_stdout_level) { - wpa_debug_print_timestamp(); - va_start(ap, fmt); - vprintf(format, ap); - va_end(ap); - printf("\n"); - } - - if ((conf_syslog & module) && level >= conf_syslog_level) { - int priority; - switch (level) { - case HOSTAPD_LEVEL_DEBUG_VERBOSE: - case HOSTAPD_LEVEL_DEBUG: - priority = LOG_DEBUG; - break; - case HOSTAPD_LEVEL_INFO: - priority = LOG_INFO; - break; - case HOSTAPD_LEVEL_NOTICE: - priority = LOG_NOTICE; - break; - case HOSTAPD_LEVEL_WARNING: - priority = LOG_WARNING; - break; - default: - priority = LOG_INFO; - break; - } - va_start(ap, fmt); - vsyslog(priority, format, ap); - va_end(ap); - } - - free(format); -} - - -const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, - size_t buflen) -{ - if (buflen == 0 || addr == NULL) - return NULL; - - if (addr->af == AF_INET) { - snprintf(buf, buflen, "%s", inet_ntoa(addr->u.v4)); - } else { - buf[0] = '\0'; - } -#ifdef CONFIG_IPV6 - if (addr->af == AF_INET6) { - if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL) - buf[0] = '\0'; - } -#endif /* CONFIG_IPV6 */ - - return buf; -} - - -static void hostapd_deauth_all_stas(hostapd *hapd) -{ -#if 0 - u8 addr[ETH_ALEN]; - - memset(addr, 0xff, ETH_ALEN); - hostapd_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); -#else - /* New Prism2.5/3 STA firmware versions seem to have issues with this - * broadcast deauth frame. This gets the firmware in odd state where - * nothing works correctly, so let's skip sending this for a while - * until the issue has been resolved. */ -#endif -} - - -/* This function will be called whenever a station associates with the AP */ -void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta, int reassoc) -{ - if (hapd->tkip_countermeasures) { - hostapd_sta_deauth(hapd, sta->addr, - WLAN_REASON_MICHAEL_MIC_FAILURE); - return; - } - - /* IEEE 802.11F (IAPP) */ - if (hapd->conf->ieee802_11f) - iapp_new_station(hapd->iapp, sta); - - /* Start accounting here, if IEEE 802.1X and WPA are not used. - * IEEE 802.1X/WPA code will start accounting after the station has - * been authorized. */ - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) - accounting_sta_start(hapd, sta); - - /* Start IEEE 802.1X authentication process for new stations */ - ieee802_1x_new_station(hapd, sta); - if (reassoc) - wpa_sm_event(hapd, sta, WPA_REAUTH); - else - wpa_new_station(hapd, sta); -} - - -static void handle_term(int sig, void *eloop_ctx, void *signal_ctx) -{ - printf("Signal %d received - terminating\n", sig); - eloop_terminate(); -} - - -static void handle_reload(int sig, void *eloop_ctx, void *signal_ctx) -{ - struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; - struct hostapd_config *newconf; - int i; - - printf("Signal %d received - reloading configuration\n", sig); - - for (i = 0; i < hapds->count; i++) { - hostapd *hapd = hapds->hapd[i]; - newconf = hostapd_config_read(hapd->config_fname); - if (newconf == NULL) { - printf("Failed to read new configuration file - " - "continuing with old.\n"); - continue; - } - /* TODO: update dynamic data based on changed configuration - * items (e.g., open/close sockets, remove stations added to - * deny list, etc.) */ - radius_client_flush(hapd->radius); - hostapd_config_free(hapd->conf); - hapd->conf = newconf; - } -} - - -#ifdef HOSTAPD_DUMP_STATE -static void hostapd_dump_state(hostapd *hapd) -{ - FILE *f; - time_t now; - struct sta_info *sta; - int i; - char *buf; - - if (!hapd->conf->dump_log_name) { - printf("Dump file not defined - ignoring dump request\n"); - return; - } - - printf("Dumping hostapd state to '%s'\n", hapd->conf->dump_log_name); - f = fopen(hapd->conf->dump_log_name, "w"); - if (f == NULL) { - printf("Could not open dump file '%s' for writing.\n", - hapd->conf->dump_log_name); - return; - } - - time(&now); - fprintf(f, "hostapd state dump - %s", ctime(&now)); - - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr)); - - fprintf(f, - " AID=%d flags=0x%x %s%s%s%s%s%s\n" - " capability=0x%x listen_interval=%d\n", - sta->aid, - sta->flags, - (sta->flags & WLAN_STA_AUTH ? "[AUTH]" : ""), - (sta->flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), - (sta->flags & WLAN_STA_PS ? "[PS]" : ""), - (sta->flags & WLAN_STA_TIM ? "[TIM]" : ""), - (sta->flags & WLAN_STA_PERM ? "[PERM]" : ""), - (sta->flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : - ""), - sta->capability, - sta->listen_interval); - - fprintf(f, " supported_rates="); - for (i = 0; i < sizeof(sta->supported_rates); i++) - if (sta->supported_rates[i] != 0) - fprintf(f, "%02x ", sta->supported_rates[i]); - fprintf(f, "%s%s%s%s\n", - (sta->tx_supp_rates & WLAN_RATE_1M ? "[1M]" : ""), - (sta->tx_supp_rates & WLAN_RATE_2M ? "[2M]" : ""), - (sta->tx_supp_rates & WLAN_RATE_5M5 ? "[5.5M]" : ""), - (sta->tx_supp_rates & WLAN_RATE_11M ? "[11M]" : "")); - - fprintf(f, - " timeout_next=%s\n", - (sta->timeout_next == STA_NULLFUNC ? "NULLFUNC POLL" : - (sta->timeout_next == STA_DISASSOC ? "DISASSOC" : - "DEAUTH"))); - - ieee802_1x_dump_state(f, " ", sta); - } - - buf = malloc(4096); - if (buf) { - int count = radius_client_get_mib(hapd->radius, buf, 4096); - if (count < 0) - count = 0; - else if (count > 4095) - count = 4095; - buf[count] = '\0'; - fprintf(f, "%s", buf); - - count = radius_server_get_mib(hapd->radius_srv, buf, 4096); - if (count < 0) - count = 0; - else if (count > 4095) - count = 4095; - buf[count] = '\0'; - fprintf(f, "%s", buf); - free(buf); - } - fclose(f); -} -#endif /* HOSTAPD_DUMP_STATE */ - - -static void handle_dump_state(int sig, void *eloop_ctx, void *signal_ctx) -{ -#ifdef HOSTAPD_DUMP_STATE - struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; - int i; - - for (i = 0; i < hapds->count; i++) { - hostapd *hapd = hapds->hapd[i]; - hostapd_dump_state(hapd); - } -#endif /* HOSTAPD_DUMP_STATE */ -} - - -static void hostapd_cleanup(struct hostapd_data *hapd) -{ - hostapd_ctrl_iface_deinit(hapd); - - free(hapd->default_wep_key); - hapd->default_wep_key = NULL; - iapp_deinit(hapd->iapp); - accounting_deinit(hapd); - wpa_deinit(hapd); - ieee802_1x_deinit(hapd); - hostapd_acl_deinit(hapd); - radius_client_deinit(hapd->radius); - hapd->radius = NULL; - radius_server_deinit(hapd->radius_srv); - hapd->radius_srv = NULL; - - hostapd_wireless_event_deinit(hapd); - - if (hapd->driver) - hostapd_driver_deinit(hapd); - - hostapd_config_free(hapd->conf); - hapd->conf = NULL; - - free(hapd->config_fname); - -#ifdef EAP_TLS_FUNCS - if (hapd->ssl_ctx) { - tls_deinit(hapd->ssl_ctx); - hapd->ssl_ctx = NULL; - } -#endif /* EAP_TLS_FUNCS */ - - if (hapd->eap_sim_db_priv) - eap_sim_db_deinit(hapd->eap_sim_db_priv); -} - - -static int hostapd_flush_old_stations(hostapd *hapd) -{ - int ret = 0; - - printf("Flushing old station entries\n"); - if (hostapd_flush(hapd)) { - printf("Could not connect to kernel driver.\n"); - ret = -1; - } - printf("Deauthenticate all stations\n"); - hostapd_deauth_all_stas(hapd); - - return ret; -} - - -static int hostapd_setup_interface(struct hostapd_data *hapd) -{ - struct hostapd_config *conf = hapd->conf; - u8 ssid[HOSTAPD_SSID_LEN + 1]; - int ssid_len, set_ssid; - int ret = 0; - - if (hostapd_driver_init(hapd)) { - printf("%s driver initialization failed.\n", - hapd->driver ? hapd->driver->name : "Unknown"); - hapd->driver = NULL; - return -1; - } - - /* - * Fetch the SSID from the system and use it or, - * if one was specified in the config file, verify they - * match. - */ - ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid)); - if (ssid_len < 0) { - printf("Could not read SSID from system\n"); - return -1; - } - if (conf->ssid_set) { - /* - * If SSID is specified in the config file and it differs - * from what is being used then force installation of the - * new SSID. - */ - set_ssid = (conf->ssid_len != ssid_len || - memcmp(conf->ssid, ssid, ssid_len) != 0); - } else { - /* - * No SSID in the config file; just use the one we got - * from the system. - */ - set_ssid = 0; - conf->ssid_len = ssid_len; - memcpy(conf->ssid, ssid, conf->ssid_len); - conf->ssid[conf->ssid_len] = '\0'; - } - - printf("Using interface %s with hwaddr " MACSTR " and ssid '%s'\n", - hapd->conf->iface, MAC2STR(hapd->own_addr), hapd->conf->ssid); - - if (hostapd_setup_wpa_psk(conf)) { - printf("WPA-PSK setup failed.\n"); - return -1; - } - - /* Set SSID for the kernel driver (to be used in beacon and probe - * response frames) */ - if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid, - conf->ssid_len)) { - printf("Could not set SSID for kernel driver\n"); - return -1; - } - - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) - conf->radius->msg_dumps = 1; - hapd->radius = radius_client_init(hapd, conf->radius); - if (hapd->radius == NULL) { - printf("RADIUS client initialization failed.\n"); - return -1; - } - if (conf->radius_server_clients) { - struct radius_server_conf srv; - memset(&srv, 0, sizeof(srv)); - srv.client_file = conf->radius_server_clients; - srv.auth_port = conf->radius_server_auth_port; - srv.hostapd_conf = conf; - srv.eap_sim_db_priv = hapd->eap_sim_db_priv; - srv.ssl_ctx = hapd->ssl_ctx; - srv.ipv6 = conf->radius_server_ipv6; - hapd->radius_srv = radius_server_init(&srv); - if (hapd->radius_srv == NULL) { - printf("RADIUS server initialization failed.\n"); - return -1; - } - } - if (hostapd_acl_init(hapd)) { - printf("ACL initialization failed.\n"); - return -1; - } - if (ieee802_1x_init(hapd)) { - printf("IEEE 802.1X initialization failed.\n"); - return -1; - } - - if (hapd->conf->wpa && wpa_init(hapd)) { - printf("WPA initialization failed.\n"); - return -1; - } - - if (accounting_init(hapd)) { - printf("Accounting initialization failed.\n"); - return -1; - } - - if (hapd->conf->ieee802_11f && - (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) { - printf("IEEE 802.11F (IAPP) initialization failed.\n"); - return -1; - } - - if (hostapd_wireless_event_init(hapd) < 0) - return -1; - - if (hostapd_flush_old_stations(hapd)) - return -1; - - if (hostapd_ctrl_iface_init(hapd)) { - printf("Failed to setup control interface\n"); - ret = -1; - } - - return ret; -} - - -struct driver { - struct driver *next; - char *name; - const struct driver_ops *ops; -}; -static struct driver *drivers = NULL; - -void driver_register(const char *name, const struct driver_ops *ops) -{ - struct driver *d; - - d = malloc(sizeof(struct driver)); - if (d == NULL) { - printf("Failed to register driver %s!\n", name); - return; - } - d->name = strdup(name); - if (d->name == NULL) { - printf("Failed to register driver %s!\n", name); - free(d); - return; - } - d->ops = ops; - - d->next = drivers; - drivers = d; -} - - -void driver_unregister(const char *name) -{ - struct driver *p, **pp; - - for (pp = &drivers; (p = *pp) != NULL; pp = &p->next) { - if (strcasecmp(p->name, name) == 0) { - *pp = p->next; - p->next = NULL; - free(p->name); - free(p); - break; - } - } -} - - -static void driver_unregister_all(void) -{ - struct driver *p, *pp; - p = drivers; - drivers = NULL; - while (p) { - pp = p; - p = p->next; - free(pp->name); - free(pp); - } -} - - -const struct driver_ops * driver_lookup(const char *name) -{ - struct driver *p; - - if (strcmp(name, "default") == 0) { - p = drivers; - while (p && p->next) - p = p->next; - return p->ops; - } - - for (p = drivers; p != NULL; p = p->next) { - if (strcasecmp(p->name, name) == 0) - return p->ops; - } - - return NULL; -} - - -static void show_version(void) -{ - fprintf(stderr, - "hostapd v" VERSION_STR "\n" - "User space daemon for IEEE 802.11 AP management,\n" - "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" - "Copyright (c) 2002-2005, Jouni Malinen " - "and contributors\n"); -} - - -static void usage(void) -{ - show_version(); - fprintf(stderr, - "\n" - "usage: hostapd [-hdBKt] \n" - "\n" - "options:\n" - " -h show this usage\n" - " -d show more debug messages (-dd for even more)\n" - " -B run daemon in the background\n" - " -K include key data in debug messages\n" - " -t include timestamps in some debug messages\n" - " -v show hostapd version\n"); - - exit(1); -} - - -static hostapd * hostapd_init(const char *config_file) -{ - hostapd *hapd; - - hapd = malloc(sizeof(*hapd)); - if (hapd == NULL) { - printf("Could not allocate memory for hostapd data\n"); - goto fail; - } - memset(hapd, 0, sizeof(*hapd)); - - hapd->config_fname = strdup(config_file); - if (hapd->config_fname == NULL) { - printf("Could not allocate memory for config_fname\n"); - goto fail; - } - - hapd->conf = hostapd_config_read(hapd->config_fname); - if (hapd->conf == NULL) { - goto fail; - } - - if (hapd->conf->individual_wep_key_len > 0) { - /* use key0 in individual key and key1 in broadcast key */ - hapd->default_wep_key_idx = 1; - } - -#ifdef EAP_TLS_FUNCS - if (hapd->conf->eap_server && - (hapd->conf->ca_cert || hapd->conf->server_cert)) { - hapd->ssl_ctx = tls_init(NULL); - if (hapd->ssl_ctx == NULL) { - printf("Failed to initialize TLS\n"); - goto fail; - } - if (tls_global_ca_cert(hapd->ssl_ctx, hapd->conf->ca_cert)) { - printf("Failed to load CA certificate (%s)\n", - hapd->conf->ca_cert); - goto fail; - } - if (tls_global_client_cert(hapd->ssl_ctx, - hapd->conf->server_cert)) { - printf("Failed to load server certificate (%s)\n", - hapd->conf->server_cert); - goto fail; - } - if (tls_global_private_key(hapd->ssl_ctx, - hapd->conf->private_key, - hapd->conf->private_key_passwd)) { - printf("Failed to load private key (%s)\n", - hapd->conf->private_key); - goto fail; - } - if (tls_global_set_verify(hapd->ssl_ctx, - hapd->conf->check_crl)) { - printf("Failed to enable check_crl\n"); - goto fail; - } - } -#endif /* EAP_TLS_FUNCS */ - - if (hapd->conf->eap_sim_db) { - hapd->eap_sim_db_priv = - eap_sim_db_init(hapd->conf->eap_sim_db); - if (hapd->eap_sim_db_priv == NULL) { - printf("Failed to initialize EAP-SIM database " - "interface\n"); - goto fail; - } - } - - if (hapd->conf->assoc_ap) - hapd->assoc_ap_state = WAIT_BEACON; - - /* FIX: need to fix this const vs. not */ - hapd->driver = (struct driver_ops *) hapd->conf->driver; - - return hapd; - -fail: - if (hapd) { - if (hapd->ssl_ctx) - tls_deinit(hapd->ssl_ctx); - if (hapd->conf) - hostapd_config_free(hapd->conf); - free(hapd->config_fname); - free(hapd); - } - return NULL; -} - - -void register_drivers(void); - -int main(int argc, char *argv[]) -{ - struct hapd_interfaces interfaces; - int ret = 1, i, j; - int c, debug = 0, daemonize = 0; - - for (;;) { - c = getopt(argc, argv, "BdhKtv"); - if (c < 0) - break; - switch (c) { - case 'h': - usage(); - break; - case 'd': - debug++; - break; - case 'B': - daemonize++; - break; - case 'K': - wpa_debug_show_keys++; - break; - case 't': - wpa_debug_timestamp++; - break; - case 'v': - show_version(); - exit(1); - break; - - default: - usage(); - break; - } - } - - if (optind == argc) - usage(); - - register_drivers(); /* NB: generated by Makefile */ - - interfaces.count = argc - optind; - - interfaces.hapd = malloc(interfaces.count * sizeof(hostapd *)); - if (interfaces.hapd == NULL) { - printf("malloc failed\n"); - exit(1); - } - - eloop_init(&interfaces); - eloop_register_signal(SIGHUP, handle_reload, NULL); - eloop_register_signal(SIGINT, handle_term, NULL); - eloop_register_signal(SIGTERM, handle_term, NULL); - eloop_register_signal(SIGUSR1, handle_dump_state, NULL); - - for (i = 0; i < interfaces.count; i++) { - printf("Configuration file: %s\n", argv[optind + i]); - interfaces.hapd[i] = hostapd_init(argv[optind + i]); - if (!interfaces.hapd[i]) - goto out; - for (j = 0; j < debug; j++) { - if (interfaces.hapd[i]->conf->logger_stdout_level > 0) - interfaces.hapd[i]->conf-> - logger_stdout_level--; - interfaces.hapd[i]->conf->debug++; - } - if (hostapd_setup_interface(interfaces.hapd[i])) - goto out; - wpa_debug_level -= interfaces.hapd[0]->conf->debug; - } - - if (daemonize && daemon(0, 0)) { - perror("daemon"); - goto out; - } - - openlog("hostapd", 0, LOG_DAEMON); - - eloop_run(); - - for (i = 0; i < interfaces.count; i++) { - hostapd_free_stas(interfaces.hapd[i]); - hostapd_flush_old_stations(interfaces.hapd[i]); - } - - ret = 0; - - out: - for (i = 0; i < interfaces.count; i++) { - if (!interfaces.hapd[i]) - continue; - - hostapd_cleanup(interfaces.hapd[i]); - free(interfaces.hapd[i]); - } - free(interfaces.hapd); - - eloop_destroy(); - - closelog(); - - driver_unregister_all(); - - return ret; -} diff --git a/contrib/hostapd-0.4.9/hostapd.conf b/contrib/hostapd-0.4.9/hostapd.conf deleted file mode 100644 index 747579f094..0000000000 --- a/contrib/hostapd-0.4.9/hostapd.conf +++ /dev/null @@ -1,341 +0,0 @@ -##### hostapd configuration file ############################################## -# Empty lines and lines starting with # are ignored - -# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for -# management frames); ath0 for madwifi -interface=wlan0 - -# In case of madwifi driver, an additional configuration parameter, bridge, -# must be used to notify hostapd if the interface is included in a bridge. This -# parameter is not used with Host AP driver. -#bridge=br0 - -# Driver interface type (hostap/wired/madwifi/prism54; default: hostap) -# driver=hostap - -# hostapd event logger configuration -# -# Two output method: syslog and stdout (only usable if not forking to -# background). -# -# Module bitfield (ORed bitfield of modules that will be logged; -1 = all -# modules): -# bit 0 (1) = IEEE 802.11 -# bit 1 (2) = IEEE 802.1X -# bit 2 (4) = RADIUS -# bit 3 (8) = WPA -# bit 4 (16) = driver interface -# bit 5 (32) = IAPP -# -# Levels (minimum value for logged events): -# 0 = verbose debugging -# 1 = debugging -# 2 = informational messages -# 3 = notification -# 4 = warning -# -logger_syslog=-1 -logger_syslog_level=2 -logger_stdout=-1 -logger_stdout_level=2 - -# Debugging: 0 = no, 1 = minimal, 2 = verbose, 3 = msg dumps, 4 = excessive -debug=0 - -# Dump file for state information (on SIGUSR1) -dump_file=/tmp/hostapd.dump - -# Interface for separate control program. If this is specified, hostapd -# will create this directory and a UNIX domain socket for listening to requests -# from external programs (CLI/GUI, etc.) for status information and -# configuration. The socket file will be named based on the interface name, so -# multiple hostapd processes/interfaces can be run at the same time if more -# than one interface is used. -# /var/run/hostapd is the recommended directory for sockets and by default, -# hostapd_cli will use it when trying to connect with hostapd. -ctrl_interface=/var/run/hostapd - -# Access control for the control interface can be configured by setting the -# directory to allow only members of a group to use sockets. This way, it is -# possible to run hostapd as root (since it needs to change network -# configuration and open raw sockets) and still allow GUI/CLI components to be -# run as non-root users. However, since the control interface can be used to -# change the network configuration, this access needs to be protected in many -# cases. By default, hostapd is configured to use gid 0 (root). If you -# want to allow non-root users to use the contron interface, add a new group -# and change this value to match with that group. Add users that should have -# control interface access to this group. -# -# This variable can be a group name or gid. -#ctrl_interface_group=wheel -ctrl_interface_group=0 - - -##### IEEE 802.11 related configuration ####################################### - -# SSID to be used in IEEE 802.11 management frames -ssid=test - -# Station MAC address -based authentication -# 0 = accept unless in deny list -# 1 = deny unless in accept list -# 2 = use external RADIUS server (accept/deny lists are searched first) -macaddr_acl=0 - -# Accept/deny lists are read from separate files (containing list of -# MAC addresses, one per line). Use absolute path name to make sure that the -# files can be read on SIGHUP configuration reloads. -#accept_mac_file=/etc/hostapd.accept -#deny_mac_file=/etc/hostapd.deny - -# IEEE 802.11 specifies two authentication algorithms. hostapd can be -# configured to allow both of these or only one. Open system authentication -# should be used with IEEE 802.1X. -# Bit fields of allowed authentication algorithms: -# bit 0 = Open System Authentication -# bit 1 = Shared Key Authentication (requires WEP) -auth_algs=3 - -# Associate as a station to another AP while still acting as an AP on the same -# channel. -#assoc_ap_addr=00:12:34:56:78:9a - - -##### IEEE 802.1X-2004 related configuration ################################## - -# Require IEEE 802.1X authorization -#ieee8021x=1 - -# IEEE 802.1X/EAPOL version -# hostapd is implemented based on IEEE Std 802.1X-2004 which defines EAPOL -# version 2. However, there are many client implementations that do not handle -# the new version number correctly (they seem to drop the frames completely). -# In order to make hostapd interoperate with these clients, the version number -# can be set to the older version (1) with this configuration value. -#eapol_version=2 - -# Optional displayable message sent with EAP Request-Identity. The first \0 -# in this string will be converted to ASCII-0 (nul). This can be used to -# separate network info (comma separated list of attribute=value pairs); see, -# e.g., draft-adrangi-eap-network-discovery-07.txt. -#eap_message=hello -#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com - -# WEP rekeying (disabled if key lengths are not set or are set to 0) -# Key lengths for default/broadcast and individual/unicast keys: -# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits) -# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits) -#wep_key_len_broadcast=5 -#wep_key_len_unicast=5 -# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once) -#wep_rekey_period=300 - -# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if -# only broadcast keys are used) -eapol_key_index_workaround=0 - -# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable -# reauthentication). -#eap_reauth_period=3600 - -# Use PAE group address (01:80:c2:00:00:03) instead of individual target -# address when sending EAPOL frames with driver=wired. This is the most common -# mechanism used in wired authentication, but it also requires that the port -# is only used by one station. -#use_pae_group_addr=1 - -##### Integrated EAP server ################################################### - -# Optionally, hostapd can be configured to use an integrated EAP server -# to process EAP authentication locally without need for an external RADIUS -# server. This functionality can be used both as a local authentication server -# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices. - -# Use integrated EAP server instead of external RADIUS authentication -# server. This is also needed if hostapd is configured to act as a RADIUS -# authentication server. -eap_server=0 - -# Path for EAP server user database -#eap_user_file=/etc/hostapd.eap_user - -# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS -#ca_cert=/etc/hostapd.ca.pem - -# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS -#server_cert=/etc/hostapd.server.pem - -# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS -# This may point to the same file as server_cert if both certificate and key -# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be -# used by commenting out server_cert and specifying the PFX file as the -# private_key. -#private_key=/etc/hostapd.server.prv - -# Passphrase for private key -#private_key_passwd=secret passphrase - -# Enable CRL verification. -# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a -# valid CRL signed by the CA is required to be included in the ca_cert file. -# This can be done by using PEM format for CA certificate and CRL and -# concatenating these into one file. Whenever CRL changes, hostapd needs to be -# restarted to take the new CRL into use. -# 0 = do not verify CRLs (default) -# 1 = check the CRL of the user certificate -# 2 = check all CRLs in the certificate path -#check_crl=1 - -# Configuration data for EAP-SIM database/authentication gateway interface. -# This is a text string in implementation specific format. The example -# implementation in eap_sim_db.c uses this as the file name for the GSM -# authentication triplets. -#eap_sim_db=/etc/hostapd.sim_db - - -##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### - -# Interface to be used for IAPP broadcast packets -#iapp_interface=eth0 - - -##### RADIUS client configuration ############################################# -# for IEEE 802.1X with external Authentication Server, IEEE 802.11 -# authentication with external ACL for MAC addresses, and accounting - -# The own IP address of the access point (used as NAS-IP-Address) -own_ip_addr=127.0.0.1 - -# Optional NAS-Identifier string for RADIUS messages. When used, this should be -# a unique to the NAS within the scope of the RADIUS server. For example, a -# fully qualified domain name can be used here. -#nas_identifier=ap.example.com - -# RADIUS authentication server -#auth_server_addr=127.0.0.1 -#auth_server_port=1812 -#auth_server_shared_secret=secret - -# RADIUS accounting server -#acct_server_addr=127.0.0.1 -#acct_server_port=1813 -#acct_server_shared_secret=secret - -# Secondary RADIUS servers; to be used if primary one does not reply to -# RADIUS packets. These are optional and there can be more than one secondary -# server listed. -#auth_server_addr=127.0.0.2 -#auth_server_port=1812 -#auth_server_shared_secret=secret2 -# -#acct_server_addr=127.0.0.2 -#acct_server_port=1813 -#acct_server_shared_secret=secret2 - -# Retry interval for trying to return to the primary RADIUS server (in -# seconds). RADIUS client code will automatically try to use the next server -# when the current server is not replying to requests. If this interval is set, -# primary server will be retried after configured amount of time even if the -# currently used secondary server is still working. -#radius_retry_primary_interval=600 - - -# Interim accounting update interval -# If this is set (larger than 0) and acct_server is configured, hostapd will -# send interim accounting updates every N seconds. Note: if set, this overrides -# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this -# value should not be configured in hostapd.conf, if RADIUS server is used to -# control the interim interval. -# This value should not be less 600 (10 minutes) and must not be less than -# 60 (1 minute). -#radius_acct_interim_interval=600 - - -##### RADIUS authentication server configuration ############################## - -# hostapd can be used as a RADIUS authentication server for other hosts. This -# requires that the integrated EAP authenticator is also enabled and both -# authentication services are sharing the same configuration. - -# File name of the RADIUS clients configuration for the RADIUS server. If this -# commented out, RADIUS server is disabled. -#radius_server_clients=/etc/hostapd.radius_clients - -# The UDP port number for the RADIUS authentication server -#radius_server_auth_port=1812 - -# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API) -#radius_server_ipv6=1 - - -##### WPA/IEEE 802.11i configuration ########################################## - -# Enable WPA. Setting this variable configures the AP to require WPA (either -# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either -# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. -# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), -# RADIUS authentication server must be configured, and WPA-EAP must be included -# in wpa_key_mgmt. -# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) -# and/or WPA2 (full IEEE 802.11i/RSN): -# bit0 = WPA -# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) -#wpa=1 - -# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit -# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase -# (8..63 characters) that will be converted to PSK. This conversion uses SSID -# so the PSK changes when ASCII passphrase is used and the SSID is changed. -# wpa_psk (dot11RSNAConfigPSKValue) -# wpa_passphrase (dot11RSNAConfigPSKPassPhrase) -#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef -#wpa_passphrase=secret passphrase - -# Optionally, WPA PSKs can be read from a separate text file (containing list -# of (PSK,MAC address) pairs. This allows more than one PSK to be configured. -# Use absolute path name to make sure that the files can be read on SIGHUP -# configuration reloads. -#wpa_psk_file=/etc/hostapd.wpa_psk - -# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The -# entries are separated with a space. -# (dot11RSNAConfigAuthenticationSuitesTable) -#wpa_key_mgmt=WPA-PSK WPA-EAP - -# Set of accepted cipher suites (encryption algorithms) for pairwise keys -# (unicast packets). This is a space separated list of algorithms: -# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] -# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] -# Group cipher suite (encryption algorithm for broadcast and multicast frames) -# is automatically selected based on this configuration. If only CCMP is -# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, -# TKIP will be used as the group cipher. -# (dot11RSNAConfigPairwiseCiphersTable) -#wpa_pairwise=TKIP CCMP - -# Time interval for rekeying GTK (broadcast/multicast encryption keys) in -# seconds. (dot11RSNAConfigGroupRekeyTime) -#wpa_group_rekey=600 - -# Rekey GTK when any STA that possesses the current GTK is leaving the BSS. -# (dot11RSNAConfigGroupRekeyStrict) -#wpa_strict_rekey=1 - -# Time interval for rekeying GMK (master key used internally to generate GTKs -# (in seconds). -#wpa_gmk_rekey=86400 - -# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up -# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN -# authentication and key handshake before actually associating with a new AP. -# (dot11RSNAPreauthenticationEnabled) -#rsn_preauth=1 -# -# Space separated list of interfaces from which pre-authentication frames are -# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all -# interface that are used for connections to other APs. This could include -# wired interfaces and WDS links. The normal wireless data interface towards -# associated stations (e.g., wlan0) should not be added, since -# pre-authentication is only used with APs other than the currently associated -# one. -#rsn_preauth_interfaces=eth0 diff --git a/contrib/hostapd-0.4.9/hostapd.deny b/contrib/hostapd-0.4.9/hostapd.deny deleted file mode 100644 index 1616678f57..0000000000 --- a/contrib/hostapd-0.4.9/hostapd.deny +++ /dev/null @@ -1,5 +0,0 @@ -# List of MAC addresses that are not allowed to authenticate (IEEE 802.11) -# with the AP. -00:20:30:40:50:60 -00:ab:cd:ef:12:34 -00:00:30:40:50:60 diff --git a/contrib/hostapd-0.4.9/hostapd.eap_user b/contrib/hostapd-0.4.9/hostapd.eap_user deleted file mode 100644 index fd7b420fb6..0000000000 --- a/contrib/hostapd-0.4.9/hostapd.eap_user +++ /dev/null @@ -1,49 +0,0 @@ -# hostapd user database for integrated EAP authenticator -# Each line must contain an identity, EAP method(s), and an optional password -# separated with whitespace (space or tab). The identity and password must be -# double quoted ("user"). [2] flag in the end of the line can be used to mark -# users for tunneled phase 2 authentication (e.g., within EAP-PEAP). In these -# cases, an anonymous identity can be used in the unencrypted phase 1 and the -# real user identity is transmitted only within the encrypted tunnel in phase -# 2. If non-anonymous access is needed, two user entries is needed, one for -# phase 1 and another with the same username for phase 2. -# -# EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-SIM do not use password option. -# EAP-MD5, EAP-MSCHAPV2, EAP-GTC, EAP-PAX, and EAP-PSK require a password. -# EAP-PEAP and EAP-TTLS require Phase 2 configuration. -# -# * can be used as a wildcard to match any user identity. The main purposes for -# this are to set anonymous phase 1 identity for EAP-PEAP and EAP-TTLS and to -# avoid having to configure every certificate for EAP-TLS authentication. The -# first matching entry is selected, so * should be used as the last phase 1 -# user entry. -# -# Multiple methods can be configured to make the authenticator try them one by -# one until the peer accepts one. The method names are separated with a -# comma (,). -# -# [ver=0] and [ver=1] flags after EAP type PEAP can be used to force PEAP -# version based on the Phase 1 identity. Without this flag, the EAP -# authenticator advertises the highest supported version and select the version -# based on the first PEAP packet from the supplicant. - -# Phase 1 users -"user" MD5 "password" -"test user" MD5 "secret" -"example user" TLS -"DOMAIN\user" MSCHAPV2 "password" -"gtc user" GTC "password" -"pax user" PAX "unknown" -"pax.user@example.com" PAX 0123456789abcdef0123456789abcdef -"psk user" PSK "unknown" -"psk.user@example.com" PSK 0123456789abcdef0123456789abcdef -"ttls" TTLS -"not anonymous" PEAP -* PEAP,TTLS,TLS,SIM - -# Phase 2 (tunnelled within EAP-PEAP or EAP-TTLS) users -"t-md5" MD5 "password" [2] -"DOMAIN\t-mschapv2" MSCHAPV2 "password" [2] -"t-gtc" GTC "password" [2] -"not anonymous" MSCHAPV2 "password" [2] -"user" MD5,GTC,MSCHAPV2 "password" [2] diff --git a/contrib/hostapd-0.4.9/hostapd.h b/contrib/hostapd-0.4.9/hostapd.h deleted file mode 100644 index fdcb420ee2..0000000000 --- a/contrib/hostapd-0.4.9/hostapd.h +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef HOSTAPD_H -#define HOSTAPD_H - -#include "common.h" -#include "ap.h" - -#ifndef ETH_ALEN -#define ETH_ALEN 6 -#endif -#ifndef IFNAMSIZ -#define IFNAMSIZ 16 -#endif -#ifndef ETH_P_ALL -#define ETH_P_ALL 0x0003 -#endif -#ifndef ETH_P_PAE -#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ -#endif /* ETH_P_PAE */ - -#ifndef BIT -#define BIT(x) (1 << (x)) -#endif - -#ifndef MAC2STR -#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] -#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" -#endif - -#include "config.h" - -struct ieee8023_hdr { - u8 dest[6]; - u8 src[6]; - u16 ethertype; -} __attribute__ ((packed)); - - -struct ieee80211_hdr { - u16 frame_control; - u16 duration_id; - u8 addr1[6]; - u8 addr2[6]; - u8 addr3[6]; - u16 seq_ctrl; - /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame - */ -} __attribute__ ((packed)); - -#define IEEE80211_DA_FROMDS addr1 -#define IEEE80211_BSSID_FROMDS addr2 -#define IEEE80211_SA_FROMDS addr3 - -#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr)) - -#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) - -/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X - * frames that might be longer than normal default MTU and they are not - * fragmented */ -#define HOSTAPD_MTU 2290 - -extern unsigned char rfc1042_header[6]; - -typedef struct hostapd_data hostapd; - -struct hostap_sta_driver_data { - unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; -}; - -struct driver_ops; -struct wpa_ctrl_dst; -struct radius_server_data; - -struct hostapd_data { - struct hostapd_config *conf; - char *config_fname; - - u8 own_addr[6]; - - int num_sta; /* number of entries in sta_list */ - struct sta_info *sta_list; /* STA info list head */ - struct sta_info *sta_hash[STA_HASH_SIZE]; - - /* pointers to STA info; based on allocated AID or NULL if AID free - * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1 - * and so on - */ - struct sta_info *sta_aid[MAX_AID_TABLE_SIZE]; - - struct driver_ops *driver; - - u8 *default_wep_key; - u8 default_wep_key_idx; - - struct radius_client_data *radius; - u32 acct_session_id_hi, acct_session_id_lo; - - struct iapp_data *iapp; - - enum { DO_NOT_ASSOC = 0, WAIT_BEACON, AUTHENTICATE, ASSOCIATE, - ASSOCIATED } assoc_ap_state; - char assoc_ap_ssid[33]; - int assoc_ap_ssid_len; - u16 assoc_ap_aid; - - struct hostapd_cached_radius_acl *acl_cache; - struct hostapd_acl_query_data *acl_queries; - - u8 *wpa_ie; - size_t wpa_ie_len; - struct wpa_authenticator *wpa_auth; - -#define PMKID_HASH_SIZE 128 -#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f) - struct rsn_pmksa_cache *pmkid[PMKID_HASH_SIZE]; - struct rsn_pmksa_cache *pmksa; - int pmksa_count; - - struct rsn_preauth_interface *preauth_iface; - time_t michael_mic_failure; - int michael_mic_failures; - int tkip_countermeasures; - - int ctrl_sock; - struct wpa_ctrl_dst *ctrl_dst; - - void *ssl_ctx; - void *eap_sim_db_priv; - struct radius_server_data *radius_srv; -}; - -void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta, int reassoc); -void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, - unsigned int module, int level, const char *fmt, - ...) __attribute__ ((format (printf, 5, 6))); - - -#define HOSTAPD_DEBUG(level, args...) \ -do { \ - if (hapd->conf == NULL || hapd->conf->debug >= (level)) \ - printf(args); \ -} while (0) - -#define HOSTAPD_DEBUG_COND(level) (hapd->conf->debug >= (level)) - -const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, - size_t buflen); - -#endif /* HOSTAPD_H */ diff --git a/contrib/hostapd-0.4.9/hostapd.radius_clients b/contrib/hostapd-0.4.9/hostapd.radius_clients deleted file mode 100644 index 3980427253..0000000000 --- a/contrib/hostapd-0.4.9/hostapd.radius_clients +++ /dev/null @@ -1,4 +0,0 @@ -# RADIUS client configuration for the RADIUS server -10.1.2.3 secret passphrase -192.168.1.0/24 another very secret passphrase -0.0.0.0/0 radius diff --git a/contrib/hostapd-0.4.9/hostapd.sim_db b/contrib/hostapd-0.4.9/hostapd.sim_db deleted file mode 100644 index 01c593de8d..0000000000 --- a/contrib/hostapd-0.4.9/hostapd.sim_db +++ /dev/null @@ -1,9 +0,0 @@ -# Example GSM authentication triplet file for EAP-SIM authenticator -# IMSI:Kc:SRES:RAND -# IMSI: ASCII string (numbers) -# Kc: hex, 8 octets -# SRES: hex, 4 octets -# RAND: hex, 16 octets -234567898765432:A0A1A2A3A4A5A6A7:D1D2D3D4:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -234567898765432:B0B1B2B3B4B5B6B7:E1E2E3E4:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -234567898765432:C0C1C2C3C4C5C6C7:F1F2F3F4:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC diff --git a/contrib/hostapd-0.4.9/hostapd.wpa_psk b/contrib/hostapd-0.4.9/hostapd.wpa_psk deleted file mode 100644 index 0a9499acd7..0000000000 --- a/contrib/hostapd-0.4.9/hostapd.wpa_psk +++ /dev/null @@ -1,9 +0,0 @@ -# List of WPA PSKs. Each line, except for empty lines and lines starting -# with #, must contain a MAC address and PSK separated with a space. -# Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that -# anyone can use. PSK can be configured as an ASCII passphrase of 8..63 -# characters or as a 256-bit hex PSK (64 hex digits). -00:00:00:00:00:00 secret passphrase -00:11:22:33:44:55 another passphrase -00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef -00:00:00:00:00:00 another passphrase for all STAs diff --git a/contrib/hostapd-0.4.9/hostapd_cli.1 b/contrib/hostapd-0.4.9/hostapd_cli.1 deleted file mode 100644 index 062fc78589..0000000000 --- a/contrib/hostapd-0.4.9/hostapd_cli.1 +++ /dev/null @@ -1,83 +0,0 @@ -.TH HOSTAPD_CLI 1 "April 7, 2005" hostapd_cli "hostapd command-line interface" -.SH NAME -hostapd_cli \- hostapd command-line interface -.SH SYNOPSIS -.B hostapd_cli -[-p] [-i] [-hv] [command..] -.SH DESCRIPTION -This manual page documents briefly the -.B hostapd_cli -utility. -.PP -.B hostapd_cli -is a command-line interface for the -.B hostapd -daemon. - -.B hostapd -is a user space daemon for access point and authentication servers. -It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server. -For more information about -.B hostapd -refer to the -.BR hostapd (8) -man page. -.SH OPTIONS -A summary of options is included below. -For a complete description, run -.BR hostapd_cli -from the command line. -.TP -.B \-p -Path to find control sockets. - -Default: /var/run/hostapd -.TP -.B \-i -Interface to listen on. - -Default: first interface found in socket path. -.TP -.B \-h -Show usage. -.TP -.B \-v -Show hostapd_cli version. -.SH COMMANDS -A summary of commands is included below. -For a complete description, run -.BR hostapd_cli -from the command line. -.TP -.B mib -Get MIB variables (dot1x, dot11, radius). -.TP -.B sta -Get MIB variables for one station. -.TP -.B all_sta -Get MIB variables for all stations. -.TP -.B help -Get usage help. -.TP -.B interface [ifname] -Show interfaces/select interface. -.TP -.B level -Change debug level. -.TP -.B license -Show full -.B hostapd_cli -license. -.TP -.B quit -Exit hostapd_cli. -.SH SEE ALSO -.BR hostapd (8). -.SH AUTHOR -hostapd_cli was written by Jouni Malinen . -.PP -This manual page was written by Faidon Liambotis , -for the Debian project (but may be used by others). diff --git a/contrib/hostapd-0.4.9/hostapd_cli.c b/contrib/hostapd-0.4.9/hostapd_cli.c deleted file mode 100644 index d93c41d57f..0000000000 --- a/contrib/hostapd-0.4.9/hostapd_cli.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * hostapd - command line interface for hostapd daemon - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include "wpa_ctrl.h" -#include "version.h" - - -static const char *hostapd_cli_version = -"hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2005, Jouni Malinen and contributors"; - - -static const char *hostapd_cli_license = -"This program is free software. You can distribute it and/or modify it\n" -"under the terms of the GNU General Public License version 2.\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license. See README and COPYING for more details.\n"; - -static const char *hostapd_cli_full_license = -"This program is free software; you can redistribute it and/or modify\n" -"it under the terms of the GNU General Public License version 2 as\n" -"published by the Free Software Foundation.\n" -"\n" -"This program is distributed in the hope that it will be useful,\n" -"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" -"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" -"GNU General Public License for more details.\n" -"\n" -"You should have received a copy of the GNU General Public License\n" -"along with this program; if not, write to the Free Software\n" -"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license.\n" -"\n" -"Redistribution and use in source and binary forms, with or without\n" -"modification, are permitted provided that the following conditions are\n" -"met:\n" -"\n" -"1. Redistributions of source code must retain the above copyright\n" -" notice, this list of conditions and the following disclaimer.\n" -"\n" -"2. Redistributions in binary form must reproduce the above copyright\n" -" notice, this list of conditions and the following disclaimer in the\n" -" documentation and/or other materials provided with the distribution.\n" -"\n" -"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" -" names of its contributors may be used to endorse or promote products\n" -" derived from this software without specific prior written permission.\n" -"\n" -"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" -"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" -"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" -"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" -"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" -"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" -"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" -"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" -"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" -"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" -"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" -"\n"; - -static const char *commands_help = -"Commands:\n" -" mib get MIB variables (dot1x, dot11, radius)\n" -" sta get MIB vatiables for one station\n" -" all_sta get MIB variables for all stations\n" -" help show this usage help\n" -" interface [ifname] show interfaces/select interface\n" -" level change debug level\n" -" license show full hostapd_cli license\n" -" quit exit hostapd_cli\n"; - -static struct wpa_ctrl *ctrl_conn; -static int hostapd_cli_quit = 0; -static int hostapd_cli_attached = 0; -static const char *ctrl_iface_dir = "/var/run/hostapd"; -static char *ctrl_ifname = NULL; - - -static void usage(void) -{ - fprintf(stderr, "%s\n", hostapd_cli_version); - fprintf(stderr, - "\n" - "usage: hostapd_cli [-p] [-i] [-hv] " - "[command..]\n" - "\n" - "Options:\n" - " -h help (show this usage text)\n" - " -v shown version information\n" - " -p path to find control sockets (default: " - "/var/run/hostapd)\n" - " -i Interface to listen on (default: first " - "interface found in the\n" - " socket path)\n\n" - "%s", - commands_help); -} - - -static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) -{ - char *cfile; - int flen; - - if (ifname == NULL) - return NULL; - - flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2; - cfile = malloc(flen); - if (cfile == NULL) - return NULL; - snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); - - ctrl_conn = wpa_ctrl_open(cfile); - free(cfile); - return ctrl_conn; -} - - -static void hostapd_cli_close_connection(void) -{ - if (ctrl_conn == NULL) - return; - - if (hostapd_cli_attached) { - wpa_ctrl_detach(ctrl_conn); - hostapd_cli_attached = 0; - } - wpa_ctrl_close(ctrl_conn); - ctrl_conn = NULL; -} - - -static void hostapd_cli_msg_cb(char *msg, size_t len) -{ - printf("%s\n", msg); -} - - -static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) -{ - char buf[4096]; - size_t len; - int ret; - - if (ctrl_conn == NULL) { - printf("Not connected to hostapd - command dropped.\n"); - return -1; - } - len = sizeof(buf) - 1; - ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, - hostapd_cli_msg_cb); - if (ret == -2) { - printf("'%s' command timed out.\n", cmd); - return -2; - } else if (ret < 0) { - printf("'%s' command failed.\n", cmd); - return -1; - } - if (print) { - buf[len] = '\0'; - printf("%s", buf); - } - return 0; -} - - -static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) -{ - return _wpa_ctrl_command(ctrl, cmd, 1); -} - - -static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) -{ - return wpa_ctrl_command(ctrl, "PING"); -} - - -static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) -{ - return wpa_ctrl_command(ctrl, "MIB"); -} - - -static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) -{ - char buf[64]; - if (argc != 1) { - printf("Invalid 'sta' command - exactly one argument, STA " - "address, is required.\n"); - return -1; - } - snprintf(buf, sizeof(buf), "STA %s", argv[0]); - return wpa_ctrl_command(ctrl, buf); -} - - -static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, - char *addr, size_t addr_len) -{ - char buf[4096], *pos; - size_t len; - int ret; - - if (ctrl_conn == NULL) { - printf("Not connected to hostapd - command dropped.\n"); - return -1; - } - len = sizeof(buf) - 1; - ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, - hostapd_cli_msg_cb); - if (ret == -2) { - printf("'%s' command timed out.\n", cmd); - return -2; - } else if (ret < 0) { - printf("'%s' command failed.\n", cmd); - return -1; - } - - buf[len] = '\0'; - if (memcmp(buf, "FAIL", 4) == 0) - return -1; - printf("%s", buf); - - pos = buf; - while (*pos != '\0' && *pos != '\n') - pos++; - *pos = '\0'; - snprintf(addr, addr_len, "%s", buf); - return 0; -} - - -static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, - char *argv[]) -{ - char addr[32], cmd[64]; - - if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) - return 0; - do { - snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); - } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); - - return -1; -} - - -static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) -{ - printf("%s", commands_help); - return 0; -} - - -static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, - char *argv[]) -{ - printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license); - return 0; -} - - -static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) -{ - hostapd_cli_quit = 1; - return 0; -} - - -static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) -{ - char cmd[256]; - if (argc != 1) { - printf("Invalid LEVEL command: needs one argument (debug " - "level)\n"); - return 0; - } - snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); - return wpa_ctrl_command(ctrl, cmd); -} - - -static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl) -{ - struct dirent *dent; - DIR *dir; - - dir = opendir(ctrl_iface_dir); - if (dir == NULL) { - printf("Control interface directory '%s' could not be " - "openned.\n", ctrl_iface_dir); - return; - } - - printf("Available interfaces:\n"); - while ((dent = readdir(dir))) { - if (strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0) - continue; - printf("%s\n", dent->d_name); - } - closedir(dir); -} - - -static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, - char *argv[]) -{ - if (argc < 1) { - hostapd_cli_list_interfaces(ctrl); - return 0; - } - - hostapd_cli_close_connection(); - free(ctrl_ifname); - ctrl_ifname = strdup(argv[0]); - - if (hostapd_cli_open_connection(ctrl_ifname)) { - printf("Connected to interface '%s.\n", ctrl_ifname); - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - } else { - printf("Warning: Failed to attach to " - "hostapd.\n"); - } - } else { - printf("Could not connect to interface '%s' - re-trying\n", - ctrl_ifname); - } - return 0; -} - - -struct hostapd_cli_cmd { - const char *cmd; - int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); -}; - -static struct hostapd_cli_cmd hostapd_cli_commands[] = { - { "ping", hostapd_cli_cmd_ping }, - { "mib", hostapd_cli_cmd_mib }, - { "sta", hostapd_cli_cmd_sta }, - { "all_sta", hostapd_cli_cmd_all_sta }, - { "help", hostapd_cli_cmd_help }, - { "interface", hostapd_cli_cmd_interface }, - { "level", hostapd_cli_cmd_level }, - { "license", hostapd_cli_cmd_license }, - { "quit", hostapd_cli_cmd_quit }, - { NULL, NULL } -}; - - -static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) -{ - struct hostapd_cli_cmd *cmd, *match = NULL; - int count; - - count = 0; - cmd = hostapd_cli_commands; - while (cmd->cmd) { - if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) { - match = cmd; - count++; - } - cmd++; - } - - if (count > 1) { - printf("Ambiguous command '%s'; possible commands:", argv[0]); - cmd = hostapd_cli_commands; - while (cmd->cmd) { - if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == - 0) { - printf(" %s", cmd->cmd); - } - cmd++; - } - printf("\n"); - } else if (count == 0) { - printf("Unknown command '%s'\n", argv[0]); - } else { - match->handler(ctrl, argc - 1, &argv[1]); - } -} - - -static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read) -{ - int first = 1; - if (ctrl_conn == NULL) - return; - while (wpa_ctrl_pending(ctrl)) { - char buf[256]; - size_t len = sizeof(buf) - 1; - if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { - buf[len] = '\0'; - if (in_read && first) - printf("\n"); - first = 0; - printf("%s\n", buf); - } else { - printf("Could not read pending message.\n"); - break; - } - } -} - - -static void hostapd_cli_interactive(void) -{ - const int max_args = 10; - char cmd[256], *res, *argv[max_args], *pos; - int argc; - - printf("\nInteractive mode\n\n"); - - do { - hostapd_cli_recv_pending(ctrl_conn, 0); - printf("> "); - alarm(1); - res = fgets(cmd, sizeof(cmd), stdin); - alarm(0); - if (res == NULL) - break; - pos = cmd; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - argc = 0; - pos = cmd; - for (;;) { - while (*pos == ' ') - pos++; - if (*pos == '\0') - break; - argv[argc] = pos; - argc++; - if (argc == max_args) - break; - while (*pos != '\0' && *pos != ' ') - pos++; - if (*pos == ' ') - *pos++ = '\0'; - } - if (argc) - wpa_request(ctrl_conn, argc, argv); - } while (!hostapd_cli_quit); -} - - -static void hostapd_cli_terminate(int sig) -{ - hostapd_cli_close_connection(); - exit(0); -} - - -static void hostapd_cli_alarm(int sig) -{ - if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { - printf("Connection to hostapd lost - trying to reconnect\n"); - hostapd_cli_close_connection(); - } - if (!ctrl_conn) { - ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); - if (ctrl_conn) { - printf("Connection to hostapd re-established\n"); - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - } else { - printf("Warning: Failed to attach to " - "hostapd.\n"); - } - } - } - if (ctrl_conn) - hostapd_cli_recv_pending(ctrl_conn, 1); - alarm(1); -} - - -int main(int argc, char *argv[]) -{ - int interactive; - int warning_displayed = 0; - int c; - - for (;;) { - c = getopt(argc, argv, "hi:p:v"); - if (c < 0) - break; - switch (c) { - case 'h': - usage(); - return 0; - case 'v': - printf("%s\n", hostapd_cli_version); - return 0; - case 'i': - ctrl_ifname = strdup(optarg); - break; - case 'p': - ctrl_iface_dir = optarg; - break; - default: - usage(); - return -1; - } - } - - interactive = argc == optind; - - if (interactive) { - printf("%s\n\n%s\n\n", hostapd_cli_version, - hostapd_cli_license); - } - - for (;;) { - if (ctrl_ifname == NULL) { - struct dirent *dent; - DIR *dir = opendir(ctrl_iface_dir); - if (dir) { - while ((dent = readdir(dir))) { - if (strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0) - continue; - printf("Selected interface '%s'\n", - dent->d_name); - ctrl_ifname = strdup(dent->d_name); - break; - } - closedir(dir); - } - } - ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); - if (ctrl_conn) { - if (warning_displayed) - printf("Connection established.\n"); - break; - } - - if (!interactive) { - perror("Failed to connect to hostapd - " - "wpa_ctrl_open"); - return -1; - } - - if (!warning_displayed) { - printf("Could not connect to hostapd - re-trying\n"); - warning_displayed = 1; - } - sleep(1); - continue; - } - - signal(SIGINT, hostapd_cli_terminate); - signal(SIGTERM, hostapd_cli_terminate); - signal(SIGALRM, hostapd_cli_alarm); - - if (interactive) { - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - } else { - printf("Warning: Failed to attach to hostapd.\n"); - } - hostapd_cli_interactive(); - } else - wpa_request(ctrl_conn, argc - optind, &argv[optind]); - - free(ctrl_ifname); - hostapd_cli_close_connection(); - return 0; -} diff --git a/contrib/hostapd-0.4.9/iapp.c b/contrib/hostapd-0.4.9/iapp.c deleted file mode 100644 index f4dd5573cf..0000000000 --- a/contrib/hostapd-0.4.9/iapp.c +++ /dev/null @@ -1,522 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -/* TODO: - * Level 1: no administrative or security support - * (e.g., static BSSID to IP address mapping in each AP) - * Level 2: support for dynamic mapping of BSSID to IP address - * Level 3: support for encryption and authentication of IAPP messages - * - add support for MOVE-notify and MOVE-response (this requires support for - * finding out IP address for previous AP using RADIUS) - * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during - * reassociation to another AP - * - implement counters etc. for IAPP MIB - * - verify endianness of fields in IAPP messages; are they big-endian as - * used here? - * - RADIUS connection for AP registration and BSSID to IP address mapping - * - TCP connection for IAPP MOVE, CACHE - * - broadcast ESP for IAPP ADD-notify - * - ESP for IAPP MOVE messages - * - security block sending/processing - * - IEEE 802.11 context transfer - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef USE_KERNEL_HEADERS -#include -#else /* USE_KERNEL_HEADERS */ -#include -#endif /* USE_KERNEL_HEADERS */ - -#include "hostapd.h" -#include "ieee802_11.h" -#include "iapp.h" -#include "eloop.h" -#include "sta_info.h" -#include "hostap_common.h" - - -#define IAPP_MULTICAST "224.0.1.178" -#define IAPP_UDP_PORT 3517 -#define IAPP_TCP_PORT 3517 - -struct iapp_hdr { - u8 version; - u8 command; - u16 identifier; - u16 length; - /* followed by length-6 octets of data */ -} __attribute__ ((packed)); - -#define IAPP_VERSION 0 - -enum IAPP_COMMAND { - IAPP_CMD_ADD_notify = 0, - IAPP_CMD_MOVE_notify = 1, - IAPP_CMD_MOVE_response = 2, - IAPP_CMD_Send_Security_Block = 3, - IAPP_CMD_ACK_Security_Block = 4, - IAPP_CMD_CACHE_notify = 5, - IAPP_CMD_CACHE_response = 6, -}; - - -/* ADD-notify - multicast UDP on the local LAN */ -struct iapp_add_notify { - u8 addr_len; /* ETH_ALEN */ - u8 reserved; - u8 mac_addr[ETH_ALEN]; - u16 seq_num; -} __attribute__ ((packed)); - - -/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ -struct iapp_layer2_update { - u8 da[ETH_ALEN]; /* broadcast */ - u8 sa[ETH_ALEN]; /* STA addr */ - u16 len; /* 6 */ - u8 dsap; /* null DSAP address */ - u8 ssap; /* null SSAP address, CR=Response */ - u8 control; - u8 xid_info[3]; -} __attribute__ ((packed)); - - -/* MOVE-notify - unicast TCP */ -struct iapp_move_notify { - u8 addr_len; /* ETH_ALEN */ - u8 reserved; - u8 mac_addr[ETH_ALEN]; - u16 seq_num; - u16 ctx_block_len; - /* followed by ctx_block_len bytes */ -} __attribute__ ((packed)); - - -/* MOVE-response - unicast TCP */ -struct iapp_move_response { - u8 addr_len; /* ETH_ALEN */ - u8 status; - u8 mac_addr[ETH_ALEN]; - u16 seq_num; - u16 ctx_block_len; - /* followed by ctx_block_len bytes */ -} __attribute__ ((packed)); - -enum { - IAPP_MOVE_SUCCESSFUL = 0, - IAPP_MOVE_DENIED = 1, - IAPP_MOVE_STALE_MOVE = 2, -}; - - -/* CACHE-notify */ -struct iapp_cache_notify { - u8 addr_len; /* ETH_ALEN */ - u8 reserved; - u8 mac_addr[ETH_ALEN]; - u16 seq_num; - u8 current_ap[ETH_ALEN]; - u16 ctx_block_len; - /* ctx_block_len bytes of context block followed by 16-bit context - * timeout */ -} __attribute__ ((packed)); - - -/* CACHE-response - unicast TCP */ -struct iapp_cache_response { - u8 addr_len; /* ETH_ALEN */ - u8 status; - u8 mac_addr[ETH_ALEN]; - u16 seq_num; -} __attribute__ ((packed)); - -enum { - IAPP_CACHE_SUCCESSFUL = 0, - IAPP_CACHE_STALE_CACHE = 1, -}; - - -/* Send-Security-Block - unicast TCP */ -struct iapp_send_security_block { - u8 iv[8]; - u16 sec_block_len; - /* followed by sec_block_len bytes of security block */ -} __attribute__ ((packed)); - - -/* ACK-Security-Block - unicast TCP */ -struct iapp_ack_security_block { - u8 iv[8]; - u8 new_ap_ack_authenticator[48]; -} __attribute__ ((packed)); - - -struct iapp_data { - struct hostapd_data *hapd; - u16 identifier; /* next IAPP identifier */ - struct in_addr own, multicast; - int udp_sock; - int packet_sock; -}; - - -static void iapp_send_add(struct iapp_data *iapp, u8 *macaddr, u16 seq_num) -{ - char buf[128]; - struct iapp_hdr *hdr; - struct iapp_add_notify *add; - struct sockaddr_in addr; - - /* Send IAPP ADD-notify to remove possible association from other APs - */ - - hdr = (struct iapp_hdr *) buf; - hdr->version = IAPP_VERSION; - hdr->command = IAPP_CMD_ADD_notify; - hdr->identifier = host_to_be16(iapp->identifier++); - hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add)); - - add = (struct iapp_add_notify *) (hdr + 1); - add->addr_len = ETH_ALEN; - add->reserved = 0; - memcpy(add->mac_addr, macaddr, ETH_ALEN); - - add->seq_num = host_to_be16(seq_num); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = iapp->multicast.s_addr; - addr.sin_port = htons(IAPP_UDP_PORT); - if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0, - (struct sockaddr *) &addr, sizeof(addr)) < 0) - perror("sendto[IAPP-ADD]"); -} - - -static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr) -{ - struct iapp_layer2_update msg; - - /* Send Level 2 Update Frame to update forwarding tables in layer 2 - * bridge devices */ - - /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) - * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ - - memset(msg.da, 0xff, ETH_ALEN); - memcpy(msg.sa, addr, ETH_ALEN); - msg.len = host_to_be16(6); - msg.dsap = 0; /* NULL DSAP address */ - msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */ - msg.control = 0xaf; /* XID response lsb.1111F101. - * F=0 (no poll command; unsolicited frame) */ - msg.xid_info[0] = 0x81; /* XID format identifier */ - msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ - msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW) - * FIX: what is correct RW with 802.11? */ - - if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0) - perror("send[L2 Update]"); -} - - -void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta) -{ - struct ieee80211_mgmt *assoc; - u16 seq; - - if (iapp == NULL) - return; - - assoc = sta->last_assoc_req; - seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0; - - /* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */ - hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP, - HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq); - iapp_send_layer2_update(iapp, sta->addr); - iapp_send_add(iapp, sta->addr, seq); - - if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) == - WLAN_FC_STYPE_REASSOC_REQ) { - /* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP, - * Context Block, Timeout) - */ - /* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to - * IP address */ - } -} - - -static void iapp_process_add_notify(struct iapp_data *iapp, - struct sockaddr_in *from, - struct iapp_hdr *hdr, int len) -{ - struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1); - struct sta_info *sta; - - if (len != sizeof(*add)) { - printf("Invalid IAPP-ADD packet length %d (expected %lu)\n", - len, (unsigned long) sizeof(*add)); - return; - } - - sta = ap_get_sta(iapp->hapd, add->mac_addr); - - /* IAPP-ADD.indication(MAC Address, Sequence Number) */ - hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, - HOSTAPD_LEVEL_INFO, - "Received IAPP ADD-notify (seq# %d) from %s:%d%s", - be_to_host16(add->seq_num), - inet_ntoa(from->sin_addr), ntohs(from->sin_port), - sta ? "" : " (STA not found)"); - - if (!sta) - return; - - /* TODO: could use seq_num to try to determine whether last association - * to this AP is newer than the one advertised in IAPP-ADD. Although, - * this is not really a reliable verification. */ - - hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, - HOSTAPD_LEVEL_DEBUG, - "Removing STA due to IAPP ADD-notify"); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); - eloop_cancel_timeout(ap_handle_timer, iapp->hapd, sta); - eloop_register_timeout(0, 0, ap_handle_timer, iapp->hapd, sta); - sta->timeout_next = STA_REMOVE; -} - - -static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct iapp_data *iapp = eloop_ctx; - int len, hlen; - unsigned char buf[128]; - struct sockaddr_in from; - socklen_t fromlen; - struct iapp_hdr *hdr; - - /* Handle incoming IAPP frames (over UDP/IP) */ - - fromlen = sizeof(from); - len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0, - (struct sockaddr *) &from, &fromlen); - if (len < 0) - perror("recvfrom"); - - if (from.sin_addr.s_addr == iapp->own.s_addr) - return; /* ignore own IAPP messages */ - - hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP, - HOSTAPD_LEVEL_DEBUG, - "Received %d byte IAPP frame from %s%s\n", - len, inet_ntoa(from.sin_addr), - len < sizeof(*hdr) ? " (too short)" : ""); - - if (len < sizeof(*hdr)) - return; - - hdr = (struct iapp_hdr *) buf; - hlen = be_to_host16(hdr->length); - hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP, - HOSTAPD_LEVEL_DEBUG, - "RX: version=%d command=%d id=%d len=%d\n", - hdr->version, hdr->command, - be_to_host16(hdr->identifier), hlen); - if (hdr->version != IAPP_VERSION) { - printf("Dropping IAPP frame with unknown version %d\n", - hdr->version); - return; - } - if (hlen > len) { - printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len); - return; - } - if (hlen < len) { - printf("Ignoring %d extra bytes from IAPP frame\n", - len - hlen); - len = hlen; - } - - switch (hdr->command) { - case IAPP_CMD_ADD_notify: - iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr)); - break; - case IAPP_CMD_MOVE_notify: - /* TODO: MOVE is using TCP; so move this to TCP handler once it - * is implemented.. */ - /* IAPP-MOVE.indication(MAC Address, New BSSID, - * Sequence Number, AP Address, Context Block) */ - /* TODO: process */ - break; - default: - printf("Unknown IAPP command %d\n", hdr->command); - break; - } -} - - -struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) -{ - struct ifreq ifr; - struct sockaddr_ll addr; - int ifindex; - struct sockaddr_in *paddr, uaddr; - struct iapp_data *iapp; - struct ip_mreqn mreq; - - iapp = malloc(sizeof(*iapp)); - if (iapp == NULL) - return NULL; - memset(iapp, 0, sizeof(*iapp)); - iapp->hapd = hapd; - iapp->udp_sock = iapp->packet_sock = -1; - - /* TODO: - * open socket for sending and receiving IAPP frames over TCP - */ - - iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (iapp->udp_sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); - iapp_deinit(iapp); - return NULL; - } - - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); - if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); - iapp_deinit(iapp); - return NULL; - } - ifindex = ifr.ifr_ifindex; - - if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFADDR)"); - iapp_deinit(iapp); - return NULL; - } - paddr = (struct sockaddr_in *) &ifr.ifr_addr; - if (paddr->sin_family != AF_INET) { - printf("Invalid address family %i (SIOCGIFADDR)\n", - paddr->sin_family); - iapp_deinit(iapp); - return NULL; - } - iapp->own.s_addr = paddr->sin_addr.s_addr; - - if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFBRDADDR)"); - iapp_deinit(iapp); - return NULL; - } - paddr = (struct sockaddr_in *) &ifr.ifr_addr; - if (paddr->sin_family != AF_INET) { - printf("Invalid address family %i (SIOCGIFBRDADDR)\n", - paddr->sin_family); - iapp_deinit(iapp); - return NULL; - } - inet_aton(IAPP_MULTICAST, &iapp->multicast); - - memset(&uaddr, 0, sizeof(uaddr)); - uaddr.sin_family = AF_INET; - uaddr.sin_port = htons(IAPP_UDP_PORT); - if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, - sizeof(uaddr)) < 0) { - perror("bind[UDP]"); - iapp_deinit(iapp); - return NULL; - } - - memset(&mreq, 0, sizeof(mreq)); - mreq.imr_multiaddr = iapp->multicast; - mreq.imr_address.s_addr = INADDR_ANY; - mreq.imr_ifindex = 0; - if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, - sizeof(mreq)) < 0) { - perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]"); - return NULL; - } - - iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (iapp->packet_sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); - return NULL; - } - - memset(&addr, 0, sizeof(addr)); - addr.sll_family = AF_PACKET; - addr.sll_ifindex = ifindex; - if (bind(iapp->packet_sock, (struct sockaddr *) &addr, - sizeof(addr)) < 0) { - perror("bind[PACKET]"); - return NULL; - } - - if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp, - iapp, NULL)) { - printf("Could not register read socket for IAPP.\n"); - return NULL; - } - - printf("IEEE 802.11F (IAPP) using interface %s\n", iface); - - /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive - * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually - * be openned only after receiving Initiate-Accept. If Initiate-Reject - * is received, IAPP is not started. */ - - return iapp; -} - - -void iapp_deinit(struct iapp_data *iapp) -{ - struct ip_mreqn mreq; - - if (iapp == NULL) - return; - - if (iapp->udp_sock >= 0) { - memset(&mreq, 0, sizeof(mreq)); - mreq.imr_multiaddr = iapp->multicast; - mreq.imr_address.s_addr = INADDR_ANY; - mreq.imr_ifindex = 0; - if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]"); - } - - eloop_unregister_read_sock(iapp->udp_sock); - close(iapp->udp_sock); - } - if (iapp->packet_sock >= 0) { - eloop_unregister_read_sock(iapp->packet_sock); - close(iapp->packet_sock); - } - free(iapp); -} diff --git a/contrib/hostapd-0.4.9/ieee802_11.c b/contrib/hostapd-0.4.9/ieee802_11.c deleted file mode 100644 index d5ad58cbb5..0000000000 --- a/contrib/hostapd-0.4.9/ieee802_11.c +++ /dev/null @@ -1,1220 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.11 Management - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "eloop.h" -#include "hostapd.h" -#include "ieee802_11.h" -#include "radius.h" -#include "radius_client.h" -#include "ieee802_11_auth.h" -#include "sta_info.h" -#include "eapol_sm.h" -#include "rc4.h" -#include "ieee802_1x.h" -#include "wpa.h" -#include "accounting.h" -#include "driver.h" -#include "hostap_common.h" - - -static u8 * hostapd_eid_supp_rates(hostapd *hapd, u8 *eid) -{ - u8 *pos = eid; - - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = 4; /* len */ - *pos++ = 0x82; /* 1 Mbps, base set */ - *pos++ = 0x84; /* 2 Mbps, base set */ - *pos++ = 0x0b; /* 5.5 Mbps */ - *pos++ = 0x16; /* 11 Mbps */ - - return pos; -} - - -static u16 hostapd_own_capab_info(hostapd *hapd) -{ - int capab = WLAN_CAPABILITY_ESS; - if (hapd->conf->wpa || - (hapd->conf->ieee802_1x && - (hapd->conf->default_wep_key_len || - hapd->conf->individual_wep_key_len))) - capab |= WLAN_CAPABILITY_PRIVACY; - return capab; -} - - -static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; - -ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, - size_t len, - struct ieee802_11_elems *elems, - int show_errors) -{ - size_t left = len; - u8 *pos = start; - int unknown = 0; - - memset(elems, 0, sizeof(*elems)); - - while (left >= 2) { - u8 id, elen; - - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) { - if (show_errors) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.11 element parse " - "failed (id=%d elen=%d " - "left=%lu)\n", - id, elen, (unsigned long) left); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) - hostapd_hexdump("IEs", start, len); - } - return ParseFailed; - } - - switch (id) { - case WLAN_EID_SSID: - elems->ssid = pos; - elems->ssid_len = elen; - break; - case WLAN_EID_SUPP_RATES: - elems->supp_rates = pos; - elems->supp_rates_len = elen; - break; - case WLAN_EID_FH_PARAMS: - elems->fh_params = pos; - elems->fh_params_len = elen; - break; - case WLAN_EID_DS_PARAMS: - elems->ds_params = pos; - elems->ds_params_len = elen; - break; - case WLAN_EID_CF_PARAMS: - elems->cf_params = pos; - elems->cf_params_len = elen; - break; - case WLAN_EID_TIM: - elems->tim = pos; - elems->tim_len = elen; - break; - case WLAN_EID_IBSS_PARAMS: - elems->ibss_params = pos; - elems->ibss_params_len = elen; - break; - case WLAN_EID_CHALLENGE: - elems->challenge = pos; - elems->challenge_len = elen; - break; - case WLAN_EID_GENERIC: - if (elen > 4 && memcmp(pos, WPA_OUI_TYPE, 4) == 0) { - elems->wpa_ie = pos; - elems->wpa_ie_len = elen; - } else if (show_errors) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, - "IEEE 802.11 element parse " - "ignored unknown generic element" - " (id=%d elen=%d OUI:type=" - "%02x-%02x-%02x:%d)\n", - id, elen, - elen >= 1 ? pos[0] : 0, - elen >= 2 ? pos[1] : 0, - elen >= 3 ? pos[2] : 0, - elen >= 4 ? pos[3] : 0); - unknown++; - } else { - unknown++; - } - break; - case WLAN_EID_RSN: - elems->rsn_ie = pos; - elems->rsn_ie_len = elen; - break; - default: - unknown++; - if (!show_errors) - break; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, - "IEEE 802.11 element parse ignored " - "unknown element (id=%d elen=%d)\n", - id, elen); - break; - } - - left -= elen; - pos += elen; - } - - if (left) - return ParseFailed; - - return unknown ? ParseUnknown : ParseOK; -} - - -static void ieee802_11_print_ssid(const u8 *ssid, u8 len) -{ - int i; - for (i = 0; i < len; i++) { - if (ssid[i] >= 32 && ssid[i] < 127) - printf("%c", ssid[i]); - else - printf("<%02x>", ssid[i]); - } -} - - -static void ieee802_11_sta_authenticate(void *eloop_ctx, void *timeout_ctx) -{ - hostapd *hapd = eloop_ctx; - struct ieee80211_mgmt mgmt; - - if (hapd->assoc_ap_state == WAIT_BEACON) - hapd->assoc_ap_state = AUTHENTICATE; - if (hapd->assoc_ap_state != AUTHENTICATE) - return; - - printf("Authenticate with AP " MACSTR " SSID=", - MAC2STR(hapd->conf->assoc_ap_addr)); - ieee802_11_print_ssid((u8 *) hapd->assoc_ap_ssid, - hapd->assoc_ap_ssid_len); - printf(" (as station)\n"); - - memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_AUTH); - /* Request TX callback */ - mgmt.frame_control |= host_to_le16(BIT(1)); - memcpy(mgmt.da, hapd->conf->assoc_ap_addr, ETH_ALEN); - memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); - memcpy(mgmt.bssid, hapd->conf->assoc_ap_addr, ETH_ALEN); - mgmt.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN); - mgmt.u.auth.auth_transaction = host_to_le16(1); - mgmt.u.auth.status_code = host_to_le16(0); - if (hostapd_send_mgmt_frame(hapd, &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.auth), 0) < 0) - perror("ieee802_11_sta_authenticate: send"); - - /* Try to authenticate again, if this attempt fails or times out. */ - eloop_register_timeout(5, 0, ieee802_11_sta_authenticate, hapd, NULL); -} - - -static void ieee802_11_sta_associate(void *eloop_ctx, void *timeout_ctx) -{ - hostapd *hapd = eloop_ctx; - u8 buf[256]; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; - u8 *p; - - if (hapd->assoc_ap_state == AUTHENTICATE) - hapd->assoc_ap_state = ASSOCIATE; - if (hapd->assoc_ap_state != ASSOCIATE) - return; - - printf("Associate with AP " MACSTR " SSID=", - MAC2STR(hapd->conf->assoc_ap_addr)); - ieee802_11_print_ssid((u8 *) hapd->assoc_ap_ssid, - hapd->assoc_ap_ssid_len); - printf(" (as station)\n"); - - memset(mgmt, 0, sizeof(*mgmt)); - mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ASSOC_REQ); - /* Request TX callback */ - mgmt->frame_control |= host_to_le16(BIT(1)); - memcpy(mgmt->da, hapd->conf->assoc_ap_addr, ETH_ALEN); - memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); - memcpy(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN); - mgmt->u.assoc_req.capab_info = host_to_le16(0); - mgmt->u.assoc_req.listen_interval = host_to_le16(1); - p = &mgmt->u.assoc_req.variable[0]; - - *p++ = WLAN_EID_SSID; - *p++ = hapd->assoc_ap_ssid_len; - memcpy(p, hapd->assoc_ap_ssid, hapd->assoc_ap_ssid_len); - p += hapd->assoc_ap_ssid_len; - - p = hostapd_eid_supp_rates(hapd, p); - - if (hostapd_send_mgmt_frame(hapd, mgmt, p - (u8 *) mgmt, 0) < 0) - perror("ieee802_11_sta_associate: send"); - - /* Try to authenticate again, if this attempt fails or times out. */ - eloop_register_timeout(5, 0, ieee802_11_sta_associate, hapd, NULL); -} - - -static u16 auth_shared_key(hostapd *hapd, struct sta_info *sta, - u16 auth_transaction, u8 *challenge, int iswep) -{ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "authentication (shared key, transaction %d)", - auth_transaction); - - if (auth_transaction == 1) { - if (!sta->challenge) { - /* Generate a pseudo-random challenge */ - u8 key[8]; - time_t now; - int r; - sta->challenge = malloc(WLAN_AUTH_CHALLENGE_LEN); - if (!sta->challenge) - return WLAN_STATUS_UNSPECIFIED_FAILURE; - memset(sta->challenge, 0, WLAN_AUTH_CHALLENGE_LEN); - - now = time(NULL); - r = random(); - memcpy(key, &now, 4); - memcpy(key + 4, &r, 4); - rc4(sta->challenge, WLAN_AUTH_CHALLENGE_LEN, - key, sizeof(key)); - } - return 0; - } - - if (auth_transaction != 3) - return WLAN_STATUS_UNSPECIFIED_FAILURE; - - /* Transaction 3 */ - if (!iswep || !sta->challenge || !challenge || - memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, - "shared key authentication - invalid " - "challenge-response"); - return WLAN_STATUS_CHALLENGE_FAIL; - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "authentication OK (shared key)"); -#ifdef IEEE80211_REQUIRE_AUTH_ACK - /* Station will be marked authenticated if it ACKs the - * authentication reply. */ -#else - sta->flags |= WLAN_STA_AUTH; - wpa_sm_event(hapd, sta, WPA_AUTH); -#endif - free(sta->challenge); - sta->challenge = NULL; - - return 0; -} - - -static void send_auth_reply(hostapd *hapd, struct ieee80211_mgmt *mgmt, - u16 auth_alg, u16 auth_transaction, u16 resp, - u8 *challenge) -{ - u8 buf[IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + 2 + - WLAN_AUTH_CHALLENGE_LEN]; - struct ieee80211_mgmt *reply; - size_t rlen; - - memset(buf, 0, sizeof(buf)); - reply = (struct ieee80211_mgmt *) buf; - reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_AUTH); - /* Request TX callback */ - reply->frame_control |= host_to_le16(BIT(1)); - memcpy(reply->da, mgmt->sa, ETH_ALEN); - memcpy(reply->sa, hapd->own_addr, ETH_ALEN); - memcpy(reply->bssid, mgmt->bssid, ETH_ALEN); - - reply->u.auth.auth_alg = host_to_le16(auth_alg); - reply->u.auth.auth_transaction = host_to_le16(auth_transaction); - reply->u.auth.status_code = host_to_le16(resp); - rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth); - if (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 2 && - challenge) { - u8 *p = reply->u.auth.variable; - *p++ = WLAN_EID_CHALLENGE; - *p++ = WLAN_AUTH_CHALLENGE_LEN; - memcpy(p, challenge, WLAN_AUTH_CHALLENGE_LEN); - rlen += 2 + WLAN_AUTH_CHALLENGE_LEN; - } else - challenge = NULL; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "authentication reply: STA=" MACSTR " auth_alg=%d " - "auth_transaction=%d resp=%d%s\n", - MAC2STR(mgmt->sa), auth_alg, auth_transaction, - resp, challenge ? " challenge" : ""); - if (hostapd_send_mgmt_frame(hapd, reply, rlen, 0) < 0) - perror("send_auth_reply: send"); -} - - -static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len) -{ - u16 auth_alg, auth_transaction, status_code; - u16 resp = WLAN_STATUS_SUCCESS; - struct sta_info *sta = NULL; - int res; - u16 fc; - u8 *challenge = NULL; - u32 session_timeout, acct_interim_interval; - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - printf("handle_auth - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - auth_alg = le_to_host16(mgmt->u.auth.auth_alg); - auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); - status_code = le_to_host16(mgmt->u.auth.status_code); - fc = le_to_host16(mgmt->frame_control); - - if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + - 2 + WLAN_AUTH_CHALLENGE_LEN && - mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE && - mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN) - challenge = &mgmt->u.auth.variable[2]; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "authentication: STA=" MACSTR " auth_alg=%d " - "auth_transaction=%d status_code=%d wep=%d%s\n", - MAC2STR(mgmt->sa), auth_alg, auth_transaction, - status_code, !!(fc & WLAN_FC_ISWEP), - challenge ? " challenge" : ""); - - if (hapd->assoc_ap_state == AUTHENTICATE && auth_transaction == 2 && - memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0 && - memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { - if (status_code != 0) { - printf("Authentication (as station) with AP " - MACSTR " failed (status_code=%d)\n", - MAC2STR(hapd->conf->assoc_ap_addr), - status_code); - return; - } - printf("Authenticated (as station) with AP " MACSTR "\n", - MAC2STR(hapd->conf->assoc_ap_addr)); - ieee802_11_sta_associate(hapd, NULL); - return; - } - - if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; - goto fail; - } - - if (!(((hapd->conf->auth_algs & HOSTAPD_AUTH_OPEN) && - auth_alg == WLAN_AUTH_OPEN) || - ((hapd->conf->auth_algs & HOSTAPD_AUTH_SHARED_KEY) && - auth_alg == WLAN_AUTH_SHARED_KEY))) { - printf("Unsupported authentication algorithm (%d)\n", - auth_alg); - resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; - goto fail; - } - - if (!(auth_transaction == 1 || - (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { - printf("Unknown authentication transaction number (%d)\n", - auth_transaction); - resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; - goto fail; - } - - if (memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { - printf("Station " MACSTR " not allowed to authenticate.\n", - MAC2STR(mgmt->sa)); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, - &session_timeout, - &acct_interim_interval); - if (res == HOSTAPD_ACL_REJECT) { - printf("Station " MACSTR " not allowed to authenticate.\n", - MAC2STR(mgmt->sa)); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - if (res == HOSTAPD_ACL_PENDING) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Authentication frame " - "from " MACSTR " waiting for an external " - "authentication\n", MAC2STR(mgmt->sa)); - /* Authentication code will re-send the authentication frame - * after it has received (and cached) information from the - * external source. */ - return; - } - - sta = ap_sta_add(hapd, mgmt->sa); - if (!sta) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - sta->flags &= ~WLAN_STA_PREAUTH; - ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); - - if (hapd->conf->radius->acct_interim_interval == 0 && - acct_interim_interval) - sta->acct_interim_interval = acct_interim_interval; - if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) - ap_sta_session_timeout(hapd, sta, session_timeout); - else - ap_sta_no_session_timeout(hapd, sta); - - switch (auth_alg) { - case WLAN_AUTH_OPEN: - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "authentication OK (open system)"); -#ifdef IEEE80211_REQUIRE_AUTH_ACK - /* Station will be marked authenticated if it ACKs the - * authentication reply. */ -#else - sta->flags |= WLAN_STA_AUTH; - wpa_sm_event(hapd, sta, WPA_AUTH); -#endif - break; - case WLAN_AUTH_SHARED_KEY: - resp = auth_shared_key(hapd, sta, auth_transaction, challenge, - fc & WLAN_FC_ISWEP); - break; - } - - fail: - send_auth_reply(hapd, mgmt, auth_alg, auth_transaction + 1, resp, - sta ? sta->challenge : NULL); -} - - -static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len, int reassoc) -{ - u16 capab_info, listen_interval; - u16 resp = WLAN_STATUS_SUCCESS; - u8 *pos, *wpa_ie; - size_t wpa_ie_len; - int send_deauth = 0, send_len, left, i; - struct sta_info *sta; - struct ieee802_11_elems elems; - - if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : - sizeof(mgmt->u.assoc_req))) { - printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)" - "\n", reassoc, (unsigned long) len); - return; - } - - if (reassoc) { - capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); - listen_interval = le_to_host16( - mgmt->u.reassoc_req.listen_interval); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "reassociation request: STA=" MACSTR - " capab_info=0x%02x " - "listen_interval=%d current_ap=" MACSTR "\n", - MAC2STR(mgmt->sa), capab_info, listen_interval, - MAC2STR(mgmt->u.reassoc_req.current_ap)); - left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); - pos = mgmt->u.reassoc_req.variable; - } else { - capab_info = le_to_host16(mgmt->u.assoc_req.capab_info); - listen_interval = le_to_host16( - mgmt->u.assoc_req.listen_interval); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "association request: STA=" MACSTR - " capab_info=0x%02x listen_interval=%d\n", - MAC2STR(mgmt->sa), capab_info, listen_interval); - left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); - pos = mgmt->u.assoc_req.variable; - } - - sta = ap_get_sta(hapd, mgmt->sa); - if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { - printf("STA " MACSTR " trying to associate before " - "authentication\n", MAC2STR(mgmt->sa)); - if (sta) { - printf(" sta: addr=" MACSTR " aid=%d flags=0x%04x\n", - MAC2STR(sta->addr), sta->aid, sta->flags); - } - send_deauth = 1; - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; - goto fail; - } - - sta->capability = capab_info; - - /* followed by SSID and Supported rates */ - if (ieee802_11_parse_elems(hapd, pos, left, &elems, 1) == ParseFailed - || !elems.ssid) { - printf("STA " MACSTR " sent invalid association request\n", - MAC2STR(sta->addr)); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - if (elems.ssid_len != hapd->conf->ssid_len || - memcmp(elems.ssid, hapd->conf->ssid, elems.ssid_len) != 0) { - printf("Station " MACSTR " tried to associate with " - "unknown SSID '", MAC2STR(sta->addr)); - ieee802_11_print_ssid(elems.ssid, elems.ssid_len); - printf("'\n"); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - if (elems.supp_rates) { - if (elems.supp_rates_len > sizeof(sta->supported_rates)) { - printf("STA " MACSTR ": Invalid supported rates " - "element length %d\n", MAC2STR(sta->addr), - elems.supp_rates_len); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); - memcpy(sta->supported_rates, elems.supp_rates, - elems.supp_rates_len); - - sta->tx_supp_rates = 0; - for (i = 0; i < elems.supp_rates_len; i++) { - if ((sta->supported_rates[i] & 0x7f) == 2) - sta->tx_supp_rates |= WLAN_RATE_1M; - if ((sta->supported_rates[i] & 0x7f) == 4) - sta->tx_supp_rates |= WLAN_RATE_2M; - if ((sta->supported_rates[i] & 0x7f) == 11) - sta->tx_supp_rates |= WLAN_RATE_5M5; - if ((sta->supported_rates[i] & 0x7f) == 22) - sta->tx_supp_rates |= WLAN_RATE_11M; - } - } else - sta->tx_supp_rates = 0xff; - - if ((hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) && elems.rsn_ie) { - wpa_ie = elems.rsn_ie; - wpa_ie_len = elems.rsn_ie_len; - } else if ((hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA) && - elems.wpa_ie) { - wpa_ie = elems.wpa_ie; - wpa_ie_len = elems.wpa_ie_len; - } else { - wpa_ie = NULL; - wpa_ie_len = 0; - } - if (hapd->conf->wpa && wpa_ie == NULL) { - printf("STA " MACSTR ": No WPA/RSN IE in association " - "request\n", MAC2STR(sta->addr)); - resp = WLAN_STATUS_INVALID_IE; - goto fail; - } - - if (hapd->conf->wpa) { - int res; - wpa_ie -= 2; - wpa_ie_len += 2; - res = wpa_validate_wpa_ie(hapd, sta, wpa_ie, wpa_ie_len, - elems.rsn_ie ? - HOSTAPD_WPA_VERSION_WPA2 : - HOSTAPD_WPA_VERSION_WPA); - if (res == WPA_INVALID_GROUP) - resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_PAIRWISE) - resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_AKMP) - resp = WLAN_STATUS_AKMP_NOT_VALID; - else if (res != WPA_IE_OK) - resp = WLAN_STATUS_INVALID_IE; - if (resp != WLAN_STATUS_SUCCESS) - goto fail; - free(sta->wpa_ie); - sta->wpa_ie = malloc(wpa_ie_len); - if (sta->wpa_ie == NULL) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - sta->wpa_ie_len = wpa_ie_len; - memcpy(sta->wpa_ie, wpa_ie, wpa_ie_len); - } - - /* get a unique AID */ - if (sta->aid > 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " old AID %d\n", sta->aid); - } else { - for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++) - if (hapd->sta_aid[sta->aid - 1] == NULL) - break; - if (sta->aid > MAX_AID_TABLE_SIZE) { - sta->aid = 0; - resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; - printf(" no room for more AIDs\n"); - goto fail; - } else { - hapd->sta_aid[sta->aid - 1] = sta; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " new AID %d\n", sta->aid); - } - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "association OK (aid %d)", sta->aid); - /* Station will be marked associated, after it acknowledges AssocResp - */ - - if (sta->last_assoc_req) - free(sta->last_assoc_req); - sta->last_assoc_req = (struct ieee80211_mgmt *) malloc(len); - if (sta->last_assoc_req) - memcpy(sta->last_assoc_req, mgmt, len); - - /* Make sure that the previously registered inactivity timer will not - * remove the STA immediately. */ - sta->timeout_next = STA_NULLFUNC; - - fail: - - /* use the queued buffer for transmission because it is large enough - * and not needed anymore */ - mgmt->frame_control = - IEEE80211_FC(WLAN_FC_TYPE_MGMT, - (send_deauth ? WLAN_FC_STYPE_DEAUTH : - (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : - WLAN_FC_STYPE_ASSOC_RESP))); - memcpy(mgmt->da, mgmt->sa, ETH_ALEN); - memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); - /* Addr3 = BSSID - already set */ - - send_len = IEEE80211_HDRLEN; - if (send_deauth) { - send_len += sizeof(mgmt->u.deauth); - mgmt->u.deauth.reason_code = host_to_le16(resp); - } else { - u8 *p; - send_len += sizeof(mgmt->u.assoc_resp); - mgmt->u.assoc_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd)); - mgmt->u.assoc_resp.status_code = host_to_le16(resp); - mgmt->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) - | BIT(14) | BIT(15)); - /* Supported rates */ - p = hostapd_eid_supp_rates(hapd, mgmt->u.assoc_resp.variable); - send_len += p - mgmt->u.assoc_resp.variable; - - /* Request TX callback */ - mgmt->frame_control |= host_to_le16(BIT(1)); - } - - if (hostapd_send_mgmt_frame(hapd, mgmt, send_len, 0) < 0) - perror("handle_assoc: send"); -} - - -static void handle_assoc_resp(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len) -{ - u16 status_code, aid; - - if (hapd->assoc_ap_state != ASSOCIATE) { - printf("Unexpected association response received from " MACSTR - "\n", MAC2STR(mgmt->sa)); - return; - } - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp)) { - printf("handle_assoc_resp - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - if (memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0 || - memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0) { - printf("Received association response from unexpected address " - "(SA=" MACSTR " BSSID=" MACSTR "\n", - MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid)); - return; - } - - status_code = le_to_host16(mgmt->u.assoc_resp.status_code); - aid = le_to_host16(mgmt->u.assoc_resp.aid); - aid &= ~(BIT(14) | BIT(15)); - - if (status_code != 0) { - printf("Association (as station) with AP " MACSTR " failed " - "(status_code=%d)\n", - MAC2STR(hapd->conf->assoc_ap_addr), status_code); - /* Try to authenticate again */ - hapd->assoc_ap_state = AUTHENTICATE; - eloop_register_timeout(5, 0, ieee802_11_sta_authenticate, - hapd, NULL); - } - - printf("Associated (as station) with AP " MACSTR " (aid=%d)\n", - MAC2STR(hapd->conf->assoc_ap_addr), aid); - hapd->assoc_ap_aid = aid; - hapd->assoc_ap_state = ASSOCIATED; - - if (hostapd_set_assoc_ap(hapd, hapd->conf->assoc_ap_addr)) { - printf("Could not set associated AP address to kernel " - "driver.\n"); - } -} - - -static void handle_disassoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len) -{ - struct sta_info *sta; - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) { - printf("handle_disassoc - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "disassocation: STA=" MACSTR " reason_code=%d\n", - MAC2STR(mgmt->sa), - le_to_host16(mgmt->u.disassoc.reason_code)); - - if (hapd->assoc_ap_state != DO_NOT_ASSOC && - memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { - printf("Assoc AP " MACSTR " sent disassociation " - "(reason_code=%d) - try to authenticate\n", - MAC2STR(hapd->conf->assoc_ap_addr), - le_to_host16(mgmt->u.disassoc.reason_code)); - hapd->assoc_ap_state = AUTHENTICATE; - ieee802_11_sta_authenticate(hapd, NULL); - eloop_register_timeout(0, 500000, ieee802_11_sta_authenticate, - hapd, NULL); - return; - } - - sta = ap_get_sta(hapd, mgmt->sa); - if (sta == NULL) { - printf("Station " MACSTR " trying to disassociate, but it " - "is not associated.\n", MAC2STR(mgmt->sa)); - return; - } - - sta->flags &= ~WLAN_STA_ASSOC; - wpa_sm_event(hapd, sta, WPA_DISASSOC); - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "disassociated"); - sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; - ieee802_1x_set_port_enabled(hapd, sta, 0); - /* Stop Accounting and IEEE 802.1X sessions, but leave the STA - * authenticated. */ - accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); - hostapd_sta_remove(hapd, sta->addr); - - if (sta->timeout_next == STA_NULLFUNC || - sta->timeout_next == STA_DISASSOC) { - sta->timeout_next = STA_DEAUTH; - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, - hapd, sta); - } -} - - -static void handle_deauth(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len) -{ - struct sta_info *sta; - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) { - printf("handle_deauth - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "deauthentication: STA=" MACSTR " reason_code=%d\n", - MAC2STR(mgmt->sa), - le_to_host16(mgmt->u.deauth.reason_code)); - - if (hapd->assoc_ap_state != DO_NOT_ASSOC && - memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { - printf("Assoc AP " MACSTR " sent deauthentication " - "(reason_code=%d) - try to authenticate\n", - MAC2STR(hapd->conf->assoc_ap_addr), - le_to_host16(mgmt->u.deauth.reason_code)); - hapd->assoc_ap_state = AUTHENTICATE; - eloop_register_timeout(0, 500000, ieee802_11_sta_authenticate, - hapd, NULL); - return; - } - - sta = ap_get_sta(hapd, mgmt->sa); - if (sta == NULL) { - printf("Station " MACSTR " trying to deauthenticate, but it " - "is not authenticated.\n", MAC2STR(mgmt->sa)); - return; - } - - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_sm_event(hapd, sta, WPA_DEAUTH); - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "deauthenticated"); - sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; - ieee802_1x_set_port_enabled(hapd, sta, 0); - ap_free_sta(hapd, sta); -} - - -static void handle_beacon(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len) -{ - struct ieee802_11_elems elems; - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) { - printf("handle_beacon - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - (void) ieee802_11_parse_elems(hapd, mgmt->u.beacon.variable, - len - (IEEE80211_HDRLEN + - sizeof(mgmt->u.beacon)), &elems, - 0); - - if (hapd->assoc_ap_state == WAIT_BEACON && - memcmp(mgmt->sa, hapd->conf->assoc_ap_addr, ETH_ALEN) == 0) { - if (elems.ssid && elems.ssid_len <= 32) { - memcpy(hapd->assoc_ap_ssid, elems.ssid, - elems.ssid_len); - hapd->assoc_ap_ssid[elems.ssid_len] = '\0'; - hapd->assoc_ap_ssid_len = elems.ssid_len; - } - ieee802_11_sta_authenticate(hapd, NULL); - } - - if (!HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_EXCESSIVE)) - return; - - printf("Beacon from " MACSTR, MAC2STR(mgmt->sa)); - if (elems.ssid) { - printf(" SSID='"); - ieee802_11_print_ssid(elems.ssid, elems.ssid_len); - printf("'"); - } - if (elems.ds_params && elems.ds_params_len == 1) - printf(" CHAN=%d", elems.ds_params[0]); - printf("\n"); -} - - -void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype) -{ - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; - - if (stype == WLAN_FC_STYPE_BEACON) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, "mgmt::beacon\n"); - handle_beacon(hapd, mgmt, len); - return; - } - - if (memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0 && - (hapd->assoc_ap_state == DO_NOT_ASSOC || - memcmp(mgmt->bssid, hapd->conf->assoc_ap_addr, ETH_ALEN) != 0)) { - printf("MGMT: BSSID=" MACSTR " not our address\n", - MAC2STR(mgmt->bssid)); - return; - } - - - if (stype == WLAN_FC_STYPE_PROBE_REQ) { - printf("mgmt::probe_req\n"); - return; - } - - if (memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { - printf("MGMT: DA=" MACSTR " not our address\n", - MAC2STR(mgmt->da)); - return; - } - - switch (stype) { - case WLAN_FC_STYPE_AUTH: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::auth\n"); - handle_auth(hapd, mgmt, len); - break; - case WLAN_FC_STYPE_ASSOC_REQ: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::assoc_req\n"); - handle_assoc(hapd, mgmt, len, 0); - break; - case WLAN_FC_STYPE_ASSOC_RESP: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::assoc_resp\n"); - handle_assoc_resp(hapd, mgmt, len); - break; - case WLAN_FC_STYPE_REASSOC_REQ: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::reassoc_req\n"); - handle_assoc(hapd, mgmt, len, 1); - break; - case WLAN_FC_STYPE_DISASSOC: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::disassoc\n"); - handle_disassoc(hapd, mgmt, len); - break; - case WLAN_FC_STYPE_DEAUTH: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::deauth\n"); - handle_deauth(hapd, mgmt, len); - break; - default: - printf("unknown mgmt frame subtype %d\n", stype); - break; - } -} - - -static void handle_auth_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len, int ok) -{ - u16 auth_alg, auth_transaction, status_code; - struct sta_info *sta; - - if (!ok) { - hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_NOTICE, - "did not acknowledge authentication response"); - return; - } - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - printf("handle_auth_cb - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - auth_alg = le_to_host16(mgmt->u.auth.auth_alg); - auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); - status_code = le_to_host16(mgmt->u.auth.status_code); - - sta = ap_get_sta(hapd, mgmt->da); - if (!sta) { - printf("handle_auth_cb: STA " MACSTR " not found\n", - MAC2STR(mgmt->da)); - return; - } - - if (status_code == WLAN_STATUS_SUCCESS && - ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || - (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "authenticated"); - sta->flags |= WLAN_STA_AUTH; - } -} - - -static void handle_assoc_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len, int reassoc, int ok) -{ - u16 status; - struct sta_info *sta; - int new_assoc = 1; - - if (!ok) { - hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "did not acknowledge association response"); - return; - } - - if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : - sizeof(mgmt->u.assoc_req))) { - printf("handle_assoc_cb(reassoc=%d) - too short payload " - "(len=%lu)\n", reassoc, (unsigned long) len); - return; - } - - if (reassoc) - status = le_to_host16(mgmt->u.reassoc_resp.status_code); - else - status = le_to_host16(mgmt->u.assoc_resp.status_code); - - sta = ap_get_sta(hapd, mgmt->da); - if (!sta) { - printf("handle_assoc_cb: STA " MACSTR " not found\n", - MAC2STR(mgmt->da)); - return; - } - - if (status != WLAN_STATUS_SUCCESS) - goto fail; - - /* Stop previous accounting session, if one is started, and allocate - * new session id for the new session. */ - accounting_sta_stop(hapd, sta); - accounting_sta_get_id(hapd, sta); - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, - "associated (aid %d, accounting session %08X-%08X)", - sta->aid, sta->acct_session_id_hi, - sta->acct_session_id_lo); - - if (sta->flags & WLAN_STA_ASSOC) - new_assoc = 0; - sta->flags |= WLAN_STA_ASSOC; - - if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, - sta->tx_supp_rates)) { - printf("Could not add station to kernel driver.\n"); - } - - wpa_sm_event(hapd, sta, WPA_ASSOC); - hostapd_new_assoc_sta(hapd, sta, !new_assoc); - - ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); - - fail: - /* Copy of the association request is not needed anymore */ - if (sta->last_assoc_req) { - free(sta->last_assoc_req); - sta->last_assoc_req = NULL; - } -} - - -void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, - u16 stype, int ok) -{ - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; - - switch (stype) { - case WLAN_FC_STYPE_AUTH: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::auth cb\n"); - handle_auth_cb(hapd, mgmt, len, ok); - break; - case WLAN_FC_STYPE_ASSOC_RESP: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "mgmt::assoc_resp cb\n"); - handle_assoc_cb(hapd, mgmt, len, 0, ok); - break; - case WLAN_FC_STYPE_REASSOC_RESP: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "mgmt::reassoc_resp cb\n"); - handle_assoc_cb(hapd, mgmt, len, 1, ok); - break; - default: - printf("unknown mgmt cb frame subtype %d\n", stype); - break; - } -} - - -static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx, - void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - hapd->tkip_countermeasures = 0; - hostapd_set_countermeasures(hapd, 0); - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended"); -} - - -static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) -{ - struct sta_info *sta; - - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated"); - - if (hapd->wpa_auth) - hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; - hapd->tkip_countermeasures = 1; - hostapd_set_countermeasures(hapd, 1); - wpa_gtk_rekey(hapd); - eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); - eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, - hapd, NULL); - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - hostapd_sta_deauth(hapd, sta->addr, - WLAN_REASON_MICHAEL_MIC_FAILURE); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | - WLAN_STA_AUTHORIZED); - hostapd_sta_remove(hapd, sta->addr); - } -} - - -void ieee80211_michael_mic_failure(struct hostapd_data *hapd, u8 *addr, - int local) -{ - time_t now; - - if (addr && local) { - struct sta_info *sta = ap_get_sta(hapd, addr); - if (sta != NULL) { - sta->dot11RSNAStatsTKIPLocalMICFailures++; - hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, - "Michael MIC failure detected in " - "received frame"); - } else { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "MLME-MICHAELMICFAILURE.indication " - "for not associated STA (" MACSTR - ") ignored\n", MAC2STR(addr)); - return; - } - } - - time(&now); - if (now > hapd->michael_mic_failure + 60) { - hapd->michael_mic_failures = 1; - } else { - hapd->michael_mic_failures++; - if (hapd->michael_mic_failures > 1) - ieee80211_tkip_countermeasures_start(hapd); - } - hapd->michael_mic_failure = now; -} - - -int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) -{ - /* TODO */ - return 0; -} - - -int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen) -{ - /* TODO */ - return 0; -} diff --git a/contrib/hostapd-0.4.9/ieee802_11.h b/contrib/hostapd-0.4.9/ieee802_11.h deleted file mode 100644 index 39cc34207b..0000000000 --- a/contrib/hostapd-0.4.9/ieee802_11.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef IEEE802_11_H -#define IEEE802_11_H - - -struct ieee80211_mgmt { - u16 frame_control; - u16 duration; - u8 da[6]; - u8 sa[6]; - u8 bssid[6]; - u16 seq_ctrl; - union { - struct { - u16 auth_alg; - u16 auth_transaction; - u16 status_code; - /* possibly followed by Challenge text */ - u8 variable[0]; - } __attribute__ ((packed)) auth; - struct { - u16 reason_code; - } __attribute__ ((packed)) deauth; - struct { - u16 capab_info; - u16 listen_interval; - /* followed by SSID and Supported rates */ - u8 variable[0]; - } __attribute__ ((packed)) assoc_req; - struct { - u16 capab_info; - u16 status_code; - u16 aid; - /* followed by Supported rates */ - u8 variable[0]; - } __attribute__ ((packed)) assoc_resp, reassoc_resp; - struct { - u16 capab_info; - u16 listen_interval; - u8 current_ap[6]; - /* followed by SSID and Supported rates */ - u8 variable[0]; - } __attribute__ ((packed)) reassoc_req; - struct { - u16 reason_code; - } __attribute__ ((packed)) disassoc; - struct { - u8 timestamp[8]; - u16 beacon_int; - u16 capab_info; - /* followed by some of SSID, Supported rates, - * FH Params, DS Params, CF Params, IBSS Params, TIM */ - u8 variable[0]; - } __attribute__ ((packed)) beacon; - } u; -} __attribute__ ((packed)); - - -/* Parsed Information Elements */ -struct ieee802_11_elems { - u8 *ssid; - u8 ssid_len; - u8 *supp_rates; - u8 supp_rates_len; - u8 *fh_params; - u8 fh_params_len; - u8 *ds_params; - u8 ds_params_len; - u8 *cf_params; - u8 cf_params_len; - u8 *tim; - u8 tim_len; - u8 *ibss_params; - u8 ibss_params_len; - u8 *challenge; - u8 challenge_len; - u8 *wpa_ie; - u8 wpa_ie_len; - u8 *rsn_ie; - u8 rsn_ie_len; -}; - -typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; - - -void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, - u16 stype); -void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, - u16 stype, int ok); -ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, - size_t len, - struct ieee802_11_elems *elems, - int show_errors); -void ieee80211_michael_mic_failure(struct hostapd_data *hapd, u8 *addr, - int local); -int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); -int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen); - -#endif /* IEEE802_11_H */ diff --git a/contrib/hostapd-0.4.9/ieee802_11_auth.c b/contrib/hostapd-0.4.9/ieee802_11_auth.c deleted file mode 100644 index 296c64017a..0000000000 --- a/contrib/hostapd-0.4.9/ieee802_11_auth.c +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.11 authentication (ACL) - * Copyright (c) 2003-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hostapd.h" -#include "ieee802_11.h" -#include "ieee802_11_auth.h" -#include "radius.h" -#include "radius_client.h" -#include "eloop.h" -#include "hostap_common.h" - -#define RADIUS_ACL_TIMEOUT 30 - - -struct hostapd_cached_radius_acl { - time_t timestamp; - macaddr addr; - int accepted; /* HOSTAPD_ACL_* */ - struct hostapd_cached_radius_acl *next; - u32 session_timeout; - u32 acct_interim_interval; -}; - - -struct hostapd_acl_query_data { - time_t timestamp; - u8 radius_id; - macaddr addr; - u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ - size_t auth_msg_len; - struct hostapd_acl_query_data *next; -}; - - -static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) -{ - struct hostapd_cached_radius_acl *prev; - - while (acl_cache) { - prev = acl_cache; - acl_cache = acl_cache->next; - free(prev); - } -} - - -static int hostapd_acl_cache_get(struct hostapd_data *hapd, u8 *addr, - u32 *session_timeout, - u32 *acct_interim_interval) -{ - struct hostapd_cached_radius_acl *entry; - time_t now; - - time(&now); - entry = hapd->acl_cache; - - while (entry) { - if (memcmp(entry->addr, addr, ETH_ALEN) == 0) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) - return -1; /* entry has expired */ - if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) - *session_timeout = entry->session_timeout; - *acct_interim_interval = entry->acct_interim_interval; - return entry->accepted; - } - - entry = entry->next; - } - - return -1; -} - - -static void hostapd_acl_query_free(struct hostapd_acl_query_data *query) -{ - if (query == NULL) - return; - free(query->auth_msg); - free(query); -} - - -static int hostapd_radius_acl_query(hostapd *hapd, u8 *addr, - struct hostapd_acl_query_data *query) -{ - struct radius_msg *msg; - char buf[128]; - - query->radius_id = radius_client_get_id(hapd->radius); - msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id); - if (msg == NULL) - return -1; - - radius_msg_make_authenticator(msg, addr, ETH_ALEN); - - snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, - strlen(buf))) { - printf("Could not add User-Name\n"); - goto fail; - } - - if (!radius_msg_add_attr_user_password( - msg, (u8 *) buf, strlen(buf), - hapd->conf->radius->auth_server->shared_secret, - hapd->conf->radius->auth_server->shared_secret_len)) { - printf("Could not add User-Password\n"); - goto fail; - } - - if (hapd->conf->own_ip_addr.af == AF_INET && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - printf("Could not add NAS-IP-Address\n"); - goto fail; - } - -#ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - printf("Could not add NAS-IPv6-Address\n"); - goto fail; - } -#endif /* CONFIG_IPV6 */ - - if (hapd->conf->nas_identifier && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, - (u8 *) hapd->conf->nas_identifier, - strlen(hapd->conf->nas_identifier))) { - printf("Could not add NAS-Identifier\n"); - goto fail; - } - - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, strlen(buf))) { - printf("Could not add Called-Station-Id\n"); - goto fail; - } - - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(addr)); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, strlen(buf))) { - printf("Could not add Calling-Station-Id\n"); - goto fail; - } - - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - printf("Could not add NAS-Port-Type\n"); - goto fail; - } - - snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, strlen(buf))) { - printf("Could not add Connect-Info\n"); - goto fail; - } - - radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr); - return 0; - - fail: - radius_msg_free(msg); - free(msg); - return -1; -} - - -int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len, - u32 *session_timeout, u32 *acct_interim_interval) -{ - *session_timeout = 0; - *acct_interim_interval = 0; - - if (hostapd_maclist_found(hapd->conf->accept_mac, - hapd->conf->num_accept_mac, addr)) - return HOSTAPD_ACL_ACCEPT; - - if (hostapd_maclist_found(hapd->conf->deny_mac, - hapd->conf->num_deny_mac, addr)) - return HOSTAPD_ACL_REJECT; - - if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) - return HOSTAPD_ACL_ACCEPT; - if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) - return HOSTAPD_ACL_REJECT; - - if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) { - struct hostapd_acl_query_data *query; - - /* Check whether ACL cache has an entry for this station */ - int res = hostapd_acl_cache_get(hapd, addr, session_timeout, - acct_interim_interval); - if (res == HOSTAPD_ACL_ACCEPT || - res == HOSTAPD_ACL_ACCEPT_TIMEOUT) - return res; - if (res == HOSTAPD_ACL_REJECT) - return HOSTAPD_ACL_REJECT; - - query = hapd->acl_queries; - while (query) { - if (memcmp(query->addr, addr, ETH_ALEN) == 0) { - /* pending query in RADIUS retransmit queue; - * do not generate a new one */ - return HOSTAPD_ACL_PENDING; - } - query = query->next; - } - - if (!hapd->conf->radius->auth_server) - return HOSTAPD_ACL_REJECT; - - /* No entry in the cache - query external RADIUS server */ - query = malloc(sizeof(*query)); - if (query == NULL) { - printf("malloc for query data failed\n"); - return HOSTAPD_ACL_REJECT; - } - memset(query, 0, sizeof(*query)); - time(&query->timestamp); - memcpy(query->addr, addr, ETH_ALEN); - if (hostapd_radius_acl_query(hapd, addr, query)) { - printf("Failed to send Access-Request for ACL " - "query.\n"); - hostapd_acl_query_free(query); - return HOSTAPD_ACL_REJECT; - } - - query->auth_msg = malloc(len); - if (query->auth_msg == NULL) { - printf("Failed to allocate memory for auth frame.\n"); - hostapd_acl_query_free(query); - return HOSTAPD_ACL_REJECT; - } - memcpy(query->auth_msg, msg, len); - query->auth_msg_len = len; - query->next = hapd->acl_queries; - hapd->acl_queries = query; - - /* Queued data will be processed in hostapd_acl_recv_radius() - * when RADIUS server replies to the sent Access-Request. */ - return HOSTAPD_ACL_PENDING; - } - - return HOSTAPD_ACL_REJECT; -} - - -static void hostapd_acl_expire_cache(hostapd *hapd, time_t now) -{ - struct hostapd_cached_radius_acl *prev, *entry, *tmp; - - prev = NULL; - entry = hapd->acl_cache; - - while (entry) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Cached ACL entry for " MACSTR - " has expired.\n", MAC2STR(entry->addr)); - if (prev) - prev->next = entry->next; - else - hapd->acl_cache = entry->next; - - tmp = entry; - entry = entry->next; - free(tmp); - continue; - } - - prev = entry; - entry = entry->next; - } -} - - -static void hostapd_acl_expire_queries(hostapd *hapd, time_t now) -{ - struct hostapd_acl_query_data *prev, *entry, *tmp; - - prev = NULL; - entry = hapd->acl_queries; - - while (entry) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "ACL query for " MACSTR - " has expired.\n", MAC2STR(entry->addr)); - if (prev) - prev->next = entry->next; - else - hapd->acl_queries = entry->next; - - tmp = entry; - entry = entry->next; - hostapd_acl_query_free(tmp); - continue; - } - - prev = entry; - entry = entry->next; - } -} - - -static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) -{ - hostapd *hapd = eloop_ctx; - time_t now; - - time(&now); - hostapd_acl_expire_cache(hapd, now); - hostapd_acl_expire_queries(hapd, now); - - eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); -} - - -/* Return 0 if RADIUS message was a reply to ACL query (and was processed here) - * or -1 if not. */ -static RadiusRxResult -hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, - void *data) -{ - struct hostapd_data *hapd = data; - struct hostapd_acl_query_data *query, *prev; - struct hostapd_cached_radius_acl *cache; - - query = hapd->acl_queries; - prev = NULL; - while (query) { - if (query->radius_id == msg->hdr->identifier) - break; - prev = query; - query = query->next; - } - if (query == NULL) - return RADIUS_RX_UNKNOWN; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Found matching Access-Request " - "for RADIUS message (id=%d)\n", query->radius_id); - - if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { - printf("Incoming RADIUS packet did not have correct " - "authenticator - dropped\n"); - return RADIUS_RX_INVALID_AUTHENTICATOR; - } - - if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && - msg->hdr->code != RADIUS_CODE_ACCESS_REJECT) { - printf("Unknown RADIUS message code %d to ACL query\n", - msg->hdr->code); - return RADIUS_RX_UNKNOWN; - } - - /* Insert Accept/Reject info into ACL cache */ - cache = malloc(sizeof(*cache)); - if (cache == NULL) { - printf("Failed to add ACL cache entry\n"); - goto done; - } - memset(cache, 0, sizeof(*cache)); - time(&cache->timestamp); - memcpy(cache->addr, query->addr, sizeof(cache->addr)); - if (msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { - if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, - &cache->session_timeout) == 0) - cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT; - else - cache->accepted = HOSTAPD_ACL_ACCEPT; - - if (radius_msg_get_attr_int32( - msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, - &cache->acct_interim_interval) == 0 && - cache->acct_interim_interval < 60) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Ignored too " - "small Acct-Interim-Interval %d for " - "STA " MACSTR "\n", - cache->acct_interim_interval, - MAC2STR(query->addr)); - cache->acct_interim_interval = 0; - } - } else - cache->accepted = HOSTAPD_ACL_REJECT; - cache->next = hapd->acl_cache; - hapd->acl_cache = cache; - - /* Re-send original authentication frame for 802.11 processing */ - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Re-sending authentication frame " - "after successful RADIUS ACL query\n"); - ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, - WLAN_FC_STYPE_AUTH); - - done: - if (prev == NULL) - hapd->acl_queries = query->next; - else - prev->next = query->next; - - hostapd_acl_query_free(query); - - return RADIUS_RX_PROCESSED; -} - - -int hostapd_acl_init(hostapd *hapd) -{ - if (radius_client_register(hapd->radius, RADIUS_AUTH, - hostapd_acl_recv_radius, hapd)) - return -1; - - eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); - - return 0; -} - - -void hostapd_acl_deinit(hostapd *hapd) -{ - struct hostapd_acl_query_data *query, *prev; - - hostapd_acl_cache_free(hapd->acl_cache); - - query = hapd->acl_queries; - while (query) { - prev = query; - query = query->next; - hostapd_acl_query_free(prev); - } -} diff --git a/contrib/hostapd-0.4.9/ieee802_11_auth.h b/contrib/hostapd-0.4.9/ieee802_11_auth.h deleted file mode 100644 index 90adc8f174..0000000000 --- a/contrib/hostapd-0.4.9/ieee802_11_auth.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef IEEE802_11_AUTH_H -#define IEEE802_11_AUTH_H - -enum { - HOSTAPD_ACL_REJECT = 0, - HOSTAPD_ACL_ACCEPT = 1, - HOSTAPD_ACL_PENDING = 2, - HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 -}; - -int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len, - u32 *session_timeout, u32 *acct_interim_interval); -int hostapd_acl_init(hostapd *hapd); -void hostapd_acl_deinit(hostapd *hapd); - -#endif /* IEEE802_11_AUTH_H */ diff --git a/contrib/hostapd-0.4.9/ieee802_1x.c b/contrib/hostapd-0.4.9/ieee802_1x.c deleted file mode 100644 index b5792e3ef0..0000000000 --- a/contrib/hostapd-0.4.9/ieee802_1x.c +++ /dev/null @@ -1,1788 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.1X Authenticator - * Copyright (c) 2002-2006, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hostapd.h" -#include "ieee802_1x.h" -#include "accounting.h" -#include "radius.h" -#include "radius_client.h" -#include "eapol_sm.h" -#include "md5.h" -#include "rc4.h" -#include "eloop.h" -#include "sta_info.h" -#include "wpa.h" -#include "driver.h" -#include "eap.h" - - -static void ieee802_1x_new_auth_session(struct hostapd_data *hapd, - struct sta_info *sta); - - -static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type, - u8 *data, size_t datalen) -{ - u8 *buf; - struct ieee802_1x_hdr *xhdr; - size_t len; - int encrypt = 0; - - len = sizeof(*xhdr) + datalen; - buf = malloc(len); - if (buf == NULL) { - printf("malloc() failed for ieee802_1x_send(len=%lu)\n", - (unsigned long) len); - return; - } - - memset(buf, 0, len); -#if 0 - /* TODO: - * According to IEEE 802.1aa/D4 EAPOL-Key should be sent before any - * remaining EAP frames, if possible. This would allow rest of the - * frames to be encrypted. This code could be used to request - * encryption from the kernel driver. */ - if (sta->eapol_sm && - sta->eapol_sm->be_auth.state == BE_AUTH_SUCCESS && - sta->eapol_sm->keyTxEnabled) - encrypt = 1; -#endif - - xhdr = (struct ieee802_1x_hdr *) buf; - xhdr->version = hapd->conf->eapol_version; - xhdr->type = type; - xhdr->length = htons(datalen); - - if (datalen > 0 && data != NULL) - memcpy(xhdr + 1, data, datalen); - - if (sta->wpa_sm && sta->wpa_sm->pairwise_set) - encrypt = 1; - if (sta->flags & WLAN_STA_PREAUTH) { - rsn_preauth_send(hapd, sta, buf, len); - } else { - hostapd_send_eapol(hapd, sta->addr, buf, len, encrypt); - } - - free(buf); -} - - -void ieee802_1x_set_sta_authorized(hostapd *hapd, struct sta_info *sta, - int authorized) -{ - if (sta->flags & WLAN_STA_PREAUTH) - return; - - if (authorized) { - sta->flags |= WLAN_STA_AUTHORIZED; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "authorizing port"); - } else { - sta->flags &= ~WLAN_STA_AUTHORIZED; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "unauthorizing port"); - } - - hostapd_set_sta_authorized(hapd, sta->addr, authorized); - if (authorized) - accounting_sta_start(hapd, sta); -} - - -static void ieee802_1x_eap_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct sta_info *sta = eloop_ctx; - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - hostapd_logger(sm->hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "EAP timeout"); - sm->eapTimeout = TRUE; - eapol_sm_step(sm); -} - - -void ieee802_1x_request_identity(struct hostapd_data *hapd, - struct sta_info *sta) -{ - u8 *buf; - struct eap_hdr *eap; - int tlen; - u8 *pos; - struct eapol_state_machine *sm = sta->eapol_sm; - - if (hapd->conf->eap_server) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Integrated EAP server in " - "use - do not generate EAP-Request/Identity\n"); - return; - } - - if (sm == NULL || !sm->auth_pae.eapRestart) - return; - - ieee802_1x_new_auth_session(hapd, sta); - - tlen = sizeof(*eap) + 1 + hapd->conf->eap_req_id_text_len; - - buf = malloc(tlen); - if (buf == NULL) { - printf("Could not allocate memory for identity request\n"); - return; - } - - memset(buf, 0, tlen); - - eap = (struct eap_hdr *) buf; - eap->code = EAP_CODE_REQUEST; - eap->identifier = ++sm->currentId; - eap->length = htons(tlen); - pos = (u8 *) (eap + 1); - *pos++ = EAP_TYPE_IDENTITY; - if (hapd->conf->eap_req_id_text) { - memcpy(pos, hapd->conf->eap_req_id_text, - hapd->conf->eap_req_id_text_len); - } - - sm->be_auth.eapReq = TRUE; - free(sm->last_eap_radius); - sm->last_eap_radius = buf; - sm->last_eap_radius_len = tlen; - - eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); - eloop_register_timeout(30, 0, ieee802_1x_eap_timeout, sta, NULL); - sm->eapTimeout = FALSE; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Generated EAP Request-Identity for " MACSTR - " (identifier %d, timeout 30)\n", MAC2STR(sta->addr), - eap->identifier); - - sm->auth_pae.eapRestart = FALSE; -} - - -void ieee802_1x_tx_canned_eap(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ - struct eap_hdr eap; - struct eapol_state_machine *sm = sta->eapol_sm; - - memset(&eap, 0, sizeof(eap)); - - eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; - eap.identifier = 1; - if (sm && sm->last_eap_radius) { - struct eap_hdr *hdr = (struct eap_hdr *) sm->last_eap_radius; - eap.identifier = hdr->identifier + 1; - } - eap.length = htons(sizeof(eap)); - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Sending canned EAP packet %s to " MACSTR - " (identifier %d)\n", success ? "SUCCESS" : "FAILURE", - MAC2STR(sta->addr), eap.identifier); - ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAP_PACKET, (u8 *) &eap, - sizeof(eap)); - if (sm) - sm->dot1xAuthEapolFramesTx++; -} - - -void ieee802_1x_tx_req(struct hostapd_data *hapd, struct sta_info *sta) -{ - struct eap_hdr *eap; - struct eapol_state_machine *sm = sta->eapol_sm; - u8 *type; - if (sm == NULL) - return; - - if (sm->last_eap_radius == NULL) { - printf("Error: TxReq called for station " MACSTR ", but there " - "is no EAP request from the authentication server\n", - MAC2STR(sm->addr)); - return; - } - - eap = (struct eap_hdr *) sm->last_eap_radius; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Sending EAP Packet to " MACSTR - " (identifier %d)\n", MAC2STR(sm->addr), - eap->identifier); - sm->currentId = eap->identifier; - ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAP_PACKET, - sm->last_eap_radius, sm->last_eap_radius_len); - sm->dot1xAuthEapolFramesTx++; - type = (u8 *) (eap + 1); - if (sm->last_eap_radius_len > sizeof(*eap) && - *type == EAP_TYPE_IDENTITY) - sm->dot1xAuthEapolReqIdFramesTx++; - else - sm->dot1xAuthEapolReqFramesTx++; -} - - -void hostapd_get_ntp_timestamp(u8 *buf) -{ - struct timeval now; - u32 sec, usec; - - /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */ - gettimeofday(&now, NULL); - sec = htonl(now.tv_sec + 2208988800U); /* Epoch to 1900 */ - /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */ - usec = now.tv_usec; - usec = htonl(4295 * usec - (usec >> 5) - (usec >> 9)); - memcpy(buf, (u8 *) &sec, 4); - memcpy(buf + 4, (u8 *) &usec, 4); -} - - -static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, - int index, int broadcast, - u8 *key_data, size_t key_len) -{ - u8 *buf, *ekey; - struct ieee802_1x_hdr *hdr; - struct ieee802_1x_eapol_key *key; - size_t len, ekey_len; - struct eapol_state_machine *sm = sta->eapol_sm; - - if (sm == NULL) - return; - - len = sizeof(*key) + key_len; - buf = malloc(sizeof(*hdr) + len); - if (buf == NULL) - return; - - memset(buf, 0, sizeof(*hdr) + len); - hdr = (struct ieee802_1x_hdr *) buf; - key = (struct ieee802_1x_eapol_key *) (hdr + 1); - key->type = EAPOL_KEY_TYPE_RC4; - key->key_length = htons(key_len); - hostapd_get_ntp_timestamp(key->replay_counter); - - if (hostapd_get_rand(key->key_iv, sizeof(key->key_iv))) { - printf("Could not get random numbers\n"); - free(buf); - return; - } - - key->key_index = index | (broadcast ? 0 : BIT(7)); - if (hapd->conf->eapol_key_index_workaround) { - /* According to some information, WinXP Supplicant seems to - * interpret bit7 as an indication whether the key is to be - * activated, so make it possible to enable workaround that - * sets this bit for all keys. */ - key->key_index |= BIT(7); - } - - /* Key is encrypted using "Key-IV + sm->eapol_key_crypt" as the - * RC4-key */ - memcpy((u8 *) (key + 1), key_data, key_len); - ekey_len = sizeof(key->key_iv) + sm->eapol_key_crypt_len; - ekey = malloc(ekey_len); - if (ekey == NULL) { - printf("Could not encrypt key\n"); - free(buf); - return; - } - memcpy(ekey, key->key_iv, sizeof(key->key_iv)); - memcpy(ekey + sizeof(key->key_iv), sm->eapol_key_crypt, - sm->eapol_key_crypt_len); - rc4((u8 *) (key + 1), key_len, ekey, ekey_len); - free(ekey); - - /* This header is needed here for HMAC-MD5, but it will be regenerated - * in ieee802_1x_send() */ - hdr->version = hapd->conf->eapol_version; - hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; - hdr->length = htons(len); - hmac_md5(sm->eapol_key_sign, sm->eapol_key_sign_len, - buf, sizeof(*hdr) + len, - key->key_signature); - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Sending EAPOL-Key to " MACSTR - " (%s index=%d)\n", MAC2STR(sm->addr), - broadcast ? "broadcast" : "unicast", index); - ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len); - if (sta->eapol_sm) - sta->eapol_sm->dot1xAuthEapolFramesTx++; - free(buf); -} - - -void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) -{ - struct eapol_state_machine *sm = sta->eapol_sm; - - if (sm == NULL || !sm->eapol_key_sign || !sm->eapol_key_crypt) - return; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR "\n", - MAC2STR(sta->addr)); - - if (hapd->default_wep_key) - ieee802_1x_tx_key_one(hapd, sta, hapd->default_wep_key_idx, 1, - hapd->default_wep_key, - hapd->conf->default_wep_key_len); - - if (hapd->conf->individual_wep_key_len > 0) { - u8 *ikey; - ikey = malloc(hapd->conf->individual_wep_key_len); - if (ikey == NULL || - hostapd_get_rand(ikey, - hapd->conf->individual_wep_key_len)) { - printf("Could not generate random individual WEP " - "key.\n"); - free(ikey); - return; - } - - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) - hostapd_hexdump("Individual WEP key", - ikey, - hapd->conf->individual_wep_key_len); - - ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey, - hapd->conf->individual_wep_key_len); - - /* TODO: set encryption in TX callback, i.e., only after STA - * has ACKed EAPOL-Key frame */ - if (hostapd_set_encryption(hapd, "WEP", sta->addr, 0, ikey, - hapd->conf-> - individual_wep_key_len)) { - printf("Could not set individual WEP encryption.\n"); - } - - free(ikey); - } -} - - -static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, - u8 *eap, size_t len) -{ - struct radius_msg *msg; - char buf[128]; - struct eapol_state_machine *sm = sta->eapol_sm; - - if (sm == NULL) - return; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Encapsulating EAP message into a RADIUS packet\n"); - - sm->radius_identifier = radius_client_get_id(hapd->radius); - msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, - sm->radius_identifier); - if (msg == NULL) { - printf("Could not create net RADIUS packet\n"); - return; - } - - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); - - if (sm->identity && - !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, - sm->identity, sm->identity_len)) { - printf("Could not add User-Name\n"); - goto fail; - } - - if (hapd->conf->own_ip_addr.af == AF_INET && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { - printf("Could not add NAS-IP-Address\n"); - goto fail; - } - -#ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { - printf("Could not add NAS-IPv6-Address\n"); - goto fail; - } -#endif /* CONFIG_IPV6 */ - - if (hapd->conf->nas_identifier && - !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, - (u8 *) hapd->conf->nas_identifier, - strlen(hapd->conf->nas_identifier))) { - printf("Could not add NAS-Identifier\n"); - goto fail; - } - - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { - printf("Could not add NAS-Port\n"); - goto fail; - } - - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, - (u8 *) buf, strlen(buf))) { - printf("Could not add Called-Station-Id\n"); - goto fail; - } - - snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, - MAC2STR(sta->addr)); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, - (u8 *) buf, strlen(buf))) { - printf("Could not add Calling-Station-Id\n"); - goto fail; - } - - /* TODO: should probably check MTU from driver config; 2304 is max for - * IEEE 802.11, but use 1400 to avoid problems with too large packets - */ - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { - printf("Could not add Framed-MTU\n"); - goto fail; - } - - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, - RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { - printf("Could not add NAS-Port-Type\n"); - goto fail; - } - - snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, strlen(buf))) { - printf("Could not add Connect-Info\n"); - goto fail; - } - - if (eap && !radius_msg_add_eap(msg, eap, len)) { - printf("Could not add EAP-Message\n"); - goto fail; - } - - /* State attribute must be copied if and only if this packet is - * Access-Request reply to the previous Access-Challenge */ - if (sm->last_recv_radius && sm->last_recv_radius->hdr->code == - RADIUS_CODE_ACCESS_CHALLENGE) { - int res = radius_msg_copy_attr(msg, sm->last_recv_radius, - RADIUS_ATTR_STATE); - if (res < 0) { - printf("Could not copy State attribute from previous " - "Access-Challenge\n"); - goto fail; - } - if (res > 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " Copied RADIUS State Attribute\n"); - } - } - - radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr); - return; - - fail: - radius_msg_free(msg); - free(msg); -} - - -static char *eap_type_text(u8 type) -{ - switch (type) { - case EAP_TYPE_IDENTITY: return "Identity"; - case EAP_TYPE_NOTIFICATION: return "Notification"; - case EAP_TYPE_NAK: return "Nak"; - case EAP_TYPE_MD5: return "MD5-Challenge"; - case EAP_TYPE_OTP: return "One-Time Password"; - case EAP_TYPE_GTC: return "Generic Token Card"; - case EAP_TYPE_TLS: return "TLS"; - case EAP_TYPE_TTLS: return "TTLS"; - case EAP_TYPE_PEAP: return "PEAP"; - default: return "Unknown"; - } -} - - -static void handle_eap_response(hostapd *hapd, struct sta_info *sta, - struct eap_hdr *eap, u8 *data, size_t len) -{ - u8 type; - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - - if (eap->identifier != sm->currentId) { - hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, - "EAP Identifier of the Response-Identity does " - "not match (was %d, expected %d) - ignored", - eap->identifier, sm->currentId); - return; - } - - if (len < 1) { - printf("handle_eap_response: too short response data\n"); - return; - } - - eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); - - free(sm->last_eap_supp); - sm->last_eap_supp_len = sizeof(*eap) + len; - sm->last_eap_supp = (u8 *) malloc(sm->last_eap_supp_len); - if (sm->last_eap_supp == NULL) { - printf("Could not alloc memory for last EAP Response\n"); - return; - } - memcpy(sm->last_eap_supp, eap, sizeof(*eap)); - memcpy(sm->last_eap_supp + sizeof(*eap), data, len); - - type = data[0]; - data++; - len--; - - hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d " - "id=%d len=%d) from STA: EAP Response-%s (%d)", - eap->code, eap->identifier, ntohs(eap->length), - eap_type_text(type), type); - - if (type == EAP_TYPE_IDENTITY) { - char *buf, *pos; - int i; - buf = malloc(4 * len + 1); - if (buf) { - pos = buf; - for (i = 0; i < len; i++) { - if (data[i] >= 32 && data[i] < 127) - *pos++ = data[i]; - else { - snprintf(pos, 5, "{%02x}", data[i]); - pos += 4; - } - } - *pos = '\0'; - hostapd_logger(hapd, sm->addr, - HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, - "STA identity '%s'", buf); - free(buf); - } - - sm->rx_identity = TRUE; - sm->dot1xAuthEapolRespIdFramesRx++; - - /* Save station identity for future RADIUS packets */ - free(sm->identity); - sm->identity = malloc(len + 1); - if (sm->identity) { - memcpy(sm->identity, data, len); - sm->identity[len] = '\0'; - sm->identity_len = len; - } - } else - sm->dot1xAuthEapolRespFramesRx++; - - sm->eapolEap = TRUE; -} - - -/* Process incoming EAP packet from Supplicant */ -static void handle_eap(hostapd *hapd, struct sta_info *sta, u8 *buf, - size_t len) -{ - struct eap_hdr *eap; - u16 eap_len; - - if (len < sizeof(*eap)) { - printf(" too short EAP packet\n"); - return; - } - - eap = (struct eap_hdr *) buf; - - eap_len = ntohs(eap->length); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " EAP: code=%d identifier=%d length=%d", - eap->code, eap->identifier, eap_len); - if (eap_len < sizeof(*eap)) { - printf(" Invalid EAP length\n"); - return; - } else if (eap_len > len) { - printf(" Too short frame to contain this EAP packet\n"); - return; - } else if (eap_len < len) { - printf(" Ignoring %lu extra bytes after EAP packet\n", - (unsigned long) len - eap_len); - } - - eap_len -= sizeof(*eap); - - switch (eap->code) { - case EAP_CODE_REQUEST: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (request)\n"); - return; - case EAP_CODE_RESPONSE: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (response)\n"); - handle_eap_response(hapd, sta, eap, (u8 *) (eap + 1), eap_len); - break; - case EAP_CODE_SUCCESS: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (success)\n"); - return; - case EAP_CODE_FAILURE: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (failure)\n"); - return; - default: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " (unknown code)\n"); - return; - } -} - - -/* Process the EAPOL frames from the Supplicant */ -void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, - size_t len) -{ - struct sta_info *sta; - struct ieee802_1x_hdr *hdr; - struct ieee802_1x_eapol_key *key; - u16 datalen; - - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) - return; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: %lu bytes from " MACSTR "\n", - (unsigned long) len, MAC2STR(sa)); - sta = ap_get_sta(hapd, sa); - if (!sta) { - printf(" no station information available\n"); - return; - } - - if (len < sizeof(*hdr)) { - printf(" too short IEEE 802.1X packet\n"); - return; - } - - hdr = (struct ieee802_1x_hdr *) buf; - datalen = ntohs(hdr->length); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " IEEE 802.1X: version=%d type=%d length=%d\n", - hdr->version, hdr->type, datalen); - - if (len - sizeof(*hdr) < datalen) { - printf(" frame too short for this IEEE 802.1X packet\n"); - if (sta->eapol_sm) - sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++; - return; - } - if (len - sizeof(*hdr) > datalen) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " ignoring %lu extra octets after IEEE 802.1X " - "packet\n", - (unsigned long) len - sizeof(*hdr) - datalen); - } - - if (sta->eapol_sm) { - sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version; - sta->eapol_sm->dot1xAuthEapolFramesRx++; - } - - key = (struct ieee802_1x_eapol_key *) (hdr + 1); - if (datalen >= sizeof(struct ieee802_1x_eapol_key) && - hdr->type == IEEE802_1X_TYPE_EAPOL_KEY && - (key->type == EAPOL_KEY_TYPE_WPA || - key->type == EAPOL_KEY_TYPE_RSN)) { - wpa_receive(hapd, sta, (u8 *) hdr, sizeof(*hdr) + datalen); - return; - } - - if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - return; - - if (!sta->eapol_sm) { - sta->eapol_sm = eapol_sm_alloc(hapd, sta); - if (!sta->eapol_sm) - return; - } - - /* since we support version 1, we can ignore version field and proceed - * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */ - /* TODO: actually, we are not version 1 anymore.. However, Version 2 - * does not change frame contents, so should be ok to process frames - * more or less identically. Some changes might be needed for - * verification of fields. */ - - switch (hdr->type) { - case IEEE802_1X_TYPE_EAP_PACKET: - handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen); - break; - - case IEEE802_1X_TYPE_EAPOL_START: - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start " - "from STA"); - if (sta->pmksa) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "cached PMKSA " - "available - ignore it since " - "STA sent EAPOL-Start"); - sta->pmksa = NULL; - } - sta->eapol_sm->auth_pae.eapolStart = TRUE; - sta->eapol_sm->dot1xAuthEapolStartFramesRx++; - wpa_sm_event(hapd, sta, WPA_REAUTH_EAPOL); - break; - - case IEEE802_1X_TYPE_EAPOL_LOGOFF: - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "received EAPOL-Logoff " - "from STA"); - sta->acct_terminate_cause = - RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; - sta->eapol_sm->auth_pae.eapolLogoff = TRUE; - sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; - break; - - case IEEE802_1X_TYPE_EAPOL_KEY: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " EAPOL-Key\n"); - if (!(sta->flags & WLAN_STA_AUTHORIZED)) { - printf(" Dropped key data from unauthorized " - "Supplicant\n"); - break; - } - break; - - case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " EAPOL-Encapsulated-ASF-Alert\n"); - /* TODO: implement support for this; show data */ - break; - - default: - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " unknown IEEE 802.1X packet type\n"); - sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++; - break; - } - - eapol_sm_step(sta->eapol_sm); -} - - -void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta) -{ - if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - return; - - if (sta->eapol_sm == NULL) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "start authentication"); - sta->eapol_sm = eapol_sm_alloc(hapd, sta); - if (sta->eapol_sm == NULL) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_INFO, - "failed to allocate state machine"); - return; - } - } - - sta->eapol_sm->portEnabled = TRUE; - - if (sta->pmksa) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, - "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); - /* Setup EAPOL state machines to already authenticated state - * because of existing PMKSA information in the cache. */ - sta->eapol_sm->keyRun = TRUE; - sta->eapol_sm->keyAvailable = TRUE; - sta->eapol_sm->auth_pae.state = AUTH_PAE_AUTHENTICATING; - sta->eapol_sm->be_auth.state = BE_AUTH_SUCCESS; - sta->eapol_sm->authSuccess = TRUE; - if (sta->eapol_sm->eap) - eap_sm_notify_cached(sta->eapol_sm->eap); - } else - eapol_sm_step(sta->eapol_sm); -} - - -void ieee802_1x_free_radius_class(struct radius_class_data *class) -{ - int i; - if (class == NULL) - return; - for (i = 0; i < class->count; i++) - free(class->attr[i].data); - free(class->attr); - class->attr = NULL; - class->count = 0; -} - - -int ieee802_1x_copy_radius_class(struct radius_class_data *dst, - struct radius_class_data *src) -{ - size_t i; - - if (src->attr == NULL) - return 0; - - dst->attr = malloc(src->count * sizeof(struct radius_attr_data)); - if (dst->attr == NULL) - return -1; - - memset(dst->attr, 0, src->count * sizeof(struct radius_attr_data)); - dst->count = 0; - - for (i = 0; i < src->count; i++) { - dst->attr[i].data = malloc(src->attr[i].len); - if (dst->attr[i].data == NULL) - break; - dst->count++; - memcpy(dst->attr[i].data, src->attr[i].data, src->attr[i].len); - dst->attr[i].len = src->attr[i].len; - } - - return 0; -} - - -void ieee802_1x_free_station(struct sta_info *sta) -{ - struct eapol_state_machine *sm = sta->eapol_sm; - - eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); - - if (sm == NULL) - return; - - sta->eapol_sm = NULL; - - if (sm->last_recv_radius) { - radius_msg_free(sm->last_recv_radius); - free(sm->last_recv_radius); - } - - free(sm->last_eap_supp); - free(sm->last_eap_radius); - free(sm->identity); - ieee802_1x_free_radius_class(&sm->radius_class); - free(sm->eapol_key_sign); - free(sm->eapol_key_crypt); - eapol_sm_free(sm); -} - - -static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) -{ - u8 *eap; - size_t len; - struct eap_hdr *hdr; - int eap_type = -1; - char buf[64]; - struct radius_msg *msg; - struct eapol_state_machine *sm = sta->eapol_sm; - - if (sm == NULL || sm->last_recv_radius == NULL) { - if (sm) - sm->be_auth.eapNoReq = TRUE; - return; - } - - msg = sm->last_recv_radius; - - eap = radius_msg_get_eap(msg, &len); - if (eap == NULL) { - /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3: - * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message - * attribute */ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_WARNING, "could not extract " - "EAP-Message from RADIUS message"); - free(sm->last_eap_radius); - sm->last_eap_radius = NULL; - sm->last_eap_radius_len = 0; - sm->be_auth.eapNoReq = TRUE; - return; - } - - if (len < sizeof(*hdr)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_WARNING, "too short EAP packet " - "received from authentication server"); - free(eap); - sm->be_auth.eapNoReq = TRUE; - return; - } - - if (len > sizeof(*hdr)) - eap_type = eap[sizeof(*hdr)]; - - hdr = (struct eap_hdr *) eap; - switch (hdr->code) { - case EAP_CODE_REQUEST: - snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", - eap_type >= 0 ? eap_type_text(eap_type) : "??", - eap_type); - break; - case EAP_CODE_RESPONSE: - snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", - eap_type >= 0 ? eap_type_text(eap_type) : "??", - eap_type); - break; - case EAP_CODE_SUCCESS: - snprintf(buf, sizeof(buf), "EAP Success"); - break; - case EAP_CODE_FAILURE: - snprintf(buf, sizeof(buf), "EAP Failure"); - break; - default: - snprintf(buf, sizeof(buf), "unknown EAP code"); - break; - } - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d " - "id=%d len=%d) from RADIUS server: %s", - hdr->code, hdr->identifier, ntohs(hdr->length), buf); - sm->be_auth.eapReq = TRUE; - - free(sm->last_eap_radius); - sm->last_eap_radius = eap; - sm->last_eap_radius_len = len; -} - - -static void ieee802_1x_get_keys(hostapd *hapd, struct sta_info *sta, - struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len) -{ - struct radius_ms_mppe_keys *keys; - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - - keys = radius_msg_get_ms_keys(msg, req, shared_secret, - shared_secret_len); - - if (keys) { - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL) && keys->send) { - size_t i; - printf("MS-MPPE-Send-Key (len=%lu):", - (unsigned long) keys->send_len); - for (i = 0; i < keys->send_len; i++) - printf(" %02x", keys->send[i]); - printf("\n"); - } - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL) && keys->recv) { - size_t i; - printf("MS-MPPE-Recv-Key (len=%lu):", - (unsigned long) keys->recv_len); - for (i = 0; i < keys->recv_len; i++) - printf(" %02x", keys->recv[i]); - printf("\n"); - } - - if (keys->send && keys->recv) { - free(sm->eapol_key_sign); - free(sm->eapol_key_crypt); - sm->eapol_key_sign = keys->send; - sm->eapol_key_sign_len = keys->send_len; - sm->eapol_key_crypt = keys->recv; - sm->eapol_key_crypt_len = keys->recv_len; - if (hapd->default_wep_key || - hapd->conf->individual_wep_key_len > 0 || - hapd->conf->wpa) - sta->eapol_sm->keyAvailable = TRUE; - } else { - free(keys->send); - free(keys->recv); - } - free(keys); - } -} - - -static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, - struct sta_info *sta, - struct radius_msg *msg) -{ - u8 *class; - size_t class_len; - struct eapol_state_machine *sm = sta->eapol_sm; - int count, i; - struct radius_attr_data *nclass; - size_t nclass_count; - - if (!hapd->conf->radius->acct_server || hapd->radius == NULL || - sm == NULL) - return; - - ieee802_1x_free_radius_class(&sm->radius_class); - count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1); - if (count <= 0) - return; - - nclass = malloc(count * sizeof(struct radius_attr_data)); - if (nclass == NULL) - return; - - nclass_count = 0; - memset(nclass, 0, count * sizeof(struct radius_attr_data)); - - class = NULL; - for (i = 0; i < count; i++) { - do { - if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, - &class, &class_len, - class) < 0) { - i = count; - break; - } - } while (class_len < 1); - - nclass[nclass_count].data = malloc(class_len); - if (nclass[nclass_count].data == NULL) - break; - - memcpy(nclass[nclass_count].data, class, class_len); - nclass[nclass_count].len = class_len; - nclass_count++; - } - - sm->radius_class.attr = nclass; - sm->radius_class.count = nclass_count; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Stored %lu RADIUS " - "Class attributes for " MACSTR "\n", - (unsigned long) sm->radius_class.count, - MAC2STR(sta->addr)); -} - - -/* Update sta->identity based on User-Name attribute in Access-Accept */ -static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, - struct sta_info *sta, - struct radius_msg *msg) -{ - u8 *buf, *identity; - size_t len; - struct eapol_state_machine *sm = sta->eapol_sm; - - if (sm == NULL) - return; - - if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len, - NULL) < 0) - return; - - identity = malloc(len + 1); - if (identity == NULL) - return; - - memcpy(identity, buf, len); - identity[len] = '\0'; - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with " - "User-Name from Access-Accept '%s'", - sm->identity ? (char *) sm->identity : "N/A", - (char *) identity); - - free(sm->identity); - sm->identity = identity; - sm->identity_len = len; -} - - -struct sta_id_search { - u8 identifier; - struct eapol_state_machine *sm; -}; - - -static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd, - struct sta_info *sta, - void *ctx) -{ - struct sta_id_search *id_search = ctx; - struct eapol_state_machine *sm = sta->eapol_sm; - - if (sm && sm->radius_identifier >= 0 && - sm->radius_identifier == id_search->identifier) { - id_search->sm = sm; - return 1; - } - return 0; -} - - -static struct eapol_state_machine * -ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier) -{ - struct sta_id_search id_search; - id_search.identifier = identifier; - id_search.sm = NULL; - ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search); - return id_search.sm; -} - - -/* Process the RADIUS frames from Authentication Server */ -static RadiusRxResult -ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, - void *data) -{ - struct hostapd_data *hapd = data; - struct sta_info *sta; - u32 session_timeout = 0, termination_action, acct_interim_interval; - int session_timeout_set; - int eap_timeout; - struct eapol_state_machine *sm; - int override_eapReq = 0; - - sm = ieee802_1x_search_radius_identifier(hapd, msg->hdr->identifier); - if (sm == NULL) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Could not " - "find matching station for this RADIUS " - "message\n"); - return RADIUS_RX_UNKNOWN; - } - sta = sm->sta; - - /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be - * present when packet contains an EAP-Message attribute */ - if (msg->hdr->code == RADIUS_CODE_ACCESS_REJECT && - radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, - 0) < 0 && - radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Allowing RADIUS " - "Access-Reject without Message-Authenticator " - "since it does not include EAP-Message\n"); - } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, - req, 1)) { - printf("Incoming RADIUS packet did not have correct " - "Message-Authenticator - dropped\n"); - return RADIUS_RX_INVALID_AUTHENTICATOR; - } - - if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && - msg->hdr->code != RADIUS_CODE_ACCESS_REJECT && - msg->hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { - printf("Unknown RADIUS message code\n"); - return RADIUS_RX_UNKNOWN; - } - - sm->radius_identifier = -1; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RADIUS packet matching with station " MACSTR "\n", - MAC2STR(sta->addr)); - - if (sm->last_recv_radius) { - radius_msg_free(sm->last_recv_radius); - free(sm->last_recv_radius); - } - - sm->last_recv_radius = msg; - - session_timeout_set = - !radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, - &session_timeout); - if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION, - &termination_action)) - termination_action = RADIUS_TERMINATION_ACTION_DEFAULT; - - if (hapd->conf->radius->acct_interim_interval == 0 && - msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT && - radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, - &acct_interim_interval) == 0) { - if (acct_interim_interval < 60) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_INFO, - "ignored too small " - "Acct-Interim-Interval %d", - acct_interim_interval); - } else - sta->acct_interim_interval = acct_interim_interval; - } - - - switch (msg->hdr->code) { - case RADIUS_CODE_ACCESS_ACCEPT: - /* RFC 3580, Ch. 3.17 */ - if (session_timeout_set && termination_action == - RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { - sm->reauth_timer.reAuthPeriod = session_timeout; - } else if (session_timeout_set) - ap_sta_session_timeout(hapd, sta, session_timeout); - - sm->eapSuccess = TRUE; - override_eapReq = 1; - ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret, - shared_secret_len); - ieee802_1x_store_radius_class(hapd, sta, msg); - ieee802_1x_update_sta_identity(hapd, sta, msg); - if (sm->keyAvailable) { - pmksa_cache_add(hapd, sta, sm->eapol_key_crypt, - session_timeout_set ? - session_timeout : -1); - } - break; - case RADIUS_CODE_ACCESS_REJECT: - sm->eapFail = TRUE; - override_eapReq = 1; - break; - case RADIUS_CODE_ACCESS_CHALLENGE: - if (session_timeout_set) { - /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */ - eap_timeout = session_timeout; - } else - eap_timeout = 30; - hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, - "using EAP timeout of %d seconds%s", - eap_timeout, - session_timeout_set ? " (from RADIUS)" : ""); - eloop_cancel_timeout(ieee802_1x_eap_timeout, sta, NULL); - eloop_register_timeout(eap_timeout, 0, ieee802_1x_eap_timeout, - sta, NULL); - sm->eapTimeout = FALSE; - break; - } - - ieee802_1x_decapsulate_radius(hapd, sta); - if (override_eapReq) - sm->be_auth.eapReq = FALSE; - - eapol_sm_step(sm); - - return RADIUS_RX_QUEUED; -} - - -/* Handler for EAPOL Backend Authentication state machine sendRespToServer. - * Forward the EAP Response from Supplicant to Authentication Server. */ -void ieee802_1x_send_resp_to_server(hostapd *hapd, struct sta_info *sta) -{ - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - - if (hapd->conf->eap_server) { - eap_set_eapRespData(sm->eap, sm->last_eap_supp, - sm->last_eap_supp_len); - } else { - ieee802_1x_encapsulate_radius(hapd, sta, sm->last_eap_supp, - sm->last_eap_supp_len); - } -} - - -void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) -{ - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "aborting authentication"); - - if (sm->last_recv_radius) { - radius_msg_free(sm->last_recv_radius); - free(sm->last_recv_radius); - sm->last_recv_radius = NULL; - } - free(sm->last_eap_supp); - sm->last_eap_supp = NULL; - sm->last_eap_supp_len = 0; - free(sm->last_eap_radius); - sm->last_eap_radius = NULL; - sm->last_eap_radius_len = 0; -} - - -void ieee802_1x_set_port_enabled(hostapd *hapd, struct sta_info *sta, - int enabled) -{ - if (!sta->eapol_sm) - return; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: station " MACSTR " port %s\n", - MAC2STR(sta->addr), enabled ? "enabled" : "disabled"); - sta->eapol_sm->portEnabled = enabled ? TRUE : FALSE; - eapol_sm_step(sta->eapol_sm); -} - - -#ifdef HOSTAPD_DUMP_STATE -void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta) -{ - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - - fprintf(f, "%sIEEE 802.1X:\n", prefix); - - if (sm->identity) { - size_t i; - fprintf(f, "%sidentity=", prefix); - for (i = 0; i < sm->identity_len; i++) - fprint_char(f, sm->identity[i]); - fprintf(f, "\n"); - } - - fprintf(f, "%scached_packets=%s%s%s\n", prefix, - sm->last_recv_radius ? "[RX RADIUS]" : "", - sm->last_eap_radius ? "[EAP RADIUS]" : "", - sm->last_eap_supp ? "[EAP SUPPLICANT]" : ""); - - eapol_sm_dump_state(f, prefix, sm); -} -#endif /* HOSTAPD_DUMP_STATE */ - - -static int ieee802_1x_rekey_broadcast(hostapd *hapd) -{ - if (hapd->conf->default_wep_key_len < 1) - return 0; - - free(hapd->default_wep_key); - hapd->default_wep_key = malloc(hapd->conf->default_wep_key_len); - if (hapd->default_wep_key == NULL || - hostapd_get_rand(hapd->default_wep_key, - hapd->conf->default_wep_key_len)) { - printf("Could not generate random WEP key.\n"); - free(hapd->default_wep_key); - hapd->default_wep_key = NULL; - return -1; - } - - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("IEEE 802.1X: New default WEP key", - hapd->default_wep_key, - hapd->conf->default_wep_key_len); - } - - return 0; -} - - -static int ieee802_1x_sta_key_available(struct hostapd_data *hapd, - struct sta_info *sta, void *ctx) -{ - if (sta->eapol_sm) { - sta->eapol_sm->keyAvailable = TRUE; - eapol_sm_step(sta->eapol_sm); - } - return 0; -} - - -static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - - if (hapd->default_wep_key_idx >= 3) - hapd->default_wep_key_idx = - hapd->conf->individual_wep_key_len > 0 ? 1 : 0; - else - hapd->default_wep_key_idx++; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: New default WEP " - "key index %d\n", hapd->default_wep_key_idx); - - if (ieee802_1x_rekey_broadcast(hapd)) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_WARNING, "failed to generate a " - "new broadcast key"); - free(hapd->default_wep_key); - hapd->default_wep_key = NULL; - return; - } - - /* TODO: Could setup key for RX here, but change default TX keyid only - * after new broadcast key has been sent to all stations. */ - if (hostapd_set_encryption(hapd, "WEP", NULL, - hapd->default_wep_key_idx, - hapd->default_wep_key, - hapd->conf->default_wep_key_len)) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_WARNING, "failed to configure a " - "new broadcast key"); - free(hapd->default_wep_key); - hapd->default_wep_key = NULL; - return; - } - - ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL); - - if (hapd->conf->wep_rekeying_period > 0) { - eloop_register_timeout(hapd->conf->wep_rekeying_period, 0, - ieee802_1x_rekey, hapd, NULL); - } -} - - -int ieee802_1x_init(hostapd *hapd) -{ - int i; - - if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && - hostapd_set_ieee8021x(hapd, 1)) - return -1; - - if (radius_client_register(hapd->radius, RADIUS_AUTH, - ieee802_1x_receive_auth, hapd)) - return -1; - - if (hapd->conf->default_wep_key_len) { - for (i = 0; i < 4; i++) - hostapd_set_encryption(hapd, "none", NULL, i, NULL, 0); - - ieee802_1x_rekey(hapd, NULL); - - if (hapd->default_wep_key == NULL) - return -1; - } - - return 0; -} - - -void ieee802_1x_deinit(hostapd *hapd) -{ - if (hapd->driver != NULL && - (hapd->conf->ieee802_1x || hapd->conf->wpa)) - hostapd_set_ieee8021x(hapd, 0); -} - - -static void ieee802_1x_new_auth_session(struct hostapd_data *hapd, - struct sta_info *sta) -{ - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: station " MACSTR " - new auth session, " - "clearing State\n", MAC2STR(sta->addr)); - - if (sm->last_recv_radius) { - radius_msg_free(sm->last_recv_radius); - free(sm->last_recv_radius); - sm->last_recv_radius = NULL; - } - - sm->eapSuccess = FALSE; - sm->eapFail = FALSE; -} - - -int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf, - size_t len, int ack) -{ - struct ieee80211_hdr *hdr; - struct ieee802_1x_hdr *xhdr; - struct ieee802_1x_eapol_key *key; - u8 *pos; - - if (sta == NULL) - return -1; - if (len < sizeof(*hdr) + sizeof(rfc1042_header) + 2 + sizeof(*xhdr)) - return 0; - - hdr = (struct ieee80211_hdr *) buf; - pos = (u8 *) (hdr + 1); - if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) - return 0; - pos += sizeof(rfc1042_header); - if (((pos[0] << 8) | pos[1]) != ETH_P_PAE) - return 0; - pos += 2; - - xhdr = (struct ieee802_1x_hdr *) pos; - pos += sizeof(*xhdr); - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: " MACSTR - " TX status - version=%d type=%d length=%d - ack=%d\n", - MAC2STR(sta->addr), xhdr->version, xhdr->type, - ntohs(xhdr->length), ack); - - /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant - * or Authenticator state machines, but EAPOL-Key packets are not - * retransmitted in case of failure. Try to re-sent failed EAPOL-Key - * packets couple of times because otherwise STA keys become - * unsynchronized with AP. */ - if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack && - pos + sizeof(*key) <= buf + len) { - key = (struct ieee802_1x_eapol_key *) pos; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key " - "frame (%scast index=%d)", - key->key_index & BIT(7) ? "uni" : "broad", - key->key_index & ~BIT(7)); - /* TODO: re-send EAPOL-Key couple of times (with short delay - * between them?). If all attempt fail, report error and - * deauthenticate STA so that it will get new keys when - * authenticating again (e.g., after returning in range). - * Separate limit/transmit state needed both for unicast and - * broadcast keys(?) */ - } - /* TODO: could move unicast key configuration from ieee802_1x_tx_key() - * to here and change the key only if the EAPOL-Key packet was Acked. - */ - - return 1; -} - - -u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len) -{ - if (sm == NULL || sm->identity == NULL) - return NULL; - - *len = sm->identity_len; - return sm->identity; -} - - -u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, - int idx) -{ - if (sm == NULL || sm->radius_class.attr == NULL || - idx >= sm->radius_class.count) - return NULL; - - *len = sm->radius_class.attr[idx].len; - return sm->radius_class.attr[idx].data; -} - - -u8 * ieee802_1x_get_key_crypt(struct eapol_state_machine *sm, size_t *len) -{ - if (sm == NULL) - return NULL; - - *len = sm->eapol_key_crypt_len; - return sm->eapol_key_crypt; -} - - -void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, - int enabled) -{ - if (sm == NULL) - return; - sm->portEnabled = enabled ? TRUE : FALSE; - eapol_sm_step(sm); -} - - -void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, - int valid) -{ - if (sm == NULL) - return; - sm->portValid = valid ? TRUE : FALSE; - eapol_sm_step(sm); -} - - -void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth) -{ - if (sm == NULL) - return; - if (pre_auth) - sm->flags |= EAPOL_SM_PREAUTH; - else - sm->flags &= ~EAPOL_SM_PREAUTH; -} - - -static const char * bool_txt(Boolean bool) -{ - return bool ? "TRUE" : "FALSE"; -} - - -int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) -{ - /* TODO */ - return 0; -} - - -int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen) -{ - int len = 0; - struct eapol_state_machine *sm = sta->eapol_sm; - - if (sm == NULL) - return 0; - - len += snprintf(buf + len, buflen - len, - "dot1xPaePortNumber=%d\n" - "dot1xPaePortProtocolVersion=%d\n" - "dot1xPaePortCapabilities=1\n" - "dot1xPaePortInitialize=%d\n" - "dot1xPaePortReauthenticate=FALSE\n", - sta->aid, - EAPOL_VERSION, - sm->initialize); - - /* dot1xAuthConfigTable */ - len += snprintf(buf + len, buflen - len, - "dot1xAuthPaeState=%d\n" - "dot1xAuthBackendAuthState=%d\n" - "dot1xAuthAdminControlledDirections=%d\n" - "dot1xAuthOperControlledDirections=%d\n" - "dot1xAuthAuthControlledPortStatus=%d\n" - "dot1xAuthAuthControlledPortControl=%d\n" - "dot1xAuthQuietPeriod=%u\n" - "dot1xAuthServerTimeout=%u\n" - "dot1xAuthReAuthPeriod=%u\n" - "dot1xAuthReAuthEnabled=%s\n" - "dot1xAuthKeyTxEnabled=%s\n", - sm->auth_pae.state + 1, - sm->be_auth.state + 1, - sm->ctrl_dir.adminControlledDirections, - sm->ctrl_dir.operControlledDirections, - sm->authPortStatus, - sm->portControl, - sm->auth_pae.quietPeriod, - sm->be_auth.serverTimeout, - sm->reauth_timer.reAuthPeriod, - bool_txt(sm->reauth_timer.reAuthEnabled), - bool_txt(sm->keyTxEnabled)); - - /* dot1xAuthStatsTable */ - len += snprintf(buf + len, buflen - len, - "dot1xAuthEapolFramesRx=%u\n" - "dot1xAuthEapolFramesTx=%u\n" - "dot1xAuthEapolStartFramesRx=%u\n" - "dot1xAuthEapolLogoffFramesRx=%u\n" - "dot1xAuthEapolRespIdFramesRx=%u\n" - "dot1xAuthEapolRespFramesRx=%u\n" - "dot1xAuthEapolReqIdFramesTx=%u\n" - "dot1xAuthEapolReqFramesTx=%u\n" - "dot1xAuthInvalidEapolFramesRx=%u\n" - "dot1xAuthEapLengthErrorFramesRx=%u\n" - "dot1xAuthLastEapolFrameVersion=%u\n" - "dot1xAuthLastEapolFrameSource=" MACSTR "\n", - sm->dot1xAuthEapolFramesRx, - sm->dot1xAuthEapolFramesTx, - sm->dot1xAuthEapolStartFramesRx, - sm->dot1xAuthEapolLogoffFramesRx, - sm->dot1xAuthEapolRespIdFramesRx, - sm->dot1xAuthEapolRespFramesRx, - sm->dot1xAuthEapolReqIdFramesTx, - sm->dot1xAuthEapolReqFramesTx, - sm->dot1xAuthInvalidEapolFramesRx, - sm->dot1xAuthEapLengthErrorFramesRx, - sm->dot1xAuthLastEapolFrameVersion, - MAC2STR(sm->addr)); - - /* dot1xAuthDiagTable */ - len += snprintf(buf + len, buflen - len, - "dot1xAuthEntersConnecting=%u\n" - "dot1xAuthEapLogoffsWhileConnecting=%u\n" - "dot1xAuthEntersAuthenticating=%u\n" - "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n" - "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n" - "dot1xAuthAuthFailWhileAuthenticating=%u\n" - "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n" - "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n" - "dot1xAuthAuthReauthsWhileAuthenticated=%u\n" - "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n" - "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n" - "dot1xAuthBackendResponses=%u\n" - "dot1xAuthBackendAccessChallenges=%u\n" - "dot1xAuthBackendOtherRequestsToSupplicant=%u\n" - "dot1xAuthBackendAuthSuccesses=%u\n" - "dot1xAuthBackendAuthFails=%u\n", - sm->auth_pae.authEntersConnecting, - sm->auth_pae.authEapLogoffsWhileConnecting, - sm->auth_pae.authEntersAuthenticating, - sm->auth_pae.authAuthSuccessesWhileAuthenticating, - sm->auth_pae.authAuthTimeoutsWhileAuthenticating, - sm->auth_pae.authAuthFailWhileAuthenticating, - sm->auth_pae.authAuthEapStartsWhileAuthenticating, - sm->auth_pae.authAuthEapLogoffWhileAuthenticating, - sm->auth_pae.authAuthReauthsWhileAuthenticated, - sm->auth_pae.authAuthEapStartsWhileAuthenticated, - sm->auth_pae.authAuthEapLogoffWhileAuthenticated, - sm->be_auth.backendResponses, - sm->be_auth.backendAccessChallenges, - sm->be_auth.backendOtherRequestsToSupplicant, - sm->be_auth.backendAuthSuccesses, - sm->be_auth.backendAuthFails); - - /* dot1xAuthSessionStatsTable */ - len += snprintf(buf + len, buflen - len, - /* TODO: dot1xAuthSessionOctetsRx */ - /* TODO: dot1xAuthSessionOctetsTx */ - /* TODO: dot1xAuthSessionFramesRx */ - /* TODO: dot1xAuthSessionFramesTx */ - "dot1xAuthSessionId=%08X-%08X\n" - "dot1xAuthSessionAuthenticMethod=%d\n" - "dot1xAuthSessionTime=%u\n" - "dot1xAuthSessionTerminateCause=999\n" - "dot1xAuthSessionUserName=%s\n", - sta->acct_session_id_hi, sta->acct_session_id_lo, - sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X ? 1 : 2, - (unsigned int) (time(NULL) - sta->acct_session_start), - sm->identity); - - return len; -} - - -void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ - u8 *key; - size_t len; - /* TODO: get PMKLifetime from WPA parameters */ - static const int dot11RSNAConfigPMKLifetime = 43200; - - key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len); - if (success && key) { - pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime); - } -} diff --git a/contrib/hostapd-0.4.9/ieee802_1x.h b/contrib/hostapd-0.4.9/ieee802_1x.h deleted file mode 100644 index 0ac06b24bd..0000000000 --- a/contrib/hostapd-0.4.9/ieee802_1x.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef IEEE802_1X_H -#define IEEE802_1X_H - -/* IEEE Std 802.1X-REV-d11, 7.2 */ - -struct ieee802_1x_hdr { - u8 version; - u8 type; - u16 length; - /* followed by length octets of data */ -} __attribute__ ((packed)); - -#define EAPOL_VERSION 2 - -enum { IEEE802_1X_TYPE_EAP_PACKET = 0, - IEEE802_1X_TYPE_EAPOL_START = 1, - IEEE802_1X_TYPE_EAPOL_LOGOFF = 2, - IEEE802_1X_TYPE_EAPOL_KEY = 3, - IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4 -}; - -/* draft-congdon-radius-8021x-20.txt */ - -struct ieee802_1x_eapol_key { - u8 type; - u16 key_length; - u8 replay_counter[8]; /* does not repeat within the life of the keying - * material used to encrypt the Key field; - * 64-bit NTP timestamp MAY be used here */ - u8 key_iv[16]; /* cryptographically random number */ - u8 key_index; /* key flag in the most significant bit: - * 0 = broadcast (default key), - * 1 = unicast (key mapping key); key index is in the - * 7 least significant bits */ - u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with - * MS-MPPE-Send-Key as the key */ - - /* followed by key: if packet body length = 44 + key length, then the - * key field (of key_length bytes) contains the key in encrypted form; - * if packet body length = 44, key field is absent and key_length - * represents the number of least significant octets from - * MS-MPPE-Send-Key attribute to be used as the keying material; - * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ -} __attribute__ ((packed)); - -enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, - EAPOL_KEY_TYPE_WPA = 254 }; - - -void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, - size_t len); -void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta); -void ieee802_1x_free_station(struct sta_info *sta); - -void ieee802_1x_request_identity(struct hostapd_data *hapd, - struct sta_info *sta); -void ieee802_1x_tx_canned_eap(struct hostapd_data *hapd, struct sta_info *sta, - int success); -void ieee802_1x_tx_req(hostapd *hapd, struct sta_info *sta); -void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); -void ieee802_1x_send_resp_to_server(hostapd *hapd, struct sta_info *sta); -void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); -void ieee802_1x_set_sta_authorized(hostapd *hapd, struct sta_info *sta, - int authorized); -void ieee802_1x_set_port_enabled(hostapd *hapd, struct sta_info *sta, - int enabled); -void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); -int ieee802_1x_init(hostapd *hapd); -void ieee802_1x_deinit(hostapd *hapd); -int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf, - size_t len, int ack); -u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); -u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, - int idx); -u8 * ieee802_1x_get_key_crypt(struct eapol_state_machine *sm, size_t *len); -void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, - int enabled); -void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, - int valid); -void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth); -int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); -int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen); -void hostapd_get_ntp_timestamp(u8 *buf); -void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success); - -struct radius_class_data; - -void ieee802_1x_free_radius_class(struct radius_class_data *class); -int ieee802_1x_copy_radius_class(struct radius_class_data *dst, - struct radius_class_data *src); - -#endif /* IEEE802_1X_H */ diff --git a/contrib/hostapd-0.4.9/l2_packet.h b/contrib/hostapd-0.4.9/l2_packet.h deleted file mode 100644 index eb966d39ed..0000000000 --- a/contrib/hostapd-0.4.9/l2_packet.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * WPA Supplicant - Layer2 packet interface definition - * Copyright (c) 2003-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * This file defines an interface for layer 2 (link layer) packet sending and - * receiving. l2_packet_linux.c is one implementation for such a layer 2 - * implementation using Linux packet sockets and l2_packet_pcap.c another one - * using libpcap and libdnet. When porting %wpa_supplicant to other operating - * systems, a new l2_packet implementation may need to be added. - */ - -#ifndef L2_PACKET_H -#define L2_PACKET_H - -#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] -#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" - -#ifndef ETH_P_EAPOL -#define ETH_P_EAPOL 0x888e -#endif - -#ifndef ETH_P_RSN_PREAUTH -#define ETH_P_RSN_PREAUTH 0x88c7 -#endif - -/** - * struct l2_packet_data - Internal l2_packet data structure - * - * This structure is used by the l2_packet implementation to store its private - * data. Other files use a pointer to this data when calling the l2_packet - * functions, but the contents of this structure should not be used directly - * outside l2_packet implementation. - */ -struct l2_packet_data; - -struct l2_ethhdr { - u8 h_dest[ETH_ALEN]; - u8 h_source[ETH_ALEN]; - u16 h_proto; -} __attribute__ ((packed)); - -/** - * l2_packet_init - Initialize l2_packet interface - * @ifname: Interface name - * @own_addr: Optional own MAC address if available from driver interface or - * %NULL if not available - * @protocol: Ethernet protocol number in host byte order - * @rx_callback: Callback function that will be called for each received packet - * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback() - * @l2_hdr: 1 = include layer 2 header, 0 = do not include header - * Returns: Pointer to internal data or %NULL on failure - * - * rx_callback function will be called with src_addr pointing to the source - * address (MAC address) of the the packet. If l2_hdr is set to 0, buf - * points to len bytes of the payload after the layer 2 header and similarly, - * TX buffers start with payload. This behavior can be changed by setting - * l2_hdr=1 to include the layer 2 header in the data buffer. - */ -struct l2_packet_data * l2_packet_init( - const char *ifname, const u8 *own_addr, unsigned short protocol, - void (*rx_callback)(void *ctx, const u8 *src_addr, - const u8 *buf, size_t len), - void *rx_callback_ctx, int l2_hdr); - -/** - * l2_packet_deinit - Deinitialize l2_packet interface - * @l2: Pointer to internal l2_packet data from l2_packet_init() - */ -void l2_packet_deinit(struct l2_packet_data *l2); - -/** - * l2_packet_get_own_addr - Get own layer 2 address - * @l2: Pointer to internal l2_packet data from l2_packet_init() - * @addr: Buffer for the own address (6 bytes) - * Returns: 0 on success, -1 on failure - */ -int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr); - -/** - * l2_packet_send - Send a packet - * @l2: Pointer to internal l2_packet data from l2_packet_init() - * @dst_addr: Destination address for the packet (only used if l2_hdr == 0) - * @proto: Protocol/ethertype for the packet in host byte order (only used if - * l2_hdr == 0) - * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was - * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet - * is included. - * @len: Length of the buffer (including l2 header only if l2_hdr == 1) - * Returns: >=0 on success, <0 on failure - */ -int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, - const u8 *buf, size_t len); - -/** - * l2_packet_get_ip_addr - Get the current IP address from the interface - * @l2: Pointer to internal l2_packet data from l2_packet_init() - * @buf: Buffer for the IP address in text format - * @len: Maximum buffer length - * Returns: 0 on success, -1 on failure - * - * This function can be used to get the current IP address from the interface - * bound to the l2_packet. This is mainly for status information and the IP - * address will be stored as an ASCII string. This function is not essential - * for %wpa_supplicant operation, so full implementation is not required. - * l2_packet implementation will need to define the function, but it can return - * -1 if the IP address information is not available. - */ -int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len); - - -/** - * l2_packet_notify_auth_start - Notify l2_packet about start of authentication - * @l2: Pointer to internal l2_packet data from l2_packet_init() - * - * This function is called when authentication is expected to start, e.g., when - * association has been completed, in order to prepare l2_packet implementation - * for EAPOL frames. This function is used mainly if the l2_packet code needs - * to do polling in which case it can increasing polling frequency. This can - * also be an empty function if the l2_packet implementation does not benefit - * from knowing about the starting authentication. - */ -void l2_packet_notify_auth_start(struct l2_packet_data *l2); - -#endif /* L2_PACKET_H */ diff --git a/contrib/hostapd-0.4.9/madwifi.conf b/contrib/hostapd-0.4.9/madwifi.conf deleted file mode 100644 index a9bf539d23..0000000000 --- a/contrib/hostapd-0.4.9/madwifi.conf +++ /dev/null @@ -1,278 +0,0 @@ -##### hostapd configuration file ############################################## -# Empty lines and lines starting with # are ignored - -# AP netdevice name (without 'ap' prefix, i.e., wlan0 uses wlan0ap for -# management frames) -interface=ath0 - -# In case of madwifi driver, an additional configuration parameter, bridge, -# must be used to notify hostapd if the interface is included in a bridge. This -# parameter is not used with Host AP driver. -bridge=br0 - -# Driver interface type (hostap/wired/madwifi; default: hostap) -driver=madwifi - -# hostapd event logger configuration -# -# Two output method: syslog and stdout (only usable if not forking to -# background). -# -# Module bitfield (ORed bitfield of modules that will be logged; -1 = all -# modules): -# bit 0 (1) = IEEE 802.11 -# bit 1 (2) = IEEE 802.1X -# bit 2 (4) = RADIUS -# bit 3 (8) = WPA -# bit 4 (16) = driver interface -# bit 5 (32) = IAPP -# -# Levels (minimum value for logged events): -# 0 = verbose debugging -# 1 = debugging -# 2 = informational messages -# 3 = notification -# 4 = warning -# -logger_syslog=-1 -logger_syslog_level=2 -logger_stdout=-1 -logger_stdout_level=2 - -# Debugging: 0 = no, 1 = minimal, 2 = verbose, 3 = msg dumps, 4 = excessive -debug=0 - -# Dump file for state information (on SIGUSR1) -dump_file=/tmp/hostapd.dump - - -##### IEEE 802.11 related configuration ####################################### - -# SSID to be used in IEEE 802.11 management frames -ssid=wpa-test - -##### IEEE 802.1X-REV related configuration ################################### - -# Require IEEE 802.1X authorization -#ieee8021x=1 - -# Optional displayable message sent with EAP Request-Identity. The first \0 -# in this string will be converted to ASCII-0 (nul). This can be used to -# separate network info (comma separated list of attribute=value pairs); see, -# e.g., draft-adrangi-eap-network-discovery-07.txt. -#eap_message=hello -#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com - -# WEP rekeying (disabled if key lengths are not set or are set to 0) -# Key lengths for default/broadcast and individual/unicast keys: -# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits) -# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits) -#wep_key_len_broadcast=5 -#wep_key_len_unicast=5 -# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once) -#wep_rekey_period=300 - -# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if -# only broadcast keys are used) -eapol_key_index_workaround=0 - -# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable -# reauthentication). -#eap_reauth_period=3600 - - -##### Integrated EAP server ################################################### - -# Optionally, hostapd can be configured to use an integrated EAP server -# to process EAP authentication locally without need for an external RADIUS -# server. This functionality can be used both as a local authentication server -# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices. - -# Use integrated EAP server instead of external RADIUS authentication -# server. This is also needed if hostapd is configured to act as a RADIUS -# authentication server. -eap_server=0 - -# Path for EAP server user database -#eap_user_file=/etc/hostapd.eap_user - -# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS -#ca_cert=/etc/hostapd.ca.pem - -# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS -#server_cert=/etc/hostapd.server.pem - -# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS -# This may point to the same file as server_cert if both certificate and key -# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be -# used by commenting out server_cert and specifying the PFX file as the -# private_key. -#private_key=/etc/hostapd.server.prv - -# Passphrase for private key -#private_key_passwd=secret passphrase - -# Enable CRL verification. -# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a -# valid CRL signed by the CA is required to be included in the ca_cert file. -# This can be done by using PEM format for CA certificate and CRL and -# concatenating these into one file. Whenever CRL changes, hostapd needs to be -# restarted to take the new CRL into use. -# 0 = do not verify CRLs (default) -# 1 = check the CRL of the user certificate -# 2 = check all CRLs in the certificate path -#check_crl=1 - -# Configuration data for EAP-SIM database/authentication gateway interface. -# This is a text string in implementation specific format. The example -# implementation in eap_sim_db.c uses this as the file name for the GSM -# authentication triplets. -#eap_sim_db=/etc/hostapd.sim_db - - -##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### - -# Interface to be used for IAPP broadcast packets -#iapp_interface=eth0 - - -##### RADIUS client configuration ############################################# -# for IEEE 802.1X with external Authentication Server, IEEE 802.11 -# authentication with external ACL for MAC addresses, and accounting - -# The own IP address of the access point (used as NAS-IP-Address) -own_ip_addr=127.0.0.1 - -# Optional NAS-Identifier string for RADIUS messages. When used, this should be -# a unique to the NAS within the scope of the RADIUS server. For example, a -# fully qualified domain name can be used here. -#nas_identifier=ap.example.com - -# RADIUS authentication server -#auth_server_addr=127.0.0.1 -#auth_server_port=1812 -#auth_server_shared_secret=secret - -# RADIUS accounting server -#acct_server_addr=127.0.0.1 -#acct_server_port=1813 -#acct_server_shared_secret=secret - -# Secondary RADIUS servers; to be used if primary one does not reply to -# RADIUS packets. These are optional and there can be more than one secondary -# server listed. -#auth_server_addr=127.0.0.2 -#auth_server_port=1812 -#auth_server_shared_secret=secret2 -# -#acct_server_addr=127.0.0.2 -#acct_server_port=1813 -#acct_server_shared_secret=secret2 - -# Retry interval for trying to return to the primary RADIUS server (in -# seconds). RADIUS client code will automatically try to use the next server -# when the current server is not replying to requests. If this interval is set, -# primary server will be retried after configured amount of time even if the -# currently used secondary server is still working. -#radius_retry_primary_interval=600 - - -# Interim accounting update interval -# If this is set (larger than 0) and acct_server is configured, hostapd will -# send interim accounting updates every N seconds. Note: if set, this overrides -# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this -# value should not be configured in hostapd.conf, if RADIUS server is used to -# control the interim interval. -# This value should not be less 600 (10 minutes) and must not be less than -# 60 (1 minute). -#radius_acct_interim_interval=600 - - -##### RADIUS authentication server configuration ############################## - -# hostapd can be used as a RADIUS authentication server for other hosts. This -# requires that the integrated EAP authenticator is also enabled and both -# authentication services are sharing the same configuration. - -# File name of the RADIUS clients configuration for the RADIUS server. If this -# commented out, RADIUS server is disabled. -#radius_server_clients=/etc/hostapd.radius_clients - -# The UDP port number for the RADIUS authentication server -#radius_server_auth_port=1812 - -# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API) -#radius_server_ipv6=1 - - -##### WPA/IEEE 802.11i configuration ########################################## - -# Enable WPA. Setting this variable configures the AP to require WPA (either -# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either -# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. -# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), -# RADIUS authentication server must be configured, and WPA-EAP must be included -# in wpa_key_mgmt. -# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) -# and/or WPA2 (full IEEE 802.11i/RSN): -# bit0 = WPA -# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) -#wpa=1 - -# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit -# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase -# (8..63 characters) that will be converted to PSK. This conversion uses SSID -# so the PSK changes when ASCII passphrase is used and the SSID is changed. -# wpa_psk (dot11RSNAConfigPSKValue) -# wpa_passphrase (dot11RSNAConfigPSKPassPhrase) -#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef -#wpa_passphrase=secret passphrase - -# Optionally, WPA PSKs can be read from a separate text file (containing list -# of (PSK,MAC address) pairs. This allows more than one PSK to be configured. -# Use absolute path name to make sure that the files can be read on SIGHUP -# configuration reloads. -#wpa_psk_file=/etc/hostapd.wpa_psk - -# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The -# entries are separated with a space. -# (dot11RSNAConfigAuthenticationSuitesTable) -#wpa_key_mgmt=WPA-PSK WPA-EAP - -# Set of accepted cipher suites (encryption algorithms) for pairwise keys -# (unicast packets). This is a space separated list of algorithms: -# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] -# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] -# Group cipher suite (encryption algorithm for broadcast and multicast frames) -# is automatically selected based on this configuration. If only CCMP is -# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, -# TKIP will be used as the group cipher. -# (dot11RSNAConfigPairwiseCiphersTable) -#wpa_pairwise=TKIP CCMP - -# Time interval for rekeying GTK (broadcast/multicast encryption keys) in -# seconds. (dot11RSNAConfigGroupRekeyTime) -#wpa_group_rekey=600 - -# Rekey GTK when any STA that possesses the current GTK is leaving the BSS. -# (dot11RSNAConfigGroupRekeyStrict) -#wpa_strict_rekey=1 - -# Time interval for rekeying GMK (master key used internally to generate GTKs -# (in seconds). -#wpa_gmk_rekey=86400 - -# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up -# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN -# authentication and key handshake before actually associating with a new AP. -# (dot11RSNAPreauthenticationEnabled) -#rsn_preauth=1 -# -# Space separated list of interfaces from which pre-authentication frames are -# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all -# interface that are used for connections to other APs. This could include -# wired interfaces and WDS links. The normal wireless data interface towards -# associated stations (e.g., wlan0) should not be added, since -# pre-authentication is only used with APs other than the currently associated -# one. -#rsn_preauth_interfaces=eth0 diff --git a/contrib/hostapd-0.4.9/md5.h b/contrib/hostapd-0.4.9/md5.h deleted file mode 100644 index a724804943..0000000000 --- a/contrib/hostapd-0.4.9/md5.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * MD5 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef MD5_H -#define MD5_H - -#define MD5_MAC_LEN 16 - -void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac); -void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac); - -#endif /* MD5_H */ diff --git a/contrib/hostapd-0.4.9/ms_funcs.c b/contrib/hostapd-0.4.9/ms_funcs.c deleted file mode 100644 index a72cf27481..0000000000 --- a/contrib/hostapd-0.4.9/ms_funcs.c +++ /dev/null @@ -1,503 +0,0 @@ -/* - * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2006, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include - -#include "common.h" -#include "sha1.h" -#include "ms_funcs.h" -#include "crypto.h" -#include "rc4.h" - - -/** - * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 - * @peer_challenge: 16-octet PeerChallenge (IN) - * @auth_challenge: 16-octet AuthenticatorChallenge (IN) - * @username: 0-to-256-char UserName (IN) - * @username_len: Length of username - * @challenge: 8-octet Challenge (OUT) - */ -static void challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, - const u8 *username, size_t username_len, - u8 *challenge) -{ - u8 hash[SHA1_MAC_LEN]; - const unsigned char *addr[3]; - size_t len[3]; - - addr[0] = peer_challenge; - len[0] = 16; - addr[1] = auth_challenge; - len[1] = 16; - addr[2] = username; - len[2] = username_len; - - sha1_vector(3, addr, len, hash); - memcpy(challenge, hash, 8); -} - - -/** - * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 - * @password: 0-to-256-unicode-char Password (IN; ASCII) - * @password_len: Length of password - * @password_hash: 16-octet PasswordHash (OUT) - */ -void nt_password_hash(const u8 *password, size_t password_len, - u8 *password_hash) -{ - u8 buf[512], *pos; - size_t i, len; - - if (password_len > 256) - return; - - /* Convert password into unicode */ - for (i = 0; i < password_len; i++) { - buf[2 * i] = password[i]; - buf[2 * i + 1] = 0; - } - - len = password_len * 2; - pos = buf; - md4_vector(1, (const u8 **) &pos, &len, password_hash); -} - - -/** - * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4 - * @password_hash: 16-octet PasswordHash (IN) - * @password_hash_hash: 16-octet PasswordHashHash (OUT) - */ -void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) -{ - size_t len = 16; - md4_vector(1, &password_hash, &len, password_hash_hash); -} - - -/** - * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5 - * @challenge: 8-octet Challenge (IN) - * @password_hash: 16-octet PasswordHash (IN) - * @response: 24-octet Response (OUT) - */ -void challenge_response(const u8 *challenge, const u8 *password_hash, - u8 *response) -{ - u8 zpwd[7]; - des_encrypt(challenge, password_hash, response); - des_encrypt(challenge, password_hash + 7, response + 8); - zpwd[0] = password_hash[14]; - zpwd[1] = password_hash[15]; - memset(zpwd + 2, 0, 5); - des_encrypt(challenge, zpwd, response + 16); -} - - -/** - * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 - * @auth_challenge: 16-octet AuthenticatorChallenge (IN) - * @peer_hallenge: 16-octet PeerChallenge (IN) - * @username: 0-to-256-char UserName (IN) - * @username_len: Length of username - * @password: 0-to-256-unicode-char Password (IN; ASCII) - * @password_len: Length of password - * @response: 24-octet Response (OUT) - */ -void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, - const u8 *username, size_t username_len, - const u8 *password, size_t password_len, - u8 *response) -{ - u8 challenge[8]; - u8 password_hash[16]; - - challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge); - nt_password_hash(password, password_len, password_hash); - challenge_response(challenge, password_hash, response); -} - - -/** - * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 - * @password: 0-to-256-unicode-char Password (IN) - * @password_len: Length of password - * @nt_response: 24-octet NT-Response (IN) - * @peer_challenge: 16-octet PeerChallenge (IN) - * @auth_challenge: 16-octet AuthenticatorChallenge (IN) - * @username: 0-to-256-char UserName (IN) - * @username_len: Length of username - * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually - * encoded as a 42-octet ASCII string (S=) - */ -void generate_authenticator_response(const u8 *password, size_t password_len, - const u8 *peer_challenge, - const u8 *auth_challenge, - const u8 *username, size_t username_len, - const u8 *nt_response, u8 *response) -{ - static const u8 magic1[39] = { - 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, - 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, - 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 - }; - static const u8 magic2[41] = { - 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, - 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, - 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, - 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, - 0x6E - }; - - u8 password_hash[16], password_hash_hash[16], challenge[8]; - const unsigned char *addr1[3]; - const size_t len1[3] = { 16, 24, sizeof(magic1) }; - const unsigned char *addr2[3]; - const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) }; - - addr1[0] = password_hash_hash; - addr1[1] = nt_response; - addr1[2] = magic1; - - addr2[0] = response; - addr2[1] = challenge; - addr2[2] = magic2; - - nt_password_hash(password, password_len, password_hash); - hash_nt_password_hash(password_hash, password_hash_hash); - sha1_vector(3, addr1, len1, response); - - challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge); - sha1_vector(3, addr2, len2, response); -} - - -/** - * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 - * @challenge: 8-octet Challenge (IN) - * @password: 0-to-256-unicode-char Password (IN; ASCII) - * @password_len: Length of password - * @response: 24-octet Response (OUT) - */ -void nt_challenge_response(const u8 *challenge, const u8 *password, - size_t password_len, u8 *response) -{ - u8 password_hash[16]; - nt_password_hash(password, password_len, password_hash); - challenge_response(challenge, password_hash, response); -} - - -/** - * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4 - * @password_hash_hash: 16-octet PasswordHashHash (IN) - * @nt_response: 24-octet NTResponse (IN) - * @master_key: 16-octet MasterKey (OUT) - */ -void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, - u8 *master_key) -{ - static const u8 magic1[27] = { - 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, - 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 - }; - const unsigned char *addr[3]; - const size_t len[3] = { 16, 24, sizeof(magic1) }; - u8 hash[SHA1_MAC_LEN]; - - addr[0] = password_hash_hash; - addr[1] = nt_response; - addr[2] = magic1; - - sha1_vector(3, addr, len, hash); - memcpy(master_key, hash, 16); -} - - -/** - * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4 - * @master_key: 16-octet MasterKey (IN) - * @session_key: 8-to-16 octet SessionKey (OUT) - * @session_key_len: SessionKeyLength (Length of session_key) (IN) - * @is_send: IsSend (IN, BOOLEAN) - * @is_server: IsServer (IN, BOOLEAN) - */ -void get_asymetric_start_key(const u8 *master_key, u8 *session_key, - size_t session_key_len, int is_send, - int is_server) -{ - static const u8 magic2[84] = { - 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, - 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, - 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, - 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, - 0x6b, 0x65, 0x79, 0x2e - }; - static const u8 magic3[84] = { - 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, - 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, - 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, - 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, - 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, - 0x6b, 0x65, 0x79, 0x2e - }; - static const u8 shs_pad1[40] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - static const u8 shs_pad2[40] = { - 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, - 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, - 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, - 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 - }; - u8 digest[SHA1_MAC_LEN]; - const unsigned char *addr[4]; - const size_t len[4] = { 16, 40, 84, 40 }; - - addr[0] = master_key; - addr[1] = shs_pad1; - if (is_send) { - addr[2] = is_server ? magic3 : magic2; - } else { - addr[2] = is_server ? magic2 : magic3; - } - addr[3] = shs_pad2; - - sha1_vector(4, addr, len, digest); - - if (session_key_len > SHA1_MAC_LEN) - session_key_len = SHA1_MAC_LEN; - memcpy(session_key, digest, session_key_len); -} - - -#define PWBLOCK_LEN 516 - -/** - * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10 - * @password: 0-to-256-unicode-char Password (IN; ASCII) - * @password_len: Length of password - * @password_hash: 16-octet PasswordHash (IN) - * @pw_block: 516-byte PwBlock (OUT) - */ -static void encrypt_pw_block_with_password_hash( - const u8 *password, size_t password_len, - const u8 *password_hash, u8 *pw_block) -{ - size_t i, offset; - u8 *pos; - - if (password_len > 256) - return; - - memset(pw_block, 0, PWBLOCK_LEN); - offset = (256 - password_len) * 2; - hostapd_get_rand(pw_block, offset); - for (i = 0; i < password_len; i++) - pw_block[offset + i * 2] = password[i]; - /* - * PasswordLength is 4 octets, but since the maximum password length is - * 256, only first two (in little endian byte order) can be non-zero. - */ - pos = &pw_block[2 * 256]; - WPA_PUT_LE16(pos, password_len * 2); - rc4(pw_block, PWBLOCK_LEN, password_hash, 16); -} - - -/** - * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 - * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII) - * @new_password_len: Length of new_password - * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) - * @old_password_len: Length of old_password - * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) - */ -void new_password_encrypted_with_old_nt_password_hash( - const u8 *new_password, size_t new_password_len, - const u8 *old_password, size_t old_password_len, - u8 *encrypted_pw_block) -{ - u8 password_hash[16]; - - nt_password_hash(old_password, old_password_len, password_hash); - encrypt_pw_block_with_password_hash(new_password, new_password_len, - password_hash, encrypted_pw_block); -} - - -/** - * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 - * @password_hash: 16-octer PasswordHash (IN) - * @block: 16-octet Block (IN) - * @cypher: 16-octer Cypher (OUT) - */ -static void nt_password_hash_encrypted_with_block(const u8 *password_hash, - const u8 *block, - u8 *cypher) -{ - des_encrypt(password_hash, block, cypher); - des_encrypt(password_hash + 8, block + 7, cypher + 8); -} - - -/** - * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 - * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII) - * @new_password_len: Length of new_password - * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) - * @old_password_len: Length of old_password - * @encrypted_password_ash: 16-octet EncryptedPasswordHash (OUT) - */ -void old_nt_password_hash_encrypted_with_new_nt_password_hash( - const u8 *new_password, size_t new_password_len, - const u8 *old_password, size_t old_password_len, - u8 *encrypted_password_hash) -{ - u8 old_password_hash[16], new_password_hash[16]; - - nt_password_hash(old_password, old_password_len, old_password_hash); - nt_password_hash(new_password, new_password_len, new_password_hash); - nt_password_hash_encrypted_with_block(old_password_hash, - new_password_hash, - encrypted_password_hash); -} - - -#ifdef TEST_MAIN_MS_FUNCS - -#include "rc4.c" - -int main(int argc, char *argv[]) -{ - /* Test vector from RFC2759 example */ - u8 *username = "User"; - u8 *password = "clientPass"; - u8 auth_challenge[] = { - 0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E, - 0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28 - }; - u8 peer_challenge[] = { - 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, - 0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E - }; - u8 challenge[] = { 0xD0, 0x2E, 0x43, 0x86, 0xBC, 0xE9, 0x12, 0x26 }; - u8 password_hash[] = { - 0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6, - 0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE - }; - u8 nt_response[] = { - 0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E, - 0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54, - 0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF - }; - u8 password_hash_hash[] = { - 0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C, - 0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F - }; - u8 authenticator_response[] = { - 0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6, - 0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66, - 0x93, 0x2C, 0xDA, 0x56 - }; - u8 master_key[] = { - 0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C, - 0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31 - }; - u8 send_start_key[] = { - 0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B, - 0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB - }; - u8 buf[32]; - - int errors = 0; - - printf("Testing ms_funcs.c\n"); - - challenge_hash(peer_challenge, auth_challenge, - username, strlen(username), - buf); - if (memcmp(challenge, buf, sizeof(challenge)) != 0) { - printf("challenge_hash failed\n"); - errors++; - } - - nt_password_hash(password, strlen(password), buf); - if (memcmp(password_hash, buf, sizeof(password_hash)) != 0) { - printf("nt_password_hash failed\n"); - errors++; - } - - generate_nt_response(auth_challenge, peer_challenge, - username, strlen(username), - password, strlen(password), - buf); - if (memcmp(nt_response, buf, sizeof(nt_response)) != 0) { - printf("generate_nt_response failed\n"); - errors++; - } - - hash_nt_password_hash(password_hash, buf); - if (memcmp(password_hash_hash, buf, sizeof(password_hash_hash)) != 0) { - printf("hash_nt_password_hash failed\n"); - errors++; - } - - generate_authenticator_response(password, strlen(password), - peer_challenge, auth_challenge, - username, strlen(username), - nt_response, buf); - if (memcmp(authenticator_response, buf, sizeof(authenticator_response)) - != 0) { - printf("generate_authenticator_response failed\n"); - errors++; - } - - get_master_key(password_hash_hash, nt_response, buf); - if (memcmp(master_key, buf, sizeof(master_key)) != 0) { - printf("get_master_key failed\n"); - errors++; - } - - get_asymetric_start_key(master_key, buf, sizeof(send_start_key), 1, 1); - if (memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) { - printf("get_asymetric_start_key failed\n"); - errors++; - } - - if (errors) - printf("FAILED! %d errors\n", errors); - - return errors; -} -#endif /* TEST_MAIN_MS_FUNCS */ diff --git a/contrib/hostapd-0.4.9/ms_funcs.h b/contrib/hostapd-0.4.9/ms_funcs.h deleted file mode 100644 index fc43a370f1..0000000000 --- a/contrib/hostapd-0.4.9/ms_funcs.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef MS_FUNCS_H -#define MS_FUNCS_H - -void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, - const u8 *username, size_t username_len, - const u8 *password, size_t password_len, - u8 *response); -void generate_authenticator_response(const u8 *password, size_t password_len, - const u8 *peer_challenge, - const u8 *auth_challenge, - const u8 *username, size_t username_len, - const u8 *nt_response, u8 *response); -void nt_challenge_response(const u8 *challenge, const u8 *password, - size_t password_len, u8 *response); - -void challenge_response(const u8 *challenge, const u8 *password_hash, - u8 *response); -void nt_password_hash(const u8 *password, size_t password_len, - u8 *password_hash); -void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); -void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, - u8 *master_key); -void get_asymetric_start_key(const u8 *master_key, u8 *session_key, - size_t session_key_len, int is_send, - int is_server); -void new_password_encrypted_with_old_nt_password_hash( - const u8 *new_password, size_t new_password_len, - const u8 *old_password, size_t old_password_len, - u8 *encrypted_pw_block); -void old_nt_password_hash_encrypted_with_new_nt_password_hash( - const u8 *new_password, size_t new_password_len, - const u8 *old_password, size_t old_password_len, - u8 *encrypted_password_hash); - -#endif /* MS_FUNCS_H */ diff --git a/contrib/hostapd-0.4.9/radius.c b/contrib/hostapd-0.4.9/radius.c deleted file mode 100644 index 86f96cb6e7..0000000000 --- a/contrib/hostapd-0.4.9/radius.c +++ /dev/null @@ -1,1158 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / RADIUS client - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#ifndef CONFIG_NATIVE_WINDOWS -#include -#include -#include -#include -#endif /* CONFIG_NATIVE_WINDOWS */ - -#include "common.h" -#include "radius.h" -#include "md5.h" -#include "crypto.h" - - -struct radius_msg *radius_msg_new(u8 code, u8 identifier) -{ - struct radius_msg *msg; - - msg = (struct radius_msg *) malloc(sizeof(*msg)); - if (msg == NULL) - return NULL; - - if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) { - free(msg); - return NULL; - } - - radius_msg_set_hdr(msg, code, identifier); - - return msg; -} - - -int radius_msg_initialize(struct radius_msg *msg, size_t init_len) -{ - if (msg == NULL || init_len < sizeof(struct radius_hdr)) - return -1; - - memset(msg, 0, sizeof(*msg)); - msg->buf = (unsigned char *) malloc(init_len); - if (msg->buf == NULL) - return -1; - memset(msg->buf, 0, init_len); - - msg->buf_size = init_len; - msg->hdr = (struct radius_hdr *) msg->buf; - msg->buf_used = sizeof(*msg->hdr); - - msg->attrs = (struct radius_attr_hdr **) - malloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attrs)); - if (msg->attrs == NULL) { - free(msg->buf); - msg->buf = NULL; - msg->hdr = NULL; - return -1; - } - - msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT; - msg->attr_used = 0; - - return 0; -} - - -void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) -{ - msg->hdr->code = code; - msg->hdr->identifier = identifier; -} - - -void radius_msg_free(struct radius_msg *msg) -{ - if (msg->buf != NULL) { - free(msg->buf); - msg->buf = NULL; - msg->hdr = NULL; - } - msg->buf_size = msg->buf_used = 0; - - if (msg->attrs != NULL) { - free(msg->attrs); - msg->attrs = NULL; - } - msg->attr_size = msg->attr_used = 0; -} - - -static const char *radius_code_string(u8 code) -{ - switch (code) { - case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request"; - case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept"; - case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject"; - case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request"; - case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response"; - case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge"; - case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; - case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; - case RADIUS_CODE_RESERVED: return "Reserved"; - default: return "?Unknown?"; - } -} - - -struct radius_attr_type { - u8 type; - char *name; - enum { - RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, - RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 - } data_type; -}; - -static struct radius_attr_type radius_attrs[] = -{ - { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, - { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, - { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, - { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, - { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, - { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, - { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id", - RADIUS_ATTR_TEXT }, - { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id", - RADIUS_ATTR_TEXT }, - { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT }, - { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT }, - { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", - RADIUS_ATTR_TEXT }, - { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, - { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, - { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", - RADIUS_ATTR_UNDIST }, - { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", - RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, -}; -#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) - - -static struct radius_attr_type *radius_get_attr_type(u8 type) -{ - int i; - - for (i = 0; i < RADIUS_ATTRS; i++) { - if (type == radius_attrs[i].type) - return &radius_attrs[i]; - } - - return NULL; -} - - -static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) -{ - struct radius_attr_type *attr; - int i, len; - unsigned char *pos; - - attr = radius_get_attr_type(hdr->type); - - printf(" Attribute %d (%s) length=%d\n", - hdr->type, attr ? attr->name : "?Unknown?", hdr->length); - - if (attr == NULL) - return; - - len = hdr->length - sizeof(struct radius_attr_hdr); - pos = (unsigned char *) (hdr + 1); - - switch (attr->data_type) { - case RADIUS_ATTR_TEXT: - printf(" Value: '"); - for (i = 0; i < len; i++) - print_char(pos[i]); - printf("'\n"); - break; - - case RADIUS_ATTR_IP: - if (len == 4) { - struct in_addr *addr = (struct in_addr *) pos; - printf(" Value: %s\n", inet_ntoa(*addr)); - } else - printf(" Invalid IP address length %d\n", len); - break; - -#ifdef CONFIG_IPV6 - case RADIUS_ATTR_IPV6: - if (len == 16) { - char buf[128]; - const char *atxt; - struct in6_addr *addr = (struct in6_addr *) pos; - atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); - printf(" Value: %s\n", atxt ? atxt : "?"); - } else - printf(" Invalid IPv6 address length %d\n", len); - break; -#endif /* CONFIG_IPV6 */ - - case RADIUS_ATTR_HEXDUMP: - case RADIUS_ATTR_UNDIST: - printf(" Value:"); - for (i = 0; i < len; i++) - printf(" %02x", pos[i]); - printf("\n"); - break; - - case RADIUS_ATTR_INT32: - if (len == 4) - printf(" Value: %u\n", WPA_GET_BE32(pos)); - else - printf(" Invalid INT32 length %d\n", len); - break; - - default: - break; - } -} - - -void radius_msg_dump(struct radius_msg *msg) -{ - int i; - - printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", - msg->hdr->code, radius_code_string(msg->hdr->code), - msg->hdr->identifier, ntohs(msg->hdr->length)); - - for (i = 0; i < msg->attr_used; i++) { - radius_msg_dump_attr(msg->attrs[i]); - } -} - - -int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len) -{ - if (secret) { - u8 auth[MD5_MAC_LEN]; - struct radius_attr_hdr *attr; - - memset(auth, 0, MD5_MAC_LEN); - attr = radius_msg_add_attr(msg, - RADIUS_ATTR_MESSAGE_AUTHENTICATOR, - auth, MD5_MAC_LEN); - if (attr == NULL) { - printf("WARNING: Could not add " - "Message-Authenticator\n"); - return -1; - } - msg->hdr->length = htons(msg->buf_used); - hmac_md5(secret, secret_len, msg->buf, msg->buf_used, - (u8 *) (attr + 1)); - } else - msg->hdr->length = htons(msg->buf_used); - - if (msg->buf_used > 0xffff) { - printf("WARNING: too long RADIUS message (%lu)\n", - (unsigned long) msg->buf_used); - return -1; - } - return 0; -} - - -int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, - size_t secret_len, const u8 *req_authenticator) -{ - u8 auth[MD5_MAC_LEN]; - struct radius_attr_hdr *attr; - const u8 *addr[4]; - size_t len[4]; - - memset(auth, 0, MD5_MAC_LEN); - attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, - auth, MD5_MAC_LEN); - if (attr == NULL) { - printf("WARNING: Could not add Message-Authenticator\n"); - return -1; - } - msg->hdr->length = htons(msg->buf_used); - memcpy(msg->hdr->authenticator, req_authenticator, - sizeof(msg->hdr->authenticator)); - hmac_md5(secret, secret_len, msg->buf, msg->buf_used, - (u8 *) (attr + 1)); - - /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ - addr[0] = (u8 *) msg->hdr; - len[0] = 1 + 1 + 2; - addr[1] = req_authenticator; - len[1] = MD5_MAC_LEN; - addr[2] = (u8 *) (msg->hdr + 1); - len[2] = msg->buf_used - sizeof(*msg->hdr); - addr[3] = secret; - len[3] = secret_len; - md5_vector(4, addr, len, msg->hdr->authenticator); - - if (msg->buf_used > 0xffff) { - printf("WARNING: too long RADIUS message (%lu)\n", - (unsigned long) msg->buf_used); - return -1; - } - return 0; -} - - -void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, - size_t secret_len) -{ - const u8 *addr[2]; - size_t len[2]; - - msg->hdr->length = htons(msg->buf_used); - memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); - addr[0] = msg->buf; - len[0] = msg->buf_used; - addr[1] = secret; - len[1] = secret_len; - md5_vector(2, addr, len, msg->hdr->authenticator); - - if (msg->buf_used > 0xffff) { - printf("WARNING: too long RADIUS messages (%lu)\n", - (unsigned long) msg->buf_used); - } -} - - -static int radius_msg_add_attr_to_array(struct radius_msg *msg, - struct radius_attr_hdr *attr) -{ - if (msg->attr_used >= msg->attr_size) { - struct radius_attr_hdr **nattrs; - int nlen = msg->attr_size * 2; - - nattrs = (struct radius_attr_hdr **) - realloc(msg->attrs, nlen * sizeof(*msg->attrs)); - - if (nattrs == NULL) - return -1; - - msg->attrs = nattrs; - msg->attr_size = nlen; - } - - msg->attrs[msg->attr_used++] = attr; - - return 0; -} - - -struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, - const u8 *data, size_t data_len) -{ - size_t buf_needed; - struct radius_attr_hdr *attr; - - if (data_len > RADIUS_MAX_ATTR_LEN) { - printf("radius_msg_add_attr: too long attribute (%lu bytes)\n", - (unsigned long) data_len); - return NULL; - } - - buf_needed = msg->buf_used + sizeof(*attr) + data_len; - - if (msg->buf_size < buf_needed) { - /* allocate more space for message buffer */ - unsigned char *nbuf; - int nlen = msg->buf_size; - int diff, i; - - while (nlen < buf_needed) - nlen *= 2; - nbuf = (unsigned char *) realloc(msg->buf, nlen); - if (nbuf == NULL) - return NULL; - diff = nbuf - msg->buf; - msg->buf = nbuf; - msg->hdr = (struct radius_hdr *) msg->buf; - /* adjust attr pointers to match with the new buffer */ - for (i = 0; i < msg->attr_used; i++) - msg->attrs[i] = (struct radius_attr_hdr *) - (((u8 *) msg->attrs[i]) + diff); - memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size); - msg->buf_size = nlen; - } - - attr = (struct radius_attr_hdr *) (msg->buf + msg->buf_used); - attr->type = type; - attr->length = sizeof(*attr) + data_len; - if (data_len > 0) - memcpy(attr + 1, data, data_len); - - msg->buf_used += sizeof(*attr) + data_len; - - if (radius_msg_add_attr_to_array(msg, attr)) - return NULL; - - return attr; -} - - -struct radius_msg *radius_msg_parse(const u8 *data, size_t len) -{ - struct radius_msg *msg; - struct radius_hdr *hdr; - struct radius_attr_hdr *attr; - size_t msg_len; - unsigned char *pos, *end; - - if (data == NULL || len < sizeof(*hdr)) - return NULL; - - hdr = (struct radius_hdr *) data; - - msg_len = ntohs(hdr->length); - if (msg_len < sizeof(*hdr) || msg_len > len) { - printf("Invalid RADIUS message length\n"); - return NULL; - } - - if (msg_len < len) { - printf("Ignored %lu extra bytes after RADIUS message\n", - (unsigned long) len - msg_len); - } - - msg = (struct radius_msg *) malloc(sizeof(*msg)); - if (msg == NULL) - return NULL; - - if (radius_msg_initialize(msg, msg_len)) { - free(msg); - return NULL; - } - - memcpy(msg->buf, data, msg_len); - msg->buf_size = msg->buf_used = msg_len; - - /* parse attributes */ - pos = (unsigned char *) (msg->hdr + 1); - end = msg->buf + msg->buf_used; - while (pos < end) { - if (end - pos < sizeof(*attr)) - goto fail; - - attr = (struct radius_attr_hdr *) pos; - - if (pos + attr->length > end || attr->length < sizeof(*attr)) - goto fail; - - /* TODO: check that attr->length is suitable for attr->type */ - - if (radius_msg_add_attr_to_array(msg, attr)) - goto fail; - - pos += attr->length; - } - - return msg; - - fail: - radius_msg_free(msg); - free(msg); - return NULL; -} - - -int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) -{ - const u8 *pos = data; - size_t left = data_len; - - while (left > 0) { - int len; - if (left > RADIUS_MAX_ATTR_LEN) - len = RADIUS_MAX_ATTR_LEN; - else - len = left; - - if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, - pos, len)) - return 0; - - pos += len; - left -= len; - } - - return 1; -} - - -u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) -{ - u8 *eap, *pos; - size_t len; - int i; - - if (msg == NULL) - return NULL; - - len = 0; - for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) - len += msg->attrs[i]->length - - sizeof(struct radius_attr_hdr); - } - - if (len == 0) - return NULL; - - eap = malloc(len); - if (eap == NULL) - return NULL; - - pos = eap; - for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) { - struct radius_attr_hdr *attr = msg->attrs[i]; - int flen = attr->length - sizeof(*attr); - memcpy(pos, attr + 1, flen); - pos += flen; - } - } - - if (eap_len) - *eap_len = len; - - return eap; -} - - -int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, - size_t secret_len, const u8 *req_auth) -{ - u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; - u8 orig_authenticator[16]; - struct radius_attr_hdr *attr = NULL; - int i; - - for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { - if (attr != NULL) { - printf("Multiple Message-Authenticator " - "attributes in RADIUS message\n"); - return 1; - } - attr = msg->attrs[i]; - } - } - - if (attr == NULL) { - printf("No Message-Authenticator attribute found\n"); - return 1; - } - - memcpy(orig, attr + 1, MD5_MAC_LEN); - memset(attr + 1, 0, MD5_MAC_LEN); - if (req_auth) { - memcpy(orig_authenticator, msg->hdr->authenticator, - sizeof(orig_authenticator)); - memcpy(msg->hdr->authenticator, req_auth, - sizeof(msg->hdr->authenticator)); - } - hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth); - memcpy(attr + 1, orig, MD5_MAC_LEN); - if (req_auth) { - memcpy(msg->hdr->authenticator, orig_authenticator, - sizeof(orig_authenticator)); - } - - if (memcmp(orig, auth, MD5_MAC_LEN) != 0) { - printf("Invalid Message-Authenticator!\n"); - return 1; - } - - return 0; -} - - -int radius_msg_verify(struct radius_msg *msg, const u8 *secret, - size_t secret_len, struct radius_msg *sent_msg, int auth) -{ - const u8 *addr[4]; - size_t len[4]; - u8 hash[MD5_MAC_LEN]; - - if (sent_msg == NULL) { - printf("No matching Access-Request message found\n"); - return 1; - } - - if (auth && - radius_msg_verify_msg_auth(msg, secret, secret_len, - sent_msg->hdr->authenticator)) { - return 1; - } - - /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ - addr[0] = (u8 *) msg->hdr; - len[0] = 1 + 1 + 2; - addr[1] = sent_msg->hdr->authenticator; - len[1] = MD5_MAC_LEN; - addr[2] = (u8 *) (msg->hdr + 1); - len[2] = msg->buf_used - sizeof(*msg->hdr); - addr[3] = secret; - len[3] = secret_len; - md5_vector(4, addr, len, hash); - if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { - printf("Response Authenticator invalid!\n"); - return 1; - } - - return 0; - -} - - -int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, - u8 type) -{ - struct radius_attr_hdr *attr = NULL; - int i; - - for (i = 0; i < src->attr_used; i++) { - if (src->attrs[i]->type == type) { - attr = src->attrs[i]; - break; - } - } - - if (attr == NULL) - return 0; - - if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), - attr->length - sizeof(*attr))) - return -1; - - return 1; -} - - -/* Create Request Authenticator. The value should be unique over the lifetime - * of the shared secret between authenticator and authentication server. - * Use one-way MD5 hash calculated from current timestamp and some data given - * by the caller. */ -void radius_msg_make_authenticator(struct radius_msg *msg, - u8 *data, size_t len) -{ - struct timeval tv; - long int l; - const u8 *addr[3]; - size_t elen[3]; - - gettimeofday(&tv, NULL); - l = random(); - addr[0] = (u8 *) &tv; - elen[0] = sizeof(tv); - addr[1] = data; - elen[1] = len; - addr[2] = (u8 *) &l; - elen[2] = sizeof(l); - md5_vector(3, addr, elen, msg->hdr->authenticator); -} - - -/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. - * Returns the Attribute payload and sets alen to indicate the length of the - * payload if a vendor attribute with subtype is found, otherwise returns NULL. - * The returned payload is allocated with malloc() and caller must free it. - */ -static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, - u8 subtype, size_t *alen) -{ - u8 *data, *pos; - int i; - size_t len; - - if (msg == NULL) - return NULL; - - for (i = 0; i < msg->attr_used; i++) { - struct radius_attr_hdr *attr = msg->attrs[i]; - int left; - u32 vendor_id; - struct radius_attr_vendor *vhdr; - - if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC) - continue; - - left = attr->length - sizeof(*attr); - if (left < 4) - continue; - - pos = (u8 *) (attr + 1); - - memcpy(&vendor_id, pos, 4); - pos += 4; - left -= 4; - - if (ntohl(vendor_id) != vendor) - continue; - - while (left >= sizeof(*vhdr)) { - vhdr = (struct radius_attr_vendor *) pos; - if (vhdr->vendor_length > left || - vhdr->vendor_length < sizeof(*vhdr)) { - left = 0; - break; - } - if (vhdr->vendor_type != subtype) { - pos += vhdr->vendor_length; - left -= vhdr->vendor_length; - continue; - } - - len = vhdr->vendor_length - sizeof(*vhdr); - data = malloc(len); - if (data == NULL) - return NULL; - memcpy(data, pos + sizeof(*vhdr), len); - if (alen) - *alen = len; - return data; - } - } - - return NULL; -} - - -static u8 * decrypt_ms_key(const u8 *key, size_t len, - const u8 *req_authenticator, - const u8 *secret, size_t secret_len, size_t *reslen) -{ - u8 *plain, *ppos, *res; - const u8 *pos; - size_t left, plen; - u8 hash[MD5_MAC_LEN]; - int i, first = 1; - const u8 *addr[3]; - size_t elen[3]; - - /* key: 16-bit salt followed by encrypted key info */ - - if (len < 2 + 16) - return NULL; - - pos = key + 2; - left = len - 2; - if (left % 16) { - printf("Invalid ms key len %lu\n", (unsigned long) left); - return NULL; - } - - plen = left; - ppos = plain = malloc(plen); - if (plain == NULL) - return NULL; - - while (left > 0) { - /* b(1) = MD5(Secret + Request-Authenticator + Salt) - * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ - - addr[0] = secret; - elen[0] = secret_len; - if (first) { - addr[1] = req_authenticator; - elen[1] = MD5_MAC_LEN; - addr[2] = key; - elen[2] = 2; /* Salt */ - } else { - addr[1] = pos - MD5_MAC_LEN; - elen[1] = MD5_MAC_LEN; - } - md5_vector(first ? 3 : 2, addr, elen, hash); - first = 0; - - for (i = 0; i < MD5_MAC_LEN; i++) - *ppos++ = *pos++ ^ hash[i]; - left -= MD5_MAC_LEN; - } - - if (plain[0] > plen - 1) { - printf("Failed to decrypt MPPE key\n"); - free(plain); - return NULL; - } - - res = malloc(plain[0]); - if (res == NULL) { - free(plain); - return NULL; - } - memcpy(res, plain + 1, plain[0]); - if (reslen) - *reslen = plain[0]; - free(plain); - return res; -} - - -static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, - const u8 *req_authenticator, - const u8 *secret, size_t secret_len, - u8 *ebuf, size_t *elen) -{ - int i, len, first = 1; - u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; - const u8 *addr[3]; - size_t _len[3]; - - saltbuf[0] = salt >> 8; - saltbuf[1] = salt; - - len = 1 + key_len; - if (len & 0x0f) { - len = (len & 0xf0) + 16; - } - memset(ebuf, 0, len); - ebuf[0] = key_len; - memcpy(ebuf + 1, key, key_len); - - *elen = len; - - pos = ebuf; - while (len > 0) { - /* b(1) = MD5(Secret + Request-Authenticator + Salt) - * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ - addr[0] = secret; - _len[0] = secret_len; - if (first) { - addr[1] = req_authenticator; - _len[1] = MD5_MAC_LEN; - addr[2] = saltbuf; - _len[2] = sizeof(saltbuf); - } else { - addr[1] = pos - MD5_MAC_LEN; - _len[1] = MD5_MAC_LEN; - } - md5_vector(first ? 3 : 2, addr, _len, hash); - first = 0; - - for (i = 0; i < MD5_MAC_LEN; i++) - *pos++ ^= hash[i]; - - len -= MD5_MAC_LEN; - } -} - - -struct radius_ms_mppe_keys * -radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, - u8 *secret, size_t secret_len) -{ - u8 *key; - size_t keylen; - struct radius_ms_mppe_keys *keys; - - if (msg == NULL || sent_msg == NULL) - return NULL; - - keys = (struct radius_ms_mppe_keys *) malloc(sizeof(*keys)); - if (keys == NULL) - return NULL; - - memset(keys, 0, sizeof(*keys)); - - key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, - RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, - &keylen); - if (key) { - keys->send = decrypt_ms_key(key, keylen, - sent_msg->hdr->authenticator, - secret, secret_len, - &keys->send_len); - free(key); - } - - key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, - RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, - &keylen); - if (key) { - keys->recv = decrypt_ms_key(key, keylen, - sent_msg->hdr->authenticator, - secret, secret_len, - &keys->recv_len); - free(key); - } - - return keys; -} - - -struct radius_ms_mppe_keys * -radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, - u8 *secret, size_t secret_len) -{ - u8 *key; - size_t keylen; - struct radius_ms_mppe_keys *keys; - - if (msg == NULL || sent_msg == NULL) - return NULL; - - keys = (struct radius_ms_mppe_keys *) malloc(sizeof(*keys)); - if (keys == NULL) - return NULL; - - memset(keys, 0, sizeof(*keys)); - - key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO, - RADIUS_CISCO_AV_PAIR, &keylen); - if (key && keylen == 51 && memcmp(key, "leap:session-key=", 17) == 0) { - keys->recv = decrypt_ms_key(key + 17, keylen - 17, - sent_msg->hdr->authenticator, - secret, secret_len, - &keys->recv_len); - } - free(key); - - return keys; -} - - -int radius_msg_add_mppe_keys(struct radius_msg *msg, - const u8 *req_authenticator, - const u8 *secret, size_t secret_len, - const u8 *send_key, size_t send_key_len, - const u8 *recv_key, size_t recv_key_len) -{ - struct radius_attr_hdr *attr; - u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT); - u8 *buf; - struct radius_attr_vendor *vhdr; - u8 *pos; - size_t elen; - int hlen; - u16 salt; - - hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; - - /* MS-MPPE-Send-Key */ - buf = malloc(hlen + send_key_len + 16); - if (buf == NULL) { - return 0; - } - pos = buf; - memcpy(pos, &vendor_id, sizeof(vendor_id)); - pos += sizeof(vendor_id); - vhdr = (struct radius_attr_vendor *) pos; - vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; - pos = (u8 *) (vhdr + 1); - salt = random() | 0x8000; - *pos++ = salt >> 8; - *pos++ = salt; - encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, - secret_len, pos, &elen); - vhdr->vendor_length = hlen + elen - sizeof(vendor_id); - - attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, - buf, hlen + elen); - free(buf); - if (attr == NULL) { - return 0; - } - - /* MS-MPPE-Recv-Key */ - buf = malloc(hlen + send_key_len + 16); - if (buf == NULL) { - return 0; - } - pos = buf; - memcpy(pos, &vendor_id, sizeof(vendor_id)); - pos += sizeof(vendor_id); - vhdr = (struct radius_attr_vendor *) pos; - vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; - pos = (u8 *) (vhdr + 1); - salt ^= 1; - *pos++ = salt >> 8; - *pos++ = salt; - encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, - secret_len, pos, &elen); - vhdr->vendor_length = hlen + elen - sizeof(vendor_id); - - attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, - buf, hlen + elen); - free(buf); - if (attr == NULL) { - return 0; - } - - return 1; -} - - -/* Add User-Password attribute to a RADIUS message and encrypt it as specified - * in RFC 2865, Chap. 5.2 */ -struct radius_attr_hdr * -radius_msg_add_attr_user_password(struct radius_msg *msg, - u8 *data, size_t data_len, - u8 *secret, size_t secret_len) -{ - u8 buf[128]; - int padlen, i, pos; - size_t buf_len; - const u8 *addr[2]; - size_t len[2]; - u8 hash[16]; - - if (data_len > 128) - return NULL; - - memcpy(buf, data, data_len); - buf_len = data_len; - - padlen = data_len % 16; - if (padlen) { - padlen = 16 - padlen; - memset(buf + data_len, 0, padlen); - buf_len += padlen; - } - - addr[0] = secret; - len[0] = secret_len; - addr[1] = msg->hdr->authenticator; - len[1] = 16; - md5_vector(2, addr, len, hash); - - for (i = 0; i < 16; i++) - buf[i] ^= hash[i]; - pos = 16; - - while (pos < buf_len) { - addr[0] = secret; - len[0] = secret_len; - addr[1] = &buf[pos - 16]; - len[1] = 16; - md5_vector(2, addr, len, hash); - - for (i = 0; i < 16; i++) - buf[pos + i] ^= hash[i]; - - pos += 16; - } - - return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, - buf, buf_len); -} - - -int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) -{ - int i; - struct radius_attr_hdr *attr = NULL; - size_t dlen; - - for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == type) { - attr = msg->attrs[i]; - break; - } - } - - if (!attr) - return -1; - - dlen = attr->length - sizeof(*attr); - if (buf) - memcpy(buf, (attr + 1), dlen > len ? len : dlen); - return dlen; -} - - -int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, - size_t *len, const u8 *start) -{ - int i; - struct radius_attr_hdr *attr = NULL; - - for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == type && - (start == NULL || (u8 *) msg->attrs[i] > start)) { - attr = msg->attrs[i]; - break; - } - } - - if (!attr) - return -1; - - *buf = (u8 *) (attr + 1); - *len = attr->length - sizeof(*attr); - return 0; -} - - -int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) -{ - int i, count; - - for (count = 0, i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == type && - msg->attrs[i]->length >= - sizeof(struct radius_attr_hdr) + min_len) - count++; - } - - return count; -} diff --git a/contrib/hostapd-0.4.9/radius.h b/contrib/hostapd-0.4.9/radius.h deleted file mode 100644 index b9f8977f42..0000000000 --- a/contrib/hostapd-0.4.9/radius.h +++ /dev/null @@ -1,226 +0,0 @@ -#ifndef RADIUS_H -#define RADIUS_H - -/* RFC 2865 - RADIUS */ - -struct radius_hdr { - u8 code; - u8 identifier; - u16 length; /* including this header */ - u8 authenticator[16]; - /* followed by length-20 octets of attributes */ -} __attribute__ ((packed)); - -enum { RADIUS_CODE_ACCESS_REQUEST = 1, - RADIUS_CODE_ACCESS_ACCEPT = 2, - RADIUS_CODE_ACCESS_REJECT = 3, - RADIUS_CODE_ACCOUNTING_REQUEST = 4, - RADIUS_CODE_ACCOUNTING_RESPONSE = 5, - RADIUS_CODE_ACCESS_CHALLENGE = 11, - RADIUS_CODE_STATUS_SERVER = 12, - RADIUS_CODE_STATUS_CLIENT = 13, - RADIUS_CODE_RESERVED = 255 -}; - -struct radius_attr_hdr { - u8 type; - u8 length; /* including this header */ - /* followed by length-2 octets of attribute value */ -} __attribute__ ((packed)); - -#define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr)) - -enum { RADIUS_ATTR_USER_NAME = 1, - RADIUS_ATTR_USER_PASSWORD = 2, - RADIUS_ATTR_NAS_IP_ADDRESS = 4, - RADIUS_ATTR_NAS_PORT = 5, - RADIUS_ATTR_FRAMED_MTU = 12, - RADIUS_ATTR_STATE = 24, - RADIUS_ATTR_CLASS = 25, - RADIUS_ATTR_VENDOR_SPECIFIC = 26, - RADIUS_ATTR_SESSION_TIMEOUT = 27, - RADIUS_ATTR_IDLE_TIMEOUT = 28, - RADIUS_ATTR_TERMINATION_ACTION = 29, - RADIUS_ATTR_CALLED_STATION_ID = 30, - RADIUS_ATTR_CALLING_STATION_ID = 31, - RADIUS_ATTR_NAS_IDENTIFIER = 32, - RADIUS_ATTR_ACCT_STATUS_TYPE = 40, - RADIUS_ATTR_ACCT_DELAY_TIME = 41, - RADIUS_ATTR_ACCT_INPUT_OCTETS = 42, - RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43, - RADIUS_ATTR_ACCT_SESSION_ID = 44, - RADIUS_ATTR_ACCT_AUTHENTIC = 45, - RADIUS_ATTR_ACCT_SESSION_TIME = 46, - RADIUS_ATTR_ACCT_INPUT_PACKETS = 47, - RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48, - RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49, - RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50, - RADIUS_ATTR_ACCT_LINK_COUNT = 51, - RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52, - RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53, - RADIUS_ATTR_EVENT_TIMESTAMP = 55, - RADIUS_ATTR_NAS_PORT_TYPE = 61, - RADIUS_ATTR_CONNECT_INFO = 77, - RADIUS_ATTR_EAP_MESSAGE = 79, - RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80, - RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, - RADIUS_ATTR_NAS_IPV6_ADDRESS = 95 -}; - - -/* Termination-Action */ -#define RADIUS_TERMINATION_ACTION_DEFAULT 0 -#define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1 - -/* NAS-Port-Type */ -#define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19 - -/* Acct-Status-Type */ -#define RADIUS_ACCT_STATUS_TYPE_START 1 -#define RADIUS_ACCT_STATUS_TYPE_STOP 2 -#define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3 -#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7 -#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8 - -/* Acct-Authentic */ -#define RADIUS_ACCT_AUTHENTIC_RADIUS 1 -#define RADIUS_ACCT_AUTHENTIC_LOCAL 2 -#define RADIUS_ACCT_AUTHENTIC_REMOTE 3 - -/* Acct-Terminate-Cause */ -#define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1 -#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2 -#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3 -#define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4 -#define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5 -#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6 -#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7 -#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8 -#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9 -#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10 -#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11 -#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12 -#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13 -#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14 -#define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15 -#define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16 -#define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17 -#define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18 - - -struct radius_attr_vendor { - u8 vendor_type; - u8 vendor_length; -} __attribute__ ((packed)); - -#define RADIUS_VENDOR_ID_CISCO 9 -#define RADIUS_CISCO_AV_PAIR 1 - -/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */ -#define RADIUS_VENDOR_ID_MICROSOFT 311 - -enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16, - RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17 -}; - -struct radius_ms_mppe_keys { - u8 *send; - size_t send_len; - u8 *recv; - size_t recv_len; -}; - - -/* RADIUS message structure for new and parsed messages */ -struct radius_msg { - unsigned char *buf; - size_t buf_size; /* total size allocated for buf */ - size_t buf_used; /* bytes used in buf */ - - struct radius_hdr *hdr; - - struct radius_attr_hdr **attrs; /* array of pointers to attributes */ - size_t attr_size; /* total size of the attribute pointer array */ - size_t attr_used; /* total number of attributes in the array */ -}; - - -/* Default size to be allocated for new RADIUS messages */ -#define RADIUS_DEFAULT_MSG_SIZE 1024 - -/* Default size to be allocated for attribute array */ -#define RADIUS_DEFAULT_ATTR_COUNT 16 - - -/* MAC address ASCII format for IEEE 802.1X use - * (draft-congdon-radius-8021x-20.txt) */ -#define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X" -/* MAC address ASCII format for non-802.1X use */ -#define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x" - -struct radius_msg *radius_msg_new(u8 code, u8 identifier); -int radius_msg_initialize(struct radius_msg *msg, size_t init_len); -void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier); -void radius_msg_free(struct radius_msg *msg); -void radius_msg_dump(struct radius_msg *msg); -int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len); -int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, - size_t secret_len, const u8 *req_authenticator); -void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, - size_t secret_len); -struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, - const u8 *data, size_t data_len); -struct radius_msg *radius_msg_parse(const u8 *data, size_t len); -int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, - size_t data_len); -u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len); -int radius_msg_verify(struct radius_msg *msg, const u8 *secret, - size_t secret_len, struct radius_msg *sent_msg, - int auth); -int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, - size_t secret_len, const u8 *req_auth); -int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, - u8 type); -void radius_msg_make_authenticator(struct radius_msg *msg, - u8 *data, size_t len); -struct radius_ms_mppe_keys * -radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, - u8 *secret, size_t secret_len); -struct radius_ms_mppe_keys * -radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, - u8 *secret, size_t secret_len); -int radius_msg_add_mppe_keys(struct radius_msg *msg, - const u8 *req_authenticator, - const u8 *secret, size_t secret_len, - const u8 *send_key, size_t send_key_len, - const u8 *recv_key, size_t recv_key_len); -struct radius_attr_hdr * -radius_msg_add_attr_user_password(struct radius_msg *msg, - u8 *data, size_t data_len, - u8 *secret, size_t secret_len); -int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); - -static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type, - u32 value) -{ - u32 val = htonl(value); - return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL; -} - -static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type, - u32 *value) -{ - u32 val; - int res; - res = radius_msg_get_attr(msg, type, (u8 *) &val, 4); - if (res != 4) - return -1; - - *value = ntohl(val); - return 0; -} -int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, - size_t *len, const u8 *start); -int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len); - -#endif /* RADIUS_H */ diff --git a/contrib/hostapd-0.4.9/radius_client.c b/contrib/hostapd-0.4.9/radius_client.c deleted file mode 100644 index 690208445b..0000000000 --- a/contrib/hostapd-0.4.9/radius_client.c +++ /dev/null @@ -1,1116 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / RADIUS client - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef CONFIG_NATIVE_WINDOWS -#include -#include -#include -#endif /* CONFIG_NATIVE_WINDOWS */ - -#include "hostapd.h" -#include "radius.h" -#include "radius_client.h" -#include "eloop.h" - -/* Defaults for RADIUS retransmit values (exponential backoff) */ -#define RADIUS_CLIENT_FIRST_WAIT 3 /* seconds */ -#define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */ -#define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts - * before entry is removed from retransmit - * list */ -#define RADIUS_CLIENT_MAX_ENTRIES 30 /* maximum number of entries in retransmit - * list (oldest will be removed, if this - * limit is exceeded) */ -#define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this - * many failed retry attempts */ - - -struct radius_rx_handler { - RadiusRxResult (*handler)(struct radius_msg *msg, - struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, - void *data); - void *data; -}; - - -/* RADIUS message retransmit list */ -struct radius_msg_list { - u8 addr[ETH_ALEN]; /* STA/client address; used to find RADIUS messages - * for the same STA. */ - struct radius_msg *msg; - RadiusType msg_type; - time_t first_try; - time_t next_try; - int attempts; - int next_wait; - struct timeval last_attempt; - - u8 *shared_secret; - size_t shared_secret_len; - - /* TODO: server config with failover to backup server(s) */ - - struct radius_msg_list *next; -}; - - -struct radius_client_data { - void *ctx; - struct hostapd_radius_servers *conf; - - int auth_serv_sock; /* socket for authentication RADIUS messages */ - int acct_serv_sock; /* socket for accounting RADIUS messages */ - int auth_serv_sock6; - int acct_serv_sock6; - int auth_sock; /* currently used socket */ - int acct_sock; /* currently used socket */ - - struct radius_rx_handler *auth_handlers; - size_t num_auth_handlers; - struct radius_rx_handler *acct_handlers; - size_t num_acct_handlers; - - struct radius_msg_list *msgs; - size_t num_msgs; - - u8 next_radius_identifier; -}; - - -static int -radius_change_server(struct radius_client_data *radius, - struct hostapd_radius_server *nserv, - struct hostapd_radius_server *oserv, - int sock, int sock6, int auth); -static int radius_client_init_acct(struct radius_client_data *radius); -static int radius_client_init_auth(struct radius_client_data *radius); - - -static void radius_client_msg_free(struct radius_msg_list *req) -{ - radius_msg_free(req->msg); - free(req->msg); - free(req); -} - - -int radius_client_register(struct radius_client_data *radius, - RadiusType msg_type, - RadiusRxResult (*handler)(struct radius_msg *msg, - struct radius_msg *req, - u8 *shared_secret, - size_t shared_secret_len, - void *data), - void *data) -{ - struct radius_rx_handler **handlers, *newh; - size_t *num; - - if (msg_type == RADIUS_ACCT) { - handlers = &radius->acct_handlers; - num = &radius->num_acct_handlers; - } else { - handlers = &radius->auth_handlers; - num = &radius->num_auth_handlers; - } - - newh = (struct radius_rx_handler *) - realloc(*handlers, - (*num + 1) * sizeof(struct radius_rx_handler)); - if (newh == NULL) - return -1; - - newh[*num].handler = handler; - newh[*num].data = data; - (*num)++; - *handlers = newh; - - return 0; -} - - -static void radius_client_handle_send_error(struct radius_client_data *radius, - int s, RadiusType msg_type) -{ -#ifndef CONFIG_NATIVE_WINDOWS - int _errno = errno; - perror("send[RADIUS]"); - if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL) { - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, - "Send failed - maybe interface status changed -" - " try to connect again"); - eloop_unregister_read_sock(s); - close(s); - if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) - radius_client_init_acct(radius); - else - radius_client_init_auth(radius); - } -#endif /* CONFIG_NATIVE_WINDOWS */ -} - - -static int radius_client_retransmit(struct radius_client_data *radius, - struct radius_msg_list *entry, time_t now) -{ - struct hostapd_radius_servers *conf = radius->conf; - int s; - - if (entry->msg_type == RADIUS_ACCT || - entry->msg_type == RADIUS_ACCT_INTERIM) { - s = radius->acct_sock; - if (entry->attempts == 0) - conf->acct_server->requests++; - else { - conf->acct_server->timeouts++; - conf->acct_server->retransmissions++; - } - } else { - s = radius->auth_sock; - if (entry->attempts == 0) - conf->auth_server->requests++; - else { - conf->auth_server->timeouts++; - conf->auth_server->retransmissions++; - } - } - - /* retransmit; remove entry if too many attempts */ - entry->attempts++; - hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", - entry->msg->hdr->identifier); - - gettimeofday(&entry->last_attempt, NULL); - if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0) - radius_client_handle_send_error(radius, s, entry->msg_type); - - entry->next_try = now + entry->next_wait; - entry->next_wait *= 2; - if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) - entry->next_wait = RADIUS_CLIENT_MAX_WAIT; - if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { - printf("Removing un-ACKed RADIUS message due to too many " - "failed retransmit attempts\n"); - return 1; - } - - return 0; -} - - -static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) -{ - struct radius_client_data *radius = eloop_ctx; - struct hostapd_radius_servers *conf = radius->conf; - time_t now, first; - struct radius_msg_list *entry, *prev, *tmp; - int auth_failover = 0, acct_failover = 0; - char abuf[50]; - - entry = radius->msgs; - if (!entry) - return; - - time(&now); - first = 0; - - prev = NULL; - while (entry) { - if (now >= entry->next_try && - radius_client_retransmit(radius, entry, now)) { - if (prev) - prev->next = entry->next; - else - radius->msgs = entry->next; - - tmp = entry; - entry = entry->next; - radius_client_msg_free(tmp); - radius->num_msgs--; - continue; - } - - if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) { - if (entry->msg_type == RADIUS_ACCT || - entry->msg_type == RADIUS_ACCT_INTERIM) - acct_failover++; - else - auth_failover++; - } - - if (first == 0 || entry->next_try < first) - first = entry->next_try; - - prev = entry; - entry = entry->next; - } - - if (radius->msgs) { - if (first < now) - first = now; - eloop_register_timeout(first - now, 0, - radius_client_timer, radius, NULL); - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " - "retransmit in %ld seconds", - (long int) (first - now)); - } - - if (auth_failover && conf->num_auth_servers > 1) { - struct hostapd_radius_server *next, *old; - old = conf->auth_server; - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_NOTICE, - "No response from Authentication server " - "%s:%d - failover", - hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), - old->port); - - for (entry = radius->msgs; entry; entry = entry->next) { - if (entry->msg_type == RADIUS_AUTH) - old->timeouts++; - } - - next = old + 1; - if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) - next = conf->auth_servers; - conf->auth_server = next; - radius_change_server(radius, next, old, - radius->auth_serv_sock, - radius->auth_serv_sock6, 1); - } - - if (acct_failover && conf->num_acct_servers > 1) { - struct hostapd_radius_server *next, *old; - old = conf->acct_server; - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_NOTICE, - "No response from Accounting server " - "%s:%d - failover", - hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), - old->port); - - for (entry = radius->msgs; entry; entry = entry->next) { - if (entry->msg_type == RADIUS_ACCT || - entry->msg_type == RADIUS_ACCT_INTERIM) - old->timeouts++; - } - - next = old + 1; - if (next > &conf->acct_servers[conf->num_acct_servers - 1]) - next = conf->acct_servers; - conf->acct_server = next; - radius_change_server(radius, next, old, - radius->acct_serv_sock, - radius->acct_serv_sock6, 0); - } -} - - -static void radius_client_update_timeout(struct radius_client_data *radius) -{ - time_t now, first; - struct radius_msg_list *entry; - - eloop_cancel_timeout(radius_client_timer, radius, NULL); - - if (radius->msgs == NULL) { - return; - } - - first = 0; - for (entry = radius->msgs; entry; entry = entry->next) { - if (first == 0 || entry->next_try < first) - first = entry->next_try; - } - - time(&now); - if (first < now) - first = now; - eloop_register_timeout(first - now, 0, radius_client_timer, radius, - NULL); - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" - " %ld seconds\n", (long int) (first - now)); -} - - -static void radius_client_list_add(struct radius_client_data *radius, - struct radius_msg *msg, - RadiusType msg_type, u8 *shared_secret, - size_t shared_secret_len, u8 *addr) -{ - struct radius_msg_list *entry, *prev; - - if (eloop_terminated()) { - /* No point in adding entries to retransmit queue since event - * loop has already been terminated. */ - radius_msg_free(msg); - free(msg); - return; - } - - entry = malloc(sizeof(*entry)); - if (entry == NULL) { - printf("Failed to add RADIUS packet into retransmit list\n"); - radius_msg_free(msg); - free(msg); - return; - } - - memset(entry, 0, sizeof(*entry)); - if (addr) - memcpy(entry->addr, addr, ETH_ALEN); - entry->msg = msg; - entry->msg_type = msg_type; - entry->shared_secret = shared_secret; - entry->shared_secret_len = shared_secret_len; - time(&entry->first_try); - entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; - entry->attempts = 1; - gettimeofday(&entry->last_attempt, NULL); - entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; - entry->next = radius->msgs; - radius->msgs = entry; - radius_client_update_timeout(radius); - - if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { - printf("Removing the oldest un-ACKed RADIUS packet due to " - "retransmit list limits.\n"); - prev = NULL; - while (entry->next) { - prev = entry; - entry = entry->next; - } - if (prev) { - prev->next = NULL; - radius_client_msg_free(entry); - } - } else - radius->num_msgs++; -} - - -static void radius_client_list_del(struct radius_client_data *radius, - RadiusType msg_type, u8 *addr) -{ - struct radius_msg_list *entry, *prev, *tmp; - - if (addr == NULL) - return; - - entry = radius->msgs; - prev = NULL; - while (entry) { - if (entry->msg_type == msg_type && - memcmp(entry->addr, addr, ETH_ALEN) == 0) { - if (prev) - prev->next = entry->next; - else - radius->msgs = entry->next; - tmp = entry; - entry = entry->next; - hostapd_logger(radius->ctx, addr, - HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, - "Removing matching RADIUS message"); - radius_client_msg_free(tmp); - radius->num_msgs--; - continue; - } - prev = entry; - entry = entry->next; - } -} - - -int radius_client_send(struct radius_client_data *radius, - struct radius_msg *msg, RadiusType msg_type, u8 *addr) -{ - struct hostapd_radius_servers *conf = radius->conf; - u8 *shared_secret; - size_t shared_secret_len; - char *name; - int s, res; - - if (msg_type == RADIUS_ACCT_INTERIM) { - /* Remove any pending interim acct update for the same STA. */ - radius_client_list_del(radius, msg_type, addr); - } - - if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { - shared_secret = conf->acct_server->shared_secret; - shared_secret_len = conf->acct_server->shared_secret_len; - radius_msg_finish_acct(msg, shared_secret, shared_secret_len); - name = "accounting"; - s = radius->acct_sock; - conf->acct_server->requests++; - } else { - shared_secret = conf->auth_server->shared_secret; - shared_secret_len = conf->auth_server->shared_secret_len; - radius_msg_finish(msg, shared_secret, shared_secret_len); - name = "authentication"; - s = radius->auth_sock; - conf->auth_server->requests++; - } - - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " - "server", name); - if (conf->msg_dumps) - radius_msg_dump(msg); - - res = send(s, msg->buf, msg->buf_used, 0); - if (res < 0) - radius_client_handle_send_error(radius, s, msg_type); - - radius_client_list_add(radius, msg, msg_type, shared_secret, - shared_secret_len, addr); - - return res; -} - - -static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct radius_client_data *radius = eloop_ctx; - struct hostapd_radius_servers *conf = radius->conf; - RadiusType msg_type = (RadiusType) sock_ctx; - int len, i, roundtrip; - unsigned char buf[3000]; - struct radius_msg *msg; - struct radius_rx_handler *handlers; - size_t num_handlers; - struct radius_msg_list *req, *prev_req; - struct timeval tv; - struct hostapd_radius_server *rconf; - int invalid_authenticator = 0; - - if (msg_type == RADIUS_ACCT) { - handlers = radius->acct_handlers; - num_handlers = radius->num_acct_handlers; - rconf = conf->acct_server; - } else { - handlers = radius->auth_handlers; - num_handlers = radius->num_auth_handlers; - rconf = conf->auth_server; - } - - len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); - if (len < 0) { - perror("recv[RADIUS]"); - return; - } - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " - "server", len); - if (len == sizeof(buf)) { - printf("Possibly too long UDP frame for our buffer - " - "dropping it\n"); - return; - } - - msg = radius_msg_parse(buf, len); - if (msg == NULL) { - printf("Parsing incoming RADIUS frame failed\n"); - rconf->malformed_responses++; - return; - } - - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); - if (conf->msg_dumps) - radius_msg_dump(msg); - - switch (msg->hdr->code) { - case RADIUS_CODE_ACCESS_ACCEPT: - rconf->access_accepts++; - break; - case RADIUS_CODE_ACCESS_REJECT: - rconf->access_rejects++; - break; - case RADIUS_CODE_ACCESS_CHALLENGE: - rconf->access_challenges++; - break; - case RADIUS_CODE_ACCOUNTING_RESPONSE: - rconf->responses++; - break; - } - - prev_req = NULL; - req = radius->msgs; - while (req) { - /* TODO: also match by src addr:port of the packet when using - * alternative RADIUS servers (?) */ - if ((req->msg_type == msg_type || - (req->msg_type == RADIUS_ACCT_INTERIM && - msg_type == RADIUS_ACCT)) && - req->msg->hdr->identifier == msg->hdr->identifier) - break; - - prev_req = req; - req = req->next; - } - - if (req == NULL) { - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, - "No matching RADIUS request found (type=%d " - "id=%d) - dropping packet", - msg_type, msg->hdr->identifier); - goto fail; - } - - gettimeofday(&tv, NULL); - roundtrip = (tv.tv_sec - req->last_attempt.tv_sec) * 100 + - (tv.tv_usec - req->last_attempt.tv_usec) / 10000; - hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, - "Received RADIUS packet matched with a pending " - "request, round trip time %d.%02d sec", - roundtrip / 100, roundtrip % 100); - rconf->round_trip_time = roundtrip; - - /* Remove ACKed RADIUS packet from retransmit list */ - if (prev_req) - prev_req->next = req->next; - else - radius->msgs = req->next; - radius->num_msgs--; - - for (i = 0; i < num_handlers; i++) { - RadiusRxResult res; - res = handlers[i].handler(msg, req->msg, req->shared_secret, - req->shared_secret_len, - handlers[i].data); - switch (res) { - case RADIUS_RX_PROCESSED: - radius_msg_free(msg); - free(msg); - /* continue */ - case RADIUS_RX_QUEUED: - radius_client_msg_free(req); - return; - case RADIUS_RX_INVALID_AUTHENTICATOR: - invalid_authenticator++; - /* continue */ - case RADIUS_RX_UNKNOWN: - /* continue with next handler */ - break; - } - } - - if (invalid_authenticator) - rconf->bad_authenticators++; - else - rconf->unknown_types++; - hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " - "(type=%d code=%d id=%d)%s - dropping packet", - msg_type, msg->hdr->code, msg->hdr->identifier, - invalid_authenticator ? " [INVALID AUTHENTICATOR]" : - ""); - radius_client_msg_free(req); - - fail: - radius_msg_free(msg); - free(msg); -} - - -u8 radius_client_get_id(struct radius_client_data *radius) -{ - struct radius_msg_list *entry, *prev, *remove; - u8 id = radius->next_radius_identifier++; - - /* remove entries with matching id from retransmit list to avoid - * using new reply from the RADIUS server with an old request */ - entry = radius->msgs; - prev = NULL; - while (entry) { - if (entry->msg->hdr->identifier == id) { - hostapd_logger(radius->ctx, entry->addr, - HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, - "Removing pending RADIUS message, " - "since its id (%d) is reused", id); - if (prev) - prev->next = entry->next; - else - radius->msgs = entry->next; - remove = entry; - } else { - remove = NULL; - prev = entry; - } - entry = entry->next; - - if (remove) - radius_client_msg_free(remove); - } - - return id; -} - - -void radius_client_flush(struct radius_client_data *radius) -{ - struct radius_msg_list *entry, *prev; - - if (!radius) - return; - - eloop_cancel_timeout(radius_client_timer, radius, NULL); - - entry = radius->msgs; - radius->msgs = NULL; - radius->num_msgs = 0; - while (entry) { - prev = entry; - entry = entry->next; - radius_client_msg_free(prev); - } -} - - -static int -radius_change_server(struct radius_client_data *radius, - struct hostapd_radius_server *nserv, - struct hostapd_radius_server *oserv, - int sock, int sock6, int auth) -{ - struct sockaddr_in serv; -#ifdef CONFIG_IPV6 - struct sockaddr_in6 serv6; -#endif /* CONFIG_IPV6 */ - struct sockaddr *addr; - socklen_t addrlen; - char abuf[50]; - int sel_sock; - - hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, - "%s server %s:%d", - auth ? "Authentication" : "Accounting", - hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), - nserv->port); - - if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || - memcmp(nserv->shared_secret, oserv->shared_secret, - nserv->shared_secret_len) != 0) { - /* Pending RADIUS packets used different shared - * secret, so they would need to be modified. Could - * update all message authenticators and - * User-Passwords, etc. and retry with new server. For - * now, just drop all pending packets. */ - radius_client_flush(radius); - } else { - /* Reset retry counters for the new server */ - struct radius_msg_list *entry; - entry = radius->msgs; - while (entry) { - entry->next_try = entry->first_try + - RADIUS_CLIENT_FIRST_WAIT; - entry->attempts = 0; - entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; - entry = entry->next; - } - if (radius->msgs) { - eloop_cancel_timeout(radius_client_timer, radius, - NULL); - eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, - radius_client_timer, radius, - NULL); - } - } - - switch (nserv->addr.af) { - case AF_INET: - memset(&serv, 0, sizeof(serv)); - serv.sin_family = AF_INET; - serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; - serv.sin_port = htons(nserv->port); - addr = (struct sockaddr *) &serv; - addrlen = sizeof(serv); - sel_sock = sock; - break; -#ifdef CONFIG_IPV6 - case AF_INET6: - memset(&serv6, 0, sizeof(serv6)); - serv6.sin6_family = AF_INET6; - memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, - sizeof(struct in6_addr)); - serv6.sin6_port = htons(nserv->port); - addr = (struct sockaddr *) &serv6; - addrlen = sizeof(serv6); - sel_sock = sock6; - break; -#endif /* CONFIG_IPV6 */ - default: - return -1; - } - - if (connect(sel_sock, addr, addrlen) < 0) { - perror("connect[radius]"); - return -1; - } - - if (auth) - radius->auth_sock = sel_sock; - else - radius->acct_sock = sel_sock; - - return 0; -} - - -static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) -{ - struct radius_client_data *radius = eloop_ctx; - struct hostapd_radius_servers *conf = radius->conf; - struct hostapd_radius_server *oserv; - - if (radius->auth_sock >= 0 && conf->auth_servers && - conf->auth_server != conf->auth_servers) { - oserv = conf->auth_server; - conf->auth_server = conf->auth_servers; - radius_change_server(radius, conf->auth_server, oserv, - radius->auth_serv_sock, - radius->auth_serv_sock6, 1); - } - - if (radius->acct_sock >= 0 && conf->acct_servers && - conf->acct_server != conf->acct_servers) { - oserv = conf->acct_server; - conf->acct_server = conf->acct_servers; - radius_change_server(radius, conf->acct_server, oserv, - radius->acct_serv_sock, - radius->acct_serv_sock6, 0); - } - - if (conf->retry_primary_interval) - eloop_register_timeout(conf->retry_primary_interval, 0, - radius_retry_primary_timer, radius, - NULL); -} - - -static int radius_client_init_auth(struct radius_client_data *radius) -{ - struct hostapd_radius_servers *conf = radius->conf; - int ok = 0; - - radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (radius->auth_serv_sock < 0) - perror("socket[PF_INET,SOCK_DGRAM]"); - else - ok++; - -#ifdef CONFIG_IPV6 - radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); - if (radius->auth_serv_sock6 < 0) - perror("socket[PF_INET6,SOCK_DGRAM]"); - else - ok++; -#endif /* CONFIG_IPV6 */ - - if (ok == 0) - return -1; - - radius_change_server(radius, conf->auth_server, NULL, - radius->auth_serv_sock, radius->auth_serv_sock6, - 1); - - if (radius->auth_serv_sock >= 0 && - eloop_register_read_sock(radius->auth_serv_sock, - radius_client_receive, radius, - (void *) RADIUS_AUTH)) { - printf("Could not register read socket for authentication " - "server\n"); - return -1; - } - -#ifdef CONFIG_IPV6 - if (radius->auth_serv_sock6 >= 0 && - eloop_register_read_sock(radius->auth_serv_sock6, - radius_client_receive, radius, - (void *) RADIUS_AUTH)) { - printf("Could not register read socket for authentication " - "server\n"); - return -1; - } -#endif /* CONFIG_IPV6 */ - - return 0; -} - - -static int radius_client_init_acct(struct radius_client_data *radius) -{ - struct hostapd_radius_servers *conf = radius->conf; - int ok = 0; - - radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (radius->acct_serv_sock < 0) - perror("socket[PF_INET,SOCK_DGRAM]"); - else - ok++; - - radius_change_server(radius, conf->acct_server, NULL, - radius->acct_serv_sock, radius->acct_serv_sock6, - 0); - - if (radius->acct_serv_sock >= 0 && - eloop_register_read_sock(radius->acct_serv_sock, - radius_client_receive, radius, - (void *) RADIUS_ACCT)) { - printf("Could not register read socket for accounting " - "server\n"); - return -1; - } - -#ifdef CONFIG_IPV6 - if (radius->acct_serv_sock6 >= 0 && - eloop_register_read_sock(radius->acct_serv_sock6, - radius_client_receive, radius, - (void *) RADIUS_ACCT)) { - printf("Could not register read socket for accounting " - "server\n"); - return -1; - } -#endif /* CONFIG_IPV6 */ - - return 0; -} - - -struct radius_client_data * -radius_client_init(void *ctx, struct hostapd_radius_servers *conf) -{ - struct radius_client_data *radius; - - radius = malloc(sizeof(struct radius_client_data)); - if (radius == NULL) - return NULL; - - memset(radius, 0, sizeof(struct radius_client_data)); - radius->ctx = ctx; - radius->conf = conf; - radius->auth_serv_sock = radius->acct_serv_sock = - radius->auth_serv_sock6 = radius->acct_serv_sock6 = - radius->auth_sock = radius->acct_sock = -1; - - if (conf->auth_server && radius_client_init_auth(radius)) { - radius_client_deinit(radius); - return NULL; - } - - if (conf->acct_server && radius_client_init_acct(radius)) { - radius_client_deinit(radius); - return NULL; - } - - if (conf->retry_primary_interval) - eloop_register_timeout(conf->retry_primary_interval, 0, - radius_retry_primary_timer, radius, - NULL); - - return radius; -} - - -void radius_client_deinit(struct radius_client_data *radius) -{ - if (!radius) - return; - - eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); - - radius_client_flush(radius); - free(radius->auth_handlers); - free(radius->acct_handlers); - free(radius); -} - - -void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr) -{ - struct radius_msg_list *entry, *prev, *tmp; - - prev = NULL; - entry = radius->msgs; - while (entry) { - if (entry->msg_type == RADIUS_AUTH && - memcmp(entry->addr, addr, ETH_ALEN) == 0) { - hostapd_logger(radius->ctx, addr, - HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, - "Removing pending RADIUS authentication" - " message for removed client"); - - if (prev) - prev->next = entry->next; - else - radius->msgs = entry->next; - - tmp = entry; - entry = entry->next; - radius_client_msg_free(tmp); - radius->num_msgs--; - continue; - } - - prev = entry; - entry = entry->next; - } -} - - -static int radius_client_dump_auth_server(char *buf, size_t buflen, - struct hostapd_radius_server *serv, - struct radius_client_data *cli) -{ - int pending = 0; - struct radius_msg_list *msg; - char abuf[50]; - - if (cli) { - for (msg = cli->msgs; msg; msg = msg->next) { - if (msg->msg_type == RADIUS_AUTH) - pending++; - } - } - - return snprintf(buf, buflen, - "radiusAuthServerIndex=%d\n" - "radiusAuthServerAddress=%s\n" - "radiusAuthClientServerPortNumber=%d\n" - "radiusAuthClientRoundTripTime=%d\n" - "radiusAuthClientAccessRequests=%u\n" - "radiusAuthClientAccessRetransmissions=%u\n" - "radiusAuthClientAccessAccepts=%u\n" - "radiusAuthClientAccessRejects=%u\n" - "radiusAuthClientAccessChallenges=%u\n" - "radiusAuthClientMalformedAccessResponses=%u\n" - "radiusAuthClientBadAuthenticators=%u\n" - "radiusAuthClientPendingRequests=%u\n" - "radiusAuthClientTimeouts=%u\n" - "radiusAuthClientUnknownTypes=%u\n" - "radiusAuthClientPacketsDropped=%u\n", - serv->index, - hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), - serv->port, - serv->round_trip_time, - serv->requests, - serv->retransmissions, - serv->access_accepts, - serv->access_rejects, - serv->access_challenges, - serv->malformed_responses, - serv->bad_authenticators, - pending, - serv->timeouts, - serv->unknown_types, - serv->packets_dropped); -} - - -static int radius_client_dump_acct_server(char *buf, size_t buflen, - struct hostapd_radius_server *serv, - struct radius_client_data *cli) -{ - int pending = 0; - struct radius_msg_list *msg; - char abuf[50]; - - if (cli) { - for (msg = cli->msgs; msg; msg = msg->next) { - if (msg->msg_type == RADIUS_ACCT || - msg->msg_type == RADIUS_ACCT_INTERIM) - pending++; - } - } - - return snprintf(buf, buflen, - "radiusAccServerIndex=%d\n" - "radiusAccServerAddress=%s\n" - "radiusAccClientServerPortNumber=%d\n" - "radiusAccClientRoundTripTime=%d\n" - "radiusAccClientRequests=%u\n" - "radiusAccClientRetransmissions=%u\n" - "radiusAccClientResponses=%u\n" - "radiusAccClientMalformedResponses=%u\n" - "radiusAccClientBadAuthenticators=%u\n" - "radiusAccClientPendingRequests=%u\n" - "radiusAccClientTimeouts=%u\n" - "radiusAccClientUnknownTypes=%u\n" - "radiusAccClientPacketsDropped=%u\n", - serv->index, - hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), - serv->port, - serv->round_trip_time, - serv->requests, - serv->retransmissions, - serv->responses, - serv->malformed_responses, - serv->bad_authenticators, - pending, - serv->timeouts, - serv->unknown_types, - serv->packets_dropped); -} - - -int radius_client_get_mib(struct radius_client_data *radius, char *buf, - size_t buflen) -{ - struct hostapd_radius_servers *conf = radius->conf; - int i; - struct hostapd_radius_server *serv; - int count = 0; - - if (conf->auth_servers) { - for (i = 0; i < conf->num_auth_servers; i++) { - serv = &conf->auth_servers[i]; - count += radius_client_dump_auth_server( - buf + count, buflen - count, serv, - serv == conf->auth_server ? - radius : NULL); - } - } - - if (conf->acct_servers) { - for (i = 0; i < conf->num_acct_servers; i++) { - serv = &conf->acct_servers[i]; - count += radius_client_dump_acct_server( - buf + count, buflen - count, serv, - serv == conf->acct_server ? - radius : NULL); - } - } - - return count; -} diff --git a/contrib/hostapd-0.4.9/radius_client.h b/contrib/hostapd-0.4.9/radius_client.h deleted file mode 100644 index d21ca83fae..0000000000 --- a/contrib/hostapd-0.4.9/radius_client.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef RADIUS_CLIENT_H -#define RADIUS_CLIENT_H - -#include "config_types.h" - -struct radius_msg; - -struct hostapd_radius_server { - /* MIB prefix for shared variables: - * @ = radiusAuth or radiusAcc depending on the type of the server */ - struct hostapd_ip_addr addr; /* @ServerAddress */ - int port; /* @ClientServerPortNumber */ - u8 *shared_secret; - size_t shared_secret_len; - - /* Dynamic (not from configuration file) MIB data */ - int index; /* @ServerIndex */ - int round_trip_time; /* @ClientRoundTripTime; in hundredths of a - * second */ - u32 requests; /* @Client{Access,}Requests */ - u32 retransmissions; /* @Client{Access,}Retransmissions */ - u32 access_accepts; /* radiusAuthClientAccessAccepts */ - u32 access_rejects; /* radiusAuthClientAccessRejects */ - u32 access_challenges; /* radiusAuthClientAccessChallenges */ - u32 responses; /* radiusAccClientResponses */ - u32 malformed_responses; /* @ClientMalformed{Access,}Responses */ - u32 bad_authenticators; /* @ClientBadAuthenticators */ - u32 timeouts; /* @ClientTimeouts */ - u32 unknown_types; /* @ClientUnknownTypes */ - u32 packets_dropped; /* @ClientPacketsDropped */ - /* @ClientPendingRequests: length of hapd->radius->msgs for matching - * msg_type */ -}; - -struct hostapd_radius_servers { - /* RADIUS Authentication and Accounting servers in priority order */ - struct hostapd_radius_server *auth_servers, *auth_server; - int num_auth_servers; - struct hostapd_radius_server *acct_servers, *acct_server; - int num_acct_servers; - - int retry_primary_interval; - int acct_interim_interval; - - int msg_dumps; -}; - - -typedef enum { - RADIUS_AUTH, - RADIUS_ACCT, - RADIUS_ACCT_INTERIM /* used only with radius_client_send(); just like - * RADIUS_ACCT, but removes any pending interim - * RADIUS Accounting packages for the same STA - * before sending the new interim update */ -} RadiusType; - -typedef enum { - RADIUS_RX_PROCESSED, - RADIUS_RX_QUEUED, - RADIUS_RX_UNKNOWN, - RADIUS_RX_INVALID_AUTHENTICATOR -} RadiusRxResult; - -struct radius_client_data; - -int radius_client_register(struct radius_client_data *radius, - RadiusType msg_type, - RadiusRxResult (*handler) - (struct radius_msg *msg, struct radius_msg *req, - u8 *shared_secret, size_t shared_secret_len, - void *data), - void *data); -int radius_client_send(struct radius_client_data *radius, - struct radius_msg *msg, - RadiusType msg_type, u8 *addr); -u8 radius_client_get_id(struct radius_client_data *radius); - -void radius_client_flush(struct radius_client_data *radius); -struct radius_client_data * -radius_client_init(void *ctx, struct hostapd_radius_servers *conf); -void radius_client_deinit(struct radius_client_data *radius); -void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr); -int radius_client_get_mib(struct radius_client_data *radius, char *buf, - size_t buflen); - -#endif /* RADIUS_CLIENT_H */ diff --git a/contrib/hostapd-0.4.9/radius_server.c b/contrib/hostapd-0.4.9/radius_server.c deleted file mode 100644 index 13c17744e0..0000000000 --- a/contrib/hostapd-0.4.9/radius_server.c +++ /dev/null @@ -1,1074 +0,0 @@ -/* - * hostapd / RADIUS authentication server - * Copyright (c) 2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "radius.h" -#include "eloop.h" -#include "config.h" -#include "eap.h" -#include "radius_server.h" - -#define RADIUS_SESSION_TIMEOUT 60 -#define RADIUS_MAX_SESSION 100 -#define RADIUS_MAX_MSG_LEN 3000 - -static struct eapol_callbacks radius_server_eapol_cb; - -struct radius_client; -struct radius_server_data; - -struct radius_session { - struct radius_session *next; - struct radius_client *client; - struct radius_server_data *server; - unsigned int sess_id; - struct eap_sm *eap; - u8 *eapKeyData, *eapReqData; - size_t eapKeyDataLen, eapReqDataLen; - Boolean eapSuccess, eapRestart, eapFail, eapResp, eapReq, eapNoReq; - Boolean portEnabled, eapTimeout; -}; - -struct radius_client { - struct radius_client *next; - struct in_addr addr; - struct in_addr mask; -#ifdef CONFIG_IPV6 - struct in6_addr addr6; - struct in6_addr mask6; -#endif /* CONFIG_IPV6 */ - char *shared_secret; - int shared_secret_len; - struct radius_session *sessions; -}; - -struct radius_server_data { - int auth_sock; - struct radius_client *clients; - struct radius_server_session *sessions; - unsigned int next_sess_id; - void *hostapd_conf; - int num_sess; - void *eap_sim_db_priv; - void *ssl_ctx; - int ipv6; -}; - - -extern int wpa_debug_level; - -#define RADIUS_DEBUG(args...) \ -wpa_printf(MSG_DEBUG, "RADIUS SRV: " args) -#define RADIUS_ERROR(args...) \ -wpa_printf(MSG_ERROR, "RADIUS SRV: " args) -#define RADIUS_DUMP(args...) \ -wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args) -#define RADIUS_DUMP_ASCII(args...) \ -wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args) - - -static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); - - - -static struct radius_client * -radius_server_get_client(struct radius_server_data *data, struct in_addr *addr, - int ipv6) -{ - struct radius_client *client = data->clients; - - while (client) { -#ifdef CONFIG_IPV6 - if (ipv6) { - struct in6_addr *addr6; - int i; - - addr6 = (struct in6_addr *) addr; - for (i = 0; i < 16; i++) { - if ((addr6->s6_addr[i] & - client->mask6.s6_addr[i]) != - (client->addr6.s6_addr[i] & - client->mask6.s6_addr[i])) { - i = 17; - break; - } - } - if (i == 16) { - break; - } - } -#endif /* CONFIG_IPV6 */ - if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) == - (addr->s_addr & client->mask.s_addr)) { - break; - } - - client = client->next; - } - - return client; -} - - -static struct radius_session * -radius_server_get_session(struct radius_client *client, unsigned int sess_id) -{ - struct radius_session *sess = client->sessions; - - while (sess) { - if (sess->sess_id == sess_id) { - break; - } - sess = sess->next; - } - - return sess; -} - - -static void radius_server_session_free(struct radius_server_data *data, - struct radius_session *sess) -{ - eloop_cancel_timeout(radius_server_session_timeout, data, sess); - free(sess->eapKeyData); - free(sess->eapReqData); - eap_sm_deinit(sess->eap); - free(sess); - data->num_sess--; -} - - -static void radius_server_session_remove(struct radius_server_data *data, - struct radius_session *sess) -{ - struct radius_client *client = sess->client; - struct radius_session *session, *prev; - - prev = NULL; - session = client->sessions; - while (session) { - if (session == sess) { - if (prev == NULL) { - client->sessions = sess->next; - } else { - prev->next = sess->next; - } - radius_server_session_free(data, sess); - break; - } - prev = session; - session = session->next; - } -} - - -static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct radius_server_data *data = eloop_ctx; - struct radius_session *sess = timeout_ctx; - - RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id); - radius_server_session_remove(data, sess); -} - - -static struct radius_session * -radius_server_new_session(struct radius_server_data *data, - struct radius_client *client) -{ - struct radius_session *sess; - - if (data->num_sess >= RADIUS_MAX_SESSION) { - RADIUS_DEBUG("Maximum number of existing session - no room " - "for a new session"); - return NULL; - } - - sess = malloc(sizeof(*sess)); - if (sess == NULL) { - return NULL; - } - memset(sess, 0, sizeof(*sess)); - sess->server = data; - sess->client = client; - sess->sess_id = data->next_sess_id++; - sess->next = client->sessions; - client->sessions = sess; - eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0, - radius_server_session_timeout, data, sess); - data->num_sess++; - return sess; -} - - -static struct radius_session * -radius_server_get_new_session(struct radius_server_data *data, - struct radius_client *client, - struct radius_msg *msg) -{ - u8 *user; - size_t user_len; - const struct hostapd_eap_user *eap_user; - int res; - struct radius_session *sess; - struct eap_config eap_conf; - - RADIUS_DEBUG("Creating a new session"); - - user = malloc(256); - if (user == NULL) { - return NULL; - } - res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256); - if (res < 0 || res > 256) { - RADIUS_DEBUG("Could not get User-Name"); - free(user); - return NULL; - } - user_len = res; - RADIUS_DUMP_ASCII("User-Name", user, user_len); - - eap_user = hostapd_get_eap_user(data->hostapd_conf, user, user_len, 0); - free(user); - - if (eap_user) { - RADIUS_DEBUG("Matching user entry found"); - sess = radius_server_new_session(data, client); - if (sess == NULL) { - RADIUS_DEBUG("Failed to create a new session"); - return NULL; - } - } else { - RADIUS_DEBUG("User-Name not found from user database"); - return NULL; - } - - memset(&eap_conf, 0, sizeof(eap_conf)); - eap_conf.ssl_ctx = data->ssl_ctx; - eap_conf.eap_sim_db_priv = data->eap_sim_db_priv; - eap_conf.backend_auth = TRUE; - sess->eap = eap_sm_init(sess, &radius_server_eapol_cb, &eap_conf); - if (sess->eap == NULL) { - RADIUS_DEBUG("Failed to initialize EAP state machine for the " - "new session"); - radius_server_session_free(data, sess); - return NULL; - } - sess->eapRestart = TRUE; - sess->portEnabled = TRUE; - - RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id); - - return sess; -} - - -static struct radius_msg * -radius_server_encapsulate_eap(struct radius_server_data *data, - struct radius_client *client, - struct radius_session *sess, - struct radius_msg *request) -{ - struct radius_msg *msg; - int code; - unsigned int sess_id; - - if (sess->eapFail) { - code = RADIUS_CODE_ACCESS_REJECT; - } else if (sess->eapSuccess) { - code = RADIUS_CODE_ACCESS_ACCEPT; - } else { - code = RADIUS_CODE_ACCESS_CHALLENGE; - } - - msg = radius_msg_new(code, request->hdr->identifier); - if (msg == NULL) { - return NULL; - } - - sess_id = htonl(sess->sess_id); - if (code == RADIUS_CODE_ACCESS_CHALLENGE && - !radius_msg_add_attr(msg, RADIUS_ATTR_STATE, - (u8 *) &sess_id, sizeof(sess_id))) { - RADIUS_DEBUG("Failed to add State attribute"); - } - - if (sess->eapReqData && - !radius_msg_add_eap(msg, sess->eapReqData, sess->eapReqDataLen)) { - RADIUS_DEBUG("Failed to add EAP-Message attribute"); - } - - if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eapKeyData) { - int len; - if (sess->eapKeyDataLen > 64) { - len = 32; - } else { - len = sess->eapKeyDataLen / 2; - } - if (!radius_msg_add_mppe_keys(msg, request->hdr->authenticator, - (u8 *) client->shared_secret, - client->shared_secret_len, - sess->eapKeyData + len, len, - sess->eapKeyData, len)) { - RADIUS_DEBUG("Failed to add MPPE key attributes"); - } - } - - if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, - client->shared_secret_len, - request->hdr->authenticator) < 0) { - RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); - } - - return msg; -} - - -static int radius_server_reject(struct radius_server_data *data, - struct radius_client *client, - struct radius_msg *request, - struct sockaddr *from, socklen_t fromlen, - const char *from_addr, int from_port) -{ - struct radius_msg *msg; - int ret = 0; - struct eap_hdr eapfail; - - RADIUS_DEBUG("Reject invalid request from %s:%d", - from_addr, from_port); - - msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, - request->hdr->identifier); - if (msg == NULL) { - return -1; - } - - memset(&eapfail, 0, sizeof(eapfail)); - eapfail.code = EAP_CODE_FAILURE; - eapfail.identifier = 0; - eapfail.length = htons(sizeof(eapfail)); - - if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) { - RADIUS_DEBUG("Failed to add EAP-Message attribute"); - } - - - if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, - client->shared_secret_len, - request->hdr->authenticator) < 0) { - RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); - } - - if (wpa_debug_level <= MSG_MSGDUMP) { - radius_msg_dump(msg); - } - - if (sendto(data->auth_sock, msg->buf, msg->buf_used, 0, - (struct sockaddr *) from, sizeof(*from)) < 0) { - perror("sendto[RADIUS SRV]"); - ret = -1; - } - - radius_msg_free(msg); - free(msg); - - return ret; -} - - -static int radius_server_request(struct radius_server_data *data, - struct radius_msg *msg, - struct sockaddr *from, socklen_t fromlen, - struct radius_client *client, - const char *from_addr, int from_port) -{ - u8 *eap = NULL; - size_t eap_len; - int res, state_included = 0; - u8 statebuf[4], resp_id; - unsigned int state; - struct radius_session *sess; - struct radius_msg *reply; - struct eap_hdr *hdr; - - /* TODO: Implement duplicate packet processing */ - - res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf, - sizeof(statebuf)); - state_included = res >= 0; - if (res == sizeof(statebuf)) { - state = (statebuf[0] << 24) | (statebuf[1] << 16) | - (statebuf[2] << 8) | statebuf[3]; - sess = radius_server_get_session(client, state); - } else { - sess = NULL; - } - - if (sess) { - RADIUS_DEBUG("Request for session 0x%x", sess->sess_id); - } else if (state_included) { - RADIUS_DEBUG("State attribute included but no session found"); - radius_server_reject(data, client, msg, from, fromlen, - from_addr, from_port); - return -1; - } else { - sess = radius_server_get_new_session(data, client, msg); - if (sess == NULL) { - RADIUS_DEBUG("Could not create a new session"); - radius_server_reject(data, client, msg, from, fromlen, - from_addr, from_port); - return -1; - } - } - - eap = radius_msg_get_eap(msg, &eap_len); - if (eap == NULL) { - RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", - from_addr); - return -1; - } - - RADIUS_DUMP("Received EAP data", eap, eap_len); - if (eap_len >= sizeof(*hdr)) { - hdr = (struct eap_hdr *) eap; - resp_id = hdr->identifier; - } else { - resp_id = 0; - } - - /* FIX: if Code is Request, Success, or Failure, send Access-Reject; - * RFC3579 Sect. 2.6.2. - * Include EAP-Response/Nak with no preferred method if - * code == request. - * If code is not 1-4, discard the packet silently. - * Or is this already done by the EAP state machine? */ - - eap_set_eapRespData(sess->eap, eap, eap_len); - free(eap); - eap = NULL; - sess->eapResp = TRUE; - eap_sm_step(sess->eap); - - if (sess->eapReqData) { - RADIUS_DUMP("EAP data from the state machine", - sess->eapReqData, sess->eapReqDataLen); - } else if (sess->eapFail) { - RADIUS_DEBUG("No EAP data from the state machine, but eapFail " - "set - generate EAP-Failure"); - hdr = malloc(sizeof(*hdr)); - if (hdr) { - memset(hdr, 0, sizeof(*hdr)); - hdr->identifier = resp_id; - hdr->length = htons(sizeof(*hdr)); - sess->eapReqData = (u8 *) hdr; - sess->eapReqDataLen = sizeof(*hdr); - } - } else { - RADIUS_DEBUG("No EAP data from the state machine - ignore this" - " Access-Request silently (assuming it was a " - "duplicate)"); - return -1; - } - - reply = radius_server_encapsulate_eap(data, client, sess, msg); - - free(sess->eapReqData); - sess->eapReqData = NULL; - sess->eapReqDataLen = 0; - - if (reply) { - RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port); - if (wpa_debug_level <= MSG_MSGDUMP) { - radius_msg_dump(reply); - } - - res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0, - (struct sockaddr *) from, fromlen); - if (res < 0) { - perror("sendto[RADIUS SRV]"); - } - radius_msg_free(reply); - free(reply); - } - - if (sess->eapSuccess || sess->eapFail) { - RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); - radius_server_session_remove(data, sess); - } - - return 0; -} - - -static void radius_server_receive_auth(int sock, void *eloop_ctx, - void *sock_ctx) -{ - struct radius_server_data *data = eloop_ctx; - u8 *buf = NULL; - struct sockaddr_storage from; - socklen_t fromlen; - int len; - struct radius_client *client = NULL; - struct radius_msg *msg = NULL; - char abuf[50]; - int from_port = 0; - - buf = malloc(RADIUS_MAX_MSG_LEN); - if (buf == NULL) { - goto fail; - } - - fromlen = sizeof(from); - len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, - (struct sockaddr *) &from, &fromlen); - if (len < 0) { - perror("recvfrom[radius_server]"); - goto fail; - } - -#ifdef CONFIG_IPV6 - if (data->ipv6) { - struct sockaddr_in6 *from6 = (struct sockaddr_in6 *) &from; - if (inet_ntop(AF_INET6, &from6->sin6_addr, abuf, sizeof(abuf)) - == NULL) - abuf[0] = '\0'; - from_port = ntohs(from6->sin6_port); - RADIUS_DEBUG("Received %d bytes from %s:%d", - len, abuf, from_port); - - client = radius_server_get_client(data, - (struct in_addr *) - &from6->sin6_addr, 1); - } -#endif /* CONFIG_IPV6 */ - - if (!data->ipv6) { - struct sockaddr_in *from4 = (struct sockaddr_in *) &from; - snprintf(abuf, sizeof(abuf), "%s", inet_ntoa(from4->sin_addr)); - from_port = ntohs(from4->sin_port); - RADIUS_DEBUG("Received %d bytes from %s:%d", - len, abuf, from_port); - - client = radius_server_get_client(data, &from4->sin_addr, 0); - } - - RADIUS_DUMP("Received data", buf, len); - - if (client == NULL) { - RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); - goto fail; - } - - msg = radius_msg_parse(buf, len); - if (msg == NULL) { - RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); - goto fail; - } - - free(buf); - buf = NULL; - - if (wpa_debug_level <= MSG_MSGDUMP) { - radius_msg_dump(msg); - } - - if (msg->hdr->code != RADIUS_CODE_ACCESS_REQUEST) { - RADIUS_DEBUG("Unexpected RADIUS code %d", msg->hdr->code); - goto fail; - } - - if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret, - client->shared_secret_len, NULL)) { - RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf); - goto fail; - } - - radius_server_request(data, msg, (struct sockaddr *) &from, fromlen, - client, abuf, from_port); - -fail: - if (msg) { - radius_msg_free(msg); - free(msg); - } - free(buf); -} - - -static int radius_server_open_socket(int port) -{ - int s; - struct sockaddr_in addr; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - perror("socket"); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); - close(s); - return -1; - } - - return s; -} - - -#ifdef CONFIG_IPV6 -static int radius_server_open_socket6(int port) -{ - int s; - struct sockaddr_in6 addr; - - s = socket(PF_INET6, SOCK_DGRAM, 0); - if (s < 0) { - perror("socket[IPv6]"); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sin6_family = AF_INET6; - memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); - addr.sin6_port = htons(port); - if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); - close(s); - return -1; - } - - return s; -} -#endif /* CONFIG_IPV6 */ - - -static void radius_server_free_sessions(struct radius_server_data *data, - struct radius_session *sessions) -{ - struct radius_session *session, *prev; - - session = sessions; - while (session) { - prev = session; - session = session->next; - radius_server_session_free(data, prev); - } -} - - -static void radius_server_free_clients(struct radius_server_data *data, - struct radius_client *clients) -{ - struct radius_client *client, *prev; - - client = clients; - while (client) { - prev = client; - client = client->next; - - radius_server_free_sessions(data, prev->sessions); - free(prev->shared_secret); - free(prev); - } -} - - -static struct radius_client * -radius_server_read_clients(const char *client_file, int ipv6) -{ - FILE *f; - const int buf_size = 1024; - char *buf, *pos; - struct radius_client *clients, *tail, *entry; - int line = 0, mask, failed = 0, i; - struct in_addr addr; -#ifdef CONFIG_IPV6 - struct in6_addr addr6; -#endif /* CONFIG_IPV6 */ - unsigned int val; - - f = fopen(client_file, "r"); - if (f == NULL) { - RADIUS_ERROR("Could not open client file '%s'", client_file); - return NULL; - } - - buf = malloc(buf_size); - if (buf == NULL) { - fclose(f); - return NULL; - } - - clients = tail = NULL; - while (fgets(buf, buf_size, f)) { - /* Configuration file format: - * 192.168.1.0/24 secret - * 192.168.1.2 secret - * fe80::211:22ff:fe33:4455/64 secretipv6 - */ - line++; - buf[buf_size - 1] = '\0'; - pos = buf; - while (*pos != '\0' && *pos != '\n') - pos++; - if (*pos == '\n') - *pos = '\0'; - if (*buf == '\0' || *buf == '#') - continue; - - pos = buf; - while ((*pos >= '0' && *pos <= '9') || *pos == '.' || - (*pos >= 'a' && *pos <= 'f') || *pos == ':' || - (*pos >= 'A' && *pos <= 'F')) { - pos++; - } - - if (*pos == '\0') { - failed = 1; - break; - } - - if (*pos == '/') { - char *end; - *pos++ = '\0'; - mask = strtol(pos, &end, 10); - if ((pos == end) || - (mask < 0 || mask > (ipv6 ? 128 : 32))) { - failed = 1; - break; - } - pos = end; - } else { - mask = ipv6 ? 128 : 32; - *pos++ = '\0'; - } - - if (!ipv6 && inet_aton(buf, &addr) == 0) { - failed = 1; - break; - } -#ifdef CONFIG_IPV6 - if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) { - if (inet_pton(AF_INET, buf, &addr) <= 0) { - failed = 1; - break; - } - /* Convert IPv4 address to IPv6 */ - if (mask <= 32) - mask += (128 - 32); - memset(addr6.s6_addr, 0, 10); - addr6.s6_addr[10] = 0xff; - addr6.s6_addr[11] = 0xff; - memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr, 4); - } -#endif /* CONFIG_IPV6 */ - - while (*pos == ' ' || *pos == '\t') { - pos++; - } - - if (*pos == '\0') { - failed = 1; - break; - } - - entry = malloc(sizeof(*entry)); - if (entry == NULL) { - failed = 1; - break; - } - memset(entry, 0, sizeof(*entry)); - entry->shared_secret = strdup(pos); - if (entry->shared_secret == NULL) { - failed = 1; - free(entry); - break; - } - entry->shared_secret_len = strlen(entry->shared_secret); - entry->addr.s_addr = addr.s_addr; - if (!ipv6) { - val = 0; - for (i = 0; i < mask; i++) - val |= 1 << (31 - i); - entry->mask.s_addr = htonl(val); - } -#ifdef CONFIG_IPV6 - if (ipv6) { - int offset = mask / 8; - - memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16); - memset(entry->mask6.s6_addr, 0xff, offset); - val = 0; - for (i = 0; i < (mask % 8); i++) - val |= 1 << (7 - i); - if (offset < 16) - entry->mask6.s6_addr[offset] = val; - } -#endif /* CONFIG_IPV6 */ - - if (tail == NULL) { - clients = tail = entry; - } else { - tail->next = entry; - tail = entry; - } - } - - if (failed) { - RADIUS_ERROR("Invalid line %d in '%s'", line, client_file); - radius_server_free_clients(NULL, clients); - clients = NULL; - } - - free(buf); - fclose(f); - - return clients; -} - - -struct radius_server_data * -radius_server_init(struct radius_server_conf *conf) -{ - struct radius_server_data *data; - -#ifndef CONFIG_IPV6 - if (conf->ipv6) { - fprintf(stderr, "RADIUS server compiled without IPv6 " - "support.\n"); - return NULL; - } -#endif /* CONFIG_IPV6 */ - - data = malloc(sizeof(*data)); - if (data == NULL) { - return NULL; - } - memset(data, 0, sizeof(*data)); - data->hostapd_conf = conf->hostapd_conf; - data->eap_sim_db_priv = conf->eap_sim_db_priv; - data->ssl_ctx = conf->ssl_ctx; - data->ipv6 = conf->ipv6; - - data->clients = radius_server_read_clients(conf->client_file, - conf->ipv6); - if (data->clients == NULL) { - printf("No RADIUS clients configured.\n"); - radius_server_deinit(data); - return NULL; - } - -#ifdef CONFIG_IPV6 - if (conf->ipv6) - data->auth_sock = radius_server_open_socket6(conf->auth_port); - else -#endif /* CONFIG_IPV6 */ - data->auth_sock = radius_server_open_socket(conf->auth_port); - if (data->auth_sock < 0) { - printf("Failed to open UDP socket for RADIUS authentication " - "server\n"); - radius_server_deinit(data); - return NULL; - } - if (eloop_register_read_sock(data->auth_sock, - radius_server_receive_auth, - data, NULL)) { - radius_server_deinit(data); - return NULL; - } - - return data; -} - - -void radius_server_deinit(struct radius_server_data *data) -{ - if (data == NULL) - return; - - if (data->auth_sock >= 0) { - eloop_unregister_read_sock(data->auth_sock); - close(data->auth_sock); - } - - radius_server_free_clients(data, data->clients); - - free(data); -} - - -int radius_server_get_mib(struct radius_server_data *data, char *buf, - size_t buflen) -{ - /* TODO: add support for RADIUS authentication server MIB */ - return 0; -} - - -static Boolean radius_server_get_bool(void *ctx, enum eapol_bool_var variable) -{ - struct radius_session *sess = ctx; - if (sess == NULL) - return FALSE; - switch (variable) { - case EAPOL_eapSuccess: - return sess->eapSuccess; - case EAPOL_eapRestart: - return sess->eapRestart; - case EAPOL_eapFail: - return sess->eapFail; - case EAPOL_eapResp: - return sess->eapResp; - case EAPOL_eapReq: - return sess->eapReq; - case EAPOL_eapNoReq: - return sess->eapNoReq; - case EAPOL_portEnabled: - return sess->portEnabled; - case EAPOL_eapTimeout: - return sess->eapTimeout; - } - return FALSE; -} - - -static void radius_server_set_bool(void *ctx, enum eapol_bool_var variable, - Boolean value) -{ - struct radius_session *sess = ctx; - if (sess == NULL) - return; - switch (variable) { - case EAPOL_eapSuccess: - sess->eapSuccess = value; - break; - case EAPOL_eapRestart: - sess->eapRestart = value; - break; - case EAPOL_eapFail: - sess->eapFail = value; - break; - case EAPOL_eapResp: - sess->eapResp = value; - break; - case EAPOL_eapReq: - sess->eapReq = value; - break; - case EAPOL_eapNoReq: - sess->eapNoReq = value; - break; - case EAPOL_portEnabled: - sess->portEnabled = value; - break; - case EAPOL_eapTimeout: - sess->eapTimeout = value; - break; - } -} - - -static void radius_server_set_eapReqData(void *ctx, const u8 *eapReqData, - size_t eapReqDataLen) -{ - struct radius_session *sess = ctx; - if (sess == NULL) - return; - - free(sess->eapReqData); - sess->eapReqData = malloc(eapReqDataLen); - if (sess->eapReqData) { - memcpy(sess->eapReqData, eapReqData, eapReqDataLen); - sess->eapReqDataLen = eapReqDataLen; - } else { - sess->eapReqDataLen = 0; - } -} - - -static void radius_server_set_eapKeyData(void *ctx, const u8 *eapKeyData, - size_t eapKeyDataLen) -{ - struct radius_session *sess = ctx; - - if (sess == NULL) - return; - - free(sess->eapKeyData); - if (eapKeyData) { - sess->eapKeyData = malloc(eapKeyDataLen); - if (sess->eapKeyData) { - memcpy(sess->eapKeyData, eapKeyData, eapKeyDataLen); - sess->eapKeyDataLen = eapKeyDataLen; - } else { - sess->eapKeyDataLen = 0; - } - } else { - sess->eapKeyData = NULL; - sess->eapKeyDataLen = 0; - } -} - - -static int radius_server_get_eap_user(void *ctx, const u8 *identity, - size_t identity_len, int phase2, - struct eap_user *user) -{ - struct radius_session *sess = ctx; - const struct hostapd_eap_user *eap_user; - - eap_user = hostapd_get_eap_user(sess->server->hostapd_conf, identity, - identity_len, phase2); - if (eap_user == NULL) - return -1; - - memset(user, 0, sizeof(*user)); - memcpy(user->methods, eap_user->methods, - EAP_USER_MAX_METHODS > EAP_MAX_METHODS ? - EAP_USER_MAX_METHODS : EAP_MAX_METHODS); - - if (eap_user->password) { - user->password = malloc(eap_user->password_len); - if (user->password == NULL) - return -1; - memcpy(user->password, eap_user->password, - eap_user->password_len); - user->password_len = eap_user->password_len; - } - user->force_version = eap_user->force_version; - - return 0; -} - - -static struct eapol_callbacks radius_server_eapol_cb = -{ - .get_bool = radius_server_get_bool, - .set_bool = radius_server_set_bool, - .set_eapReqData = radius_server_set_eapReqData, - .set_eapKeyData = radius_server_set_eapKeyData, - .get_eap_user = radius_server_get_eap_user, -}; diff --git a/contrib/hostapd-0.4.9/radius_server.h b/contrib/hostapd-0.4.9/radius_server.h deleted file mode 100644 index 5c4c39c37e..0000000000 --- a/contrib/hostapd-0.4.9/radius_server.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef RADIUS_SERVER_H -#define RADIUS_SERVER_H - -struct radius_server_data; - -struct radius_server_conf { - int auth_port; - char *client_file; - void *hostapd_conf; - void *eap_sim_db_priv; - void *ssl_ctx; - int ipv6; -}; - - -#ifdef RADIUS_SERVER - -struct radius_server_data * -radius_server_init(struct radius_server_conf *conf); - -void radius_server_deinit(struct radius_server_data *data); - -int radius_server_get_mib(struct radius_server_data *data, char *buf, - size_t buflen); - -#else /* RADIUS_SERVER */ - -static inline struct radius_server_data * -radius_server_init(struct radius_server_conf *conf) -{ - return NULL; -} - -static inline void radius_server_deinit(struct radius_server_data *data) -{ -} - -static inline int radius_server_get_mib(struct radius_server_data *data, - char *buf, size_t buflen) -{ - return 0; -} - -#endif /* RADIUS_SERVER */ - -#endif /* RADIUS_SERVER_H */ diff --git a/contrib/hostapd-0.4.9/rc4.c b/contrib/hostapd-0.4.9/rc4.c deleted file mode 100644 index 4cf14d91d1..0000000000 --- a/contrib/hostapd-0.4.9/rc4.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * RC4 stream cipher - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include "common.h" -#include "rc4.h" - -#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) - -/** - * rc4 - XOR RC4 stream to given data with skip-stream-start - * @key: RC4 key - * @keylen: RC4 key length - * @skip: number of bytes to skip from the beginning of the RC4 stream - * @data: data to be XOR'ed with RC4 stream - * @data_len: buf length - * - * Generate RC4 pseudo random stream for the given key, skip beginning of the - * stream, and XOR the end result with the data buffer to perform RC4 - * encryption/decryption. - */ -void rc4_skip(const u8 *key, size_t keylen, size_t skip, - u8 *data, size_t data_len) -{ - u32 i, j, k; - u8 S[256], *pos; - int kpos; - - /* Setup RC4 state */ - for (i = 0; i < 256; i++) - S[i] = i; - j = 0; - kpos = 0; - for (i = 0; i < 256; i++) { - j = (j + S[i] + key[kpos]) & 0xff; - kpos++; - if (kpos >= keylen) - kpos = 0; - S_SWAP(i, j); - } - - /* Skip the start of the stream */ - i = j = 0; - for (k = 0; k < skip; k++) { - i = (i + 1) & 0xff; - j = (j + S[i]) & 0xff; - S_SWAP(i, j); - } - - /* Apply RC4 to data */ - pos = data; - for (k = 0; k < data_len; k++) { - i = (i + 1) & 0xff; - j = (j + S[i]) & 0xff; - S_SWAP(i, j); - *pos++ ^= S[(S[i] + S[j]) & 0xff]; - } -} - - -/** - * rc4 - XOR RC4 stream to given data - * @buf: data to be XOR'ed with RC4 stream - * @len: buf length - * @key: RC4 key - * @key_len: RC4 key length - * - * Generate RC4 pseudo random stream for the given key and XOR this with the - * data buffer to perform RC4 encryption/decryption. - */ -void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len) -{ - rc4_skip(key, key_len, 0, buf, len); -} diff --git a/contrib/hostapd-0.4.9/rc4.h b/contrib/hostapd-0.4.9/rc4.h deleted file mode 100644 index 3873240496..0000000000 --- a/contrib/hostapd-0.4.9/rc4.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * RC4 stream cipher - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef RC4_H -#define RC4_H - -void rc4_skip(const u8 *key, size_t keylen, size_t skip, - u8 *data, size_t data_len); -void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len); - -#endif /* RC4_H */ diff --git a/contrib/hostapd-0.4.9/sha1.c b/contrib/hostapd-0.4.9/sha1.c deleted file mode 100644 index 7e32e31ca1..0000000000 --- a/contrib/hostapd-0.4.9/sha1.c +++ /dev/null @@ -1,994 +0,0 @@ -/* - * SHA1 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include - -#include "common.h" -#include "sha1.h" -#include "md5.h" -#include "crypto.h" - - -/** - * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104) - * @key: Key for HMAC operations - * @key_len: Length of the key in bytes - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash (20 bytes) - */ -void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) -{ - unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ - unsigned char tk[20]; - int i; - const u8 *_addr[6]; - size_t _len[6]; - - if (num_elem > 5) { - /* - * Fixed limit on the number of fragments to avoid having to - * allocate memory (which could fail). - */ - return; - } - - /* if key is longer than 64 bytes reset it to key = SHA1(key) */ - if (key_len > 64) { - sha1_vector(1, &key, &key_len, tk); - key = tk; - key_len = 20; - } - - /* the HMAC_SHA1 transform looks like: - * - * SHA1(K XOR opad, SHA1(K XOR ipad, text)) - * - * where K is an n byte key - * ipad is the byte 0x36 repeated 64 times - * opad is the byte 0x5c repeated 64 times - * and text is the data being protected */ - - /* start out by storing key in ipad */ - memset(k_pad, 0, sizeof(k_pad)); - memcpy(k_pad, key, key_len); - /* XOR key with ipad values */ - for (i = 0; i < 64; i++) - k_pad[i] ^= 0x36; - - /* perform inner SHA1 */ - _addr[0] = k_pad; - _len[0] = 64; - for (i = 0; i < num_elem; i++) { - _addr[i + 1] = addr[i]; - _len[i + 1] = len[i]; - } - sha1_vector(1 + num_elem, _addr, _len, mac); - - memset(k_pad, 0, sizeof(k_pad)); - memcpy(k_pad, key, key_len); - /* XOR key with opad values */ - for (i = 0; i < 64; i++) - k_pad[i] ^= 0x5c; - - /* perform outer SHA1 */ - _addr[0] = k_pad; - _len[0] = 64; - _addr[1] = mac; - _len[1] = SHA1_MAC_LEN; - sha1_vector(2, _addr, _len, mac); -} - - -/** - * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104) - * @key: Key for HMAC operations - * @key_len: Length of the key in bytes - * @data: Pointers to the data area - * @data_len: Length of the data area - * @mac: Buffer for the hash (20 bytes) - */ -void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac) -{ - hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); -} - - -/** - * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) - * @key: Key for PRF - * @key_len: Length of the key in bytes - * @label: A unique label for each purpose of the PRF - * @data: Extra data to bind into the key - * @data_len: Length of the data - * @buf: Buffer for the generated pseudo-random key - * @buf_len: Number of bytes of key to generate - * - * This function is used to derive new, cryptographically separate keys from a - * given key (e.g., PMK in IEEE 802.11i). - */ -void sha1_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) -{ - u8 zero = 0, counter = 0; - size_t pos, plen; - u8 hash[SHA1_MAC_LEN]; - size_t label_len = strlen(label); - const unsigned char *addr[4]; - size_t len[4]; - - addr[0] = (u8 *) label; - len[0] = label_len; - addr[1] = &zero; - len[1] = 1; - addr[2] = data; - len[2] = data_len; - addr[3] = &counter; - len[3] = 1; - - pos = 0; - while (pos < buf_len) { - plen = buf_len - pos; - if (plen >= SHA1_MAC_LEN) { - hmac_sha1_vector(key, key_len, 4, addr, len, - &buf[pos]); - pos += SHA1_MAC_LEN; - } else { - hmac_sha1_vector(key, key_len, 4, addr, len, - hash); - memcpy(&buf[pos], hash, plen); - break; - } - counter++; - } -} - - -/** - * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) - * @key: Key for PRF - * @key_len: Length of the key in bytes - * @label: A unique label for each purpose of the PRF - * @seed: Seed value to bind into the key - * @seed_len: Length of the seed - * @buf: Buffer for the generated pseudo-random key - * @buf_len: Number of bytes of key to generate - * - * This function is used to derive new, cryptographically separate keys from a - * given key for EAP-FAST. T-PRF is defined in - * draft-cam-winget-eap-fast-02.txt, Appendix B. - */ -void sha1_t_prf(const u8 *key, size_t key_len, const char *label, - const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len) -{ - unsigned char counter = 0; - size_t pos, plen; - u8 hash[SHA1_MAC_LEN]; - size_t label_len = strlen(label); - u8 output_len[2]; - const unsigned char *addr[5]; - size_t len[5]; - - addr[0] = hash; - len[0] = 0; - addr[1] = (unsigned char *) label; - len[1] = label_len + 1; - addr[2] = seed; - len[2] = seed_len; - addr[3] = output_len; - len[3] = 2; - addr[4] = &counter; - len[4] = 1; - - output_len[0] = (buf_len >> 8) & 0xff; - output_len[1] = buf_len & 0xff; - pos = 0; - while (pos < buf_len) { - counter++; - plen = buf_len - pos; - hmac_sha1_vector(key, key_len, 5, addr, len, hash); - if (plen >= SHA1_MAC_LEN) { - memcpy(&buf[pos], hash, SHA1_MAC_LEN); - pos += SHA1_MAC_LEN; - } else { - memcpy(&buf[pos], hash, plen); - break; - } - len[0] = SHA1_MAC_LEN; - } -} - - -/** - * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) - * @secret: Key for PRF - * @secret_len: Length of the key in bytes - * @label: A unique label for each purpose of the PRF - * @seed: Seed value to bind into the key - * @seed_len: Length of the seed - * @out: Buffer for the generated pseudo-random key - * @outlen: Number of bytes of key to generate -* - * This function is used to derive new, cryptographically separate keys from a - * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. - */ -int tls_prf(const u8 *secret, size_t secret_len, const char *label, - const u8 *seed, size_t seed_len, u8 *out, size_t outlen) -{ - size_t L_S1, L_S2; - const u8 *S1, *S2; - u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN]; - u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN]; - int i, MD5_pos, SHA1_pos; - const u8 *MD5_addr[3]; - size_t MD5_len[3]; - const unsigned char *SHA1_addr[3]; - size_t SHA1_len[3]; - - if (secret_len & 1) - return -1; - - MD5_addr[0] = A_MD5; - MD5_len[0] = MD5_MAC_LEN; - MD5_addr[1] = (unsigned char *) label; - MD5_len[1] = strlen(label); - MD5_addr[2] = seed; - MD5_len[2] = seed_len; - - SHA1_addr[0] = A_SHA1; - SHA1_len[0] = SHA1_MAC_LEN; - SHA1_addr[1] = (unsigned char *) label; - SHA1_len[1] = strlen(label); - SHA1_addr[2] = seed; - SHA1_len[2] = seed_len; - - /* RFC 2246, Chapter 5 - * A(0) = seed, A(i) = HMAC(secret, A(i-1)) - * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + .. - * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed) - */ - - L_S1 = L_S2 = (secret_len + 1) / 2; - S1 = secret; - S2 = secret + L_S1; - - hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); - hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); - - MD5_pos = MD5_MAC_LEN; - SHA1_pos = SHA1_MAC_LEN; - for (i = 0; i < outlen; i++) { - if (MD5_pos == MD5_MAC_LEN) { - hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); - MD5_pos = 0; - hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); - } - if (SHA1_pos == SHA1_MAC_LEN) { - hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, - P_SHA1); - SHA1_pos = 0; - hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1); - } - - out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos]; - - MD5_pos++; - SHA1_pos++; - } - - return 0; -} - - -static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, - size_t ssid_len, int iterations, int count, - u8 *digest) -{ - unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN]; - int i, j; - unsigned char count_buf[4]; - const u8 *addr[2]; - size_t len[2]; - size_t passphrase_len = strlen(passphrase); - - addr[0] = (u8 *) ssid; - len[0] = ssid_len; - addr[1] = count_buf; - len[1] = 4; - - /* F(P, S, c, i) = U1 xor U2 xor ... Uc - * U1 = PRF(P, S || i) - * U2 = PRF(P, U1) - * Uc = PRF(P, Uc-1) - */ - - count_buf[0] = (count >> 24) & 0xff; - count_buf[1] = (count >> 16) & 0xff; - count_buf[2] = (count >> 8) & 0xff; - count_buf[3] = count & 0xff; - hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, tmp); - memcpy(digest, tmp, SHA1_MAC_LEN); - - for (i = 1; i < iterations; i++) { - hmac_sha1((u8 *) passphrase, passphrase_len, tmp, SHA1_MAC_LEN, - tmp2); - memcpy(tmp, tmp2, SHA1_MAC_LEN); - for (j = 0; j < SHA1_MAC_LEN; j++) - digest[j] ^= tmp2[j]; - } -} - - -/** - * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i - * @passphrase: ASCII passphrase - * @ssid: SSID - * @ssid_len: SSID length in bytes - * @interations: Number of iterations to run - * @buf: Buffer for the generated key - * @buflen: Length of the buffer in bytes - * - * This function is used to derive PSK for WPA-PSK. For this protocol, - * iterations is set to 4096 and buflen to 32. This function is described in - * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. - */ -void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, - int iterations, u8 *buf, size_t buflen) -{ - int count = 0; - unsigned char *pos = buf; - size_t left = buflen, plen; - unsigned char digest[SHA1_MAC_LEN]; - - while (left > 0) { - count++; - pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, count, - digest); - plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left; - memcpy(pos, digest, plen); - pos += plen; - left -= plen; - } -} - - -#ifndef EAP_TLS_FUNCS - -typedef struct { - u32 state[5]; - u32 count[2]; - unsigned char buffer[64]; -} SHA1_CTX; - -static void SHA1Init(SHA1_CTX *context); -static void SHA1Update(SHA1_CTX *context, const void *data, u32 len); -static void SHA1Final(unsigned char digest[20], SHA1_CTX* context); -static void SHA1Transform(u32 state[5], const unsigned char buffer[64]); - - -/** - * sha1_vector - SHA-1 hash for data vector - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash - */ -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac) -{ - SHA1_CTX ctx; - int i; - - SHA1Init(&ctx); - for (i = 0; i < num_elem; i++) - SHA1Update(&ctx, addr[i], len[i]); - SHA1Final(mac, &ctx); -} - - -/** - * sha1_transform - Perform one SHA-1 transform step - * @state: SHA-1 state - * @data: Input data for the SHA-1 transform - * - * This function is used to implement random number generation specified in - * NIST FIPS Publication 186-2 for EAP-SIM. This PRF uses a function that is - * similar to SHA-1, but has different message padding and as such, access to - * just part of the SHA-1 is needed. - */ -void sha1_transform(u8 *state, const u8 data[64]) -{ - SHA1Transform((u32 *) state, data); -} - - -/* ===== start - public domain SHA1 implementation ===== */ - -/* -SHA-1 in C -By Steve Reid -100% Public Domain - ------------------ -Modified 7/98 -By James H. Brown -Still 100% Public Domain - -Corrected a problem which generated improper hash values on 16 bit machines -Routine SHA1Update changed from - void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int -len) -to - void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned -long len) - -The 'len' parameter was declared an int which works fine on 32 bit machines. -However, on 16 bit machines an int is too small for the shifts being done -against -it. This caused the hash function to generate incorrect values if len was -greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). - -Since the file IO in main() reads 16K at a time, any file 8K or larger would -be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million -"a"s). - -I also changed the declaration of variables i & j in SHA1Update to -unsigned long from unsigned int for the same reason. - -These changes should make no difference to any 32 bit implementations since -an -int and a long are the same size in those environments. - --- -I also corrected a few compiler warnings generated by Borland C. -1. Added #include for exit() prototype -2. Removed unused variable 'j' in SHA1Final -3. Changed exit(0) to return(0) at end of main. - -ALL changes I made can be located by searching for comments containing 'JHB' ------------------ -Modified 8/98 -By Steve Reid -Still 100% public domain - -1- Removed #include and used return() instead of exit() -2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) -3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net - ------------------ -Modified 4/01 -By Saul Kravitz -Still 100% PD -Modified to run on Compaq Alpha hardware. - ------------------ -Modified 4/01 -By Jouni Malinen -Minor changes to match the coding style used in Dynamics. - -Modified September 24, 2004 -By Jouni Malinen -Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined. - -*/ - -/* -Test Vectors (from FIPS PUB 180-1) -"abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ - -#define SHA1HANDSOFF - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ -#ifndef WORDS_BIGENDIAN -#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ - (rol(block->l[i], 8) & 0x00FF00FF)) -#else -#define blk0(i) block->l[i] -#endif -#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ - block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) \ - z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R1(v,w,x,y,z,i) \ - z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R2(v,w,x,y,z,i) \ - z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); -#define R3(v,w,x,y,z,i) \ - z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ - w = rol(w, 30); -#define R4(v,w,x,y,z,i) \ - z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ - w=rol(w, 30); - - -#ifdef VERBOSE /* SAK */ -void SHAPrintContext(SHA1_CTX *context, char *msg) -{ - printf("%s (%d,%d) %x %x %x %x %x\n", - msg, - context->count[0], context->count[1], - context->state[0], - context->state[1], - context->state[2], - context->state[3], - context->state[4]); -} -#endif - -/* Hash a single 512-bit block. This is the core of the algorithm. */ - -static void SHA1Transform(u32 state[5], const unsigned char buffer[64]) -{ - u32 a, b, c, d, e; - typedef union { - unsigned char c[64]; - u32 l[16]; - } CHAR64LONG16; - CHAR64LONG16* block; -#ifdef SHA1HANDSOFF - u32 workspace[16]; - block = (CHAR64LONG16 *) workspace; - memcpy(block, buffer, 64); -#else - block = (CHAR64LONG16 *) buffer; -#endif - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - /* Wipe variables */ - a = b = c = d = e = 0; -#ifdef SHA1HANDSOFF - memset(block, 0, 64); -#endif -} - - -/* SHA1Init - Initialize new context */ - -static void SHA1Init(SHA1_CTX* context) -{ - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - - -/* Run your data through this. */ - -static void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) -{ - u32 i, j; - const unsigned char *data = _data; - -#ifdef VERBOSE - SHAPrintContext(context, "before"); -#endif - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) - context->count[1]++; - context->count[1] += (len >> 29); - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - SHA1Transform(context->state, &data[i]); - } - j = 0; - } - else i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -#ifdef VERBOSE - SHAPrintContext(context, "after "); -#endif -} - - -/* Add padding and return the message digest. */ - -static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) -{ - u32 i; - unsigned char finalcount[8]; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char) - ((context->count[(i >= 4 ? 0 : 1)] >> - ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ - } - SHA1Update(context, (unsigned char *) "\200", 1); - while ((context->count[0] & 504) != 448) { - SHA1Update(context, (unsigned char *) "\0", 1); - } - SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() - */ - for (i = 0; i < 20; i++) { - digest[i] = (unsigned char) - ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & - 255); - } - /* Wipe variables */ - i = 0; - memset(context->buffer, 0, 64); - memset(context->state, 0, 20); - memset(context->count, 0, 8); - memset(finalcount, 0, 8); -} - -/* ===== end - public domain SHA1 implementation ===== */ - -#endif /* EAP_TLS_FUNCS */ - - -#ifdef TEST_MAIN - -#include "md5.c" - -static int test_eap_fast(void) -{ - /* draft-cam-winget-eap-fast-01.txt */ - const u8 pac_key[] = { - 0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09, - 0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B, - 0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA, - 0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14 - }; - const u8 seed[] = { - 0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A, - 0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3, - 0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93, - 0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A, - 0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A, - 0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F, - 0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A, - 0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00 - }; - const u8 master_secret[] = { - 0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02, - 0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64, - 0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77, - 0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29, - 0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27, - 0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2 - }; - const u8 key_block[] = { - 0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74, - 0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35, - 0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B, - 0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57, - 0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70, - 0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB, - 0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF, - 0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44, - 0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29, - 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, - 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, - 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84, - 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, - 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 - }; - const u8 sks[] = { - 0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05, - 0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96, - 0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84, - 0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98, - 0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71 - }; - const u8 isk[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - const u8 imck[] = { - 0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9, - 0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80, - 0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96, - 0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1, - 0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5, - 0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9, - 0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8, - 0x15, 0xEC, 0x57, 0x7B - }; - const u8 msk[] = { - 0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED, - 0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33, - 0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51, - 0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9, - 0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A, - 0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49, - 0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59, - 0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3 - }; - u8 tlv[] = { - 0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00, - 0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8, - 0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14, - 0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62, - 0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58, - 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF, - 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC, - 0x05, 0xC5, 0x5B, 0xB7 - }; - const u8 compound_mac[] = { - 0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF, - 0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC, - 0x05, 0xC5, 0x5B, 0xB7 - }; - u8 buf[512]; - const u8 *simck, *cmk; - int errors = 0; - - printf("EAP-FAST test cases\n"); - - printf("- T-PRF (SHA1) test case / master_secret\n"); - sha1_t_prf(pac_key, sizeof(pac_key), "PAC to master secret label hash", - seed, sizeof(seed), buf, sizeof(master_secret)); - if (memcmp(master_secret, buf, sizeof(master_secret)) != 0) { - printf("T-PRF test - FAILED!\n"); - errors++; - } - - printf("- PRF (TLS, SHA1/MD5) test case / key_block\n"); - tls_prf(master_secret, sizeof(master_secret), "key expansion", - seed, sizeof(seed), buf, sizeof(key_block)); - if (memcmp(key_block, buf, sizeof(key_block)) != 0) { - printf("PRF test - FAILED!\n"); - errors++; - } - - printf("- T-PRF (SHA1) test case / IMCK\n"); - sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys", - isk, sizeof(isk), buf, sizeof(imck)); - if (memcmp(imck, buf, sizeof(imck)) != 0) { - printf("T-PRF test - FAILED!\n"); - errors++; - } - - simck = imck; - cmk = imck + 40; - - printf("- T-PRF (SHA1) test case / MSK\n"); - sha1_t_prf(simck, 40, "Session Key Generating Function", - "", 0, buf, sizeof(msk)); - if (memcmp(msk, buf, sizeof(msk)) != 0) { - printf("T-PRF test - FAILED!\n"); - errors++; - } - - printf("- Compound MAC test case\n"); - memset(tlv + sizeof(tlv) - 20, 0, 20); - hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20); - if (memcmp(tlv + sizeof(tlv) - 20, compound_mac, sizeof(compound_mac)) - != 0) { - printf("Compound MAC test - FAILED!\n"); - errors++; - } - - return errors; -} - - -static u8 key0[] = -{ - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b -}; -static u8 data0[] = "Hi There"; -static u8 prf0[] = -{ - 0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84, - 0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54, - 0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06, - 0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee, - 0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88, - 0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb, - 0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e, - 0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a -}; - -static u8 key1[] = "Jefe"; -static u8 data1[] = "what do ya want for nothing?"; -static u8 prf1[] = -{ - 0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad, - 0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4, - 0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58, - 0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09, - 0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa, - 0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02, - 0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7, - 0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc -}; - - -static u8 key2[] = -{ - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xaa, 0xaa, 0xaa, 0xaa -}; -static u8 data2[] = -{ - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, - 0xdd, 0xdd -}; -static u8 prf2[] = -{ - 0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f, - 0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1, - 0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1, - 0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce, - 0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc, - 0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae, - 0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6, - 0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07 -}; - - -struct passphrase_test { - char *passphrase; - char *ssid; - char psk[32]; -}; - -static struct passphrase_test passphrase_tests[] = -{ - { - "password", - "IEEE", - { - 0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef, - 0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90, - 0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2, - 0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e - } - }, - { - "ThisIsAPassword", - "ThisIsASSID", - { - 0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6, - 0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3, - 0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08, - 0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf - } - }, - { - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ", - { - 0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83, - 0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c, - 0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48, - 0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62 - } - }, -}; - -#define NUM_PASSPHRASE_TESTS \ -(sizeof(passphrase_tests) / sizeof(passphrase_tests[0])) - - -int main(int argc, char *argv[]) -{ - u8 res[512]; - int ret = 0, i; - - printf("PRF-SHA1 test cases:\n"); - - sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1, - res, sizeof(prf0)); - if (memcmp(res, prf0, sizeof(prf0)) == 0) - printf("Test case 0 - OK\n"); - else { - printf("Test case 0 - FAILED!\n"); - ret++; - } - - sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1, - res, sizeof(prf1)); - if (memcmp(res, prf1, sizeof(prf1)) == 0) - printf("Test case 1 - OK\n"); - else { - printf("Test case 1 - FAILED!\n"); - ret++; - } - - sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2), - res, sizeof(prf2)); - if (memcmp(res, prf2, sizeof(prf2)) == 0) - printf("Test case 2 - OK\n"); - else { - printf("Test case 2 - FAILED!\n"); - ret++; - } - - ret += test_eap_fast(); - - printf("PBKDF2-SHA1 Passphrase test cases:\n"); - for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) { - u8 psk[32]; - struct passphrase_test *test = &passphrase_tests[i]; - pbkdf2_sha1(test->passphrase, - test->ssid, strlen(test->ssid), - 4096, psk, 32); - if (memcmp(psk, test->psk, 32) == 0) - printf("Test case %d - OK\n", i); - else { - printf("Test case %d - FAILED!\n", i); - ret++; - } - } - - return ret; -} -#endif /* TEST_MAIN */ diff --git a/contrib/hostapd-0.4.9/sha1.h b/contrib/hostapd-0.4.9/sha1.h deleted file mode 100644 index 3c6d915146..0000000000 --- a/contrib/hostapd-0.4.9/sha1.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SHA1 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef SHA1_H -#define SHA1_H - -#define SHA1_MAC_LEN 20 - -void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac); -void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac); -void sha1_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len); -void sha1_t_prf(const u8 *key, size_t key_len, const char *label, - const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); -int tls_prf(const u8 *secret, size_t secret_len, const char *label, - const u8 *seed, size_t seed_len, u8 *out, size_t outlen); -void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, - int iterations, u8 *buf, size_t buflen); - -#endif /* SHA1_H */ diff --git a/contrib/hostapd-0.4.9/sta_info.c b/contrib/hostapd-0.4.9/sta_info.c deleted file mode 100644 index 9eb0bca2e3..0000000000 --- a/contrib/hostapd-0.4.9/sta_info.c +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / Station table - * Copyright (c) 2002-2004, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include "hostapd.h" -#include "sta_info.h" -#include "eloop.h" -#include "accounting.h" -#include "ieee802_1x.h" -#include "ieee802_11.h" -#include "radius.h" -#include "eapol_sm.h" -#include "wpa.h" -#include "radius_client.h" -#include "driver.h" -#include "hostap_common.h" - - -int ap_for_each_sta(struct hostapd_data *hapd, - int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, - void *ctx), - void *ctx) -{ - struct sta_info *sta; - - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (cb(hapd, sta, ctx)) - return 1; - } - - return 0; -} - - -struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) -{ - struct sta_info *s; - - s = hapd->sta_hash[STA_HASH(sta)]; - while (s != NULL && memcmp(s->addr, sta, 6) != 0) - s = s->hnext; - return s; -} - - -static void ap_sta_list_del(hostapd *hapd, struct sta_info *sta) -{ - struct sta_info *tmp; - - if (hapd->sta_list == sta) { - hapd->sta_list = sta->next; - return; - } - - tmp = hapd->sta_list; - while (tmp != NULL && tmp->next != sta) - tmp = tmp->next; - if (tmp == NULL) { - printf("Could not remove STA " MACSTR " from list.\n", - MAC2STR(sta->addr)); - } else - tmp->next = sta->next; -} - - -void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta) -{ - sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)]; - hapd->sta_hash[STA_HASH(sta->addr)] = sta; -} - - -static void ap_sta_hash_del(hostapd *hapd, struct sta_info *sta) -{ - struct sta_info *s; - - s = hapd->sta_hash[STA_HASH(sta->addr)]; - if (s == NULL) return; - if (memcmp(s->addr, sta->addr, 6) == 0) { - hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext; - return; - } - - while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, 6) != 0) - s = s->hnext; - if (s->hnext != NULL) - s->hnext = s->hnext->hnext; - else - printf("AP: could not remove STA " MACSTR " from hash table\n", - MAC2STR(sta->addr)); -} - - -void ap_free_sta(hostapd *hapd, struct sta_info *sta) -{ - accounting_sta_stop(hapd, sta); - if (!(sta->flags & WLAN_STA_PREAUTH)) - hostapd_sta_remove(hapd, sta->addr); - - ap_sta_hash_del(hapd, sta); - ap_sta_list_del(hapd, sta); - - if (sta->aid > 0) - hapd->sta_aid[sta->aid - 1] = NULL; - - hapd->num_sta--; - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - - ieee802_1x_free_station(sta); - wpa_free_station(sta); - radius_client_flush_auth(hapd->radius, sta->addr); - - if (sta->last_assoc_req) - free(sta->last_assoc_req); - - free(sta->challenge); - free(sta->wpa_ie); - - free(sta); -} - - -void hostapd_free_stas(hostapd *hapd) -{ - struct sta_info *sta, *prev; - - sta = hapd->sta_list; - - while (sta) { - prev = sta; - sta = sta->next; - printf("Removing station " MACSTR "\n", MAC2STR(prev->addr)); - ap_free_sta(hapd, prev); - } -} - - -void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) -{ - hostapd *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - unsigned long next_time = 0; - - if (sta->timeout_next == STA_REMOVE) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "deauthenticated due to " - "local deauth request"); - ap_free_sta(hapd, sta); - return; - } - - if ((sta->flags & WLAN_STA_ASSOC) && - (sta->timeout_next == STA_NULLFUNC || - sta->timeout_next == STA_DISASSOC)) { - int inactive_sec; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Checking STA " MACSTR " inactivity:\n", - MAC2STR(sta->addr)); - inactive_sec = hostapd_get_inact_sec(hapd, sta->addr); - if (inactive_sec == -1) { - printf(" Could not get station info from kernel " - "driver for " MACSTR ".\n", - MAC2STR(sta->addr)); - } else if (inactive_sec < AP_MAX_INACTIVITY && - sta->flags & WLAN_STA_ASSOC) { - /* station activity detected; reset timeout state */ - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " Station has been active\n"); - sta->timeout_next = STA_NULLFUNC; - next_time = AP_MAX_INACTIVITY - inactive_sec; - } - } - - if ((sta->flags & WLAN_STA_ASSOC) && - sta->timeout_next == STA_DISASSOC && - !(sta->flags & WLAN_STA_PENDING_POLL)) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " Station has ACKed data poll\n"); - /* data nullfunc frame poll did not produce TX errors; assume - * station ACKed it */ - sta->timeout_next = STA_NULLFUNC; - next_time = AP_MAX_INACTIVITY; - } - - if (next_time) { - eloop_register_timeout(next_time, 0, ap_handle_timer, hapd, - sta); - return; - } - - if (sta->timeout_next == STA_NULLFUNC && - (sta->flags & WLAN_STA_ASSOC)) { - /* send data frame to poll STA and check whether this frame - * is ACKed */ - struct ieee80211_hdr hdr; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - " Polling STA with data frame\n"); - sta->flags |= WLAN_STA_PENDING_POLL; - - /* FIX: WLAN_FC_STYPE_NULLFUNC would be more appropriate, but - * it is apparently not retried so TX Exc events are not - * received for it */ - memset(&hdr, 0, sizeof(hdr)); - hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); - hdr.frame_control |= host_to_le16(BIT(1)); - hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); - memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN); - memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, ETH_ALEN); - memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN); - - if (hostapd_send_mgmt_frame(hapd, &hdr, sizeof(hdr), 0) < 0) - perror("ap_handle_timer: send"); - } else if (sta->timeout_next != STA_REMOVE) { - int deauth = sta->timeout_next == STA_DEAUTH; - - printf(" Sending %s info to STA " MACSTR "\n", - deauth ? "deauthentication" : "disassociation", - MAC2STR(sta->addr)); - - if (deauth) { - hostapd_sta_deauth(hapd, sta->addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); - } else { - hostapd_sta_disassoc( - hapd, sta->addr, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); - } - } - - switch (sta->timeout_next) { - case STA_NULLFUNC: - sta->timeout_next = STA_DISASSOC; - eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer, - hapd, sta); - break; - case STA_DISASSOC: - sta->flags &= ~WLAN_STA_ASSOC; - ieee802_1x_set_port_enabled(hapd, sta, 0); - if (!sta->acct_terminate_cause) - sta->acct_terminate_cause = - RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; - accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "disassociated due to " - "inactivity"); - sta->timeout_next = STA_DEAUTH; - eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, - hapd, sta); - break; - case STA_DEAUTH: - case STA_REMOVE: - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "deauthenticated due to " - "inactivity"); - if (!sta->acct_terminate_cause) - sta->acct_terminate_cause = - RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; - ap_free_sta(hapd, sta); - break; - } -} - - -void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) -{ - hostapd *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - - if (!(sta->flags & WLAN_STA_AUTH)) - return; - - hostapd_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "deauthenticated due to " - "session timeout"); - sta->acct_terminate_cause = - RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; - ap_free_sta(hapd, sta); -} - - -void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta, - u32 session_timeout) -{ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d " - "seconds", session_timeout); - eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); - eloop_register_timeout(session_timeout, 0, ap_handle_session_timer, - hapd, sta); -} - - -void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta) -{ - eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); -} - - -struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) -{ - struct sta_info *sta; - - sta = ap_get_sta(hapd, addr); - if (sta) - return sta; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " New STA\n"); - if (hapd->num_sta >= MAX_STA_COUNT) { - /* FIX: might try to remove some old STAs first? */ - printf(" no more room for new STAs (%d/%d)\n", - hapd->num_sta, MAX_STA_COUNT); - return NULL; - } - - sta = (struct sta_info *) malloc(sizeof(struct sta_info)); - if (sta == NULL) { - printf(" malloc failed\n"); - return NULL; - } - memset(sta, 0, sizeof(struct sta_info)); - sta->acct_interim_interval = hapd->conf->radius->acct_interim_interval; - - /* initialize STA info data */ - eloop_register_timeout(AP_MAX_INACTIVITY, 0, ap_handle_timer, - hapd, sta); - memcpy(sta->addr, addr, ETH_ALEN); - sta->next = hapd->sta_list; - hapd->sta_list = sta; - hapd->num_sta++; - ap_sta_hash_add(hapd, sta); - - return sta; -} diff --git a/contrib/hostapd-0.4.9/sta_info.h b/contrib/hostapd-0.4.9/sta_info.h deleted file mode 100644 index e2d7b4ef4b..0000000000 --- a/contrib/hostapd-0.4.9/sta_info.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef STA_INFO_H -#define STA_INFO_H - -int ap_for_each_sta(struct hostapd_data *hapd, - int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, - void *ctx), - void *ctx); -struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); -void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta); -void ap_free_sta(hostapd *hapd, struct sta_info *sta); -void ap_free_sta(hostapd *hapd, struct sta_info *sta); -void hostapd_free_stas(hostapd *hapd); -void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); -void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta, - u32 session_timeout); -void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta); -struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); - -#endif /* STA_INFO_H */ diff --git a/contrib/hostapd-0.4.9/tls.h b/contrib/hostapd-0.4.9/tls.h deleted file mode 100644 index a6a8110c7d..0000000000 --- a/contrib/hostapd-0.4.9/tls.h +++ /dev/null @@ -1,463 +0,0 @@ -/* - * WPA Supplicant / SSL/TLS interface definition - * Copyright (c) 2004-2006, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef TLS_H -#define TLS_H - -struct tls_connection; - -struct tls_keys { - const u8 *master_key; - size_t master_key_len; - const u8 *client_random; - size_t client_random_len; - const u8 *server_random; - size_t server_random_len; - - /* - * If TLS library does not provide access to master_key, but only to - * EAP key block, this pointer can be set to point to the result of - * PRF(master_secret, "client EAP encryption", - * client_random + server_random). - */ - const u8 *eap_tls_prf; - size_t eap_tls_prf_len; -}; - -struct tls_config { - const char *opensc_engine_path; - const char *pkcs11_engine_path; - const char *pkcs11_module_path; -}; - -/** - * struct tls_connection_params - Parameters for TLS connection - * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER - * format - * @ca_cert_blob: ca_cert as inlined data or %NULL if not used - * @ca_cert_blob_len: ca_cert_blob length - * @ca_path: Path to CA certificates (OpenSSL specific) - * @subject_match: String to match in the subject of the peer certificate or - * %NULL to allow all subjects - * @altsubject_match: String to match in the alternative subject of the peer - * certificate or %NULL to allow all alternative subjects - * @client_cert: File or reference name for client X.509 certificate in PEM or - * DER format - * @client_cert_blob: client_cert as inlined data or %NULL if not used - * @client_cert_blob_len: client_cert_blob length - * @private_key: File or reference name for client private key in PEM or DER - * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY) - * @private_key_blob: private_key as inlined data or %NULL if not used - * @private_key_blob_len: private_key_blob length - * @private_key_passwd: Passphrase for decrypted private key, %NULL if no - * passphrase is used. - * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used - * @dh_blob: dh_file as inlined data or %NULL if not used - * @dh_blob_len: dh_blob length - * @engine: 1 = use engine (e.g., a smartcard) for private key operations - * (this is OpenSSL specific for now) - * @engine_id: engine id string (this is OpenSSL specific for now) - * @ppin: pointer to the pin variable in the configuration - * (this is OpenSSL specific for now) - * @key_id: the private key's key id (this is OpenSSL specific for now) - * - * TLS connection parameters to be configured with tls_connection_set_params(). - * - * Certificates and private key can be configured either as a reference name - * (file path or reference to certificate store) or by providing the same data - * as a pointer to the data in memory. Only one option will be used for each - * field. - */ -struct tls_connection_params { - const char *ca_cert; - const u8 *ca_cert_blob; - size_t ca_cert_blob_len; - const char *ca_path; - const char *subject_match; - const char *altsubject_match; - const char *client_cert; - const u8 *client_cert_blob; - size_t client_cert_blob_len; - const char *private_key; - const u8 *private_key_blob; - size_t private_key_blob_len; - const char *private_key_passwd; - const char *dh_file; - const u8 *dh_blob; - size_t dh_blob_len; - - /* OpenSSL specific variables */ - int engine; - const char *engine_id; - const char *pin; - const char *key_id; -}; - - -/** - * tls_init - Initialize TLS library - * @conf: Configuration data for TLS library - * Returns: Context data to be used as tls_ctx in calls to other functions, - * or %NULL on failure. - * - * Called once during program startup and once for each RSN pre-authentication - * session. In other words, there can be two concurrent TLS contexts. If global - * library initialization is needed (i.e., one that is shared between both - * authentication types), the TLS library wrapper should maintain a reference - * counter and do global initialization only when moving from 0 to 1 reference. - */ -void * tls_init(const struct tls_config *conf); - -/** - * tls_deinit - Deinitialize TLS library - * @tls_ctx: TLS context data from tls_init() - * - * Called once during program shutdown and once for each RSN pre-authentication - * session. If global library deinitialization is needed (i.e., one that is - * shared between both authentication types), the TLS library wrapper should - * maintain a reference counter and do global deinitialization only when moving - * from 1 to 0 references. - */ -void tls_deinit(void *tls_ctx); - -/** - * tls_get_errors - Process pending errors - * @tls_ctx: TLS context data from tls_init() - * - * Returns: Number of found error, 0 if no errors detected. - * - * Process all pending TLS errors. - */ -int tls_get_errors(void *tls_ctx); - -/** - * tls_connection_init - Initialize a new TLS connection - * @tls_ctx: TLS context data from tls_init() - * - * Returns: Connection context data, conn for other function calls - */ -struct tls_connection * tls_connection_init(void *tls_ctx); - -/** - * tls_connection_deinit - Free TLS connection data - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * - * Release all resources allocated for TLS connection. - */ -void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); - -/** - * tls_connection_established - Has the TLS connection been completed? - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * - * Returns: 1 if TLS connection has been completed, 0 if not. - */ -int tls_connection_established(void *tls_ctx, struct tls_connection *conn); - -/** - * tls_connection_shutdown - Shutdown TLS connection data. - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * - * Returns: 0 on success, -1 on failure - * - * Shutdown current TLS connection without releasing all resources. New - * connection can be started by using the same conn without having to call - * tls_connection_init() or setting certificates etc. again. The new - * connection should try to use session resumption. - */ -int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); - -enum { - TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, - TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 -}; -/** - * tls_connection_set_params - Set TLS connection parameters - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @params: Connection parameters - * - * Returns: 0 on success, -1 on failure, - * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing - * PKCS#11 engine failure, or - * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the - * PKCS#11 engine private key. - */ -int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, - const struct tls_connection_params *params); - -/** - * tls_global_ca_cert - Set trusted CA certificate for all TLS connections - * @tls_ctx: TLS context data from tls_init() - * @ca_cert: File name for CA certificate in PEM or DER format - * %NULL to allow all subjects - * - * Returns: 0 on success, -1 on failure - */ -int tls_global_ca_cert(void *tls_ctx, const char *ca_cert); - -/** - * tls_global_set_verify - Set global certificate verification options - * @tls_ctx: TLS context data from tls_init() - * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, - * 2 = verify CRL for all certificates - * - * Returns: 0 on success, -1 on failure - */ -int tls_global_set_verify(void *tls_ctx, int check_crl); - -/** - * tls_connection_set_verify - Set certificate verification options - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @verify_peer: 1 = verify peer certificate - * - * Returns: 0 on success, -1 on failure - */ -int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer); - -/** - * tls_global_client_cert - Set client certificate for all TLS connections - * @tls_ctx: TLS context data from tls_init() - * @client_cert: File name for client certificate in PEM or DER format - * - * Returns: 0 on success, -1 on failure - */ -int tls_global_client_cert(void *tls_ctx, const char *client_cert); - -/** - * tls_global_private_key - Set private key for all TLS connections - * @tls_ctx: TLS context data from tls_init() - * @private_key: File name for client private key in PEM or DER format - * @private_key_passwd: Passphrase for decrypted private key, %NULL if no - * passphrase is used. - * - * Returns: 0 on success, -1 on failure - */ -int tls_global_private_key(void *tls_ctx, const char *private_key, - const char *private_key_passwd); - -/** - * tls_connection_get_keys - Get master key and random data from TLS connection - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @keys: Structure of key/random data (filled on success) - * - * Returns: 0 on success, -1 on failure - */ -int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, - struct tls_keys *keys); - -/** - * tls_connection_handshake - Process TLS handshake (client side) - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @in_data: Input data from TLS peer - * @in_len: Input data length - * @out_len: Length of the output buffer. - * - * Returns: Pointer to output data, %NULL on failure - * - * Caller is responsible for freeing returned output data. - * - * This function is used during TLS handshake. The first call is done with - * in_data == %NULL and the library is expected to return ClientHello packet. - * This packet is then send to the server and a response from server is given - * to TLS library by calling this function again with in_data pointing to the - * TLS message from the server. - * - * If the TLS handshake fails, this function may return %NULL. However, if the - * TLS library has a TLS alert to send out, that should be returned as the - * output data. In this case, tls_connection_get_failed() must return failure - * (> 0). - * - * tls_connection_established() should return 1 once the TLS handshake has been - * completed successfully. - */ -u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len); - -/** - * tls_connection_server_handshake - Process TLS handshake (server side) - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @in_data: Input data from TLS peer - * @in_len: Input data length - * @out_len: Length of the output buffer. - * - * Returns: pointer to output data, %NULL on failure - * - * Caller is responsible for freeing returned output data. - */ -u8 * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len); - -/** - * tls_connection_encrypt - Encrypt data into TLS tunnel - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @in_data: Pointer to plaintext data to be encrypted - * @in_len: Input buffer length - * @out_data: Pointer to output buffer (encrypted TLS data) - * @out_len: Maximum out_data length - * - * Returns: Number of bytes written to out_data, -1 on failure - * - * This function is used after TLS handshake has been completed successfully to - * send data in the encrypted tunnel. - */ -int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len); - -/** - * tls_connection_decrypt - Decrypt data from TLS tunnel - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @in_data: Pointer to input buffer (encrypted TLS data) - * @in_len: Input buffer length - * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) - * @out_len: Maximum out_data length - * - * Returns: Number of bytes written to out_data, -1 on failure - * - * This function is used after TLS handshake has been completed successfully to - * receive data from the encrypted tunnel. - */ -int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len); - -/** - * tls_connection_resumed - Was session resumption used - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * - * Returns: 1 if current session used session resumption, 0 if not - */ -int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); - -/** - * tls_connection_set_master_key - Configure master secret for TLS connection - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @key: TLS pre-master-secret - * @key_len: length of key in bytes - * - * Returns: 0 on success, -1 on failure - */ -int tls_connection_set_master_key(void *tls_ctx, struct tls_connection *conn, - const u8 *key, size_t key_len); - -/** - * tls_connection_set_anon_dh - Configure TLS connection to use anonymous DH - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * - * Returns: 0 on success, -1 on failure - * - * TODO: consider changing this to more generic routine for configuring allowed - * ciphers - */ -int tls_connection_set_anon_dh(void *tls_ctx, struct tls_connection *conn); - -/** - * tls_get_cipher - Get current cipher name - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @buf: Buffer for the cipher name - * @buflen: buf size - * - * Returns: 0 on success, -1 on failure - * - * Get the name of the currently used cipher. - */ -int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, - char *buf, size_t buflen); - -/** - * tls_connection_enable_workaround - Enable TLS workaround options - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * - * Returns: 0 on success, -1 on failure - * - * This function is used to enable connection-specific workaround options for - * buffer SSL/TLS implementations. - */ -int tls_connection_enable_workaround(void *tls_ctx, - struct tls_connection *conn); - -/** - * tls_connection_client_hello_ext - Set TLS extension for ClientHello - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @ext_type: Extension type - * @data: Extension payload (NULL to remove extension) - * @data_len: Extension payload length - * - * Returns: 0 on success, -1 on failure - */ -int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, - int ext_type, const u8 *data, - size_t data_len); - -/** - * tls_connection_get_failed - Get connection failure status - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * - * Returns >0 if connection has failed, 0 if not. - */ -int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn); - -/** - * tls_connection_get_read_alerts - Get connection read alert status - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * - * Returns: Number of times a fatal read (remote end reported error) has - * happened during this connection. - */ -int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); - -/** - * tls_connection_get_write_alerts - Get connection write alert status - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * - * Returns: Number of times a fatal write (locally detected error) has happened - * during this connection. - */ -int tls_connection_get_write_alerts(void *tls_ctx, - struct tls_connection *conn); - -/** - * tls_connection_get_keyblock_size - Get TLS key_block size - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * Returns: Size of the key_block for the negotiated cipher suite or -1 on - * failure - */ -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn); - -#endif /* TLS_H */ diff --git a/contrib/hostapd-0.4.9/tls_none.c b/contrib/hostapd-0.4.9/tls_none.c deleted file mode 100644 index 07fd879ec8..0000000000 --- a/contrib/hostapd-0.4.9/tls_none.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * WPA Supplicant / SSL/TLS interface functions for no TLS case - * Copyright (c) 2004, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include - -#include "common.h" -#include "tls.h" - -void * tls_init(const struct tls_config *conf) -{ - return (void *) 1; -} - -void tls_deinit(void *ssl_ctx) -{ -} diff --git a/contrib/hostapd-0.4.9/tls_openssl.c b/contrib/hostapd-0.4.9/tls_openssl.c deleted file mode 100644 index 37878c7c20..0000000000 --- a/contrib/hostapd-0.4.9/tls_openssl.c +++ /dev/null @@ -1,2144 +0,0 @@ -/* - * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2006, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include - -#ifndef CONFIG_SMARTCARD -#ifndef OPENSSL_NO_ENGINE -#define OPENSSL_NO_ENGINE -#endif -#endif - -#include -#include -#include -#include -#ifndef OPENSSL_NO_ENGINE -#include -#endif /* OPENSSL_NO_ENGINE */ - -#include "common.h" -#include "tls.h" - -#if OPENSSL_VERSION_NUMBER >= 0x0090800fL -#define OPENSSL_d2i_TYPE const unsigned char ** -#else -#define OPENSSL_d2i_TYPE unsigned char ** -#endif - -static int tls_openssl_ref_count = 0; - -struct tls_connection { - SSL *ssl; - BIO *ssl_in, *ssl_out; -#ifndef OPENSSL_NO_ENGINE - ENGINE *engine; /* functional reference to the engine */ - EVP_PKEY *private_key; /* the private key if using engine */ -#endif /* OPENSSL_NO_ENGINE */ - char *subject_match, *altsubject_match; - int read_alerts, write_alerts, failed; - - u8 *pre_shared_secret; - size_t pre_shared_secret_len; -}; - - -#ifdef CONFIG_NO_STDOUT_DEBUG - -static void _tls_show_errors(void) -{ - unsigned long err; - - while ((err = ERR_get_error())) { - /* Just ignore the errors, since stdout is disabled */ - } -} -#define tls_show_errors(l, f, t) _tls_show_errors() - -#else /* CONFIG_NO_STDOUT_DEBUG */ - -static void tls_show_errors(int level, const char *func, const char *txt) -{ - unsigned long err; - - wpa_printf(level, "OpenSSL: %s - %s %s", - func, txt, ERR_error_string(ERR_get_error(), NULL)); - - while ((err = ERR_get_error())) { - wpa_printf(MSG_INFO, "OpenSSL: pending error: %s", - ERR_error_string(err, NULL)); - } -} - -#endif /* CONFIG_NO_STDOUT_DEBUG */ - - -#ifdef CONFIG_NATIVE_WINDOWS - -/* Windows CryptoAPI and access to certificate stores */ -#include - -#ifdef __MINGW32_VERSION -/* - * MinGW does not yet include all the needed definitions for CryptoAPI, so - * define here whatever extra is needed. - */ -#define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5) -#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16) -#define CERT_STORE_READONLY_FLAG 0x00008000 -#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 -#define CRYPT_ACQUIRE_COMPARE_KEY_FLAG 0x00000004 - -static BOOL WINAPI -(*CryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT pCert, DWORD dwFlags, - void *pvReserved, HCRYPTPROV *phCryptProv, - DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) -= NULL; /* to be loaded from crypt32.dll */ - -static PCCERT_CONTEXT WINAPI -(*CertEnumCertificatesInStore)(HCERTSTORE hCertStore, - PCCERT_CONTEXT pPrevCertContext) -= NULL; /* to be loaded from crypt32.dll */ - -static int mingw_load_crypto_func(void) -{ - HINSTANCE dll; - - /* MinGW does not yet have full CryptoAPI support, so load the needed - * function here. */ - - if (CryptAcquireCertificatePrivateKey) - return 0; - - dll = LoadLibrary("crypt32"); - if (dll == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " - "library"); - return -1; - } - - CryptAcquireCertificatePrivateKey = GetProcAddress( - dll, "CryptAcquireCertificatePrivateKey"); - if (CryptAcquireCertificatePrivateKey == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " - "CryptAcquireCertificatePrivateKey() address from " - "crypt32 library"); - return -1; - } - - CertEnumCertificatesInStore = (void *) GetProcAddress( - dll, "CertEnumCertificatesInStore"); - if (CertEnumCertificatesInStore == NULL) { - wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " - "CertEnumCertificatesInStore() address from " - "crypt32 library"); - return -1; - } - - return 0; -} - -#else /* __MINGW32_VERSION */ - -static int mingw_load_crypto_func(void) -{ - return 0; -} - -#endif /* __MINGW32_VERSION */ - - -struct cryptoapi_rsa_data { - const CERT_CONTEXT *cert; - HCRYPTPROV crypt_prov; - DWORD key_spec; - BOOL free_crypt_prov; -}; - - -static void cryptoapi_error(const char *msg) -{ - wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u", - msg, (unsigned int) GetLastError()); -} - - -static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from, - unsigned char *to, RSA *rsa, int padding) -{ - wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); - return 0; -} - - -static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from, - unsigned char *to, RSA *rsa, int padding) -{ - wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); - return 0; -} - - -static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from, - unsigned char *to, RSA *rsa, int padding) -{ - struct cryptoapi_rsa_data *priv = - (struct cryptoapi_rsa_data *) rsa->meth->app_data; - HCRYPTHASH hash; - DWORD hash_size, len, i; - unsigned char *buf = NULL; - int ret = 0; - - if (priv == NULL) { - RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, - ERR_R_PASSED_NULL_PARAMETER); - return 0; - } - - if (padding != RSA_PKCS1_PADDING) { - RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, - RSA_R_UNKNOWN_PADDING_TYPE); - return 0; - } - - if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) { - wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported", - __func__); - RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, - RSA_R_INVALID_MESSAGE_LENGTH); - return 0; - } - - if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) - { - cryptoapi_error("CryptCreateHash failed"); - return 0; - } - - len = sizeof(hash_size); - if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, - 0)) { - cryptoapi_error("CryptGetHashParam failed"); - goto err; - } - - if (hash_size != flen) { - wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)", - (unsigned) hash_size, flen); - RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, - RSA_R_INVALID_MESSAGE_LENGTH); - goto err; - } - if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) { - cryptoapi_error("CryptSetHashParam failed"); - goto err; - } - - len = RSA_size(rsa); - buf = malloc(len); - if (buf == NULL) { - RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); - goto err; - } - - if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) { - cryptoapi_error("CryptSignHash failed"); - goto err; - } - - for (i = 0; i < len; i++) - to[i] = buf[len - i - 1]; - ret = len; - -err: - free(buf); - CryptDestroyHash(hash); - - return ret; -} - - -static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from, - unsigned char *to, RSA *rsa, int padding) -{ - wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); - return 0; -} - - -static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv) -{ - if (priv == NULL) - return; - if (priv->crypt_prov && priv->free_crypt_prov) - CryptReleaseContext(priv->crypt_prov, 0); - if (priv->cert) - CertFreeCertificateContext(priv->cert); - free(priv); -} - - -static int cryptoapi_finish(RSA *rsa) -{ - cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data); - free((void *) rsa->meth); - rsa->meth = NULL; - return 1; -} - - -static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store) -{ - HCERTSTORE cs; - const CERT_CONTEXT *ret = NULL; - - cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, - store | CERT_STORE_OPEN_EXISTING_FLAG | - CERT_STORE_READONLY_FLAG, L"MY"); - if (cs == NULL) { - cryptoapi_error("Failed to open 'My system store'"); - return NULL; - } - - if (strncmp(name, "cert://", 7) == 0) { - unsigned short wbuf[255]; - MultiByteToWideChar(CP_ACP, 0, name + 7, -1, - wbuf, sizeof(wbuf)); - ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING | - PKCS_7_ASN_ENCODING, - 0, CERT_FIND_SUBJECT_STR, - wbuf, NULL); - } else if (strncmp(name, "hash://", 7) == 0) { - CRYPT_HASH_BLOB blob; - int len; - const char *hash = name + 7; - unsigned char *buf; - - len = strlen(hash) / 2; - buf = malloc(len); - if (buf && hexstr2bin(hash, buf, len) == 0) { - blob.cbData = len; - blob.pbData = buf; - ret = CertFindCertificateInStore(cs, - X509_ASN_ENCODING | - PKCS_7_ASN_ENCODING, - 0, CERT_FIND_HASH, - &blob, NULL); - } - free(buf); - } - - CertCloseStore(cs, 0); - - return ret; -} - - -static int tls_cryptoapi_cert(SSL *ssl, const char *name) -{ - X509 *cert = NULL; - RSA *rsa = NULL, *pub_rsa; - struct cryptoapi_rsa_data *priv; - RSA_METHOD *rsa_meth; - - if (name == NULL || - (strncmp(name, "cert://", 7) != 0 && - strncmp(name, "hash://", 7) != 0)) - return -1; - - priv = malloc(sizeof(*priv)); - rsa_meth = malloc(sizeof(*rsa_meth)); - if (priv == NULL || rsa_meth == NULL) { - wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory " - "for CryptoAPI RSA method"); - free(priv); - free(rsa_meth); - return -1; - } - memset(priv, 0, sizeof(*priv)); - memset(rsa_meth, 0, sizeof(*rsa_meth)); - - priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER); - if (priv->cert == NULL) { - priv->cert = cryptoapi_find_cert( - name, CERT_SYSTEM_STORE_LOCAL_MACHINE); - } - if (priv->cert == NULL) { - wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate " - "'%s'", name); - goto err; - } - - cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded, - priv->cert->cbCertEncoded); - if (cert == NULL) { - wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER " - "encoding"); - goto err; - } - - if (mingw_load_crypto_func()) - goto err; - - if (!CryptAcquireCertificatePrivateKey(priv->cert, - CRYPT_ACQUIRE_COMPARE_KEY_FLAG, - NULL, &priv->crypt_prov, - &priv->key_spec, - &priv->free_crypt_prov)) { - cryptoapi_error("Failed to acquire a private key for the " - "certificate"); - goto err; - } - - rsa_meth->name = "Microsoft CryptoAPI RSA Method"; - rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc; - rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec; - rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc; - rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec; - rsa_meth->finish = cryptoapi_finish; - rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK; - rsa_meth->app_data = (char *) priv; - - rsa = RSA_new(); - if (rsa == NULL) { - SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, - ERR_R_MALLOC_FAILURE); - goto err; - } - - if (!SSL_use_certificate(ssl, cert)) - goto err; - pub_rsa = cert->cert_info->key->pkey->pkey.rsa; - X509_free(cert); - cert = NULL; - - rsa->n = BN_dup(pub_rsa->n); - rsa->e = BN_dup(pub_rsa->e); - if (!RSA_set_method(rsa, rsa_meth)) - goto err; - - if (!SSL_use_RSAPrivateKey(ssl, rsa)) - goto err; - RSA_free(rsa); - - return 0; - -err: - if (cert) - X509_free(cert); - if (rsa) - RSA_free(rsa); - else { - free(rsa_meth); - cryptoapi_free_data(priv); - } - return -1; -} - - -static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) -{ - HCERTSTORE cs; - PCCERT_CONTEXT ctx = NULL; - X509 *cert; - char buf[128]; - - if (mingw_load_crypto_func()) - return -1; - - if (name == NULL || strncmp(name, "cert_store://", 13) != 0) - return -1; - - cs = CertOpenSystemStore(0, name + 13); - if (cs == NULL) { - wpa_printf(MSG_DEBUG, "%s: failed to open system cert store " - "'%s': error=%d", __func__, name + 13, - (int) GetLastError()); - return -1; - } - - while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { - cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded, - ctx->cbCertEncoded); - if (cert == NULL) { - wpa_printf(MSG_INFO, "CryptoAPI: Could not process " - "X509 DER encoding for CA cert"); - continue; - } - - X509_NAME_oneline(X509_get_subject_name(cert), buf, - sizeof(buf)); - wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for " - "system certificate store: subject='%s'", buf); - - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { - tls_show_errors(MSG_WARNING, __func__, - "Failed to add ca_cert to OpenSSL " - "certificate store"); - } - - X509_free(cert); - } - - if (!CertCloseStore(cs, 0)) { - wpa_printf(MSG_DEBUG, "%s: failed to close system cert store " - "'%s': error=%d", __func__, name + 13, - (int) GetLastError()); - } - - return 0; -} - - -#else /* CONFIG_NATIVE_WINDOWS */ - -static int tls_cryptoapi_cert(SSL *ssl, const char *name) -{ - return -1; -} - -#endif /* CONFIG_NATIVE_WINDOWS */ - - -static void ssl_info_cb(const SSL *ssl, int where, int ret) -{ - const char *str; - int w; - - wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret); - w = where & ~SSL_ST_MASK; - if (w & SSL_ST_CONNECT) - str = "SSL_connect"; - else if (w & SSL_ST_ACCEPT) - str = "SSL_accept"; - else - str = "undefined"; - - if (where & SSL_CB_LOOP) { - wpa_printf(MSG_DEBUG, "SSL: %s:%s", - str, SSL_state_string_long(ssl)); - } else if (where & SSL_CB_ALERT) { - wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", - where & SSL_CB_READ ? - "read (remote end reported an error)" : - "write (local SSL3 detected an error)", - SSL_alert_type_string_long(ret), - SSL_alert_desc_string_long(ret)); - if ((ret >> 8) == SSL3_AL_FATAL) { - struct tls_connection *conn = - SSL_get_app_data((SSL *) ssl); - if (where & SSL_CB_READ) - conn->read_alerts++; - else - conn->write_alerts++; - } - } else if (where & SSL_CB_EXIT && ret <= 0) { - wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", - str, ret == 0 ? "failed" : "error", - SSL_state_string_long(ssl)); - } -} - - -#ifndef OPENSSL_NO_ENGINE -/** - * tls_engine_load_dynamic_generic - load any openssl engine - * @pre: an array of commands and values that load an engine initialized - * in the engine specific function - * @post: an array of commands and values that initialize an already loaded - * engine (or %NULL if not required) - * @id: the engine id of the engine to load (only required if post is not %NULL - * - * This function is a generic function that loads any openssl engine. - * - * Returns: 0 on success, -1 on failure - */ -static int tls_engine_load_dynamic_generic(const char *pre[], - const char *post[], const char *id) -{ - ENGINE *engine; - const char *dynamic_id = "dynamic"; - - engine = ENGINE_by_id(id); - if (engine) { - ENGINE_free(engine); - wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " - "available", id); - return 0; - } - ERR_clear_error(); - - engine = ENGINE_by_id(dynamic_id); - if (engine == NULL) { - wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", - dynamic_id, - ERR_error_string(ERR_get_error(), NULL)); - return -1; - } - - /* Perform the pre commands. This will load the engine. */ - while (pre && pre[0]) { - wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]); - if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) { - wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: " - "%s %s [%s]", pre[0], pre[1], - ERR_error_string(ERR_get_error(), NULL)); - ENGINE_free(engine); - return -1; - } - pre += 2; - } - - /* - * Free the reference to the "dynamic" engine. The loaded engine can - * now be looked up using ENGINE_by_id(). - */ - ENGINE_free(engine); - - engine = ENGINE_by_id(id); - if (engine == NULL) { - wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", - id, ERR_error_string(ERR_get_error(), NULL)); - return -1; - } - - while (post && post[0]) { - wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]); - if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) { - wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:" - " %s %s [%s]", post[0], post[1], - ERR_error_string(ERR_get_error(), NULL)); - ENGINE_remove(engine); - ENGINE_free(engine); - return -1; - } - post += 2; - } - ENGINE_free(engine); - - return 0; -} - - -/** - * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc - * @pkcs11_so_path: pksc11_so_path from the configuration - * @pcks11_module_path: pkcs11_module_path from the configuration - */ -static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, - const char *pkcs11_module_path) -{ - char *engine_id = "pkcs11"; - const char *pre_cmd[] = { - "SO_PATH", pkcs11_so_path, - "ID", engine_id, - "LIST_ADD", "1", - /* "NO_VCHECK", "1", */ - "LOAD", NULL, - NULL, NULL - }; - const char *post_cmd[] = { - "MODULE_PATH", pkcs11_module_path, - NULL, NULL - }; - - if (!pkcs11_so_path || !pkcs11_module_path) - return 0; - - wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", - pkcs11_so_path); - - return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id); -} - - -/** - * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc - * @opensc_so_path: opensc_so_path from the configuration - */ -static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) -{ - char *engine_id = "opensc"; - const char *pre_cmd[] = { - "SO_PATH", opensc_so_path, - "ID", engine_id, - "LIST_ADD", "1", - "LOAD", NULL, - NULL, NULL - }; - - if (!opensc_so_path) - return 0; - - wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s", - opensc_so_path); - - return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id); -} -#endif /* OPENSSL_NO_ENGINE */ - - -void * tls_init(const struct tls_config *conf) -{ - SSL_CTX *ssl; - - if (tls_openssl_ref_count == 0) { - SSL_load_error_strings(); - SSL_library_init(); - /* TODO: if /dev/urandom is available, PRNG is seeded - * automatically. If this is not the case, random data should - * be added here. */ - -#ifdef PKCS12_FUNCS - PKCS12_PBE_add(); -#endif /* PKCS12_FUNCS */ - } - tls_openssl_ref_count++; - - ssl = SSL_CTX_new(TLSv1_method()); - if (ssl == NULL) - return NULL; - - SSL_CTX_set_info_callback(ssl, ssl_info_cb); - -#ifndef OPENSSL_NO_ENGINE - if (conf && - (conf->opensc_engine_path || conf->pkcs11_engine_path || - conf->pkcs11_module_path)) { - wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); - ERR_load_ENGINE_strings(); - ENGINE_load_dynamic(); - - if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || - tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, - conf->pkcs11_module_path)) { - tls_deinit(ssl); - return NULL; - } - } -#endif /* OPENSSL_NO_ENGINE */ - - return ssl; -} - - -void tls_deinit(void *ssl_ctx) -{ - SSL_CTX *ssl = ssl_ctx; - SSL_CTX_free(ssl); - - tls_openssl_ref_count--; - if (tls_openssl_ref_count == 0) { -#ifndef OPENSSL_NO_ENGINE - ENGINE_cleanup(); -#endif /* OPENSSL_NO_ENGINE */ - ERR_free_strings(); - EVP_cleanup(); - } -} - - -static int tls_engine_init(struct tls_connection *conn, const char *engine_id, - const char *pin, const char *key_id) -{ -#ifndef OPENSSL_NO_ENGINE - int ret = -1; - if (engine_id == NULL) { - wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); - return -1; - } - if (pin == NULL) { - wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set"); - return -1; - } - if (key_id == NULL) { - wpa_printf(MSG_ERROR, "ENGINE: Key Id not set"); - return -1; - } - - ERR_clear_error(); - conn->engine = ENGINE_by_id(engine_id); - if (!conn->engine) { - wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]", - engine_id, ERR_error_string(ERR_get_error(), NULL)); - goto err; - } - if (ENGINE_init(conn->engine) != 1) { - wpa_printf(MSG_ERROR, "ENGINE: engine init failed " - "(engine: %s) [%s]", engine_id, - ERR_error_string(ERR_get_error(), NULL)); - goto err; - } - wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); - - if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { - wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]", - ERR_error_string(ERR_get_error(), NULL)); - goto err; - } - conn->private_key = ENGINE_load_private_key(conn->engine, - key_id, NULL, NULL); - if (!conn->private_key) { - wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id" - " '%s' [%s]", key_id, - ERR_error_string(ERR_get_error(), NULL)); - ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; - goto err; - } - return 0; - -err: - if (conn->engine) { - ENGINE_free(conn->engine); - conn->engine = NULL; - } - - if (conn->private_key) { - EVP_PKEY_free(conn->private_key); - conn->private_key = NULL; - } - - return ret; -#else /* OPENSSL_NO_ENGINE */ - return 0; -#endif /* OPENSSL_NO_ENGINE */ -} - - -static void tls_engine_deinit(struct tls_connection *conn) -{ -#ifndef OPENSSL_NO_ENGINE - wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); - if (conn->private_key) { - EVP_PKEY_free(conn->private_key); - conn->private_key = NULL; - } - if (conn->engine) { - ENGINE_finish(conn->engine); - conn->engine = NULL; - } -#endif /* OPENSSL_NO_ENGINE */ -} - - -int tls_get_errors(void *ssl_ctx) -{ - int count = 0; - unsigned long err; - - while ((err = ERR_get_error())) { - wpa_printf(MSG_INFO, "TLS - SSL error: %s", - ERR_error_string(err, NULL)); - count++; - } - - return count; -} - -struct tls_connection * tls_connection_init(void *ssl_ctx) -{ - SSL_CTX *ssl = ssl_ctx; - struct tls_connection *conn; - - conn = malloc(sizeof(*conn)); - if (conn == NULL) - return NULL; - memset(conn, 0, sizeof(*conn)); - conn->ssl = SSL_new(ssl); - if (conn->ssl == NULL) { - tls_show_errors(MSG_INFO, __func__, - "Failed to initialize new SSL connection"); - free(conn); - return NULL; - } - - SSL_set_app_data(conn->ssl, conn); - SSL_set_options(conn->ssl, - SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | - SSL_OP_SINGLE_DH_USE); - - conn->ssl_in = BIO_new(BIO_s_mem()); - if (!conn->ssl_in) { - tls_show_errors(MSG_INFO, __func__, - "Failed to create a new BIO for ssl_in"); - SSL_free(conn->ssl); - free(conn); - return NULL; - } - - conn->ssl_out = BIO_new(BIO_s_mem()); - if (!conn->ssl_out) { - tls_show_errors(MSG_INFO, __func__, - "Failed to create a new BIO for ssl_out"); - SSL_free(conn->ssl); - BIO_free(conn->ssl_in); - free(conn); - return NULL; - } - - SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out); - - return conn; -} - - -void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return; - free(conn->pre_shared_secret); - SSL_free(conn->ssl); - tls_engine_deinit(conn); - free(conn->subject_match); - free(conn->altsubject_match); - free(conn); -} - - -int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) -{ - return conn ? SSL_is_init_finished(conn->ssl) : 0; -} - - -int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - - /* Shutdown previous TLS connection without notifying the peer - * because the connection was already terminated in practice - * and "close notify" shutdown alert would confuse AS. */ - SSL_set_quiet_shutdown(conn->ssl, 1); - SSL_shutdown(conn->ssl); - return 0; -} - - -static int tls_match_altsubject(X509 *cert, const char *match) -{ - GENERAL_NAME *gen; - char *field, *tmp; - void *ext; - int i, found = 0; - size_t len; - - ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); - - for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { - gen = sk_GENERAL_NAME_value(ext, i); - switch (gen->type) { - case GEN_EMAIL: - field = "EMAIL"; - break; - case GEN_DNS: - field = "DNS"; - break; - case GEN_URI: - field = "URI"; - break; - default: - field = NULL; - wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " - "unsupported type=%d", gen->type); - break; - } - - if (!field) - continue; - - wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", - field, gen->d.ia5->data); - len = strlen(field) + 1 + strlen((char *) gen->d.ia5->data) + - 1; - tmp = malloc(len); - if (tmp == NULL) - continue; - snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); - if (strstr(tmp, match)) - found++; - free(tmp); - } - - return found; -} - - -static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ - char buf[256]; - X509 *err_cert; - int err, depth; - SSL *ssl; - struct tls_connection *conn; - char *match, *altmatch; - - err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); - err = X509_STORE_CTX_get_error(x509_ctx); - depth = X509_STORE_CTX_get_error_depth(x509_ctx); - ssl = X509_STORE_CTX_get_ex_data(x509_ctx, - SSL_get_ex_data_X509_STORE_CTX_idx()); - X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); - - conn = SSL_get_app_data(ssl); - match = conn ? conn->subject_match : NULL; - altmatch = conn ? conn->altsubject_match : NULL; - - if (!preverify_ok) { - wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," - " error %d (%s) depth %d for '%s'", err, - X509_verify_cert_error_string(err), depth, buf); - } else { - wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " - "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", - preverify_ok, err, - X509_verify_cert_error_string(err), depth, buf); - if (depth == 0 && match && strstr(buf, match) == NULL) { - wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " - "match with '%s'", buf, match); - preverify_ok = 0; - } else if (depth == 0 && altmatch && - !tls_match_altsubject(err_cert, altmatch)) { - wpa_printf(MSG_WARNING, "TLS: altSubjectName match " - "'%s' not found", altmatch); - preverify_ok = 0; - } - } - - return preverify_ok; -} - - -#ifndef OPENSSL_NO_STDIO -static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) -{ - SSL_CTX *ssl_ctx = _ssl_ctx; - X509_LOOKUP *lookup; - int ret = 0; - - lookup = X509_STORE_add_lookup(ssl_ctx->cert_store, - X509_LOOKUP_file()); - if (lookup == NULL) { - tls_show_errors(MSG_WARNING, __func__, - "Failed add lookup for X509 store"); - return -1; - } - - if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) { - unsigned long err = ERR_peek_error(); - tls_show_errors(MSG_WARNING, __func__, - "Failed load CA in DER format"); - if (ERR_GET_LIB(err) == ERR_LIB_X509 && - ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { - wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " - "cert already in hash table error", - __func__); - } else - ret = -1; - } - - return ret; -} -#endif /* OPENSSL_NO_STDIO */ - - -static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, - const char *ca_cert, const u8 *ca_cert_blob, - size_t ca_cert_blob_len, const char *ca_path) -{ - SSL_CTX *ssl_ctx = _ssl_ctx; - - if (ca_cert_blob) { - X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, - ca_cert_blob_len); - if (cert == NULL) { - tls_show_errors(MSG_WARNING, __func__, - "Failed to parse ca_cert_blob"); - return -1; - } - - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { - tls_show_errors(MSG_WARNING, __func__, - "Failed to add ca_cert_blob to " - "certificate store"); - X509_free(cert); - return -1; - } - X509_free(cert); - wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob " - "to certificate store", __func__); - SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); - return 0; - } - -#ifdef CONFIG_NATIVE_WINDOWS - if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) == - 0) { - wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from " - "system certificate store"); - SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); - return 0; - } -#endif /* CONFIG_NATIVE_WINDOWS */ - - if (ca_cert || ca_path) { -#ifndef OPENSSL_NO_STDIO - if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) != - 1) { - tls_show_errors(MSG_WARNING, __func__, - "Failed to load root certificates"); - if (ca_cert && - tls_load_ca_der(ssl_ctx, ca_cert) == 0) { - wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " - "DER format CA certificate", - __func__); - } else - return -1; - } else { - wpa_printf(MSG_DEBUG, "TLS: Trusted root " - "certificate(s) loaded"); - tls_get_errors(ssl_ctx); - } - SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); -#else /* OPENSSL_NO_STDIO */ - wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", - __func__); - return -1; -#endif /* OPENSSL_NO_STDIO */ - } else { - /* No ca_cert configured - do not try to verify server - * certificate */ - SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); - } - - return 0; -} - - -int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) -{ - SSL_CTX *ssl_ctx = _ssl_ctx; - if (ca_cert) { - if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) - { - tls_show_errors(MSG_WARNING, __func__, - "Failed to load root certificates"); - return -1; - } - - wpa_printf(MSG_DEBUG, "TLS: Trusted root " - "certificate(s) loaded"); - -#ifndef OPENSSL_NO_STDIO - /* Add the same CAs to the client certificate requests */ - SSL_CTX_set_client_CA_list(ssl_ctx, - SSL_load_client_CA_file(ca_cert)); -#endif /* OPENSSL_NO_STDIO */ - } - - return 0; -} - - -int tls_global_set_verify(void *ssl_ctx, int check_crl) -{ - int flags; - - if (check_crl) { - X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); - if (cs == NULL) { - tls_show_errors(MSG_INFO, __func__, "Failed to get " - "certificate store when enabling " - "check_crl"); - return -1; - } - flags = X509_V_FLAG_CRL_CHECK; - if (check_crl == 2) - flags |= X509_V_FLAG_CRL_CHECK_ALL; - X509_STORE_set_flags(cs, flags); - } - return 0; -} - - -static int tls_connection_set_subject_match(void *ssl_ctx, - struct tls_connection *conn, - const char *subject_match, - const char *altsubject_match) -{ - free(conn->subject_match); - conn->subject_match = NULL; - if (subject_match) { - conn->subject_match = strdup(subject_match); - if (conn->subject_match == NULL) - return -1; - } - - free(conn->altsubject_match); - conn->altsubject_match = NULL; - if (altsubject_match) { - conn->altsubject_match = strdup(altsubject_match); - if (conn->altsubject_match == NULL) - return -1; - } - - return 0; -} - - -int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) -{ - if (conn == NULL) - return -1; - - if (verify_peer) { - SSL_set_verify(conn->ssl, SSL_VERIFY_PEER | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT | - SSL_VERIFY_CLIENT_ONCE, tls_verify_cb); - } else { - SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); - } - - SSL_set_accept_state(conn->ssl); - - return 0; -} - - -static int tls_connection_client_cert(void *ssl_ctx, - struct tls_connection *conn, - const char *client_cert, - const u8 *client_cert_blob, - size_t client_cert_blob_len) -{ - if (client_cert == NULL && client_cert_blob == NULL) - return 0; - - if (client_cert_blob && - SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, - client_cert_blob_len) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> " - "OK"); - return 0; - } else if (client_cert_blob) { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_certificate_ASN1 failed"); - } - - if (client_cert == NULL) - return -1; - -#ifndef OPENSSL_NO_STDIO - if (SSL_use_certificate_file(conn->ssl, client_cert, - SSL_FILETYPE_ASN1) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)" - " --> OK"); - return 0; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_certificate_file (DER) failed"); - } - - if (SSL_use_certificate_file(conn->ssl, client_cert, - SSL_FILETYPE_PEM) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)" - " --> OK"); - return 0; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_certificate_file (PEM) failed"); - } -#else /* OPENSSL_NO_STDIO */ - wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); -#endif /* OPENSSL_NO_STDIO */ - - return -1; -} - - -int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) -{ -#ifndef OPENSSL_NO_STDIO - SSL_CTX *ssl_ctx = _ssl_ctx; - if (client_cert == NULL) - return 0; - - if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert, - SSL_FILETYPE_ASN1) != 1 && - SSL_CTX_use_certificate_file(ssl_ctx, client_cert, - SSL_FILETYPE_PEM) != 1) { - tls_show_errors(MSG_INFO, __func__, - "Failed to load client certificate"); - return -1; - } - return 0; -#else /* OPENSSL_NO_STDIO */ - if (client_cert == NULL) - return 0; - wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); - return -1; -#endif /* OPENSSL_NO_STDIO */ -} - - -static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) -{ - if (password == NULL) { - return 0; - } - strncpy(buf, (char *) password, size); - buf[size - 1] = '\0'; - return strlen(buf); -} - - -#ifdef PKCS12_FUNCS -static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, - const char *passwd) -{ - EVP_PKEY *pkey; - X509 *cert; - STACK_OF(X509) *certs; - int res = 0; - char buf[256]; - - pkey = NULL; - cert = NULL; - certs = NULL; - if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { - tls_show_errors(MSG_DEBUG, __func__, - "Failed to parse PKCS12 file"); - PKCS12_free(p12); - return -1; - } - wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data"); - - if (cert) { - X509_NAME_oneline(X509_get_subject_name(cert), buf, - sizeof(buf)); - wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: " - "subject='%s'", buf); - if (ssl) { - if (SSL_use_certificate(ssl, cert) != 1) - res = -1; - } else { - if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1) - res = -1; - } - X509_free(cert); - } - - if (pkey) { - wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12"); - if (ssl) { - if (SSL_use_PrivateKey(ssl, pkey) != 1) - res = -1; - } else { - if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) - res = -1; - } - EVP_PKEY_free(pkey); - } - - if (certs) { - while ((cert = sk_X509_pop(certs)) != NULL) { - X509_NAME_oneline(X509_get_subject_name(cert), buf, - sizeof(buf)); - wpa_printf(MSG_DEBUG, "TLS: additional certificate" - " from PKCS12: subject='%s'", buf); - /* - * There is no SSL equivalent for the chain cert - so - * always add it to the context... - */ - if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) { - res = -1; - break; - } - } - sk_X509_free(certs); - } - - PKCS12_free(p12); - - if (res < 0) - tls_get_errors(ssl_ctx); - - return res; -} -#endif /* PKCS12_FUNCS */ - - -static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, - const char *passwd) -{ -#ifdef PKCS12_FUNCS - FILE *f; - PKCS12 *p12; - - f = fopen(private_key, "r"); - if (f == NULL) - return -1; - - p12 = d2i_PKCS12_fp(f, NULL); - fclose(f); - - if (p12 == NULL) { - tls_show_errors(MSG_INFO, __func__, - "Failed to use PKCS#12 file"); - return -1; - } - - return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); - -#else /* PKCS12_FUNCS */ - wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " - "p12/pfx files"); - return -1; -#endif /* PKCS12_FUNCS */ -} - - -static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, - const u8 *blob, size_t len, const char *passwd) -{ -#ifdef PKCS12_FUNCS - PKCS12 *p12; - - p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len); - if (p12 == NULL) { - tls_show_errors(MSG_INFO, __func__, - "Failed to use PKCS#12 blob"); - return -1; - } - - return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); - -#else /* PKCS12_FUNCS */ - wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " - "p12/pfx blobs"); - return -1; -#endif /* PKCS12_FUNCS */ -} - - -static int tls_connection_engine_private_key(void *_ssl_ctx, - struct tls_connection *conn) -{ -#ifndef OPENSSL_NO_ENGINE - if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { - tls_show_errors(MSG_ERROR, __func__, - "ENGINE: cannot use private key for TLS"); - return -1; - } - if (!SSL_check_private_key(conn->ssl)) { - tls_show_errors(MSG_INFO, __func__, - "Private key failed verification"); - return -1; - } - return 0; -#else /* OPENSSL_NO_ENGINE */ - wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but " - "engine support was not compiled in"); - return -1; -#endif /* OPENSSL_NO_ENGINE */ -} - - -static int tls_connection_private_key(void *_ssl_ctx, - struct tls_connection *conn, - const char *private_key, - const char *private_key_passwd, - const u8 *private_key_blob, - size_t private_key_blob_len) -{ - SSL_CTX *ssl_ctx = _ssl_ctx; - char *passwd; - int ok; - - if (private_key == NULL && private_key_blob == NULL) - return 0; - - if (private_key_passwd) { - passwd = strdup(private_key_passwd); - if (passwd == NULL) - return -1; - } else - passwd = NULL; - - SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - - ok = 0; - while (private_key_blob) { - if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl, - (u8 *) private_key_blob, - private_key_blob_len) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" - "ASN1(EVP_PKEY_RSA) --> OK"); - ok = 1; - break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)" - " failed"); - } - - if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl, - (u8 *) private_key_blob, - private_key_blob_len) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" - "ASN1(EVP_PKEY_DSA) --> OK"); - ok = 1; - break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)" - " failed"); - } - - if (SSL_use_RSAPrivateKey_ASN1(conn->ssl, - (u8 *) private_key_blob, - private_key_blob_len) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: " - "SSL_use_RSAPrivateKey_ASN1 --> OK"); - ok = 1; - break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_RSAPrivateKey_ASN1 failed"); - } - - if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, - private_key_blob_len, passwd) == 0) { - wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " - "OK"); - ok = 1; - break; - } - - break; - } - - while (!ok && private_key) { -#ifndef OPENSSL_NO_STDIO - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_ASN1) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: " - "SSL_use_PrivateKey_File (DER) --> OK"); - ok = 1; - break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_File (DER) " - "failed"); - } - - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_PEM) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: " - "SSL_use_PrivateKey_File (PEM) --> OK"); - ok = 1; - break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_File (PEM) " - "failed"); - } -#else /* OPENSSL_NO_STDIO */ - wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", - __func__); -#endif /* OPENSSL_NO_STDIO */ - - if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd) - == 0) { - wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " - "--> OK"); - ok = 1; - break; - } - - if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) { - wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to " - "access certificate store --> OK"); - ok = 1; - break; - } - - break; - } - - if (!ok) { - wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key"); - free(passwd); - ERR_clear_error(); - return -1; - } - ERR_clear_error(); - SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); - free(passwd); - - if (!SSL_check_private_key(conn->ssl)) { - tls_show_errors(MSG_INFO, __func__, "Private key failed " - "verification"); - return -1; - } - - wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully"); - return 0; -} - - -int tls_global_private_key(void *_ssl_ctx, const char *private_key, - const char *private_key_passwd) -{ - SSL_CTX *ssl_ctx = _ssl_ctx; - char *passwd; - - if (private_key == NULL) - return 0; - - if (private_key_passwd) { - passwd = strdup(private_key_passwd); - if (passwd == NULL) - return -1; - } else - passwd = NULL; - - SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - if ( -#ifndef OPENSSL_NO_STDIO - SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, - SSL_FILETYPE_ASN1) != 1 && - SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, - SSL_FILETYPE_PEM) != 1 && -#endif /* OPENSSL_NO_STDIO */ - tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { - tls_show_errors(MSG_INFO, __func__, - "Failed to load private key"); - free(passwd); - ERR_clear_error(); - return -1; - } - free(passwd); - ERR_clear_error(); - SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); - - if (!SSL_CTX_check_private_key(ssl_ctx)) { - tls_show_errors(MSG_INFO, __func__, - "Private key failed verification"); - return -1; - } - - return 0; -} - - -static int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, - const char *dh_file) -{ -#ifdef OPENSSL_NO_DH - if (dh_file == NULL) - return 0; - wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but " - "dh_file specified"); - return -1; -#else /* OPENSSL_NO_DH */ - DH *dh; - BIO *bio; - - /* TODO: add support for dh_blob */ - if (dh_file == NULL) - return 0; - if (conn == NULL) - return -1; - - bio = BIO_new_file(dh_file, "r"); - if (bio == NULL) { - wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s", - dh_file, ERR_error_string(ERR_get_error(), NULL)); - return -1; - } - dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - BIO_free(bio); -#ifndef OPENSSL_NO_DSA - while (dh == NULL) { - DSA *dsa; - wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -" - " trying to parse as DSA params", dh_file, - ERR_error_string(ERR_get_error(), NULL)); - bio = BIO_new_file(dh_file, "r"); - if (bio == NULL) - break; - dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL); - BIO_free(bio); - if (!dsa) { - wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file " - "'%s': %s", dh_file, - ERR_error_string(ERR_get_error(), NULL)); - break; - } - - wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format"); - dh = DSA_dup_DH(dsa); - DSA_free(dsa); - if (dh == NULL) { - wpa_printf(MSG_INFO, "TLS: Failed to convert DSA " - "params into DH params"); - break; - } - break; - } -#endif /* !OPENSSL_NO_DSA */ - if (dh == NULL) { - wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file " - "'%s'", dh_file); - return -1; - } - - if (SSL_set_tmp_dh(conn->ssl, dh) != 1) { - wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': " - "%s", dh_file, - ERR_error_string(ERR_get_error(), NULL)); - DH_free(dh); - return -1; - } - DH_free(dh); - return 0; -#endif /* OPENSSL_NO_DH */ -} - - -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) -{ - SSL *ssl; - - if (conn == NULL || keys == NULL) - return -1; - ssl = conn->ssl; - if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) - return -1; - - memset(keys, 0, sizeof(*keys)); - keys->master_key = ssl->session->master_key; - keys->master_key_len = ssl->session->master_key_length; - keys->client_random = ssl->s3->client_random; - keys->client_random_len = SSL3_RANDOM_SIZE; - keys->server_random = ssl->s3->server_random; - keys->server_random_len = SSL3_RANDOM_SIZE; - - return 0; -} - - -u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) -{ - int res; - u8 *out_data; - - /* - * Give TLS handshake data from the server (if available) to OpenSSL - * for processing. - */ - if (in_data && - BIO_write(conn->ssl_in, in_data, in_len) < 0) { - tls_show_errors(MSG_INFO, __func__, - "Handshake failed - BIO_write"); - return NULL; - } - - /* Initiate TLS handshake or continue the existing handshake */ - res = SSL_connect(conn->ssl); - if (res != 1) { - int err = SSL_get_error(conn->ssl, res); - if (err == SSL_ERROR_WANT_READ) - wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want " - "more data"); - else if (err == SSL_ERROR_WANT_WRITE) - wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to " - "write"); - else { - tls_show_errors(MSG_INFO, __func__, "SSL_connect"); - conn->failed++; - } - } - - /* Get the TLS handshake data to be sent to the server */ - res = BIO_ctrl_pending(conn->ssl_out); - wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); - out_data = malloc(res == 0 ? 1 : res); - if (out_data == NULL) { - wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " - "handshake output (%d bytes)", res); - if (BIO_reset(conn->ssl_out) < 0) { - tls_show_errors(MSG_INFO, __func__, - "BIO_reset failed"); - } - *out_len = 0; - return NULL; - } - res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); - if (res < 0) { - tls_show_errors(MSG_INFO, __func__, - "Handshake failed - BIO_read"); - if (BIO_reset(conn->ssl_out) < 0) { - tls_show_errors(MSG_INFO, __func__, - "BIO_reset failed"); - } - *out_len = 0; - return NULL; - } - *out_len = res; - return out_data; -} - - -u8 * tls_connection_server_handshake(void *ssl_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) -{ - int res; - u8 *out_data; - char buf[10]; - - if (in_data && - BIO_write(conn->ssl_in, in_data, in_len) < 0) { - tls_show_errors(MSG_INFO, __func__, - "Handshake failed - BIO_write"); - return NULL; - } - - res = SSL_read(conn->ssl, buf, sizeof(buf)); - if (res >= 0) { - wpa_printf(MSG_DEBUG, "SSL: Unexpected data from SSL_read " - "(res=%d)", res); - } - - res = BIO_ctrl_pending(conn->ssl_out); - wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); - out_data = malloc(res == 0 ? 1 : res); - if (out_data == NULL) { - wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " - "handshake output (%d bytes)", res); - if (BIO_reset(conn->ssl_out) < 0) { - tls_show_errors(MSG_INFO, __func__, - "BIO_reset failed"); - } - *out_len = 0; - return NULL; - } - res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); - if (res < 0) { - tls_show_errors(MSG_INFO, __func__, - "Handshake failed - BIO_read"); - if (BIO_reset(conn->ssl_out) < 0) { - tls_show_errors(MSG_INFO, __func__, - "BIO_reset failed"); - } - *out_len = 0; - return NULL; - } - *out_len = res; - return out_data; -} - - -int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) -{ - int res; - - if (conn == NULL) - return -1; - - /* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */ - if ((res = BIO_reset(conn->ssl_in)) < 0 || - (res = BIO_reset(conn->ssl_out)) < 0) { - tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); - return res; - } - res = SSL_write(conn->ssl, in_data, in_len); - if (res < 0) { - tls_show_errors(MSG_INFO, __func__, - "Encryption failed - SSL_write"); - return res; - } - - /* Read encrypted data to be sent to the server */ - res = BIO_read(conn->ssl_out, out_data, out_len); - if (res < 0) { - tls_show_errors(MSG_INFO, __func__, - "Encryption failed - BIO_read"); - return res; - } - - return res; -} - - -int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) -{ - int res; - - /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */ - res = BIO_write(conn->ssl_in, in_data, in_len); - if (res < 0) { - tls_show_errors(MSG_INFO, __func__, - "Decryption failed - BIO_write"); - return res; - } - if (BIO_reset(conn->ssl_out) < 0) { - tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); - return res; - } - - /* Read decrypted data for further processing */ - res = SSL_read(conn->ssl, out_data, out_len); - if (res < 0) { - tls_show_errors(MSG_INFO, __func__, - "Decryption failed - SSL_read"); - return res; - } - - return res; -} - - -int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) -{ - return conn ? conn->ssl->hit : 0; -} - - -#ifdef EAP_FAST -/* Pre-shared secred requires a patch to openssl, so this function is - * commented out unless explicitly needed for EAP-FAST in order to be able to - * build this file with unmodified openssl. */ - -static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, - STACK_OF(SSL_CIPHER) *peer_ciphers, - SSL_CIPHER **cipher, void *arg) -{ - struct tls_connection *conn = arg; - - if (conn == NULL || conn->pre_shared_secret == 0) - return 0; - - memcpy(secret, conn->pre_shared_secret, conn->pre_shared_secret_len); - *secret_len = conn->pre_shared_secret_len; - - return 1; -} - - -int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - if (conn == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) - return -1; - - free(conn->pre_shared_secret); - conn->pre_shared_secret = NULL; - conn->pre_shared_secret_len = 0; - - if (key) { - conn->pre_shared_secret = malloc(key_len); - if (conn->pre_shared_secret) { - memcpy(conn->pre_shared_secret, key, key_len); - conn->pre_shared_secret_len = key_len; - } - if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, - conn) != 1) - return -1; - } else { - if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) - return -1; - } - - return 0; -} -#endif /* EAP_FAST */ - - -int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL || conn->ssl == NULL) - return -1; - - if (SSL_set_cipher_list(conn->ssl, "ADH-AES128-SHA") != 1) { - tls_show_errors(MSG_INFO, __func__, - "Anon DH configuration failed"); - return -1; - } - - return 0; -} - - -int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, - char *buf, size_t buflen) -{ - const char *name; - if (conn == NULL || conn->ssl == NULL) - return -1; - - name = SSL_get_cipher(conn->ssl); - if (name == NULL) - return -1; - - snprintf(buf, buflen, "%s", name); - return 0; -} - - -int tls_connection_enable_workaround(void *ssl_ctx, - struct tls_connection *conn) -{ - SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); - - return 0; -} - - -#ifdef EAP_FAST -/* ClientHello TLS extensions require a patch to openssl, so this function is - * commented out unless explicitly needed for EAP-FAST in order to be able to - * build this file with unmodified openssl. */ -int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, - int ext_type, const u8 *data, - size_t data_len) -{ - if (conn == NULL || conn->ssl == NULL) - return -1; - - if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, - data_len) != 1) - return -1; - - return 0; -} -#endif /* EAP_FAST */ - - -int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->failed; -} - - -int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->read_alerts; -} - - -int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) -{ - if (conn == NULL) - return -1; - return conn->write_alerts; -} - - -int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, - const struct tls_connection_params *params) -{ - int ret; - unsigned long err; - - if (conn == NULL) - return -1; - - while ((err = ERR_get_error())) { - wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", - __func__, ERR_error_string(err, NULL)); - } - - if (tls_connection_set_subject_match(tls_ctx, conn, - params->subject_match, - params->altsubject_match)) - return -1; - if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, - params->ca_cert_blob, - params->ca_cert_blob_len, - params->ca_path)) - return -1; - if (tls_connection_client_cert(tls_ctx, conn, params->client_cert, - params->client_cert_blob, - params->client_cert_blob_len)) - return -1; - - if (params->engine) { - wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); - ret = tls_engine_init(conn, params->engine_id, params->pin, - params->key_id); - if (ret) - return ret; - if (tls_connection_engine_private_key(tls_ctx, conn)) - return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; - } else if (tls_connection_private_key(tls_ctx, conn, - params->private_key, - params->private_key_passwd, - params->private_key_blob, - params->private_key_blob_len)) { - wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'", - params->private_key); - return -1; - } - - if (tls_connection_dh(tls_ctx, conn, params->dh_file)) { - wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", - params->dh_file); - return -1; - } - - tls_get_errors(tls_ctx); - - return 0; -} - - -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ - const EVP_CIPHER *c; - const EVP_MD *h; - - if (conn == NULL || conn->ssl == NULL || - conn->ssl->enc_read_ctx == NULL || - conn->ssl->enc_read_ctx->cipher == NULL || - conn->ssl->read_hash == NULL) - return -1; - - c = conn->ssl->enc_read_ctx->cipher; - h = conn->ssl->read_hash; - - return 2 * (EVP_CIPHER_key_length(c) + - EVP_MD_size(h) + - EVP_CIPHER_iv_length(c)); -} diff --git a/contrib/hostapd-0.4.9/version.h b/contrib/hostapd-0.4.9/version.h deleted file mode 100644 index 819bfcfe78..0000000000 --- a/contrib/hostapd-0.4.9/version.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef VERSION_H -#define VERSION_H - -#define VERSION_STR "0.4.9" - -#endif /* VERSION_H */ diff --git a/contrib/hostapd-0.4.9/wired.conf b/contrib/hostapd-0.4.9/wired.conf deleted file mode 100644 index 956f8c53c5..0000000000 --- a/contrib/hostapd-0.4.9/wired.conf +++ /dev/null @@ -1,40 +0,0 @@ -##### hostapd configuration file ############################################## -# Empty lines and lines starting with # are ignored - -# Example configuration file for wired authenticator. See hostapd.conf for -# more details. - -interface=eth0 -driver=wired -logger_stdout=-1 -logger_stdout_level=1 -debug=2 -dump_file=/tmp/hostapd.dump - -ieee8021x=1 -eap_reauth_period=3600 - -use_pae_group_addr=1 - - -##### RADIUS configuration #################################################### -# for IEEE 802.1X with external Authentication Server, IEEE 802.11 -# authentication with external ACL for MAC addresses, and accounting - -# The own IP address of the access point (used as NAS-IP-Address) -own_ip_addr=127.0.0.1 - -# Optional NAS-Identifier string for RADIUS messages. When used, this should be -# a unique to the NAS within the scope of the RADIUS server. For example, a -# fully qualified domain name can be used here. -nas_identifier=ap.example.com - -# RADIUS authentication server -auth_server_addr=127.0.0.1 -auth_server_port=1812 -auth_server_shared_secret=radius - -# RADIUS accounting server -acct_server_addr=127.0.0.1 -acct_server_port=1813 -acct_server_shared_secret=radius diff --git a/contrib/hostapd-0.4.9/wpa.c b/contrib/hostapd-0.4.9/wpa.c deleted file mode 100644 index 08f8f3aa88..0000000000 --- a/contrib/hostapd-0.4.9/wpa.c +++ /dev/null @@ -1,2897 +0,0 @@ -/* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / WPA Authenticator - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "hostapd.h" -#include "eapol_sm.h" -#include "wpa.h" -#include "driver.h" -#include "sha1.h" -#include "md5.h" -#include "rc4.h" -#include "aes_wrap.h" -#include "ieee802_1x.h" -#include "ieee802_11.h" -#include "eloop.h" -#include "sta_info.h" -#include "l2_packet.h" -#include "accounting.h" -#include "hostap_common.h" - - -static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); -static void wpa_sm_step(struct wpa_state_machine *sm); -static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); -static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); -static void wpa_group_sm_step(struct hostapd_data *hapd); -static void pmksa_cache_free(struct hostapd_data *hapd); -static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, - u8 *spa, u8 *pmkid); - - -/* Default timeouts are 100 ms, but this seems to be a bit too fast for most - * WPA Supplicants, so use a bit longer timeout. */ -static const u32 dot11RSNAConfigGroupUpdateTimeOut = 1000; /* ms */ -static const u32 dot11RSNAConfigGroupUpdateCount = 3; -static const u32 dot11RSNAConfigPairwiseUpdateTimeOut = 1000; /* ms */ -static const u32 dot11RSNAConfigPairwiseUpdateCount = 3; - -/* TODO: make these configurable */ -static const int dot11RSNAConfigPMKLifetime = 43200; -static const int dot11RSNAConfigPMKReauthThreshold = 70; -static const int dot11RSNAConfigSATimeout = 60; -static const int pmksa_cache_max_entries = 1024; - - -static const int WPA_SELECTOR_LEN = 4; -static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; -static const u16 WPA_VERSION = 1; -static const u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 }; -static const u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 }; -static const u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 }; -static const u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 }; -static const u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 }; -static const u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 }; -static const u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 }; -static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; - -static const int RSN_SELECTOR_LEN = 4; -static const u16 RSN_VERSION = 1; -static const u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 }; -static const u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 }; -static const u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 }; -static const u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 }; -static const u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 }; -static const u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 }; -static const u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 }; -static const u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 }; - -/* EAPOL-Key Key Data Encapsulation - * GroupKey and STAKey require encryption, otherwise, encryption is optional. - */ -static const u8 RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 }; -static const u8 RSN_KEY_DATA_STAKEY[] = { 0x00, 0x0f, 0xac, 2 }; -static const u8 RSN_KEY_DATA_MAC_ADDR[] = { 0x00, 0x0f, 0xac, 3 }; -static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; - -/* WPA IE version 1 - * 00-50-f2:1 (OUI:OUI type) - * 0x01 0x00 (version; little endian) - * (all following fields are optional:) - * Group Suite Selector (4 octets) (default: TKIP) - * Pairwise Suite Count (2 octets, little endian) (default: 1) - * Pairwise Suite List (4 * n octets) (default: TKIP) - * Authenticated Key Management Suite Count (2 octets, little endian) - * (default: 1) - * Authenticated Key Management Suite List (4 * n octets) - * (default: unspec 802.1X) - * WPA Capabilities (2 octets, little endian) (default: 0) - */ - -struct wpa_ie_hdr { - u8 elem_id; - u8 len; - u8 oui[3]; - u8 oui_type; - u16 version; -} __attribute__ ((packed)); - - -/* RSN IE version 1 - * 0x01 0x00 (version; little endian) - * (all following fields are optional:) - * Group Suite Selector (4 octets) (default: CCMP) - * Pairwise Suite Count (2 octets, little endian) (default: 1) - * Pairwise Suite List (4 * n octets) (default: CCMP) - * Authenticated Key Management Suite Count (2 octets, little endian) - * (default: 1) - * Authenticated Key Management Suite List (4 * n octets) - * (default: unspec 802.1X) - * RSN Capabilities (2 octets, little endian) (default: 0) - * PMKID Count (2 octets) (default: 0) - * PMKID List (16 * n octets) - */ - -struct rsn_ie_hdr { - u8 elem_id; /* WLAN_EID_RSN */ - u8 len; - u16 version; -} __attribute__ ((packed)); - - -static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) -{ - struct wpa_ie_hdr *hdr; - int num_suites; - u8 *pos, *count; - - hdr = (struct wpa_ie_hdr *) buf; - hdr->elem_id = WLAN_EID_GENERIC; - memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN); - hdr->version = host_to_le16(WPA_VERSION); - pos = (u8 *) (hdr + 1); - - if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { - memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { - memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { - memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) { - memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN); - } else { - printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group); - return -1; - } - pos += WPA_SELECTOR_LEN; - - num_suites = 0; - count = pos; - pos += 2; - - if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { - memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { - memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) { - memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - - if (num_suites == 0) { - printf("Invalid pairwise cipher (%d).\n", - hapd->conf->wpa_pairwise); - return -1; - } - *count++ = num_suites & 0xff; - *count = (num_suites >> 8) & 0xff; - - num_suites = 0; - count = pos; - pos += 2; - - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { - memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { - memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, - WPA_SELECTOR_LEN); - pos += WPA_SELECTOR_LEN; - num_suites++; - } - - if (num_suites == 0) { - printf("Invalid key management type (%d).\n", - hapd->conf->wpa_key_mgmt); - return -1; - } - *count++ = num_suites & 0xff; - *count = (num_suites >> 8) & 0xff; - - /* WPA Capabilities; use defaults, so no need to include it */ - - hdr->len = (pos - buf) - 2; - - return pos - buf; -} - - -static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) -{ - struct rsn_ie_hdr *hdr; - int num_suites; - u8 *pos, *count; - - hdr = (struct rsn_ie_hdr *) buf; - hdr->elem_id = WLAN_EID_RSN; - pos = (u8 *) &hdr->version; - *pos++ = RSN_VERSION & 0xff; - *pos++ = RSN_VERSION >> 8; - pos = (u8 *) (hdr + 1); - - if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { - memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { - memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { - memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) { - memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN); - } else { - printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group); - return -1; - } - pos += RSN_SELECTOR_LEN; - - num_suites = 0; - count = pos; - pos += 2; - - if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { - memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { - memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) { - memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - - if (num_suites == 0) { - printf("Invalid pairwise cipher (%d).\n", - hapd->conf->wpa_pairwise); - return -1; - } - *count++ = num_suites & 0xff; - *count = (num_suites >> 8) & 0xff; - - num_suites = 0; - count = pos; - pos += 2; - - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { - memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { - memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, - RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - num_suites++; - } - - if (num_suites == 0) { - printf("Invalid key management type (%d).\n", - hapd->conf->wpa_key_mgmt); - return -1; - } - *count++ = num_suites & 0xff; - *count = (num_suites >> 8) & 0xff; - - /* RSN Capabilities */ - *pos++ = hapd->conf->rsn_preauth ? BIT(0) : 0; - *pos++ = 0; - - hdr->len = (pos - buf) - 2; - - return pos - buf; -} - - -static int wpa_gen_wpa_ie(struct hostapd_data *hapd) -{ - u8 *pos, buf[100]; - int res; - - pos = buf; - - if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) { - res = wpa_write_rsn_ie(hapd, pos, buf + sizeof(buf) - pos); - if (res < 0) - return res; - pos += res; - } - if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA) { - res = wpa_write_wpa_ie(hapd, pos, buf + sizeof(buf) - pos); - if (res < 0) - return res; - pos += res; - } - - free(hapd->wpa_ie); - hapd->wpa_ie = malloc(pos - buf); - if (hapd->wpa_ie == NULL) - return -1; - memcpy(hapd->wpa_ie, buf, pos - buf); - hapd->wpa_ie_len = pos - buf; - - return 0; -} - - -static void wpa_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta) -{ - hostapd_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta); - sta->timeout_next = STA_REMOVE; -} - - -static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - - if (hapd->wpa_auth) { - if (hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) { - printf("Failed to get random data for WPA " - "initialization.\n"); - } else { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "GMK rekeyd"); - } - } - - if (hapd->conf->wpa_gmk_rekey) { - eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, - wpa_rekey_gmk, hapd, NULL); - } -} - - -static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - - if (hapd->wpa_auth) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "rekeying GTK"); - hapd->wpa_auth->GTKReKey = TRUE; - do { - hapd->wpa_auth->changed = FALSE; - wpa_group_sm_step(hapd); - } while (hapd->wpa_auth->changed); - } - if (hapd->conf->wpa_group_rekey) { - eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, - wpa_rekey_gtk, hapd, NULL); - } -} - - -#ifdef CONFIG_RSN_PREAUTH - -static void rsn_preauth_receive(void *ctx, const u8 *src_addr, - const u8 *buf, size_t len) -{ - struct rsn_preauth_interface *piface = ctx; - struct hostapd_data *hapd = piface->hapd; - struct ieee802_1x_hdr *hdr; - struct sta_info *sta; - struct l2_ethhdr *ethhdr; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: receive pre-auth packet " - "from interface '%s'\n", piface->ifname); - if (len < sizeof(*ethhdr) + sizeof(*hdr)) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: too short pre-auth " - "packet (len=%lu)\n", (unsigned long) len); - return; - } - - ethhdr = (struct l2_ethhdr *) buf; - hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); - - if (memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for " - "foreign address " MACSTR "\n", - MAC2STR(ethhdr->h_dest)); - return; - } - - sta = ap_get_sta(hapd, ethhdr->h_source); - if (sta && (sta->flags & WLAN_STA_ASSOC)) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for " - "already association STA " MACSTR "\n", - MAC2STR(sta->addr)); - return; - } - if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { - sta = (struct sta_info *) malloc(sizeof(struct sta_info)); - if (sta == NULL) - return; - memset(sta, 0, sizeof(*sta)); - memcpy(sta->addr, ethhdr->h_source, ETH_ALEN); - sta->flags = WLAN_STA_PREAUTH; - sta->next = hapd->sta_list; - sta->wpa = WPA_VERSION_WPA2; - hapd->sta_list = sta; - hapd->num_sta++; - ap_sta_hash_add(hapd, sta); - - ieee802_1x_new_station(hapd, sta); - if (sta->eapol_sm == NULL) { - ap_free_sta(hapd, sta); - sta = NULL; - } else { - sta->eapol_sm->radius_identifier = -1; - sta->eapol_sm->portValid = TRUE; - sta->eapol_sm->flags |= EAPOL_SM_PREAUTH; - } - } - if (sta == NULL) - return; - sta->preauth_iface = piface; - ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1), - len - sizeof(*ethhdr)); -} - - -static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) -{ - struct rsn_preauth_interface *piface; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN pre-auth interface '%s'\n", - ifname); - - piface = malloc(sizeof(*piface)); - if (piface == NULL) - return -1; - memset(piface, 0, sizeof(*piface)); - piface->hapd = hapd; - - piface->ifname = strdup(ifname); - if (piface->ifname == NULL) { - goto fail1; - } - - piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, - rsn_preauth_receive, piface, 1); - if (piface->l2 == NULL) { - printf("Failed to open register layer 2 access to " - "ETH_P_PREAUTH\n"); - goto fail2; - } - - piface->next = hapd->preauth_iface; - hapd->preauth_iface = piface; - return 0; - -fail2: - free(piface->ifname); -fail1: - free(piface); - return -1; -} - - -static void rsn_preauth_iface_deinit(struct hostapd_data *hapd) -{ - struct rsn_preauth_interface *piface, *prev; - - piface = hapd->preauth_iface; - hapd->preauth_iface = NULL; - while (piface) { - prev = piface; - piface = piface->next; - l2_packet_deinit(prev->l2); - free(prev->ifname); - free(prev); - } -} - - -static int rsn_preauth_iface_init(struct hostapd_data *hapd) -{ - char *tmp, *start, *end; - - if (hapd->conf->rsn_preauth_interfaces == NULL) - return 0; - - tmp = strdup(hapd->conf->rsn_preauth_interfaces); - if (tmp == NULL) - return -1; - start = tmp; - for (;;) { - while (*start == ' ') - start++; - if (*start == '\0') - break; - end = strchr(start, ' '); - if (end) - *end = '\0'; - - if (rsn_preauth_iface_add(hapd, start)) { - rsn_preauth_iface_deinit(hapd); - return -1; - } - - if (end) - start = end + 1; - else - break; - } - free(tmp); - return 0; -} - - -static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " - MACSTR, MAC2STR(sta->addr)); - ap_free_sta(hapd, sta); -} - - -void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ - u8 *key; - size_t len; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "pre-authentication %s", - success ? "succeeded" : "failed"); - - key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len); - if (success && key) { - pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime); - } - - /* - * Finish STA entry removal from timeout in order to avoid freeing - * STA data before the caller has finished processing. - */ - eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); -} - - -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len) -{ - struct rsn_preauth_interface *piface; - struct l2_ethhdr *ethhdr; - - piface = hapd->preauth_iface; - while (piface) { - if (piface == sta->preauth_iface) - break; - piface = piface->next; - } - - if (piface == NULL) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: Could not find " - "pre-authentication interface for " MACSTR "\n", - MAC2STR(sta->addr)); - return; - } - - ethhdr = malloc(sizeof(*ethhdr) + len); - if (ethhdr == NULL) - return; - - memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); - memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); - ethhdr->h_proto = htons(ETH_P_PREAUTH); - memcpy(ethhdr + 1, buf, len); - - if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, - sizeof(*ethhdr) + len) < 0) { - printf("Failed to send preauth packet using l2_packet_send\n"); - } - free(ethhdr); -} - -#else /* CONFIG_RSN_PREAUTH */ - -static inline int rsn_preauth_iface_init(struct hostapd_data *hapd) -{ - return 0; -} - -static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) -{ -} - -static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) -{ -} - -void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ -} - -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len) -{ -} - -#endif /* CONFIG_RSN_PREAUTH */ - - -int wpa_init(struct hostapd_data *hapd) -{ - u8 rkey[32]; - u8 buf[ETH_ALEN + 8]; - - if (rsn_preauth_iface_init(hapd)) - return -1; - - if (hostapd_set_privacy(hapd, 1)) { - printf("Could not set PrivacyInvoked for interface %s\n", - hapd->conf->iface); - return -1; - } - - if (wpa_gen_wpa_ie(hapd)) { - printf("Could not generate WPA IE.\n"); - return -1; - } - - if (hostapd_set_generic_elem(hapd, hapd->wpa_ie, hapd->wpa_ie_len)) { - printf("Failed to configure WPA IE for the kernel driver.\n"); - return -1; - } - - hapd->wpa_auth = malloc(sizeof(struct wpa_authenticator)); - if (hapd->wpa_auth == NULL) - return -1; - memset(hapd->wpa_auth, 0, sizeof(struct wpa_authenticator)); - hapd->wpa_auth->GTKAuthenticator = TRUE; - switch (hapd->conf->wpa_group) { - case WPA_CIPHER_CCMP: - hapd->wpa_auth->GTK_len = 16; - break; - case WPA_CIPHER_TKIP: - hapd->wpa_auth->GTK_len = 32; - break; - case WPA_CIPHER_WEP104: - hapd->wpa_auth->GTK_len = 13; - break; - case WPA_CIPHER_WEP40: - hapd->wpa_auth->GTK_len = 5; - break; - } - - /* Counter = PRF-256(Random number, "Init Counter", - * Local MAC Address || Time) - */ - memcpy(buf, hapd->own_addr, ETH_ALEN); - hostapd_get_ntp_timestamp(buf + ETH_ALEN); - if (hostapd_get_rand(rkey, sizeof(rkey)) || - hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) { - printf("Failed to get random data for WPA initialization.\n"); - free(hapd->wpa_auth); - hapd->wpa_auth = NULL; - return -1; - } - - sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), - hapd->wpa_auth->Counter, WPA_NONCE_LEN); - - if (hapd->conf->wpa_gmk_rekey) { - eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, - wpa_rekey_gmk, hapd, NULL); - } - - if (hapd->conf->wpa_group_rekey) { - eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, - wpa_rekey_gtk, hapd, NULL); - } - - hapd->wpa_auth->GInit = TRUE; - wpa_group_sm_step(hapd); - hapd->wpa_auth->GInit = FALSE; - wpa_group_sm_step(hapd); - - return 0; -} - - -void wpa_deinit(struct hostapd_data *hapd) -{ - rsn_preauth_iface_deinit(hapd); - - eloop_cancel_timeout(wpa_rekey_gmk, hapd, NULL); - eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); - - if (hostapd_set_privacy(hapd, 0)) { - printf("Could not disable PrivacyInvoked for interface %s\n", - hapd->conf->iface); - } - - if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { - printf("Could not remove generic information element from " - "interface %s\n", hapd->conf->iface); - } - - free(hapd->wpa_ie); - hapd->wpa_ie = NULL; - free(hapd->wpa_auth); - hapd->wpa_auth = NULL; - - pmksa_cache_free(hapd); -} - - -static int wpa_selector_to_bitfield(u8 *s) -{ - if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0) - return WPA_CIPHER_NONE; - if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0) - return WPA_CIPHER_WEP40; - if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0) - return WPA_CIPHER_TKIP; - if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0) - return WPA_CIPHER_CCMP; - if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0) - return WPA_CIPHER_WEP104; - return 0; -} - - -static int wpa_key_mgmt_to_bitfield(u8 *s) -{ - if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0) - return WPA_KEY_MGMT_IEEE8021X; - if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) == - 0) - return WPA_KEY_MGMT_PSK; - return 0; -} - - -static int rsn_selector_to_bitfield(u8 *s) -{ - if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_NONE; - if (memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_WEP40; - if (memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_TKIP; - if (memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_CCMP; - if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0) - return WPA_CIPHER_WEP104; - return 0; -} - - -static int rsn_key_mgmt_to_bitfield(u8 *s) -{ - if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0) - return WPA_KEY_MGMT_IEEE8021X; - if (memcmp(s, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN) == - 0) - return WPA_KEY_MGMT_PSK; - return 0; -} - - -static void rsn_pmkid(const u8 *pmk, const u8 *aa, const u8 *spa, u8 *pmkid) -{ - char *title = "PMK Name"; - const u8 *addr[3]; - const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA1_MAC_LEN]; - - addr[0] = (u8 *) title; - addr[1] = aa; - addr[2] = spa; - - hmac_sha1_vector(pmk, PMK_LEN, 3, addr, len, hash); - memcpy(pmkid, hash, PMKID_LEN); -} - - -static void pmksa_cache_set_expiration(struct hostapd_data *hapd); - - -static void _pmksa_cache_free_entry(struct rsn_pmksa_cache *entry) -{ - if (entry == NULL) - return; - free(entry->identity); - ieee802_1x_free_radius_class(&entry->radius_class); - free(entry); -} - - -static void pmksa_cache_free_entry(struct hostapd_data *hapd, - struct rsn_pmksa_cache *entry) -{ - struct sta_info *sta; - struct rsn_pmksa_cache *pos, *prev; - hapd->pmksa_count--; - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - if (sta->pmksa == entry) - sta->pmksa = NULL; - } - pos = hapd->pmkid[PMKID_HASH(entry->pmkid)]; - prev = NULL; - while (pos) { - if (pos == entry) { - if (prev != NULL) { - prev->hnext = pos->hnext; - } else { - hapd->pmkid[PMKID_HASH(entry->pmkid)] = - pos->hnext; - } - break; - } - prev = pos; - pos = pos->hnext; - } - - pos = hapd->pmksa; - prev = NULL; - while (pos) { - if (pos == entry) { - if (prev != NULL) - prev->next = pos->next; - else - hapd->pmksa = pos->next; - break; - } - prev = pos; - pos = pos->next; - } - _pmksa_cache_free_entry(entry); -} - - -static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - time_t now; - - time(&now); - while (hapd->pmksa && hapd->pmksa->expiration <= now) { - struct rsn_pmksa_cache *entry = hapd->pmksa; - hapd->pmksa = entry->next; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RSN: expired PMKSA cache entry for " - MACSTR, MAC2STR(entry->spa)); - pmksa_cache_free_entry(hapd, entry); - } - - pmksa_cache_set_expiration(hapd); -} - - -static void pmksa_cache_set_expiration(struct hostapd_data *hapd) -{ - int sec; - eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); - if (hapd->pmksa == NULL) - return; - sec = hapd->pmksa->expiration - time(NULL); - if (sec < 0) - sec = 0; - eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, hapd, NULL); -} - - -static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache *entry, - struct eapol_state_machine *eapol) -{ - if (eapol == NULL) - return; - - if (eapol->identity) { - entry->identity = malloc(eapol->identity_len); - if (entry->identity) { - entry->identity_len = eapol->identity_len; - memcpy(entry->identity, eapol->identity, - eapol->identity_len); - } - } - - ieee802_1x_copy_radius_class(&entry->radius_class, - &eapol->radius_class); -} - - -static void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache *entry, - struct eapol_state_machine *eapol) -{ - if (entry == NULL || eapol == NULL) - return; - - if (entry->identity) { - free(eapol->identity); - eapol->identity = malloc(entry->identity_len); - if (eapol->identity) { - eapol->identity_len = entry->identity_len; - memcpy(eapol->identity, entry->identity, - entry->identity_len); - } - wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", - eapol->identity, eapol->identity_len); - } - - ieee802_1x_free_radius_class(&eapol->radius_class); - ieee802_1x_copy_radius_class(&eapol->radius_class, - &entry->radius_class); - if (eapol->radius_class.attr) { - wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " - "PMKSA", (unsigned long) eapol->radius_class.count); - } -} - - -void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, - int session_timeout) -{ - struct rsn_pmksa_cache *entry, *pos, *prev; - - if (sta->wpa != WPA_VERSION_WPA2) - return; - - entry = malloc(sizeof(*entry)); - if (entry == NULL) - return; - memset(entry, 0, sizeof(*entry)); - memcpy(entry->pmk, pmk, PMK_LEN); - rsn_pmkid(pmk, hapd->own_addr, sta->addr, entry->pmkid); - time(&entry->expiration); - if (session_timeout > 0) - entry->expiration += session_timeout; - else - entry->expiration += dot11RSNAConfigPMKLifetime; - entry->akmp = WPA_KEY_MGMT_IEEE8021X; - memcpy(entry->spa, sta->addr, ETH_ALEN); - pmksa_cache_from_eapol_data(entry, sta->eapol_sm); - - /* Replace an old entry for the same STA (if found) with the new entry - */ - pos = pmksa_cache_get(hapd, sta->addr, NULL); - if (pos) - pmksa_cache_free_entry(hapd, pos); - - if (hapd->pmksa_count >= pmksa_cache_max_entries && hapd->pmksa) { - /* Remove the oldest entry to make room for the new entry */ - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RSN: removed the oldest PMKSA cache entry (for " - MACSTR ") to make room for new one", - MAC2STR(hapd->pmksa->spa)); - pmksa_cache_free_entry(hapd, hapd->pmksa); - } - - /* Add the new entry; order by expiration time */ - pos = hapd->pmksa; - prev = NULL; - while (pos) { - if (pos->expiration > entry->expiration) - break; - prev = pos; - pos = pos->next; - } - if (prev == NULL) { - entry->next = hapd->pmksa; - hapd->pmksa = entry; - } else { - entry->next = prev->next; - prev->next = entry; - } - entry->hnext = hapd->pmkid[PMKID_HASH(entry->pmkid)]; - hapd->pmkid[PMKID_HASH(entry->pmkid)] = entry; - - hapd->pmksa_count++; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "added PMKSA cache entry"); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("RSN: added PMKID", entry->pmkid, PMKID_LEN); - } -} - - -static void pmksa_cache_free(struct hostapd_data *hapd) -{ - struct rsn_pmksa_cache *entry, *prev; - int i; - struct sta_info *sta; - - entry = hapd->pmksa; - hapd->pmksa = NULL; - while (entry) { - prev = entry; - entry = entry->next; - _pmksa_cache_free_entry(prev); - } - eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); - for (i = 0; i < PMKID_HASH_SIZE; i++) - hapd->pmkid[i] = NULL; - for (sta = hapd->sta_list; sta; sta = sta->next) - sta->pmksa = NULL; -} - - -static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, - u8 *spa, u8 *pmkid) -{ - struct rsn_pmksa_cache *entry; - - if (pmkid) - entry = hapd->pmkid[PMKID_HASH(pmkid)]; - else - entry = hapd->pmksa; - while (entry) { - if ((spa == NULL || memcmp(entry->spa, spa, ETH_ALEN) == 0) && - (pmkid == NULL || - memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) - return entry; - entry = pmkid ? entry->hnext : entry->next; - } - return NULL; -} - - -struct wpa_ie_data { - int pairwise_cipher; - int group_cipher; - int key_mgmt; - int capabilities; - size_t num_pmkid; - u8 *pmkid; -}; - - -static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, - struct wpa_ie_data *data) -{ - struct wpa_ie_hdr *hdr; - u8 *pos; - int left; - int i, count; - - memset(data, 0, sizeof(*data)); - data->pairwise_cipher = WPA_CIPHER_TKIP; - data->group_cipher = WPA_CIPHER_TKIP; - data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - - if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) - return -1; - - hdr = (struct wpa_ie_hdr *) wpa_ie; - - if (hdr->elem_id != WLAN_EID_GENERIC || - hdr->len != wpa_ie_len - 2 || - memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 || - le_to_host16(hdr->version) != WPA_VERSION) { - return -2; - } - - pos = (u8 *) (hdr + 1); - left = wpa_ie_len - sizeof(*hdr); - - if (left >= WPA_SELECTOR_LEN) { - data->group_cipher = wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } else if (left > 0) - return -3; - - if (left >= 2) { - data->pairwise_cipher = 0; - count = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) - return -4; - for (i = 0; i < count; i++) { - data->pairwise_cipher |= wpa_selector_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) - return -5; - - if (left >= 2) { - data->key_mgmt = 0; - count = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) - return -6; - for (i = 0; i < count; i++) { - data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); - pos += WPA_SELECTOR_LEN; - left -= WPA_SELECTOR_LEN; - } - } else if (left == 1) - return -7; - - if (left >= 2) { - data->capabilities = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - } - - if (left > 0) { - return -8; - } - - return 0; -} - - -static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, - struct wpa_ie_data *data) -{ - struct rsn_ie_hdr *hdr; - u8 *pos; - int left; - int i, count; - - memset(data, 0, sizeof(*data)); - data->pairwise_cipher = WPA_CIPHER_CCMP; - data->group_cipher = WPA_CIPHER_CCMP; - data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; - - if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) - return -1; - - hdr = (struct rsn_ie_hdr *) rsn_ie; - - if (hdr->elem_id != WLAN_EID_RSN || - hdr->len != rsn_ie_len - 2 || - le_to_host16(hdr->version) != RSN_VERSION) { - return -2; - } - - pos = (u8 *) (hdr + 1); - left = rsn_ie_len - sizeof(*hdr); - - if (left >= RSN_SELECTOR_LEN) { - data->group_cipher = rsn_selector_to_bitfield(pos); - pos += RSN_SELECTOR_LEN; - left -= RSN_SELECTOR_LEN; - } else if (left > 0) - return -3; - - if (left >= 2) { - data->pairwise_cipher = 0; - count = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - if (count == 0 || left < count * RSN_SELECTOR_LEN) - return -4; - for (i = 0; i < count; i++) { - data->pairwise_cipher |= rsn_selector_to_bitfield(pos); - pos += RSN_SELECTOR_LEN; - left -= RSN_SELECTOR_LEN; - } - } else if (left == 1) - return -5; - - if (left >= 2) { - data->key_mgmt = 0; - count = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - if (count == 0 || left < count * RSN_SELECTOR_LEN) - return -6; - for (i = 0; i < count; i++) { - data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); - pos += RSN_SELECTOR_LEN; - left -= RSN_SELECTOR_LEN; - } - } else if (left == 1) - return -7; - - if (left >= 2) { - data->capabilities = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - } - - if (left >= 2) { - data->num_pmkid = pos[0] | (pos[1] << 8); - pos += 2; - left -= 2; - if (left < data->num_pmkid * PMKID_LEN) { - printf("RSN: too short RSN IE for PMKIDs " - "(num=%lu, left=%d)\n", - (unsigned long) data->num_pmkid, left); - return -9; - } - data->pmkid = pos; - pos += data->num_pmkid * PMKID_LEN; - left -= data->num_pmkid * PMKID_LEN; - } - - if (left > 0) { - return -8; - } - - return 0; -} - - -int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *wpa_ie, size_t wpa_ie_len, int version) -{ - struct wpa_ie_data data; - int ciphers, key_mgmt, res, i; - const u8 *selector; - - if (version == HOSTAPD_WPA_VERSION_WPA2) { - res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); - - selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; - if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) - selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; - else if (data.key_mgmt & WPA_KEY_MGMT_PSK) - selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; - memcpy(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected, - selector, RSN_SELECTOR_LEN); - - selector = RSN_CIPHER_SUITE_CCMP; - if (data.pairwise_cipher & WPA_CIPHER_CCMP) - selector = RSN_CIPHER_SUITE_CCMP; - else if (data.pairwise_cipher & WPA_CIPHER_TKIP) - selector = RSN_CIPHER_SUITE_TKIP; - else if (data.pairwise_cipher & WPA_CIPHER_WEP104) - selector = RSN_CIPHER_SUITE_WEP104; - else if (data.pairwise_cipher & WPA_CIPHER_WEP40) - selector = RSN_CIPHER_SUITE_WEP40; - else if (data.pairwise_cipher & WPA_CIPHER_NONE) - selector = RSN_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, - selector, RSN_SELECTOR_LEN); - - selector = RSN_CIPHER_SUITE_CCMP; - if (data.group_cipher & WPA_CIPHER_CCMP) - selector = RSN_CIPHER_SUITE_CCMP; - else if (data.group_cipher & WPA_CIPHER_TKIP) - selector = RSN_CIPHER_SUITE_TKIP; - else if (data.group_cipher & WPA_CIPHER_WEP104) - selector = RSN_CIPHER_SUITE_WEP104; - else if (data.group_cipher & WPA_CIPHER_WEP40) - selector = RSN_CIPHER_SUITE_WEP40; - else if (data.group_cipher & WPA_CIPHER_NONE) - selector = RSN_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, - selector, RSN_SELECTOR_LEN); - } else { - res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); - - selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; - if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) - selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; - else if (data.key_mgmt & WPA_KEY_MGMT_PSK) - selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; - memcpy(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected, - selector, WPA_SELECTOR_LEN); - - selector = WPA_CIPHER_SUITE_TKIP; - if (data.pairwise_cipher & WPA_CIPHER_CCMP) - selector = WPA_CIPHER_SUITE_CCMP; - else if (data.pairwise_cipher & WPA_CIPHER_TKIP) - selector = WPA_CIPHER_SUITE_TKIP; - else if (data.pairwise_cipher & WPA_CIPHER_WEP104) - selector = WPA_CIPHER_SUITE_WEP104; - else if (data.pairwise_cipher & WPA_CIPHER_WEP40) - selector = WPA_CIPHER_SUITE_WEP40; - else if (data.pairwise_cipher & WPA_CIPHER_NONE) - selector = WPA_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, - selector, WPA_SELECTOR_LEN); - - selector = WPA_CIPHER_SUITE_TKIP; - if (data.group_cipher & WPA_CIPHER_CCMP) - selector = WPA_CIPHER_SUITE_CCMP; - else if (data.group_cipher & WPA_CIPHER_TKIP) - selector = WPA_CIPHER_SUITE_TKIP; - else if (data.group_cipher & WPA_CIPHER_WEP104) - selector = WPA_CIPHER_SUITE_WEP104; - else if (data.group_cipher & WPA_CIPHER_WEP40) - selector = WPA_CIPHER_SUITE_WEP40; - else if (data.group_cipher & WPA_CIPHER_NONE) - selector = WPA_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, - selector, WPA_SELECTOR_LEN); - } - if (res) { - printf("Failed to parse WPA/RSN IE from " MACSTR " (res=%d)\n", - MAC2STR(sta->addr), res); - hostapd_hexdump("WPA/RSN IE", wpa_ie, wpa_ie_len); - return WPA_INVALID_IE; - } - - if (data.group_cipher != hapd->conf->wpa_group) { - printf("Invalid WPA group cipher (0x%x) from " MACSTR "\n", - data.group_cipher, MAC2STR(sta->addr)); - return WPA_INVALID_GROUP; - } - - key_mgmt = data.key_mgmt & hapd->conf->wpa_key_mgmt; - if (!key_mgmt) { - printf("Invalid WPA key mgmt (0x%x) from " MACSTR "\n", - data.key_mgmt, MAC2STR(sta->addr)); - return WPA_INVALID_AKMP; - } - if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) - sta->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; - else - sta->wpa_key_mgmt = WPA_KEY_MGMT_PSK; - - ciphers = data.pairwise_cipher & hapd->conf->wpa_pairwise; - if (!ciphers) { - printf("Invalid WPA pairwise cipher (0x%x) from " MACSTR "\n", - data.pairwise_cipher, MAC2STR(sta->addr)); - return WPA_INVALID_PAIRWISE; - } - - if (ciphers & WPA_CIPHER_CCMP) - sta->pairwise = WPA_CIPHER_CCMP; - else - sta->pairwise = WPA_CIPHER_TKIP; - - /* TODO: clear WPA/WPA2 state if STA changes from one to another */ - if (wpa_ie[0] == WLAN_EID_RSN) - sta->wpa = WPA_VERSION_WPA2; - else - sta->wpa = WPA_VERSION_WPA; - - for (i = 0; i < data.num_pmkid; i++) { - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("RSN IE: STA PMKID", - &data.pmkid[i * PMKID_LEN], PMKID_LEN); - } - sta->pmksa = pmksa_cache_get(hapd, sta->addr, - &data.pmkid[i * PMKID_LEN]); - if (sta->pmksa) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "PMKID found from PMKSA cache"); - if (hapd->wpa_auth) { - memcpy(hapd->wpa_auth->dot11RSNAPMKIDUsed, - sta->pmksa->pmkid, PMKID_LEN); - } - break; - } - } - - return WPA_IE_OK; -} - - -void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta) -{ - struct wpa_state_machine *sm; - - if (!hapd->conf->wpa) - return; - - if (sta->wpa_sm) { - sm = sta->wpa_sm; - memset(sm->key_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); - sm->ReAuthenticationRequest = TRUE; - wpa_sm_step(sm); - return; - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "start authentication"); - sm = malloc(sizeof(struct wpa_state_machine)); - if (sm == NULL) - return; - memset(sm, 0, sizeof(struct wpa_state_machine)); - - sm->hapd = hapd; - sm->sta = sta; - sta->wpa_sm = sm; - - sm->Init = TRUE; - wpa_sm_step(sm); - sm->Init = FALSE; - sm->AuthenticationRequest = TRUE; - wpa_sm_step(sm); -} - - -void wpa_free_station(struct sta_info *sta) -{ - struct wpa_state_machine *sm = sta->wpa_sm; - - if (sm == NULL) - return; - - if (sm->hapd->conf->wpa_strict_rekey && sm->has_GTK) { - hostapd_logger(sm->hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "strict rekeying - force " - "GTK rekey since STA is leaving"); - eloop_cancel_timeout(wpa_rekey_gtk, sm->hapd, NULL); - eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->hapd, - NULL); - } - - eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sta); - eloop_cancel_timeout(wpa_sm_call_step, sm->hapd, sta->wpa_sm); - eloop_cancel_timeout(rsn_preauth_finished_cb, sm->hapd, sta); - free(sm->last_rx_eapol_key); - free(sm); - sta->wpa_sm = NULL; -} - - -static void wpa_request_new_ptk(struct hostapd_data *hapd, - struct sta_info *sta) -{ - struct wpa_state_machine *sm = sta->wpa_sm; - - if (sm == NULL) - return; - - sm->PTKRequest = TRUE; - sm->PTK_valid = 0; -} - - -void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, - u8 *data, size_t data_len) -{ - struct wpa_state_machine *sm = sta->wpa_sm; - struct ieee802_1x_hdr *hdr; - struct wpa_eapol_key *key; - u16 key_info, key_data_length; - enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg; - char *msgtxt; - - if (!hapd->conf->wpa) - return; - - if (sm == NULL) - return; - - if (data_len < sizeof(*hdr) + sizeof(*key)) - return; - - hdr = (struct ieee802_1x_hdr *) data; - key = (struct wpa_eapol_key *) (hdr + 1); - key_info = ntohs(key->key_info); - key_data_length = ntohs(key->key_data_length); - if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { - wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " - "key_data overflow (%d > %lu)", - key_data_length, - (unsigned long) (data_len - sizeof(*hdr) - - sizeof(*key))); - return; - } - - /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys - * are set */ - - if (key_info & WPA_KEY_INFO_REQUEST) { - msg = REQUEST; - msgtxt = "Request"; - } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { - msg = GROUP_2; - msgtxt = "2/2 Group"; - } else if (key_data_length == 0) { - msg = PAIRWISE_4; - msgtxt = "4/4 Pairwise"; - } else { - msg = PAIRWISE_2; - msgtxt = "2/4 Pairwise"; - } - - if (key_info & WPA_KEY_INFO_REQUEST) { - if (sta->req_replay_counter_used && - memcmp(key->replay_counter, sta->req_replay_counter, - WPA_REPLAY_COUNTER_LEN) <= 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_WARNING, - "received EAPOL-Key request with " - "replayed counter"); - return; - } - } - - if (!(key_info & WPA_KEY_INFO_REQUEST) && - (!sm->key_replay_counter_valid || - memcmp(key->replay_counter, sm->key_replay_counter, - WPA_REPLAY_COUNTER_LEN) != 0)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key %s with unexpected replay " - "counter", msgtxt); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("expected replay counter", - sm->key_replay_counter, - WPA_REPLAY_COUNTER_LEN); - hostapd_hexdump("received replay counter", - key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - } - return; - } - - switch (msg) { - case PAIRWISE_2: - if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && - sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 2/4 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_state); - return; - } - if (sta->wpa_ie == NULL || - sta->wpa_ie_len != key_data_length || - memcmp(sta->wpa_ie, key + 1, key_data_length) != 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "WPA IE from (Re)AssocReq did not match" - " with msg 2/4"); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - if (sta->wpa_ie) { - hostapd_hexdump("WPA IE in AssocReq", - sta->wpa_ie, - sta->wpa_ie_len); - } - hostapd_hexdump("WPA IE in msg 2/4", - (u8 *) (key + 1), - key_data_length); - } - /* MLME-DEAUTHENTICATE.request */ - wpa_sta_disconnect(hapd, sta); - return; - } - break; - case PAIRWISE_4: - if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || - !sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 4/4 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_state); - return; - } - break; - case GROUP_2: - if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING - || !sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 2/2 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_group_state); - return; - } - break; - case REQUEST: - break; - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "received EAPOL-Key frame (%s)", - msgtxt); - - if (key_info & WPA_KEY_INFO_ACK) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received invalid EAPOL-Key: Key Ack set"); - return; - } - - if (!(key_info & WPA_KEY_INFO_MIC)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received invalid EAPOL-Key: Key MIC not set"); - return; - } - - sm->MICVerified = FALSE; - if (sm->PTK_valid) { - if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key with invalid MIC"); - return; - } - sm->MICVerified = TRUE; - eloop_cancel_timeout(wpa_send_eapol_timeout, sta->wpa_sm->hapd, - sta); - } - - if (key_info & WPA_KEY_INFO_REQUEST) { - if (sm->MICVerified) { - sta->req_replay_counter_used = 1; - memcpy(sta->req_replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - } else { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key request with " - "invalid MIC"); - return; - } - - if (key_info & WPA_KEY_INFO_ERROR) { - /* Supplicant reported a Michael MIC error */ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Error Request " - "(STA detected Michael MIC failure)"); - ieee80211_michael_mic_failure(hapd, sta->addr, 0); - sta->dot11RSNAStatsTKIPRemoteMICFailures++; - hapd->wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; - /* Error report is not a request for a new key - * handshake, but since Authenticator may do it, let's - * change the keys now anyway. */ - wpa_request_new_ptk(hapd, sta); - } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Request for new " - "4-Way Handshake"); - wpa_request_new_ptk(hapd, sta); - } else { - /* TODO: this could also be a request for STAKey - * if Key Data fields contains peer MAC address KDE. - * STAKey request should have 0xdd 00-0F-AC:2 in - * the beginning of Key Data */ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Request for GTK " - "rekeying"); - wpa_request_new_ptk(hapd, sta); - eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); - wpa_rekey_gtk(hapd, NULL); - } - } else { - /* Do not allow the same key replay counter to be reused. */ - sm->key_replay_counter_valid = FALSE; - } - - free(sm->last_rx_eapol_key); - sm->last_rx_eapol_key = malloc(data_len); - if (sm->last_rx_eapol_key == NULL) - return; - memcpy(sm->last_rx_eapol_key, data, data_len); - sm->last_rx_eapol_key_len = data_len; - - sm->EAPOLKeyReceived = TRUE; - sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); - sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); - memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); - wpa_sm_step(sm); -} - - -static void wpa_pmk_to_ptk(struct hostapd_data *hapd, const u8 *pmk, - const u8 *addr1, const u8 *addr2, - const u8 *nonce1, const u8 *nonce2, - u8 *ptk, size_t ptk_len) -{ - u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; - - /* PTK = PRF-X(PMK, "Pairwise key expansion", - * Min(AA, SA) || Max(AA, SA) || - * Min(ANonce, SNonce) || Max(ANonce, SNonce)) */ - - if (memcmp(addr1, addr2, ETH_ALEN) < 0) { - memcpy(data, addr1, ETH_ALEN); - memcpy(data + ETH_ALEN, addr2, ETH_ALEN); - } else { - memcpy(data, addr2, ETH_ALEN); - memcpy(data + ETH_ALEN, addr1, ETH_ALEN); - } - - if (memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) { - memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN); - memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2, - WPA_NONCE_LEN); - } else { - memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN); - memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1, - WPA_NONCE_LEN); - } - - sha1_prf(pmk, WPA_PMK_LEN, "Pairwise key expansion", - data, sizeof(data), ptk, ptk_len); - - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("PMK", pmk, WPA_PMK_LEN); - hostapd_hexdump("PTK", ptk, ptk_len); - } -} - - -static void wpa_gmk_to_gtk(struct hostapd_data *hapd, u8 *gmk, - u8 *addr, u8 *gnonce, u8 *gtk, size_t gtk_len) -{ - u8 data[ETH_ALEN + WPA_NONCE_LEN]; - - /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ - memcpy(data, addr, ETH_ALEN); - memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); - - sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", - data, sizeof(data), gtk, gtk_len); - - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("GMK", gmk, WPA_GMK_LEN); - hostapd_hexdump("GTK", gtk, gtk_len); - } -} - - -static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - - if (!sta->wpa_sm || !(sta->flags & WLAN_STA_ASSOC)) - return; - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "EAPOL-Key timeout"); - sta->wpa_sm->TimeoutEvt = TRUE; - wpa_sm_step(sta->wpa_sm); -} - - -static int wpa_calc_eapol_key_mic(int ver, u8 *key, u8 *data, size_t len, - u8 *mic) -{ - u8 hash[SHA1_MAC_LEN]; - - switch (ver) { - case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: - hmac_md5(key, 16, data, len, mic); - break; - case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: - hmac_sha1(key, 16, data, len, hash); - memcpy(mic, hash, MD5_MAC_LEN); - break; - default: - return -1; - } - return 0; -} - - -static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, - int secure, int mic, int ack, int install, - int pairwise, u8 *key_rsc, u8 *nonce, - u8 *ie, size_t ie_len, u8 *gtk, size_t gtk_len, - int keyidx) -{ - struct wpa_state_machine *sm = sta->wpa_sm; - struct ieee802_1x_hdr *hdr; - struct wpa_eapol_key *key; - size_t len; - int key_info, alg; - int timeout_ms; - int key_data_len, pad_len = 0; - u8 *buf, *pos; - - if (sm == NULL) - return; - - len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); - - if (sta->wpa == WPA_VERSION_WPA2) { - key_data_len = ie_len + gtk_len; - if (gtk_len) - key_data_len += 2 + RSN_SELECTOR_LEN + 2; - } else { - if (pairwise) { - /* WPA does not include GTK in 4-Way Handshake */ - gtk = NULL; - gtk_len = 0; - - /* key_rsc is for group key, so mask it out in case of - * WPA Pairwise key negotiation. */ - key_rsc = NULL; - } - key_data_len = ie_len + gtk_len; - } - - if (sta->pairwise == WPA_CIPHER_CCMP) { - key_info = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - if (gtk) { - pad_len = key_data_len % 8; - if (pad_len) - pad_len = 8 - pad_len; - key_data_len += pad_len + 8; - } - } else { - key_info = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - } - - len += key_data_len; - - hdr = malloc(len); - if (hdr == NULL) - return; - memset(hdr, 0, len); - hdr->version = hapd->conf->eapol_version; - hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; - hdr->length = htons(len - sizeof(*hdr)); - key = (struct wpa_eapol_key *) (hdr + 1); - - key->type = sta->wpa == WPA_VERSION_WPA2 ? - EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - if (secure) - key_info |= WPA_KEY_INFO_SECURE; - if (mic) - key_info |= WPA_KEY_INFO_MIC; - if (ack) - key_info |= WPA_KEY_INFO_ACK; - if (install) - key_info |= WPA_KEY_INFO_INSTALL; - if (pairwise) - key_info |= WPA_KEY_INFO_KEY_TYPE; - if (gtk && sta->wpa == WPA_VERSION_WPA2) - key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; - if (sta->wpa != WPA_VERSION_WPA2) { - if (pairwise) - keyidx = 0; - key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; - } - key->key_info = htons(key_info); - - alg = pairwise ? sta->pairwise : hapd->conf->wpa_group; - switch (alg) { - case WPA_CIPHER_CCMP: - key->key_length = htons(16); - break; - case WPA_CIPHER_TKIP: - key->key_length = htons(32); - break; - case WPA_CIPHER_WEP40: - key->key_length = htons(5); - break; - case WPA_CIPHER_WEP104: - key->key_length = htons(13); - break; - } - - inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); - memcpy(key->replay_counter, sm->key_replay_counter, - WPA_REPLAY_COUNTER_LEN); - sm->key_replay_counter_valid = TRUE; - - if (nonce) - memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); - - if (key_rsc) - memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); - - if (ie && !gtk) { - memcpy(key + 1, ie, ie_len); - key->key_data_length = htons(ie_len); - } else if (gtk) { - buf = malloc(key_data_len); - if (buf == NULL) { - free(hdr); - return; - } - memset(buf, 0, key_data_len); - pos = buf; - if (ie) { - memcpy(pos, ie, ie_len); - pos += ie_len; - } - if (sta->wpa == WPA_VERSION_WPA2) { - *pos++ = WLAN_EID_GENERIC; - *pos++ = RSN_SELECTOR_LEN + 2 + gtk_len; - memcpy(pos, RSN_KEY_DATA_GROUPKEY, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - *pos++ = keyidx & 0x03; - *pos++ = 0; - } - memcpy(pos, gtk, gtk_len); - pos += gtk_len; - if (pad_len) - *pos++ = 0xdd; - - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("Plaintext EAPOL-Key Key Data", - buf, key_data_len); - } - if (key_info & WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { - aes_wrap(sm->PTK.encr_key, (key_data_len - 8) / 8, buf, - (u8 *) (key + 1)); - key->key_data_length = htons(key_data_len); - } else { - u8 ek[32]; - memcpy(key->key_iv, - hapd->wpa_auth->Counter + WPA_NONCE_LEN - 16, - 16); - inc_byte_array(hapd->wpa_auth->Counter, WPA_NONCE_LEN); - memcpy(ek, key->key_iv, 16); - memcpy(ek + 16, sm->PTK.encr_key, 16); - memcpy(key + 1, buf, key_data_len); - rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); - key->key_data_length = htons(key_data_len); - } - free(buf); - } - - if (mic) { - if (!sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "PTK not valid " - "when sending EAPOL-Key frame"); - free(hdr); - return; - } - wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK, - sm->PTK.mic_key, (u8 *) hdr, len, - key->key_mic); - } - - if (sta->eapol_sm) - sta->eapol_sm->dot1xAuthEapolFramesTx++; - hostapd_send_eapol(hapd, sta->addr, (u8 *) hdr, len, sm->pairwise_set); - free(hdr); - - timeout_ms = pairwise ? dot11RSNAConfigPairwiseUpdateTimeOut : - dot11RSNAConfigGroupUpdateTimeOut; - eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, - wpa_send_eapol_timeout, hapd, sta); -} - - -static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) -{ - struct ieee802_1x_hdr *hdr; - struct wpa_eapol_key *key; - u16 key_info; - int type, ret = 0; - u8 mic[16]; - - if (data_len < sizeof(*hdr) + sizeof(*key)) - return -1; - - hdr = (struct ieee802_1x_hdr *) data; - key = (struct wpa_eapol_key *) (hdr + 1); - key_info = ntohs(key->key_info); - type = key_info & WPA_KEY_INFO_TYPE_MASK; - memcpy(mic, key->key_mic, 16); - memset(key->key_mic, 0, 16); - if (wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK, - PTK->mic_key, data, data_len, key->key_mic) - || memcmp(mic, key->key_mic, 16) != 0) - ret = -1; - memcpy(key->key_mic, mic, 16); - return ret; -} - - -void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, - wpa_event event) -{ - struct wpa_state_machine *sm = sta->wpa_sm; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "event %d notification", event); - if (sm == NULL) - return; - - switch (event) { - case WPA_AUTH: - case WPA_ASSOC: - break; - case WPA_DEAUTH: - case WPA_DISASSOC: - sm->DeauthenticationRequest = TRUE; - break; - case WPA_REAUTH: - case WPA_REAUTH_EAPOL: - sm->ReAuthenticationRequest = TRUE; - break; - } - - sm->PTK_valid = FALSE; - memset(&sm->PTK, 0, sizeof(sm->PTK)); - - if (event != WPA_REAUTH_EAPOL) { - sm->pairwise_set = FALSE; - hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, - (u8 *) "", 0); - } - - wpa_sm_step(sm); -} - - -static const char * wpa_alg_txt(int alg) -{ - switch (alg) { - case WPA_CIPHER_CCMP: - return "CCMP"; - case WPA_CIPHER_TKIP: - return "TKIP"; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return "WEP"; - default: - return ""; - } -} - - -/* Definitions for clarifying state machine implementation */ -#define SM_STATE(machine, state) \ -static void sm_ ## machine ## _ ## state ## _Enter(struct wpa_state_machine \ -*sm) - -#define SM_ENTRY(machine, _state, _data) \ -sm->changed = TRUE; \ -sm->_data ## _ ## state = machine ## _ ## _state; \ -if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \ - printf("WPA: " MACSTR " " #machine " entering state " #_state \ - "\n", MAC2STR(sm->sta->addr)); - -#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm) - -#define SM_STEP(machine) \ -static void sm_ ## machine ## _Step(struct wpa_state_machine *sm) - -#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) - - -SM_STATE(WPA_PTK, INITIALIZE) -{ - struct hostapd_data *hapd = sm->hapd; - - SM_ENTRY(WPA_PTK, INITIALIZE, wpa_ptk); - if (sm->Init) { - /* Init flag is not cleared here, so avoid busy - * loop by claiming nothing changed. */ - sm->changed = FALSE; - } - - sm->keycount = 0; - if (sm->GUpdateStationKeys) - hapd->wpa_auth->GKeyDoneStations--; - sm->GUpdateStationKeys = FALSE; - if (sm->sta->wpa == WPA_VERSION_WPA) - sm->PInitAKeys = FALSE; - if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and - * Local AA > Remote AA)) */) { - sm->Pair = TRUE; - } - ieee802_1x_notify_port_enabled(sm->sta->eapol_sm, 0); - hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, (u8 *) "", - 0); - sm->pairwise_set = FALSE; - sm->PTK_valid = FALSE; - memset(&sm->PTK, 0, sizeof(sm->PTK)); - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 0); - sm->TimeoutCtr = 0; - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0); -} - - -SM_STATE(WPA_PTK, DISCONNECT) -{ - SM_ENTRY(WPA_PTK, DISCONNECT, wpa_ptk); - sm->Disconnect = FALSE; - wpa_sta_disconnect(sm->hapd, sm->sta); -} - - -SM_STATE(WPA_PTK, DISCONNECTED) -{ - SM_ENTRY(WPA_PTK, DISCONNECTED, wpa_ptk); - sm->hapd->wpa_auth->GNoStations--; - sm->DeauthenticationRequest = FALSE; -} - - -SM_STATE(WPA_PTK, AUTHENTICATION) -{ - SM_ENTRY(WPA_PTK, AUTHENTICATION, wpa_ptk); - sm->hapd->wpa_auth->GNoStations++; - memset(&sm->PTK, 0, sizeof(sm->PTK)); - sm->PTK_valid = FALSE; - if (sm->sta->eapol_sm) { - sm->sta->eapol_sm->portControl = Auto; - sm->sta->eapol_sm->portEnabled = TRUE; - } - sm->AuthenticationRequest = FALSE; -} - - -SM_STATE(WPA_PTK, AUTHENTICATION2) -{ - SM_ENTRY(WPA_PTK, AUTHENTICATION2, wpa_ptk); - memcpy(sm->ANonce, sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN); - sm->ReAuthenticationRequest = FALSE; - /* IEEE 802.11i/D9.0 does not clear TimeoutCtr here, but this is more - * logical place than INITIALIZE since AUTHENTICATION2 can be - * re-entered on ReAuthenticationRequest without going through - * INITIALIZE. */ - sm->TimeoutCtr = 0; -} - - -SM_STATE(WPA_PTK, INITPMK) -{ - u8 *key; - size_t len; - SM_ENTRY(WPA_PTK, INITPMK, wpa_ptk); - if (sm->sta->pmksa) { - wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); - memcpy(sm->PMK, sm->sta->pmksa->pmk, WPA_PMK_LEN); - pmksa_cache_to_eapol_data(sm->sta->pmksa, sm->sta->eapol_sm); - } else if ((key = ieee802_1x_get_key_crypt(sm->sta->eapol_sm, &len))) { - wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " - "(len=%lu)", (unsigned long) len); - if (len > WPA_PMK_LEN) - len = WPA_PMK_LEN; - memcpy(sm->PMK, key, len); - } else { - wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); - } - sm->sta->req_replay_counter_used = 0; - /* IEEE 802.11i/D9.0 does not set keyRun to FALSE, but not doing this - * will break reauthentication since EAPOL state machines may not be - * get into AUTHENTICATING state that clears keyRun before WPA state - * machine enters AUTHENTICATION2 state and goes immediately to INITPMK - * state and takes PMK from the previously used AAA Key. This will - * eventually fail in 4-Way Handshake because Supplicant uses PMK - * derived from the new AAA Key. Setting keyRun = FALSE here seems to - * be good workaround for this issue. */ - if (sm->sta->eapol_sm) - sm->sta->eapol_sm->keyRun = FALSE; -} - - -SM_STATE(WPA_PTK, INITPSK) -{ - const u8 *psk; - SM_ENTRY(WPA_PTK, INITPSK, wpa_ptk); - psk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL); - if (psk) - memcpy(sm->PMK, psk, WPA_PMK_LEN); - sm->sta->req_replay_counter_used = 0; -} - - -SM_STATE(WPA_PTK, PTKSTART) -{ - u8 *pmkid = NULL; - size_t pmkid_len = 0; - - SM_ENTRY(WPA_PTK, PTKSTART, wpa_ptk); - sm->PTKRequest = FALSE; - sm->TimeoutEvt = FALSE; - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 1/4 msg of 4-Way Handshake"); - if (sm->sta->pmksa && - (pmkid = malloc(2 + RSN_SELECTOR_LEN + PMKID_LEN))) { - pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; - pmkid[0] = WLAN_EID_GENERIC; - pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; - memcpy(&pmkid[2], RSN_KEY_DATA_PMKID, RSN_SELECTOR_LEN); - memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->sta->pmksa->pmkid, - PMKID_LEN); - } - wpa_send_eapol(sm->hapd, sm->sta, 0, 0, 1, 0, 1, NULL, sm->ANonce, - pmkid, pmkid_len, NULL, 0, 0); - free(pmkid); - sm->TimeoutCtr++; -} - - -SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) -{ - struct wpa_ptk PTK; - int ok = 0; - const u8 *pmk = NULL; - - SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); - sm->EAPOLKeyReceived = FALSE; - - /* WPA with IEEE 802.1X: use the derived PMK from EAP - * WPA-PSK: iterate through possible PSKs and select the one matching - * the packet */ - for (;;) { - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { - pmk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, - pmk); - if (pmk == NULL) - break; - } else - pmk = sm->PMK; - - wpa_pmk_to_ptk(sm->hapd, pmk, sm->hapd->own_addr, - sm->sta->addr, sm->ANonce, sm->SNonce, - (u8 *) &PTK, sizeof(PTK)); - - if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, - sm->last_rx_eapol_key_len) == 0) { - ok = 1; - break; - } - - if (sm->sta->wpa_key_mgmt != WPA_KEY_MGMT_PSK) - break; - } - - if (!ok) { - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "invalid MIC in msg 2/4 " - "of 4-Way Handshake"); - return; - } - - eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sm->sta); - - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { - /* PSK may have changed from the previous choice, so update - * state machine data based on whatever PSK was selected here. - */ - memcpy(sm->PMK, pmk, WPA_PMK_LEN); - } - - sm->MICVerified = TRUE; - - memcpy(&sm->PTK, &PTK, sizeof(PTK)); - sm->PTK_valid = TRUE; -} - - -SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) -{ - SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); - sm->TimeoutCtr = 0; -} - - -SM_STATE(WPA_PTK, PTKINITNEGOTIATING) -{ - u8 rsc[WPA_KEY_RSC_LEN]; - struct wpa_authenticator *gsm = sm->hapd->wpa_auth; - u8 *wpa_ie; - int wpa_ie_len; - - SM_ENTRY(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); - sm->TimeoutEvt = FALSE; - /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN]) - */ - memset(rsc, 0, WPA_KEY_RSC_LEN); - hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc); - wpa_ie = sm->hapd->wpa_ie; - wpa_ie_len = sm->hapd->wpa_ie_len; - if (sm->sta->wpa == WPA_VERSION_WPA && - (sm->hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) && - wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { - /* WPA-only STA, remove RSN IE */ - wpa_ie = wpa_ie + wpa_ie[1] + 2; - wpa_ie_len = wpa_ie[1] + 2; - } - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 3/4 msg of 4-Way Handshake"); - wpa_send_eapol(sm->hapd, sm->sta, - sm->sta->wpa == WPA_VERSION_WPA2 ? 1 : 0, - 1, 1, 1, 1, rsc, sm->ANonce, - wpa_ie, wpa_ie_len, - gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN); - sm->TimeoutCtr++; -} - - -SM_STATE(WPA_PTK, PTKINITDONE) -{ - SM_ENTRY(WPA_PTK, PTKINITDONE, wpa_ptk); - sm->EAPOLKeyReceived = FALSE; - if (sm->Pair) { - char *alg; - int klen; - if (sm->sta->pairwise == WPA_CIPHER_TKIP) { - alg = "TKIP"; - klen = 32; - } else { - alg = "CCMP"; - klen = 16; - } - if (hostapd_set_encryption(sm->hapd, alg, sm->sta->addr, 0, - sm->PTK.tk1, klen)) { - wpa_sta_disconnect(sm->hapd, sm->sta); - return; - } - /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ - sm->pairwise_set = TRUE; - - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1); - } - - if (0 /* IBSS == TRUE */) { - sm->keycount++; - if (sm->keycount == 2) { - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1); - } - } else { - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1); - } - if (sm->sta->eapol_sm) { - sm->sta->eapol_sm->keyAvailable = FALSE; - sm->sta->eapol_sm->keyDone = TRUE; - } - if (sm->sta->wpa == WPA_VERSION_WPA) - sm->PInitAKeys = TRUE; - else - sm->has_GTK = TRUE; - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "pairwise key handshake completed " - "(%s)", - sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - accounting_sta_start(sm->hapd, sm->sta); -} - - -SM_STEP(WPA_PTK) -{ - struct wpa_authenticator *wpa_auth = sm->hapd->wpa_auth; - - if (sm->Init) - SM_ENTER(WPA_PTK, INITIALIZE); - else if (sm->Disconnect - /* || FIX: dot11RSNAConfigSALifetime timeout */) - SM_ENTER(WPA_PTK, DISCONNECT); - else if (sm->DeauthenticationRequest) - SM_ENTER(WPA_PTK, DISCONNECTED); - else if (sm->AuthenticationRequest) - SM_ENTER(WPA_PTK, AUTHENTICATION); - else if (sm->ReAuthenticationRequest) - SM_ENTER(WPA_PTK, AUTHENTICATION2); - else if (sm->PTKRequest) - SM_ENTER(WPA_PTK, PTKSTART); - else switch (sm->wpa_ptk_state) { - case WPA_PTK_INITIALIZE: - break; - case WPA_PTK_DISCONNECT: - SM_ENTER(WPA_PTK, DISCONNECTED); - break; - case WPA_PTK_DISCONNECTED: - SM_ENTER(WPA_PTK, INITIALIZE); - break; - case WPA_PTK_AUTHENTICATION: - SM_ENTER(WPA_PTK, AUTHENTICATION2); - break; - case WPA_PTK_AUTHENTICATION2: - if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X) && - sm->sta->eapol_sm && sm->sta->eapol_sm->keyRun) - SM_ENTER(WPA_PTK, INITPMK); - else if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - /* FIX: && 802.1X::keyRun */) - SM_ENTER(WPA_PTK, INITPSK); - break; - case WPA_PTK_INITPMK: - if (sm->sta->eapol_sm && sm->sta->eapol_sm->keyAvailable) - SM_ENTER(WPA_PTK, PTKSTART); - else { - wpa_auth->dot11RSNA4WayHandshakeFailures++; - SM_ENTER(WPA_PTK, DISCONNECT); - } - break; - case WPA_PTK_INITPSK: - if (hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL)) - SM_ENTER(WPA_PTK, PTKSTART); - else { - hostapd_logger(sm->hapd, sm->sta->addr, - HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "no PSK configured for the STA"); - wpa_auth->dot11RSNA4WayHandshakeFailures++; - SM_ENTER(WPA_PTK, DISCONNECT); - } - break; - case WPA_PTK_PTKSTART: - if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - sm->EAPOLKeyPairwise) - SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); - else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { - wpa_auth->dot11RSNA4WayHandshakeFailures++; - SM_ENTER(WPA_PTK, DISCONNECT); - } else if (sm->TimeoutEvt) - SM_ENTER(WPA_PTK, PTKSTART); - break; - case WPA_PTK_PTKCALCNEGOTIATING: - if (sm->MICVerified) - SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2); - else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - sm->EAPOLKeyPairwise) - SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); - else if (sm->TimeoutEvt) - SM_ENTER(WPA_PTK, PTKSTART); - break; - case WPA_PTK_PTKCALCNEGOTIATING2: - SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); - break; - case WPA_PTK_PTKINITNEGOTIATING: - if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - sm->EAPOLKeyPairwise && sm->MICVerified) - SM_ENTER(WPA_PTK, PTKINITDONE); - else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { - wpa_auth->dot11RSNA4WayHandshakeFailures++; - SM_ENTER(WPA_PTK, DISCONNECT); - } else if (sm->TimeoutEvt) - SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); - break; - case WPA_PTK_PTKINITDONE: - break; - } -} - - -SM_STATE(WPA_PTK_GROUP, IDLE) -{ - SM_ENTRY(WPA_PTK_GROUP, IDLE, wpa_ptk_group); - if (sm->Init) { - /* Init flag is not cleared here, so avoid busy - * loop by claiming nothing changed. */ - sm->changed = FALSE; - } - sm->GTimeoutCtr = 0; -} - - -SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) -{ - u8 rsc[WPA_KEY_RSC_LEN]; - struct wpa_authenticator *gsm = sm->hapd->wpa_auth; - - SM_ENTRY(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); - if (sm->sta->wpa == WPA_VERSION_WPA) - sm->PInitAKeys = FALSE; - sm->TimeoutEvt = FALSE; - /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ - memset(rsc, 0, WPA_KEY_RSC_LEN); - if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) - hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc); - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 1/2 msg of Group Key Handshake"); - wpa_send_eapol(sm->hapd, sm->sta, 1, 1, 1, !sm->Pair, 0, rsc, - gsm->GNonce, NULL, 0, - gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN); - sm->GTimeoutCtr++; -} - - -SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) -{ - SM_ENTRY(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); - sm->EAPOLKeyReceived = FALSE; - sm->GUpdateStationKeys = FALSE; - sm->hapd->wpa_auth->GKeyDoneStations--; - sm->GTimeoutCtr = 0; - /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "group key handshake completed " - "(%s)", - sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); - sm->has_GTK = TRUE; -} - - -SM_STATE(WPA_PTK_GROUP, KEYERROR) -{ - SM_ENTRY(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); - sm->hapd->wpa_auth->GKeyDoneStations--; - sm->GUpdateStationKeys = FALSE; - sm->Disconnect = TRUE; -} - - -SM_STEP(WPA_PTK_GROUP) -{ - if (sm->Init) - SM_ENTER(WPA_PTK_GROUP, IDLE); - else switch (sm->wpa_ptk_group_state) { - case WPA_PTK_GROUP_IDLE: - if (sm->GUpdateStationKeys || - (sm->sta->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) - SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); - break; - case WPA_PTK_GROUP_REKEYNEGOTIATING: - if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - !sm->EAPOLKeyPairwise && sm->MICVerified) - SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); - else if (sm->GTimeoutCtr > dot11RSNAConfigGroupUpdateCount) - SM_ENTER(WPA_PTK_GROUP, KEYERROR); - else if (sm->TimeoutEvt) - SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); - break; - case WPA_PTK_GROUP_KEYERROR: - SM_ENTER(WPA_PTK_GROUP, IDLE); - break; - case WPA_PTK_GROUP_REKEYESTABLISHED: - SM_ENTER(WPA_PTK_GROUP, IDLE); - break; - } -} - - -static void wpa_group_gtk_init(struct hostapd_data *hapd) -{ - struct wpa_authenticator *sm = hapd->wpa_auth; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state GTK_INIT\n"); - sm->changed = FALSE; /* GInit is not cleared here; avoid loop */ - sm->wpa_group_state = WPA_GROUP_GTK_INIT; - - /* GTK[0..N] = 0 */ - memset(sm->GTK, 0, sizeof(sm->GTK)); - sm->GN = 1; - sm->GM = 2; - /* GTK[GN] = CalcGTK() */ - /* FIX: is this the correct way of getting GNonce? */ - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); -} - - -static void wpa_group_setkeys(struct hostapd_data *hapd) -{ - struct wpa_authenticator *sm = hapd->wpa_auth; - struct sta_info *sta; - int tmp; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state SETKEYS\n"); - sm->changed = TRUE; - sm->wpa_group_state = WPA_GROUP_SETKEYS; - sm->GTKReKey = FALSE; - tmp = sm->GM; - sm->GM = sm->GN; - sm->GN = tmp; - sm->GKeyDoneStations = sm->GNoStations; - /* FIX: is this the correct way of getting GNonce? */ - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); - - sta = hapd->sta_list; - while (sta) { - if (sta->wpa_sm) { - sta->wpa_sm->GUpdateStationKeys = TRUE; - wpa_sm_step(sta->wpa_sm); - } - sta = sta->next; - } -} - - -static void wpa_group_setkeysdone(struct hostapd_data *hapd) -{ - struct wpa_authenticator *sm = hapd->wpa_auth; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state SETKEYSDONE\n"); - sm->changed = TRUE; - sm->wpa_group_state = WPA_GROUP_SETKEYSDONE; - hostapd_set_encryption(hapd, wpa_alg_txt(hapd->conf->wpa_group), - NULL, sm->GN, sm->GTK[sm->GN - 1], sm->GTK_len); -} - - -static void wpa_group_sm_step(struct hostapd_data *hapd) -{ - struct wpa_authenticator *sm = hapd->wpa_auth; - - if (sm->GInit) { - wpa_group_gtk_init(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_GTK_INIT && - sm->GTKAuthenticator) { - wpa_group_setkeysdone(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_SETKEYSDONE && - sm->GTKReKey) { - wpa_group_setkeys(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_SETKEYS) { - if (sm->GKeyDoneStations == 0) - wpa_group_setkeysdone(hapd); - else if (sm->GTKReKey) - wpa_group_setkeys(hapd); - } -} - - -static int wpa_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr) -{ - struct sta_info *sta; - sta = ap_get_sta(hapd, addr); - if (sta == NULL || sta->wpa_sm == NULL) - return 0; - return 1; -} - - -static void wpa_sm_step(struct wpa_state_machine *sm) -{ - struct hostapd_data *hapd; - u8 addr[6]; - if (sm == NULL || sm->sta == NULL || sm->sta->wpa_sm == NULL) - return; - hapd = sm->hapd; - - memcpy(addr, sm->sta->addr, 6); - do { - sm->changed = FALSE; - sm->hapd->wpa_auth->changed = FALSE; - - SM_STEP_RUN(WPA_PTK); - if (!wpa_sm_sta_entry_alive(hapd, addr)) - break; - SM_STEP_RUN(WPA_PTK_GROUP); - if (!wpa_sm_sta_entry_alive(hapd, addr)) - break; - wpa_group_sm_step(sm->hapd); - if (!wpa_sm_sta_entry_alive(hapd, addr)) - break; - } while (sm->changed || sm->hapd->wpa_auth->changed); -} - - -static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_state_machine *sm = timeout_ctx; - wpa_sm_step(sm); -} - - -void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta) -{ - if (sta->wpa_sm == NULL) - return; - eloop_register_timeout(0, 0, wpa_sm_call_step, hapd, sta->wpa_sm); -} - - -void wpa_gtk_rekey(struct hostapd_data *hapd) -{ - struct wpa_authenticator *sm = hapd->wpa_auth; - int tmp, i; - - if (sm == NULL) - return; - - for (i = 0; i < 2; i++) { - tmp = sm->GM; - sm->GM = sm->GN; - sm->GN = tmp; - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); - } -} - - -static const char * wpa_bool_txt(int bool) -{ - return bool ? "TRUE" : "FALSE"; -} - - -static int wpa_cipher_bits(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_CCMP: - return 128; - case WPA_CIPHER_TKIP: - return 256; - case WPA_CIPHER_WEP104: - return 104; - case WPA_CIPHER_WEP40: - return 40; - default: - return 0; - } -} - - -#define RSN_SUITE "%02x-%02x-%02x-%d" -#define RSN_SUITE_ARG(s) (s)[0], (s)[1], (s)[2], (s)[3] - -int wpa_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) -{ - int len = 0, i; - char pmkid_txt[PMKID_LEN * 2 + 1], *pos; - - len += snprintf(buf + len, buflen - len, - "dot11RSNAOptionImplemented=TRUE\n" - "dot11RSNAPreauthenticationImplemented=TRUE\n" - "dot11RSNAEnabled=%s\n" - "dot11RSNAPreauthenticationEnabled=%s\n", - wpa_bool_txt(hapd->conf->wpa & - HOSTAPD_WPA_VERSION_WPA2), - wpa_bool_txt(hapd->conf->rsn_preauth)); - - if (hapd->wpa_auth == NULL) - return len; - - pos = pmkid_txt; - for (i = 0; i < PMKID_LEN; i++) { - pos += sprintf(pos, "%02x", - hapd->wpa_auth->dot11RSNAPMKIDUsed[i]); - } - - len += snprintf(buf + len, buflen - len, - "dot11RSNAConfigVersion=%u\n" - "dot11RSNAConfigPairwiseKeysSupported=9999\n" - /* FIX: dot11RSNAConfigGroupCipher */ - /* FIX: dot11RSNAConfigGroupRekeyMethod */ - /* FIX: dot11RSNAConfigGroupRekeyTime */ - /* FIX: dot11RSNAConfigGroupRekeyPackets */ - "dot11RSNAConfigGroupRekeyStrict=%u\n" - "dot11RSNAConfigGroupUpdateCount=%u\n" - "dot11RSNAConfigPairwiseUpdateCount=%u\n" - "dot11RSNAConfigGroupCipherSize=%u\n" - "dot11RSNAConfigPMKLifetime=%u\n" - "dot11RSNAConfigPMKReauthThreshold=%u\n" - "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" - "dot11RSNAConfigSATimeout=%u\n" - "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" - "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" - "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" - "dot11RSNAPMKIDUsed=%s\n" - "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" - "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" - "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" - "dot11RSNATKIPCounterMeasuresInvoked=%u\n" - "dot11RSNA4WayHandshakeFailures=%u\n" - "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", - RSN_VERSION, - !!hapd->conf->wpa_strict_rekey, - dot11RSNAConfigGroupUpdateCount, - dot11RSNAConfigPairwiseUpdateCount, - wpa_cipher_bits(hapd->conf->wpa_group), - dot11RSNAConfigPMKLifetime, - dot11RSNAConfigPMKReauthThreshold, - dot11RSNAConfigSATimeout, - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAAuthenticationSuiteSelected), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAPairwiseCipherSelected), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAGroupCipherSelected), - pmkid_txt, - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAAuthenticationSuiteRequested), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAPairwiseCipherRequested), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAGroupCipherRequested), - hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, - hapd->wpa_auth->dot11RSNA4WayHandshakeFailures); - - /* TODO: dot11RSNAConfigPairwiseCiphersTable */ - /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ - - /* Private MIB */ - len += snprintf(buf + len, buflen - len, - "hostapdWPAGroupState=%d\n", - hapd->wpa_auth->wpa_group_state); - - return len; -} - - -int wpa_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen) -{ - int len = 0; - u8 not_used[4] = { 0, 0, 0, 0 }; - const u8 *pairwise = not_used; - - /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ - - /* dot11RSNAStatsEntry */ - - if (sta->wpa == WPA_VERSION_WPA) { - if (sta->pairwise == WPA_CIPHER_CCMP) - pairwise = WPA_CIPHER_SUITE_CCMP; - else if (sta->pairwise == WPA_CIPHER_TKIP) - pairwise = WPA_CIPHER_SUITE_TKIP; - else if (sta->pairwise == WPA_CIPHER_WEP104) - pairwise = WPA_CIPHER_SUITE_WEP104; - else if (sta->pairwise == WPA_CIPHER_WEP40) - pairwise = WPA_CIPHER_SUITE_WEP40; - else if (sta->pairwise == WPA_CIPHER_NONE) - pairwise = WPA_CIPHER_SUITE_NONE; - } else if (sta->wpa == WPA_VERSION_WPA2) { - if (sta->pairwise == WPA_CIPHER_CCMP) - pairwise = RSN_CIPHER_SUITE_CCMP; - else if (sta->pairwise == WPA_CIPHER_TKIP) - pairwise = RSN_CIPHER_SUITE_TKIP; - else if (sta->pairwise == WPA_CIPHER_WEP104) - pairwise = RSN_CIPHER_SUITE_WEP104; - else if (sta->pairwise == WPA_CIPHER_WEP40) - pairwise = RSN_CIPHER_SUITE_WEP40; - else if (sta->pairwise == WPA_CIPHER_NONE) - pairwise = RSN_CIPHER_SUITE_NONE; - } else - return 0; - - len += snprintf(buf + len, buflen - len, - /* TODO: dot11RSNAStatsIndex */ - "dot11RSNAStatsSTAAddress=" MACSTR "\n" - "dot11RSNAStatsVersion=1\n" - "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" - /* TODO: dot11RSNAStatsTKIPICVErrors */ - "dot11RSNAStatsTKIPLocalMICFailures=%u\n" - "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" - /* TODO: dot11RSNAStatsCCMPReplays */ - /* TODO: dot11RSNAStatsCCMPDecryptErrors */ - /* TODO: dot11RSNAStatsTKIPReplays */, - MAC2STR(sta->addr), - RSN_SUITE_ARG(pairwise), - sta->dot11RSNAStatsTKIPLocalMICFailures, - sta->dot11RSNAStatsTKIPRemoteMICFailures); - - if (sta->wpa_sm == NULL) - return len; - - /* Private MIB */ - len += snprintf(buf + len, buflen - len, - "hostapdWPAPTKState=%d\n" - "hostapdWPAPTKGroupState=%d\n", - sta->wpa_sm->wpa_ptk_state, - sta->wpa_sm->wpa_ptk_group_state); - - return len; -} diff --git a/contrib/hostapd-0.4.9/wpa.h b/contrib/hostapd-0.4.9/wpa.h deleted file mode 100644 index 62159e78cf..0000000000 --- a/contrib/hostapd-0.4.9/wpa.h +++ /dev/null @@ -1,196 +0,0 @@ -#ifndef WPA_H -#define WPA_H - -#define WPA_NONCE_LEN 32 -#define WPA_PMK_LEN PMK_LEN -#define WPA_REPLAY_COUNTER_LEN 8 -#define WPA_GMK_LEN 32 -#define WPA_GTK_MAX_LEN 32 -#define WPA_KEY_RSC_LEN 8 -#define PMKID_LEN 16 - -struct rsn_pmksa_cache { - struct rsn_pmksa_cache *next, *hnext; - u8 pmkid[PMKID_LEN]; - u8 pmk[PMK_LEN]; - time_t expiration; - int akmp; /* WPA_KEY_MGMT_* */ - u8 spa[ETH_ALEN]; - u8 *identity; - size_t identity_len; - struct radius_class_data radius_class; -}; - -struct rsn_preauth_interface { - struct rsn_preauth_interface *next; - struct hostapd_data *hapd; - struct l2_packet_data *l2; - char *ifname; - int ifindex; -}; - -struct wpa_eapol_key { - u8 type; - u16 key_info; - u16 key_length; - u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; - u8 key_nonce[WPA_NONCE_LEN]; - u8 key_iv[16]; - u8 key_rsc[WPA_KEY_RSC_LEN]; - u8 key_id[8]; /* Reserved */ - u8 key_mic[16]; - u16 key_data_length; - /* followed by key_data_length bytes of key_data */ -} __attribute__ ((packed)); - -#define WPA_KEY_INFO_TYPE_MASK (BIT(0) | BIT(1) | BIT(2)) -#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) -#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) -#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ -/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ -#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) -#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 -#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ -#define WPA_KEY_INFO_TXRX BIT(6) /* group */ -#define WPA_KEY_INFO_ACK BIT(7) -#define WPA_KEY_INFO_MIC BIT(8) -#define WPA_KEY_INFO_SECURE BIT(9) -#define WPA_KEY_INFO_ERROR BIT(10) -#define WPA_KEY_INFO_REQUEST BIT(11) -#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) - - -/* per STA state machine data */ - -struct wpa_ptk { - u8 mic_key[16]; /* EAPOL-Key MIC Key (MK) */ - u8 encr_key[16]; /* EAPOL-Key Encryption Key (EK) */ - u8 tk1[16]; /* Temporal Key 1 (TK1) */ - union { - u8 tk2[16]; /* Temporal Key 2 (TK2) */ - struct { - u8 tx_mic_key[8]; - u8 rx_mic_key[8]; - } auth; - } u; -} __attribute__ ((packed)); - -struct wpa_state_machine { - struct hostapd_data *hapd; - struct sta_info *sta; - - enum { - WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, - WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, - WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, - WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, - WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE - } wpa_ptk_state; - - enum { - WPA_PTK_GROUP_IDLE = 0, - WPA_PTK_GROUP_REKEYNEGOTIATING, - WPA_PTK_GROUP_REKEYESTABLISHED, - WPA_PTK_GROUP_KEYERROR - } wpa_ptk_group_state; - - Boolean Init; - Boolean DeauthenticationRequest; - Boolean AuthenticationRequest; - Boolean ReAuthenticationRequest; - Boolean Disconnect; - int TimeoutCtr; - int GTimeoutCtr; - Boolean TimeoutEvt; - Boolean EAPOLKeyReceived; - Boolean EAPOLKeyPairwise; - Boolean EAPOLKeyRequest; - Boolean MICVerified; - Boolean GUpdateStationKeys; - u8 ANonce[WPA_NONCE_LEN]; - u8 SNonce[WPA_NONCE_LEN]; - u8 PMK[WPA_PMK_LEN]; - struct wpa_ptk PTK; - Boolean PTK_valid; - Boolean pairwise_set; - int keycount; - Boolean Pair; - u8 key_replay_counter[WPA_REPLAY_COUNTER_LEN]; - Boolean key_replay_counter_valid; - Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i/D8 */ - Boolean PTKRequest; /* not in IEEE 802.11i state machine */ - Boolean has_GTK; - - u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ - size_t last_rx_eapol_key_len; - - Boolean changed; -}; - -/* per authenticator data */ -struct wpa_authenticator { - Boolean GInit; - int GNoStations; - int GKeyDoneStations; - Boolean GTKReKey; - int GTK_len; - int GN, GM; - Boolean GTKAuthenticator; - u8 Counter[WPA_NONCE_LEN]; - - enum { - WPA_GROUP_GTK_INIT = 0, - WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE - } wpa_group_state; - - u8 GMK[WPA_GMK_LEN]; - u8 GTK[2][WPA_GTK_MAX_LEN]; - u8 GNonce[WPA_NONCE_LEN]; - Boolean changed; - - unsigned int dot11RSNAStatsTKIPRemoteMICFailures; - u8 dot11RSNAAuthenticationSuiteSelected[4]; - u8 dot11RSNAPairwiseCipherSelected[4]; - u8 dot11RSNAGroupCipherSelected[4]; - u8 dot11RSNAPMKIDUsed[PMKID_LEN]; - u8 dot11RSNAAuthenticationSuiteRequested[4]; /* FIX: update */ - u8 dot11RSNAPairwiseCipherRequested[4]; /* FIX: update */ - u8 dot11RSNAGroupCipherRequested[4]; /* FIX: update */ - unsigned int dot11RSNATKIPCounterMeasuresInvoked; - unsigned int dot11RSNA4WayHandshakeFailures; -}; - - -int wpa_init(struct hostapd_data *hapd); -void wpa_deinit(struct hostapd_data *hapd); - -enum { - WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, - WPA_INVALID_AKMP -}; - -int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *wpa_ie, size_t wpa_ie_len, int version); -void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta); -void wpa_free_station(struct sta_info *sta); -void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, - u8 *data, size_t data_len); -typedef enum { - WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, - WPA_REAUTH_EAPOL -} wpa_event; -void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, - wpa_event event); -void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta); -void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, - int session_timeout); -void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success); -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len); -void wpa_gtk_rekey(struct hostapd_data *hapd); -int wpa_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); -int wpa_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen); - -#endif /* WPA_H */ diff --git a/contrib/hostapd-0.4.9/wpa_ctrl.c b/contrib/hostapd-0.4.9/wpa_ctrl.c deleted file mode 100644 index 98e0669a47..0000000000 --- a/contrib/hostapd-0.4.9/wpa_ctrl.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * wpa_supplicant/hostapd control interface library - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include -#include -#include -#include -#include -#include -#ifndef CONFIG_NATIVE_WINDOWS -#include -#include -#include -#endif /* CONFIG_NATIVE_WINDOWS */ - -#include "wpa_ctrl.h" -#ifdef CONFIG_NATIVE_WINDOWS -#include "common.h" -#endif /* CONFIG_NATIVE_WINDOWS */ - - -/** - * struct wpa_ctrl - Internal structure for control interface library - * - * This structure is used by the wpa_supplicant/hostapd control interface - * library to store internal data. Programs using the library should not touch - * this data directly. They can only use the pointer to the data structure as - * an identifier for the control interface connection and use this as one of - * the arguments for most of the control interface library functions. - */ -struct wpa_ctrl { - int s; -#ifdef CONFIG_CTRL_IFACE_UDP - struct sockaddr_in local; - struct sockaddr_in dest; -#else /* CONFIG_CTRL_IFACE_UDP */ - struct sockaddr_un local; - struct sockaddr_un dest; -#endif /* CONFIG_CTRL_IFACE_UDP */ -}; - - -struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) -{ - struct wpa_ctrl *ctrl; -#ifndef CONFIG_CTRL_IFACE_UDP - static int counter = 0; -#endif /* CONFIG_CTRL_IFACE_UDP */ - - ctrl = malloc(sizeof(*ctrl)); - if (ctrl == NULL) - return NULL; - memset(ctrl, 0, sizeof(*ctrl)); - -#ifdef CONFIG_CTRL_IFACE_UDP - ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); - if (ctrl->s < 0) { - perror("socket"); - free(ctrl); - return NULL; - } - - ctrl->local.sin_family = AF_INET; - ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); - if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, - sizeof(ctrl->local)) < 0) { - close(ctrl->s); - free(ctrl); - return NULL; - } - - ctrl->dest.sin_family = AF_INET; - ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); - ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); - if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, - sizeof(ctrl->dest)) < 0) { - perror("connect"); - close(ctrl->s); - free(ctrl); - return NULL; - } -#else /* CONFIG_CTRL_IFACE_UDP */ - ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); - if (ctrl->s < 0) { - free(ctrl); - return NULL; - } - - ctrl->local.sun_family = AF_UNIX; - snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), - "/tmp/wpa_ctrl_%d-%d", getpid(), counter++); - if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, - sizeof(ctrl->local)) < 0) { - close(ctrl->s); - free(ctrl); - return NULL; - } - - ctrl->dest.sun_family = AF_UNIX; - snprintf(ctrl->dest.sun_path, sizeof(ctrl->dest.sun_path), "%s", - ctrl_path); - if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, - sizeof(ctrl->dest)) < 0) { - close(ctrl->s); - unlink(ctrl->local.sun_path); - free(ctrl); - return NULL; - } -#endif /* CONFIG_CTRL_IFACE_UDP */ - - return ctrl; -} - - -void wpa_ctrl_close(struct wpa_ctrl *ctrl) -{ -#ifndef CONFIG_CTRL_IFACE_UDP - unlink(ctrl->local.sun_path); -#endif /* CONFIG_CTRL_IFACE_UDP */ - close(ctrl->s); - free(ctrl); -} - - -int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, - char *reply, size_t *reply_len, - void (*msg_cb)(char *msg, size_t len)) -{ - struct timeval tv; - int res; - fd_set rfds; - - if (send(ctrl->s, cmd, cmd_len, 0) < 0) - return -1; - - for (;;) { - tv.tv_sec = 2; - tv.tv_usec = 0; - FD_ZERO(&rfds); - FD_SET(ctrl->s, &rfds); - res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); - if (FD_ISSET(ctrl->s, &rfds)) { - res = recv(ctrl->s, reply, *reply_len, 0); - if (res < 0) - return res; - if (res > 0 && reply[0] == '<') { - /* This is an unsolicited message from - * wpa_supplicant, not the reply to the - * request. Use msg_cb to report this to the - * caller. */ - if (msg_cb) { - /* Make sure the message is nul - * terminated. */ - if ((size_t) res == *reply_len) - res = (*reply_len) - 1; - reply[res] = '\0'; - msg_cb(reply, res); - } - continue; - } - *reply_len = res; - break; - } else { - return -2; - } - } - return 0; -} - - -static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) -{ - char buf[10]; - int ret; - size_t len = 10; - - ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, - buf, &len, NULL); - if (ret < 0) - return ret; - if (len == 3 && memcmp(buf, "OK\n", 3) == 0) - return 0; - return -1; -} - - -int wpa_ctrl_attach(struct wpa_ctrl *ctrl) -{ - return wpa_ctrl_attach_helper(ctrl, 1); -} - - -int wpa_ctrl_detach(struct wpa_ctrl *ctrl) -{ - return wpa_ctrl_attach_helper(ctrl, 0); -} - - -int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) -{ - int res; - - res = recv(ctrl->s, reply, *reply_len, 0); - if (res < 0) - return res; - *reply_len = res; - return 0; -} - - -int wpa_ctrl_pending(struct wpa_ctrl *ctrl) -{ - struct timeval tv; - int res; - fd_set rfds; - tv.tv_sec = 0; - tv.tv_usec = 0; - FD_ZERO(&rfds); - FD_SET(ctrl->s, &rfds); - res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); - return FD_ISSET(ctrl->s, &rfds); -} - - -int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) -{ - return ctrl->s; -} diff --git a/contrib/hostapd-0.4.9/wpa_ctrl.h b/contrib/hostapd-0.4.9/wpa_ctrl.h deleted file mode 100644 index c8fa48d693..0000000000 --- a/contrib/hostapd-0.4.9/wpa_ctrl.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * wpa_supplicant/hostapd control interface library - * Copyright (c) 2004-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef WPA_CTRL_H -#define WPA_CTRL_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* wpa_supplicant control interface - fixed message prefixes */ - -/** Interactive request for identity/password/pin */ -#define WPA_CTRL_REQ "CTRL-REQ-" - -/** Response to identity/password/pin request */ -#define WPA_CTRL_RSP "CTRL-RSP-" - -/* Event messages with fixed prefix */ -/** Authentication completed successfully and data connection enabled */ -#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " -/** Disconnected, data connection is not available */ -#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " -/** wpa_supplicant is exiting */ -#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " -/** Password change was completed successfully */ -#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED " -/** EAP-Request/Notification received */ -#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION " -/** EAP authentication started (EAP-Request/Identity received) */ -#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED " -/** EAP method selected */ -#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD " -/** EAP authentication completed successfully */ -#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " -/** EAP authentication failed (EAP-Failure received) */ -#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " - - -/* wpa_supplicant/hostapd control interface access */ - -/** - * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd - * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. - * Returns: Pointer to abstract control interface data or %NULL on failure - * - * This function is used to open a control interface to wpa_supplicant/hostapd. - * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path - * is configured in wpa_supplicant/hostapd and other programs using the control - * interface need to use matching path configuration. - */ -struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); - - -/** - * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd - * @ctrl: Control interface data from wpa_ctrl_open() - * - * This function is used to close a control interface. - */ -void wpa_ctrl_close(struct wpa_ctrl *ctrl); - - -/** - * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd - * @ctrl: Control interface data from wpa_ctrl_open() - * @cmd: Command; usually, ASCII text, e.g., "PING" - * @cmd_len: Length of the cmd in bytes - * @reply: Buffer for the response - * @reply_len: Reply buffer length - * @msg_cb: Callback function for unsolicited messages or %NULL if not used - * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout - * - * This function is used to send commands to wpa_supplicant/hostapd. Received - * response will be written to reply and reply_len is set to the actual length - * of the reply. This function will block for up to two seconds while waiting - * for the reply. If unsolicited messages are received, the blocking time may - * be longer. - * - * msg_cb can be used to register a callback function that will be called for - * unsolicited messages received while waiting for the command response. These - * messages may be received if wpa_ctrl_request() is called at the same time as - * wpa_supplicant/hostapd is sending such a message. This can happen only if - * the program has used wpa_ctrl_attach() to register itself as a monitor for - * event messages. Alternatively to msg_cb, programs can register two control - * interface connections and use one of them for commands and the other one for - * receiving event messages, in other words, call wpa_ctrl_attach() only for - * the control interface connection that will be used for event messages. - */ -int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, - char *reply, size_t *reply_len, - void (*msg_cb)(char *msg, size_t len)); - - -/** - * wpa_ctrl_attach - Register as an event monitor for the control interface - * @ctrl: Control interface data from wpa_ctrl_open() - * Returns: 0 on success, -1 on failure, -2 on timeout - * - * This function registers the control interface connection as a monitor for - * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the - * control interface connection starts receiving event messages that can be - * read with wpa_ctrl_recv(). - */ -int wpa_ctrl_attach(struct wpa_ctrl *ctrl); - - -/** - * wpa_ctrl_detach - Unregister event monitor from the control interface - * @ctrl: Control interface data from wpa_ctrl_open() - * Returns: 0 on success, -1 on failure, -2 on timeout - * - * This function unregisters the control interface connection as a monitor for - * wpa_supplicant/hostapd events, i.e., cancels the registration done with - * wpa_ctrl_attach(). - */ -int wpa_ctrl_detach(struct wpa_ctrl *ctrl); - - -/** - * wpa_ctrl_recv - Receive a pending control interface message - * @ctrl: Control interface data from wpa_ctrl_open() - * @reply: Buffer for the message data - * @reply_len: Length of the reply buffer - * Returns: 0 on success, -1 on failure - * - * This function will receive a pending control interface message. This - * function will block if no messages are available. The received response will - * be written to reply and reply_len is set to the actual length of the reply. - * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach() - * must have been used to register the control interface as an event monitor. - */ -int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); - - -/** - * wpa_ctrl_pending - Check whether there are pending event messages - * @ctrl: Control interface data from wpa_ctrl_open() - * Returns: Non-zero if there are pending messages - * - * This function will check whether there are any pending control interface - * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is - * only used for event messages, i.e., wpa_ctrl_attach() must have been used to - * register the control interface as an event monitor. - */ -int wpa_ctrl_pending(struct wpa_ctrl *ctrl); - - -/** - * wpa_ctrl_get_fd - Get file descriptor used by the control interface - * @ctrl: Control interface data from wpa_ctrl_open() - * Returns: File descriptor used for the connection - * - * This function can be used to get the file descriptor that is used for the - * control interface connection. The returned value can be used, e.g., with - * select() while waiting for multiple events. - * - * The returned file descriptor must not be used directly for sending or - * receiving packets; instead, the library functions wpa_ctrl_request() and - * wpa_ctrl_recv() must be used for this. - */ -int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); - -#ifdef CONFIG_CTRL_IFACE_UDP -#define WPA_CTRL_IFACE_PORT 9877 -#define WPA_GLOBAL_CTRL_IFACE_PORT 9878 -#endif /* CONFIG_CTRL_IFACE_UDP */ - - -#ifdef __cplusplus -} -#endif - -#endif /* WPA_CTRL_H */ diff --git a/contrib/hostapd/COPYING b/contrib/hostapd/COPYING index 14f5453722..8a98582c89 100644 --- a/contrib/hostapd/COPYING +++ b/contrib/hostapd/COPYING @@ -1,340 +1,22 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 +wpa_supplicant and hostapd +-------------------------- - 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. +Copyright (c) 2002-2012, Jouni Malinen and contributors +All Rights Reserved. - 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. +See the README file for the current license terms. - 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. +This software was previously distributed under BSD/GPL v2 dual license +terms that allowed either of those license alternatives to be +selected. As of February 11, 2012, the project has chosen to use only +the BSD license option for future distribution. As such, the GPL v2 +license option is no longer used. It should be noted that the BSD +license option (the one with advertisement clause removed) is compatible +with GPL and as such, does not prevent use of this software in projects +that use GPL. - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 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. - - , 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. +Some of the files may still include pointers to GPL version 2 license +terms. However, such copyright and license notifications are maintained +only for attribution purposes and any distribution of this software +after February 11, 2012 is no longer under the GPL v2 option. diff --git a/contrib/hostapd/README b/contrib/hostapd/README index 9c6be85d9e..8de14a64fa 100644 --- a/contrib/hostapd/README +++ b/contrib/hostapd/README @@ -1,19 +1,56 @@ -wpa_supplicant and hostapd v0.6.x ---------------------------------- +wpa_supplicant and hostapd +-------------------------- -Copyright (c) 2002-2007, Jouni Malinen and contributors +Copyright (c) 2002-2014, Jouni Malinen 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. +These programs are licensed under the BSD license (the one with +advertisement clause removed). + +If you are submitting changes to the project, please see CONTRIBUTIONS +file for more instructions. 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). +Source code files were moved around in v0.6.x releases and compared to +earlier releases, the programs are now built by first going to a +subdirectory (wpa_supplicant or hostapd) and creating build +configuration (.config) and running 'make' there (for Linux/BSD/cygwin +builds). + + +License +------- + +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. diff --git a/contrib/hostapd/README.DELETED b/contrib/hostapd/README.DELETED deleted file mode 100644 index ff95c0cac9..0000000000 --- a/contrib/hostapd/README.DELETED +++ /dev/null @@ -1,36 +0,0 @@ -hostapd/.gitignore -hostapd/Makefile -hostapd/defconfig -hostapd/doc/ -hostapd/eap_testing.txt -hostapd/hostapd.8 -hostapd/hostapd.accept -hostapd/hostapd.conf -hostapd/hostapd.deny -hostapd/hostapd.eap_user -hostapd/hostapd.radius_clients -hostapd/hostapd.sim_db -hostapd/hostapd.vlan -hostapd/hostapd.wpa_psk -hostapd/hostapd_cli.1 -hostapd/logwatch/hostapd -hostapd/logwatch/hostapd.conf -hostapd/wired.conf -src/Makefile -src/common/Makefile -src/crypto/Makefile -src/drivers/Makefile -src/drivers/driver_iphone.m -src/drivers/driver_osx.m -src/eap_common/Makefile -src/eap_peer/Makefile -src/eap_server/Makefile -src/eapol_supp/Makefile -src/hlr_auc_gw/Makefile -src/hlr_auc_gw/hlr_auc_gw.milenage_db -src/l2_packet/Makefile -src/radius/Makefile -src/rsn_supp/Makefile -src/tls/Makefile -src/utils/Makefile -src/wps/Makefile diff --git a/contrib/hostapd/README.DRAGONFLY b/contrib/hostapd/README.DRAGONFLY deleted file mode 100644 index 276444866d..0000000000 --- a/contrib/hostapd/README.DRAGONFLY +++ /dev/null @@ -1,23 +0,0 @@ - HOSTAPD-0.6.10 AS USED BY DRAGONFLY - - This directory contains a selected set of files from the - hostapd-6.10.tar.gz distribution. No files have been moved - or modified from their extracted position. - - This distribution was downloaded from the following site: - - http://hostap.epitest.fi/hostapd/ - - MD5 (hostapd-0.6.10.tar.gz) = 1ac442d1f984273f108b3de579c1b70d - SHA1 (hostapd-0.6.10.tar.gz) = 2cacf994abd3cebad36679d32f64c9e9906ccff8 - - - DO NOT CREATE OR EDIT ANY FILES IN THIS DIRECTORY HIERARCHY! THIS - HIERARCHY REPRESENTS AN EXACT COPY, MINUS UNNEEDED FILES, OF THE - ORIGINAL ARCHIVE. All modifications are made on the master branch or in - src/usr.sbin/802_11. - - The only additional files added to this directory are README.DRAGONFLY - and README.DELETED. - - For mor information, check development(7). diff --git a/contrib/hostapd/hostapd/ChangeLog b/contrib/hostapd/hostapd/ChangeLog index 18af4b176b..5ef96768f8 100644 --- a/contrib/hostapd/hostapd/ChangeLog +++ b/contrib/hostapd/hostapd/ChangeLog @@ -1,42 +1,330 @@ ChangeLog for hostapd -2010-01-12 - v0.6.10 - * 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] - * fixed WPS selected registrar expiration for internal PIN registrar - * disable PMTU discovery for RADIUS packets - * fixed WPS UPnP SSDP on 32-bit targets - * fixed WPS AP reconfiguration with drivers that do not use hostapd - MLME - * fixed RSN parameter setting for multi-BSS case - * added WPS workarounds for known interoperability issues with broken, - deployed implementation - * update IEEE 802.11w implementation to match with the published - standard - * fixed OpCode when proxying WSC_ACK or WSC_NACK from WPS ER - * fixed proxying of WSC_NACK to WPS ER - * fixed compilation with newer GnuTLS versions - * added support for defining timeout for WPS PINs +2014-02-04 - v2.1 + * added support for simultaneous authentication of equals (SAE) for + stronger password-based authentication with WPA2-Personal + * added nl80211 functionality + - VHT configuration for nl80211 + - support split wiphy dump + - driver-based MAC ACL + - QoS Mapping configuration + * added fully automated regression testing with mac80211_hwsim + * allow ctrl_iface group to be specified on command line (-G) + * allow single hostapd process to control independent WPS interfaces + (wps_independent=1) instead of synchronized operations through all + configured interfaces within a process + * avoid processing received management frames multiple times when using + nl80211 with multiple BSSes + * added support for DFS (processing radar detection events, CAC, channel + re-selection) + * added EAP-EKE server + * added automatic channel selection (ACS) + * added option for using per-BSS (vif) configuration files with + -b: + * extended global control interface ADD/REMOVE commands to allow BSSes + of a radio to be removed individually without having to add/remove all + other BSSes of the radio at the same time + * added support for sending debug info to Linux tracing (-T on command + line) + * replace dump_file functionality with same information being available + through the hostapd control interface + * added support for using Protected Dual of Public Action frames for + GAS/ANQP exchanges when PMF is enabled + * added support for WPS+NFC updates + - improved protocol + - option to fetch and report alternative carrier records for external + NFC operations + * various bug fixes + +2013-01-12 - v2.0 + * added AP-STA-DISCONNECTED ctrl_iface event + * improved debug logging (human readable event names, interface name + included in more entries) + * added number of small changes to make it easier for static analyzers + to understand the implementation + * added a workaround for Windows 7 Michael MIC failure reporting and + use of the Secure bit in EAPOL-Key msg 3/4 + * fixed number of small bugs (see git logs for more details) + * changed OpenSSL to read full certificate chain from server_cert file + * nl80211: number of updates to use new cfg80211/nl80211 functionality + - replace monitor interface with nl80211 commands + - additional information for driver-based AP SME + * EAP-pwd: + - fix KDF for group 21 and zero-padding + - added support for fragmentation + - increased maximum number of hunting-and-pecking iterations + * avoid excessive Probe Response retries for broadcast Probe Request + frames (only with drivers using hostapd SME/MLME) + * added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y) + * fixed WPS operation stopping on dual concurrent AP + * added wps_rf_bands configuration parameter for overriding RF Bands + value for WPS + * added support for getting per-device PSK from RADIUS Tunnel-Password + * added support for libnl 3.2 and newer + * increased initial group key handshake retransmit timeout to 500 ms + * added a workaround for 4-way handshake to update SNonce even after + having sent EAPOL-Key 3/4 to avoid issues with some supplicant + implementations that can change SNonce for each EAP-Key 2/4 + * added a workaround for EAPOL-Key 4/4 using incorrect type value in + WPA2 mode (some deployed stations use WPA type in that message) + * added a WPS workaround for mixed mode AP Settings with Windows 7 + * changed WPS AP PIN disabling mechanism to disable the PIN after 10 + consecutive failures in addition to using the exponential lockout + period + * added support for WFA Hotspot 2.0 + - GAS/ANQP advertisement of network information + - disable_dgaf parameter to disable downstream group-addressed + forwarding + * simplified licensing terms by selecting the BSD license as the only + alternative + * EAP-SIM: fixed re-authentication not to update pseudonym + * EAP-SIM: use Notification round before EAP-Failure + * EAP-AKA: added support for AT_COUNTER_TOO_SMALL + * EAP-AKA: skip AKA/Identity exchange if EAP identity is recognized + * EAP-AKA': fixed identity for MK derivation + * EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this + breaks interoperability with older versions + * EAP-SIM/AKA: allow pseudonym to be used after unknown reauth id + * changed ANonce to be a random number instead of Counter-based + * added support for canceling WPS operations with hostapd_cli wps_cancel + * fixed EAP/WPS to PSK transition on reassociation in cases where + deauthentication is missed + * hlr_auc_gw enhancements: + - a new command line parameter -u can be used to enable updating of + SQN in Milenage file + - use 5 bit IND for SQN updates + - SQLite database can now be used to store Milenage information + * EAP-SIM/AKA DB: added optional use of SQLite database for pseudonyms + and reauth data + * added support for Chargeable-User-Identity (RFC 4372) + * added radius_auth_req_attr and radius_acct_req_attr configuration + parameters to allow adding/overriding of RADIUS attributes in + Access-Request and Accounting-Request packets + * added support for RADIUS dynamic authorization server (RFC 5176) + * added initial support for WNM operations + - BSS max idle period + - WNM-Sleep Mode + * added new WPS NFC ctrl_iface mechanism + - removed obsoleted WPS_OOB command (including support for deprecated + UFD config_method) + * added FT support for drivers that implement MLME internally + * added SA Query support for drivers that implement MLME internally + * removed default ACM=1 from AC_VO and AC_VI + * changed VENDOR-TEST EAP method to use proper private enterprise number + (this will not interoperate with older versions) + * added hostapd.conf parameter vendor_elements to allow arbitrary vendor + specific elements to be added to the Beacon and Probe Response frames + * added support for configuring GCMP cipher for IEEE 802.11ad + * added support for 256-bit AES with internal TLS implementation + * changed EAPOL transmission to use AC_VO if WMM is active + * fixed EAP-TLS/PEAP/TTLS/FAST server to validate TLS Message Length + correctly; invalid messages could have caused the hostapd process to + terminate before this fix [CVE-2012-4445] + * limit number of active wildcard PINs for WPS Registrar to one to avoid + confusing behavior with multiple wildcard PINs + * added a workaround for WPS PBC session overlap detection to avoid + interop issues with deployed station implementations that do not + remove active PBC indication from Probe Request frames properly + * added support for using SQLite for the eap_user database + * added Acct-Session-Id attribute into Access-Request messages + * fixed EAPOL frame transmission to non-QoS STAs with nl80211 + (do not send QoS frames if the STA did not negotiate use of QoS for + this association) + +2012-05-10 - v1.0 + * Add channel selection support in hostapd. See hostapd.conf. + * Add support for IEEE 802.11v Time Advertisement mechanism with UTC + TSF offset. See hostapd.conf for config info. + * Delay STA entry removal until Deauth/Disassoc TX status in AP mode. + This allows the driver to use PS buffering of Deauthentication and + Disassociation frames when the STA is in power save sleep. Only + available with drivers that provide TX status events for Deauth/ + Disassoc frames (nl80211). + * Allow PMKSA caching to be disabled on the Authenticator. See + hostap.conf config parameter disable_pmksa_caching. + * atheros: Add support for IEEE 802.11w configuration. + * bsd: Add support for setting HT values in IFM_MMASK. + * Allow client isolation to be configured with ap_isolate. Client + isolation can be used to prevent low-level bridging of frames + between associated stations in the BSS. By default, this bridging + is allowed. + * Allow coexistance of HT BSSes with WEP/TKIP BSSes. + * Add require_ht config parameter, which can be used to configure + hostapd to reject association with any station that does not support + HT PHY. + * Add support for writing debug log to a file using "-f" option. Also + add relog CLI command to re-open the log file. + * Add bridge handling for WDS STA interfaces. By default they are + added to the configured bridge of the AP interface (if present), + but the user can also specify a separate bridge using cli command + wds_bridge. + * hostapd_cli: + - Add wds_bridge command for specifying bridge for WDS STA + interfaces. + - Add relog command for reopening log file. + - Send AP-STA-DISCONNECTED event when an AP disconnects a station + due to inactivity. + - Add wps_config ctrl_interface command for configuring AP. This + command can be used to configure the AP using the internal WPS + registrar. It works in the same way as new AP settings received + from an ER. + - Many WPS/WPS ER commands - see WPS/WPS ER sections for details. + - Add command get version, that returns hostapd version string. + * WNM: Add BSS Transition Management Request for ESS Disassoc Imminent. + Use hostapd_cli ess_disassoc (STA addr) (URL) to send the + notification to the STA. + * Allow AP mode to disconnect STAs based on low ACK condition (when + the data connection is not working properly, e.g., due to the STA + going outside the range of the AP). Disabled by default, enable by + config option disassoc_low_ack. + * Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad + config file. + * WPS: + - Send AP Settings as a wrapped Credential attribute to ctrl_iface + in WPS-NEW-AP-SETTINGS. + - Dispatch more WPS events through hostapd ctrl_iface. + - Add mechanism for indicating non-standard WPS errors. + - Change concurrent radio AP to use only one WPS UPnP instance. + - Add wps_check_pin command for processing PIN from user input. + UIs can use this command to process a PIN entered by a user and to + validate the checksum digit (if present). + - Add hostap_cli get_config command to display current AP config. + - Add new hostapd_cli command, wps_ap_pin, to manage AP PIN at + runtime and support dynamic AP PIN management. + - Disable AP PIN after 10 consecutive failures. Slow down attacks + on failures up to 10. + - Allow AP to start in Enrollee mode without AP PIN for probing, + to be compatible with Windows 7. + - Add Config Error into WPS-FAIL events to provide more info + to the user on how to resolve the issue. + - When controlling multiple interfaces: + - apply WPS commands to all interfaces configured to use WPS + - apply WPS config changes to all interfaces that use WPS + - when an attack is detected on any interface, disable AP PIN on + all interfaces + * WPS ER: + - Show SetSelectedRegistrar events as ctrl_iface events. + - Add special AP Setup Locked mode to allow read only ER. + ap_setup_locked=2 can now be used to enable a special mode where + WPS ER can learn the current AP settings, but cannot change them. + * WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2) + - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool + for testing protocol extensibility. + - Add build option CONFIG_WPS_STRICT to allow disabling of WPS + workarounds. + - Add support for AuthorizedMACs attribute. + * TDLS: + - Allow TDLS use or TDLS channel switching in the BSS to be + prohibited in the BSS, using config params tdls_prohibit and + tdls_prohibit_chan_switch. + * EAP server: Add support for configuring fragment size (see + fragment_size in hostapd.conf). + * wlantest: Add a tool wlantest for IEEE802.11 protocol testing. + wlantest can be used to capture frames from a monitor interface + for realtime capturing or from pcap files for offline analysis. + * Interworking: Support added for 802.11u. Enable in .config with + CONFIG_INTERWORKING. See hostapd.conf for config parameters for + interworking. + * Android: Add build and runtime support for Android hostapd. + * Add a new debug message level for excessive information. Use + -ddd to enable. + * TLS: Add support for tls_disable_time_checks=1 in client mode. + * Internal TLS: + - Add support for TLS v1.1 (RFC 4346). Enable with build parameter + CONFIG_TLSV11. + - Add domainComponent parser for X.509 names + * Reorder some IEs to get closer to IEEE 802.11 standard. Move + WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames. + Move HT IEs to be later in (Re)Assoc Resp. + * Many bugfixes. + +2010-04-18 - v0.7.2 + * fix WPS internal Registrar use when an external Registrar is also + active + * bsd: Cleaned up driver wrapper and added various low-level + configuration options + * 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) + * cleaned up driver wrapper API for multi-BSS operations + * nl80211: fix multi-BSS and VLAN operations + * fix number of issues with IEEE 802.11r/FT; this version is not + backwards compatible with old versions + * add SA Query Request processing in AP mode (IEEE 802.11w) + * fix IGTK PN in group rekeying (IEEE 802.11w) + * fix WPS PBC session overlap detection to use correct attribute + * hostapd_notif_Assoc() can now be called with all IEs to simplify + driver wrappers + * work around interoperability issue with some WPS External Registrar + implementations + * nl80211: fix WPS IE update + * hostapd_cli: add support for action script operations (run a script + on hostapd events) + * fix DH padding with internal crypto code (mainly, for WPS) + * fix WPS association with both WPS IE and WPA/RSN IE present with + driver wrappers that use hostapd MLME (e.g., nl80211) + +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 + * fixed HT Capabilities IE with nl80211 drivers + * moved generic AP functionality code into src/ap + * WPS: handle Selected Registrar as union of info from all Registrars + * remove obsolte Prism54.org driver wrapper + * added internal debugging mechanism with backtrace support and memory + allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y) + * EAP-FAST server: piggyback Phase 2 start with the end of Phase 1 + * WPS: add support for dynamically selecting whether to provision the + PSK as an ASCII passphrase or PSK + * added support for WDS (4-address frame) mode with per-station virtual + interfaces (wds_sta=1 in config file; only supported with + driver=nl80211 for now) * fixed WPS Probe Request processing to handle missing required attribute * fixed PKCS#12 use with OpenSSL 1.0.0 + * detect bridge interface automatically so that bridge parameter in + hostapd.conf becomes optional (though, it may now be used to + automatically add then WLAN interface into a bridge with + driver=nl80211) -2009-03-23 - v0.6.9 +2009-11-21 - v0.7.0 + * increased hostapd_cli ping interval to 5 seconds and made this + configurable with a new command line options (-G) + * driver_nl80211: use Linux socket filter to improve performance + * added support for external Registrars with WPS (UPnP transport) + * 802.11n: scan for overlapping BSSes before starting 20/40 MHz channel * driver_nl80211: fixed STA accounting data collection (TX/RX bytes reported correctly; TX/RX packets not yet available from kernel) + * added support for WPS USBA out-of-band mechanism with USB Flash + Drives (UFD) (CONFIG_WPS_UFD=y) * fixed EAPOL/EAP reauthentication when using an external RADIUS authentication server - * driver_prism54: fixed segmentation fault on initialization * fixed TNC with EAP-TTLS * fixed IEEE 802.11r key derivation function to match with the standard (note: this breaks interoperability with previous version) [Bug 303] - -2009-02-15 - v0.6.8 - * increased hostapd_cli ping interval to 5 seconds and made this - configurable with a new command line options (-G) - * driver_nl80211: use Linux socket filter to improve performance - * added support for external Registrars with WPS (UPnP transport) + * 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] + * added number of code size optimizations to remove unnecessary + functionality from the program binary based on build configuration + (part of this automatic; part configurable with CONFIG_NO_* build + options) + * use shared driver wrapper files with wpa_supplicant + * 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 some IEEE 802.11n co-existence rules to disable 40 MHz channels + or modify primary/secondary channels if needed based on neighboring + networks + * added support for NFC out-of-band mechanism with WPS + * added preliminary support for IEEE 802.11r RIC processing 2009-01-06 - v0.6.7 * added support for Wi-Fi Protected Setup (WPS) diff --git a/contrib/hostapd/hostapd/README b/contrib/hostapd/hostapd/README index eb9aa4815b..50868ee1de 100644 --- a/contrib/hostapd/hostapd/README +++ b/contrib/hostapd/hostapd/README @@ -2,37 +2,22 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2009, Jouni Malinen and contributors +Copyright (c) 2002-2014, Jouni Malinen and contributors All Rights Reserved. -This program is dual-licensed under both the GPL version 2 and BSD -license. Either license may be used at your option. +This program is licensed under the BSD license (the one with +advertisement clause removed). + +If you are submitting changes to the project, please see CONTRIBUTIONS +file for more instructions. 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: +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 @@ -95,9 +80,6 @@ Current hardware/software requirements: madwifi driver root directory in .config (see defconfig file for an example: CFLAGS += -I) - Prism54 driver for Intersil/Conexant Prism GT/Duette/Indigo - (http://www.prism54.org/) - mac80211-based drivers that support AP mode (with driver=nl80211). This includes drivers for Atheros (ath9k) and Broadcom (b43) chipsets. diff --git a/contrib/hostapd/hostapd/README-WPS b/contrib/hostapd/hostapd/README-WPS index e0e370b5bc..654b5bcec3 100644 --- a/contrib/hostapd/hostapd/README-WPS +++ b/contrib/hostapd/hostapd/README-WPS @@ -62,10 +62,14 @@ includes WPS support and uses madwifi driver interface: CONFIG_DRIVER_MADWIFI=y CFLAGS += -I/usr/src/madwifi-0.9.3 -CONFIG_EAP=y CONFIG_WPS=y +CONFIG_WPS2=y CONFIG_WPS_UPNP=y +Following parameter can be used to enable support for NFC config method: + +CONFIG_WPS_NFC=y + Following section shows an example runtime configuration (hostapd.conf) that enables WPS: @@ -120,6 +124,13 @@ pushbutton event (for PBC) to allow a new WPS Enrollee to join the network. hostapd uses the control interface as an input channel for these events. +The PIN value used in the commands must be processed by an UI to +remove non-digit characters and potentially, to verify the checksum +digit. "hostapd_cli wps_check_pin " can be used to do such +processing. It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if +the checksum digit is incorrect, or the processed PIN (non-digit +characters removed) if the PIN is valid. + When a client device (WPS Enrollee) connects to hostapd (WPS Registrar) in order to start PIN mode negotiation for WPS, an identifier (Enrollee UUID) is sent. hostapd will need to be configured @@ -172,10 +183,17 @@ hostapd_cli wps_pin any 12345670 To reduce likelihood of PIN being used with other devices or of forgetting an active PIN available for potential attackers, expiration -time can be set for the new PIN: +time in seconds can be set for the new PIN (value 0 indicates no +expiration): hostapd_cli wps_pin any 12345670 300 +If the MAC address of the enrollee is known, it should be configured +to allow the AP to advertise list of authorized enrollees: + +hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c \ + 12345670 300 00:11:22:33:44:55 + After this, the Enrollee can connect to the AP again and complete WPS negotiation. At that point, a new, random WPA PSK is generated for the @@ -196,6 +214,44 @@ which will generate a new WPA PSK in the same way as the PIN method described above. +When an external Registrar is used, the AP can act as an Enrollee and +use its AP PIN. A static AP PIN (e.g., one one a label in the AP +device) can be configured in hostapd.conf (ap_pin parameter). A more +secure option is to use hostapd_cli wps_ap_pin command to enable the +AP PIN only based on user action (and even better security by using a +random AP PIN for each session, i.e., by using "wps_ap_pin random" +command with a timeout value). Following commands are available for +managing the dynamic AP PIN operations: + +hostapd_cli wps_ap_pin disable +- disable AP PIN (i.e., do not allow external Registrars to use it to + learn the current AP settings or to reconfigure the AP) + +hostapd_cli wps_ap_pin random [timeout] +- generate a random AP PIN and enable it +- if the optional timeout parameter is given, the AP PIN will be enabled + for the specified number of seconds + +hostapd_cli wps_ap_pin get +- fetch the current AP PIN + +hostapd_cli wps_ap_pin set [timeout] +- set the AP PIN and enable it +- if the optional timeout parameter is given, the AP PIN will be enabled + for the specified number of seconds + +hostapd_cli get_config +- display the current configuration + +hostapd_cli wps_config +examples: + hostapd_cli wps_config testing WPA2PSK CCMP 12345678 + hostapd_cli wps_config "no security" OPEN NONE "" + + must be one of the following: OPEN WPAPSK WPA2PSK + must be one of the following: NONE WEP TKIP CCMP + + Credential generation and configuration changes ----------------------------------------------- @@ -225,7 +281,7 @@ WPS-REG-SUCCESS For example: <2>WPS-REG-SUCCESS 02:66:a0:ee:17:27 2b7093f1-d6fb-5108-adbb-bea66bb87333 -This can be used to tricker change from unconfigured to configured +This can be used to trigger change from unconfigured to configured state (random configuration based on the first successful WPS registration). In addition, this can be used to update AP UI about the status of WPS registration progress. @@ -237,3 +293,62 @@ For example: This can be used to update the externally stored AP configuration and then update hostapd configuration (followed by restarting of hostapd). + + +WPS with NFC +------------ + +WPS can be used with NFC-based configuration method. An NFC tag +containing a password token from the Enrollee can be used to +authenticate the connection instead of the PIN. In addition, an NFC tag +with a configuration token can be used to transfer AP settings without +going through the WPS protocol. + +When the AP acts as an Enrollee, a local NFC tag with a password token +can be used by touching the NFC interface of an external Registrar. The +wps_nfc_token command is used to manage use of the NFC password token +from the AP. "wps_nfc_token enable" enables the use of the AP's NFC +password token (in place of AP PIN) and "wps_nfc_token disable" disables +the NFC password token. + +The NFC password token that is either pre-configured in the +configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey, +wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with +"wps_nfc_token " command. The nfc_pw_token tool from +wpa_supplicant can be used to generate NFC password tokens during +manufacturing (each AP needs to have its own random keys). + +The "wps_nfc_config_token " command can be used to build an +NFC configuration token. The output value from this command is a hexdump +of the current AP configuration (WPS parameter requests this to include +only the WPS attributes; NDEF parameter requests additional NDEF +encapsulation to be included). This data needs to be written to an NFC +tag with an external program. Once written, the NFC configuration token +can be used to touch an NFC interface on a station to provision the +credentials needed to access the network. + +When the NFC device on the AP reads an NFC tag with a MIME media type +"application/vnd.wfa.wsc", the NDEF message payload (with or without +NDEF encapsulation) can be delivered to hostapd using the +following hostapd_cli command: + +wps_nfc_tag_read + +If the NFC tag contains a password token, the token is added to the +internal Registrar. This allows station Enrollee from which the password +token was received to run through WPS protocol to provision the +credential. + +"nfc_get_handover_sel " command can be used to build the +contents of a Handover Select Message for connection handover when this +does not depend on the contents of the Handover Request Message. The +first argument selects the format of the output data and the second +argument selects which type of connection handover is requested (WPS = +Wi-Fi handover as specified in WSC 2.0). + +"nfc_report_handover WPS +" is used to report completed NFC +connection handover. The first parameter indicates whether the local +device initiated or responded to the connection handover and the carrier +records are the selected carrier from the handover request and select +messages as a hexdump. diff --git a/contrib/hostapd/hostapd/accounting.h b/contrib/hostapd/hostapd/accounting.h deleted file mode 100644 index 51e6b4d498..0000000000 --- a/contrib/hostapd/hostapd/accounting.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * hostapd / RADIUS Accounting - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef ACCOUNTING_H -#define ACCOUNTING_H - -void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); -void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta); -void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); -int accounting_init(struct hostapd_data *hapd); -void accounting_deinit(struct hostapd_data *hapd); -int accounting_reconfig(struct hostapd_data *hapd, - struct hostapd_config *oldconf); - -#endif /* ACCOUNTING_H */ diff --git a/contrib/hostapd/hostapd/ap.h b/contrib/hostapd/hostapd/ap.h deleted file mode 100644 index 2c6d7e9799..0000000000 --- a/contrib/hostapd/hostapd/ap.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * hostapd / Station table data structures - * Copyright (c) 2002-2008, Jouni Malinen - * Copyright (c) 2007-2008, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef AP_H -#define AP_H - -#ifdef CONFIG_IEEE80211N -#include "ieee802_11_defs.h" -#endif /* CONFIG_IEEE80211N */ - -/* STA flags */ -#define WLAN_STA_AUTH BIT(0) -#define WLAN_STA_ASSOC BIT(1) -#define WLAN_STA_PS BIT(2) -#define WLAN_STA_TIM BIT(3) -#define WLAN_STA_PERM BIT(4) -#define WLAN_STA_AUTHORIZED BIT(5) -#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ -#define WLAN_STA_SHORT_PREAMBLE BIT(7) -#define WLAN_STA_PREAUTH BIT(8) -#define WLAN_STA_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_NONERP BIT(31) - -/* Maximum number of supported rates (from both Supported Rates and Extended - * Supported Rates IEs). */ -#define WLAN_SUPP_RATES_MAX 32 - - -struct sta_info { - struct sta_info *next; /* next entry in sta list */ - struct sta_info *hnext; /* next entry in hash table list */ - u8 addr[6]; - u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ - u32 flags; - u16 capability; - u16 listen_interval; /* or beacon_int for APs */ - u8 supported_rates[WLAN_SUPP_RATES_MAX]; - int supported_rates_len; - - unsigned int nonerp_set:1; - unsigned int no_short_slot_time_set:1; - unsigned int no_short_preamble_set:1; - unsigned int no_ht_gf_set:1; - unsigned int no_ht_set:1; - unsigned int ht_20mhz_set:1; - - u16 auth_alg; - u8 previous_ap[6]; - - enum { - STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE - } timeout_next; - - /* IEEE 802.1X related data */ - struct eapol_state_machine *eapol_sm; - - /* IEEE 802.11f (IAPP) related data */ - struct ieee80211_mgmt *last_assoc_req; - - u32 acct_session_id_hi; - u32 acct_session_id_lo; - time_t acct_session_start; - int acct_session_started; - int acct_terminate_cause; /* Acct-Terminate-Cause */ - int acct_interim_interval; /* Acct-Interim-Interval */ - - unsigned long last_rx_bytes; - unsigned long last_tx_bytes; - u32 acct_input_gigawords; /* Acct-Input-Gigawords */ - u32 acct_output_gigawords; /* Acct-Output-Gigawords */ - - u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ - - struct wpa_state_machine *wpa_sm; - struct rsn_preauth_interface *preauth_iface; - - struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */ - struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ - - int vlan_id; - -#ifdef CONFIG_IEEE80211N - struct ht_cap_ie ht_capabilities; /* IEEE 802.11n capabilities */ -#endif /* CONFIG_IEEE80211N */ - -#ifdef CONFIG_IEEE80211W - int sa_query_count; /* number of pending SA Query requests; - * 0 = no SA Query in progress */ - int sa_query_timed_out; - u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * - * sa_query_count octets of pending SA Query - * transaction identifiers */ - struct os_time sa_query_start; -#endif /* CONFIG_IEEE80211W */ - - struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ -}; - - -/* Maximum number of AIDs to use for STAs; must be 2007 or lower - * (8802.11 limitation) */ -#define MAX_AID_TABLE_SIZE 128 - -#define STA_HASH_SIZE 256 -#define STA_HASH(sta) (sta[5]) - - -/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has - * passed since last received frame from the station, a nullfunc data frame is - * sent to the station. If this frame is not acknowledged and no other frames - * have been received, the station will be disassociated after - * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated - * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ -#define AP_MAX_INACTIVITY (5 * 60) -#define AP_DISASSOC_DELAY (1) -#define AP_DEAUTH_DELAY (1) -/* Number of seconds to keep STA entry with Authenticated flag after it has - * been disassociated. */ -#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30) -/* Number of seconds to keep STA entry after it has been deauthenticated. */ -#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) - -#endif /* AP_H */ diff --git a/contrib/hostapd/hostapd/ap_list.c b/contrib/hostapd/hostapd/ap_list.c deleted file mode 100644 index 4f217dc541..0000000000 --- a/contrib/hostapd/hostapd/ap_list.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * hostapd / AP table - * Copyright (c) 2002-2003, Jouni Malinen - * Copyright (c) 2003-2004, Instant802 Networks, Inc. - * Copyright (c) 2006, Devicescape Software, Inc. - * Copyright (c) 2007-2008, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "hostapd.h" -#include "ieee802_11.h" -#include "eloop.h" -#include "ap_list.h" -#include "hw_features.h" -#include "beacon.h" - - -struct ieee80211_frame_info { - u32 version; - u32 length; - u64 mactime; - u64 hosttime; - u32 phytype; - u32 channel; - u32 datarate; - u32 antenna; - u32 priority; - u32 ssi_type; - u32 ssi_signal; - u32 ssi_noise; - u32 preamble; - u32 encoding; - - /* Note: this structure is otherwise identical to capture format used - * in linux-wlan-ng, but this additional field is used to provide meta - * data about the frame to hostapd. This was the easiest method for - * providing this information, but this might change in the future. */ - u32 msg_type; -} __attribute__ ((packed)); - - -enum ieee80211_phytype { - ieee80211_phytype_fhss_dot11_97 = 1, - ieee80211_phytype_dsss_dot11_97 = 2, - ieee80211_phytype_irbaseband = 3, - ieee80211_phytype_dsss_dot11_b = 4, - ieee80211_phytype_pbcc_dot11_b = 5, - ieee80211_phytype_ofdm_dot11_g = 6, - ieee80211_phytype_pbcc_dot11_g = 7, - ieee80211_phytype_ofdm_dot11_a = 8, - ieee80211_phytype_dsss_dot11_turbog = 255, - ieee80211_phytype_dsss_dot11_turbo = 256, -}; - - -/* AP list is a double linked list with head->prev pointing to the end of the - * list and tail->next = NULL. Entries are moved to the head of the list - * whenever a beacon has been received from the AP in question. The tail entry - * in this link will thus be the least recently used entry. */ - - -static void ap_list_new_ap(struct hostapd_iface *iface, struct ap_info *ap) -{ - wpa_printf(MSG_DEBUG, "New AP detected: " MACSTR, MAC2STR(ap->addr)); - - /* TODO: could send a notification message to an external program that - * would then determine whether a rogue AP has been detected */ -} - - -static void ap_list_expired_ap(struct hostapd_iface *iface, struct ap_info *ap) -{ - wpa_printf(MSG_DEBUG, "AP info expired: " MACSTR, MAC2STR(ap->addr)); - - /* TODO: could send a notification message to an external program */ -} - - -static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) -{ - int i; - - if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || - ap->phytype != ieee80211_phytype_pbcc_dot11_g || - iface->conf->channel != ap->channel) - return 0; - - if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT)) - return 1; - - for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { - int rate = (ap->supported_rates[i] & 0x7f) * 5; - if (rate == 60 || rate == 90 || rate > 110) - return 0; - } - - return 1; -} - - -#ifdef CONFIG_IEEE80211N -static int ap_list_beacon_olbc_ht(struct hostapd_iface *iface, - struct ap_info *ap) -{ - return !ap->ht_support; -} -#endif /* CONFIG_IEEE80211N */ - - -struct ap_info * ap_get_ap(struct hostapd_iface *iface, u8 *ap) -{ - struct ap_info *s; - - s = iface->ap_hash[STA_HASH(ap)]; - while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0) - s = s->hnext; - return s; -} - - -static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap) -{ - if (iface->ap_list) { - ap->prev = iface->ap_list->prev; - iface->ap_list->prev = ap; - } else - ap->prev = ap; - ap->next = iface->ap_list; - iface->ap_list = ap; -} - - -static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap) -{ - if (iface->ap_list == ap) - iface->ap_list = ap->next; - else - ap->prev->next = ap->next; - - if (ap->next) - ap->next->prev = ap->prev; - else if (iface->ap_list) - iface->ap_list->prev = ap->prev; -} - - -static void ap_ap_iter_list_add(struct hostapd_iface *iface, - struct ap_info *ap) -{ - if (iface->ap_iter_list) { - ap->iter_prev = iface->ap_iter_list->iter_prev; - iface->ap_iter_list->iter_prev = ap; - } else - ap->iter_prev = ap; - ap->iter_next = iface->ap_iter_list; - iface->ap_iter_list = ap; -} - - -static void ap_ap_iter_list_del(struct hostapd_iface *iface, - struct ap_info *ap) -{ - if (iface->ap_iter_list == ap) - iface->ap_iter_list = ap->iter_next; - else - ap->iter_prev->iter_next = ap->iter_next; - - if (ap->iter_next) - ap->iter_next->iter_prev = ap->iter_prev; - else if (iface->ap_iter_list) - iface->ap_iter_list->iter_prev = ap->iter_prev; -} - - -static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap) -{ - ap->hnext = iface->ap_hash[STA_HASH(ap->addr)]; - iface->ap_hash[STA_HASH(ap->addr)] = ap; -} - - -static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) -{ - struct ap_info *s; - - s = iface->ap_hash[STA_HASH(ap->addr)]; - if (s == NULL) return; - if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) { - iface->ap_hash[STA_HASH(ap->addr)] = s->hnext; - return; - } - - while (s->hnext != NULL && - os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0) - s = s->hnext; - if (s->hnext != NULL) - s->hnext = s->hnext->hnext; - else - printf("AP: could not remove AP " MACSTR " from hash table\n", - MAC2STR(ap->addr)); -} - - -static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap) -{ - ap_ap_hash_del(iface, ap); - ap_ap_list_del(iface, ap); - ap_ap_iter_list_del(iface, ap); - - iface->num_ap--; - os_free(ap); -} - - -static void hostapd_free_aps(struct hostapd_iface *iface) -{ - struct ap_info *ap, *prev; - - ap = iface->ap_list; - - while (ap) { - prev = ap; - ap = ap->next; - ap_free_ap(iface, prev); - } - - iface->ap_list = NULL; -} - - -int ap_ap_for_each(struct hostapd_iface *iface, - int (*func)(struct ap_info *s, void *data), void *data) -{ - struct ap_info *s; - int ret = 0; - - s = iface->ap_list; - - while (s) { - ret = func(s, data); - if (ret) - break; - s = s->next; - } - - return ret; -} - - -static struct ap_info * ap_ap_add(struct hostapd_iface *iface, u8 *addr) -{ - struct ap_info *ap; - - ap = os_zalloc(sizeof(struct ap_info)); - if (ap == NULL) - return NULL; - - /* initialize AP info data */ - os_memcpy(ap->addr, addr, ETH_ALEN); - ap_ap_list_add(iface, ap); - iface->num_ap++; - ap_ap_hash_add(iface, ap); - ap_ap_iter_list_add(iface, ap); - - if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) { - wpa_printf(MSG_DEBUG, "Removing the least recently used AP " - MACSTR " from AP table", MAC2STR(ap->prev->addr)); - if (iface->conf->passive_scan_interval > 0) - ap_list_expired_ap(iface, ap->prev); - ap_free_ap(iface, ap->prev); - } - - return ap; -} - - -void ap_list_process_beacon(struct hostapd_iface *iface, - struct ieee80211_mgmt *mgmt, - struct ieee802_11_elems *elems, - struct hostapd_frame_info *fi) -{ - struct ap_info *ap; - int new_ap = 0; - size_t len; - int set_beacon = 0; - - if (iface->conf->ap_table_max_size < 1) - return; - - ap = ap_get_ap(iface, mgmt->bssid); - if (!ap) { - ap = ap_ap_add(iface, mgmt->bssid); - if (!ap) { - printf("Failed to allocate AP information entry\n"); - return; - } - new_ap = 1; - } - - ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); - ap->capability = le_to_host16(mgmt->u.beacon.capab_info); - - if (elems->ssid) { - len = elems->ssid_len; - if (len >= sizeof(ap->ssid)) - len = sizeof(ap->ssid) - 1; - os_memcpy(ap->ssid, elems->ssid, len); - ap->ssid[len] = '\0'; - ap->ssid_len = len; - } - - os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX); - len = 0; - if (elems->supp_rates) { - len = elems->supp_rates_len; - if (len > WLAN_SUPP_RATES_MAX) - len = WLAN_SUPP_RATES_MAX; - os_memcpy(ap->supported_rates, elems->supp_rates, len); - } - if (elems->ext_supp_rates) { - int len2; - if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX) - len2 = WLAN_SUPP_RATES_MAX - len; - else - len2 = elems->ext_supp_rates_len; - os_memcpy(ap->supported_rates + len, elems->ext_supp_rates, - len2); - } - - ap->wpa = elems->wpa_ie != NULL; - - if (elems->erp_info && elems->erp_info_len == 1) - ap->erp = elems->erp_info[0]; - else - ap->erp = -1; - - if (elems->ds_params && elems->ds_params_len == 1) - ap->channel = elems->ds_params[0]; - else if (fi) - ap->channel = fi->channel; - - if (elems->ht_capabilities) - ap->ht_support = 1; - else - ap->ht_support = 0; - - ap->num_beacons++; - time(&ap->last_beacon); - if (fi) { - ap->phytype = fi->phytype; - ap->ssi_signal = fi->ssi_signal; - ap->datarate = fi->datarate; - } - - if (new_ap) { - if (iface->conf->passive_scan_interval > 0) - ap_list_new_ap(iface, ap); - } else if (ap != iface->ap_list) { - /* move AP entry into the beginning of the list so that the - * oldest entry is always in the end of the list */ - ap_ap_list_del(iface, ap); - ap_ap_list_add(iface, ap); - } - - if (!iface->olbc && - ap_list_beacon_olbc(iface, ap)) { - iface->olbc = 1; - wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable " - "protection", MAC2STR(ap->addr)); - set_beacon++; - } - -#ifdef CONFIG_IEEE80211N - if (!iface->olbc_ht && ap_list_beacon_olbc_ht(iface, ap)) { - iface->olbc_ht = 1; - hostapd_ht_operation_update(iface); - wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR - " - enable protection", MAC2STR(ap->addr)); - set_beacon++; - } -#endif /* CONFIG_IEEE80211N */ - - if (set_beacon) - ieee802_11_set_beacons(iface); -} - - -static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_iface *iface = eloop_ctx; - time_t now; - struct ap_info *ap; - int set_beacon = 0; - - eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); - - if (!iface->ap_list) - return; - - time(&now); - - /* FIX: it looks like jkm-Purina ended up in busy loop in this - * function. Apparently, something can still cause a loop in the AP - * list.. */ - - while (iface->ap_list) { - ap = iface->ap_list->prev; - if (ap->last_beacon + iface->conf->ap_table_expiration_time >= - now) - break; - - if (iface->conf->passive_scan_interval > 0) - ap_list_expired_ap(iface, ap); - ap_free_ap(iface, ap); - } - - if (iface->olbc || iface->olbc_ht) { - int olbc = 0; - int olbc_ht = 0; - - ap = iface->ap_list; - while (ap && (olbc == 0 || olbc_ht == 0)) { - if (ap_list_beacon_olbc(iface, ap)) - olbc = 1; -#ifdef CONFIG_IEEE80211N - if (ap_list_beacon_olbc_ht(iface, ap)) - olbc_ht = 1; -#endif /* CONFIG_IEEE80211N */ - ap = ap->next; - } - if (!olbc && iface->olbc) { - wpa_printf(MSG_DEBUG, "OLBC not detected anymore"); - iface->olbc = 0; - set_beacon++; - } -#ifdef CONFIG_IEEE80211N - if (!olbc_ht && iface->olbc_ht) { - wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore"); - iface->olbc_ht = 0; - hostapd_ht_operation_update(iface); - set_beacon++; - } -#endif /* CONFIG_IEEE80211N */ - } - - if (set_beacon) - ieee802_11_set_beacons(iface); -} - - -int ap_list_init(struct hostapd_iface *iface) -{ - eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); - return 0; -} - - -void ap_list_deinit(struct hostapd_iface *iface) -{ - eloop_cancel_timeout(ap_list_timer, iface, NULL); - hostapd_free_aps(iface); -} - - -int ap_list_reconfig(struct hostapd_iface *iface, - struct hostapd_config *oldconf) -{ - time_t now; - struct ap_info *ap; - - if (iface->conf->ap_table_max_size == oldconf->ap_table_max_size && - iface->conf->ap_table_expiration_time == - oldconf->ap_table_expiration_time) - return 0; - - time(&now); - - while (iface->ap_list) { - ap = iface->ap_list->prev; - if (iface->num_ap <= iface->conf->ap_table_max_size && - ap->last_beacon + iface->conf->ap_table_expiration_time >= - now) - break; - - if (iface->conf->passive_scan_interval > 0) - ap_list_expired_ap(iface, iface->ap_list->prev); - ap_free_ap(iface, iface->ap_list->prev); - } - - return 0; -} diff --git a/contrib/hostapd/hostapd/ap_list.h b/contrib/hostapd/hostapd/ap_list.h deleted file mode 100644 index 93704f8bb6..0000000000 --- a/contrib/hostapd/hostapd/ap_list.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * hostapd / AP table - * Copyright (c) 2002-2003, Jouni Malinen - * Copyright (c) 2003-2004, Instant802 Networks, Inc. - * Copyright (c) 2006, Devicescape Software, Inc. - * Copyright (c) 2007-2008, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef AP_LIST_H -#define AP_LIST_H - -struct ap_info { - /* Note: next/prev pointers are updated whenever a new beacon is - * received because these are used to find the least recently used - * entries. iter_next/iter_prev are updated only when adding new BSSes - * and when removing old ones. These should be used when iterating - * through the table in a manner that allows beacons to be received - * during the iteration. */ - struct ap_info *next; /* next entry in AP list */ - struct ap_info *prev; /* previous entry in AP list */ - struct ap_info *hnext; /* next entry in hash table list */ - struct ap_info *iter_next; /* next entry in AP iteration list */ - struct ap_info *iter_prev; /* previous entry in AP iteration list */ - u8 addr[6]; - u16 beacon_int; - u16 capability; - u8 supported_rates[WLAN_SUPP_RATES_MAX]; - u8 ssid[33]; - size_t ssid_len; - int wpa; - int erp; /* ERP Info or -1 if ERP info element not present */ - - int phytype; /* .11a / .11b / .11g / Atheros Turbo */ - int channel; - int datarate; /* in 100 kbps */ - int ssi_signal; - - int ht_support; - - unsigned int num_beacons; /* number of beacon frames received */ - time_t last_beacon; - - int already_seen; /* whether API call AP-NEW has already fetched - * information about this AP */ -}; - -struct ieee802_11_elems; -struct hostapd_frame_info; - -struct ap_info * ap_get_ap(struct hostapd_iface *iface, u8 *sta); -int ap_ap_for_each(struct hostapd_iface *iface, - int (*func)(struct ap_info *s, void *data), void *data); -void ap_list_process_beacon(struct hostapd_iface *iface, - struct ieee80211_mgmt *mgmt, - struct ieee802_11_elems *elems, - struct hostapd_frame_info *fi); -int ap_list_init(struct hostapd_iface *iface); -void ap_list_deinit(struct hostapd_iface *iface); -int ap_list_reconfig(struct hostapd_iface *iface, - struct hostapd_config *oldconf); - -#endif /* AP_LIST_H */ diff --git a/contrib/hostapd/hostapd/beacon.c b/contrib/hostapd/hostapd/beacon.c deleted file mode 100644 index 1f82d9cb72..0000000000 --- a/contrib/hostapd/hostapd/beacon.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response - * Copyright (c) 2002-2004, Instant802 Networks, Inc. - * Copyright (c) 2005-2006, Devicescape Software, Inc. - * Copyright (c) 2008, Jouni Malinen - * Copyright (c) 2007-2008, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#ifndef CONFIG_NATIVE_WINDOWS - -#include "hostapd.h" -#include "ieee802_11.h" -#include "wpa.h" -#include "wme.h" -#include "beacon.h" -#include "hw_features.h" -#include "driver.h" -#include "sta_info.h" -#include "wps_hostapd.h" - - -static u8 ieee802_11_erp_info(struct hostapd_data *hapd) -{ - u8 erp = 0; - - if (hapd->iface->current_mode == NULL || - hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) - return 0; - - switch (hapd->iconf->cts_protection_type) { - case CTS_PROTECTION_FORCE_ENABLED: - erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION; - break; - case CTS_PROTECTION_FORCE_DISABLED: - erp = 0; - break; - case CTS_PROTECTION_AUTOMATIC: - if (hapd->iface->olbc) - erp |= ERP_INFO_USE_PROTECTION; - /* continue */ - case CTS_PROTECTION_AUTOMATIC_NO_OLBC: - if (hapd->iface->num_sta_non_erp > 0) { - erp |= ERP_INFO_NON_ERP_PRESENT | - ERP_INFO_USE_PROTECTION; - } - break; - } - if (hapd->iface->num_sta_no_short_preamble > 0) - erp |= ERP_INFO_BARKER_PREAMBLE_MODE; - - return erp; -} - - -static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid) -{ - *eid++ = WLAN_EID_DS_PARAMS; - *eid++ = 1; - *eid++ = hapd->iconf->channel; - return eid; -} - - -static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) -{ - if (hapd->iface->current_mode == NULL || - hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) - return eid; - - /* Set NonERP_present and use_protection bits if there - * are any associated NonERP stations. */ - /* TODO: use_protection bit can be set to zero even if - * there are NonERP stations present. This optimization - * might be useful if NonERP stations are "quiet". - * See 802.11g/D6 E-1 for recommended practice. - * In addition, Non ERP present might be set, if AP detects Non ERP - * operation on other APs. */ - - /* Add ERP Information element */ - *eid++ = WLAN_EID_ERP_INFO; - *eid++ = 1; - *eid++ = ieee802_11_erp_info(hapd); - - return eid; -} - - -static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing, - struct hostapd_channel_data *start, - struct hostapd_channel_data *prev) -{ - if (end - pos < 3) - return pos; - - /* first channel number */ - *pos++ = start->chan; - /* number of channels */ - *pos++ = (prev->chan - start->chan) / chan_spacing + 1; - /* maximum transmit power level */ - *pos++ = start->max_tx_power; - - return pos; -} - - -static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, - int max_len) -{ - u8 *pos = eid; - u8 *end = eid + max_len; - int i; - struct hostapd_hw_modes *mode; - struct hostapd_channel_data *start, *prev; - int chan_spacing = 1; - - if (!hapd->iconf->ieee80211d || max_len < 6 || - hapd->iface->current_mode == NULL) - return eid; - - *pos++ = WLAN_EID_COUNTRY; - pos++; /* length will be set later */ - os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */ - pos += 3; - - mode = hapd->iface->current_mode; - if (mode->mode == HOSTAPD_MODE_IEEE80211A) - chan_spacing = 4; - - start = prev = NULL; - for (i = 0; i < mode->num_channels; i++) { - struct hostapd_channel_data *chan = &mode->channels[i]; - if (chan->flag & HOSTAPD_CHAN_DISABLED) - continue; - if (start && prev && - prev->chan + chan_spacing == chan->chan && - start->max_tx_power == chan->max_tx_power) { - prev = chan; - continue; /* can use same entry */ - } - - if (start) { - pos = hostapd_eid_country_add(pos, end, chan_spacing, - start, prev); - start = NULL; - } - - /* Start new group */ - start = prev = chan; - } - - if (start) { - pos = hostapd_eid_country_add(pos, end, chan_spacing, - start, prev); - } - - if ((pos - eid) & 1) { - if (end - pos < 1) - return eid; - *pos++ = 0; /* pad for 16-bit alignment */ - } - - eid[1] = (pos - eid) - 2; - - return pos; -} - - -static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len, - struct sta_info *sta) -{ - const u8 *ie; - size_t ielen; - - ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen); - if (ie == NULL || ielen > len) - return eid; - - os_memcpy(eid, ie, ielen); - return eid + ielen; -} - - -void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, - size_t len) -{ - struct ieee80211_mgmt *resp; - struct ieee802_11_elems elems; - char *ssid; - u8 *pos, *epos, *ie; - size_t ssid_len, ie_len; - struct sta_info *sta = NULL; - - ie = mgmt->u.probe_req.variable; - ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); - - hostapd_wps_probe_req_rx(hapd, mgmt->sa, ie, ie_len); - - if (!hapd->iconf->send_probe_response) - return; - - if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { - wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR, - MAC2STR(mgmt->sa)); - return; - } - - ssid = NULL; - ssid_len = 0; - - if ((!elems.ssid || !elems.supp_rates)) { - wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request " - "without SSID or supported rates element", - MAC2STR(mgmt->sa)); - return; - } - - if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) { - wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " - "broadcast SSID ignored", MAC2STR(mgmt->sa)); - return; - } - - sta = ap_get_sta(hapd, mgmt->sa); - - if (elems.ssid_len == 0 || - (elems.ssid_len == hapd->conf->ssid.ssid_len && - os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) == - 0)) { - ssid = hapd->conf->ssid.ssid; - ssid_len = hapd->conf->ssid.ssid_len; - if (sta) - sta->ssid_probe = &hapd->conf->ssid; - } - - if (!ssid) { - if (!(mgmt->da[0] & 0x01)) { - char ssid_txt[33]; - ieee802_11_print_ssid(ssid_txt, elems.ssid, - elems.ssid_len); - wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR - " for foreign SSID '%s'", - MAC2STR(mgmt->sa), ssid_txt); - } - return; - } - - /* TODO: verify that supp_rates contains at least one matching rate - * with AP configuration */ -#define MAX_PROBERESP_LEN 768 - resp = os_zalloc(MAX_PROBERESP_LEN); - if (resp == NULL) - return; - epos = ((u8 *) resp) + MAX_PROBERESP_LEN; - - resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_PROBE_RESP); - os_memcpy(resp->da, mgmt->sa, ETH_ALEN); - os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); - - os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); - resp->u.probe_resp.beacon_int = - host_to_le16(hapd->iconf->beacon_int); - - /* hardware or low-level driver will setup seq_ctrl and timestamp */ - resp->u.probe_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); - - pos = resp->u.probe_resp.variable; - *pos++ = WLAN_EID_SSID; - *pos++ = ssid_len; - os_memcpy(pos, ssid, ssid_len); - pos += ssid_len; - - /* Supported rates */ - pos = hostapd_eid_supp_rates(hapd, pos); - - /* DS Params */ - pos = hostapd_eid_ds_params(hapd, pos); - - pos = hostapd_eid_country(hapd, pos, epos - pos); - - /* ERP Information element */ - pos = hostapd_eid_erp_info(hapd, pos); - - /* Extended supported rates */ - pos = hostapd_eid_ext_supp_rates(hapd, pos); - - pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta); - - /* Wi-Fi Alliance WMM */ - pos = hostapd_eid_wmm(hapd, pos); - - pos = hostapd_eid_ht_capabilities_info(hapd, pos); - pos = hostapd_eid_ht_operation(hapd, pos); - -#ifdef CONFIG_WPS - if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) { - os_memcpy(pos, hapd->wps_probe_resp_ie, - hapd->wps_probe_resp_ie_len); - pos += hapd->wps_probe_resp_ie_len; - } -#endif /* CONFIG_WPS */ - - if (hostapd_send_mgmt_frame(hapd, resp, pos - (u8 *) resp, 0) < 0) - perror("handle_probe_req: send"); - - os_free(resp); - - wpa_printf(MSG_MSGDUMP, "STA " MACSTR " sent probe request for %s " - "SSID", MAC2STR(mgmt->sa), - elems.ssid_len == 0 ? "broadcast" : "our"); -} - - -void ieee802_11_set_beacon(struct hostapd_data *hapd) -{ - struct ieee80211_mgmt *head; - u8 *pos, *tail, *tailpos; - int preamble; - u16 capab_info; - size_t head_len, tail_len; - int cts_protection = ((ieee802_11_erp_info(hapd) & - ERP_INFO_USE_PROTECTION) ? 1 : 0); - -#define BEACON_HEAD_BUF_SIZE 256 -#define BEACON_TAIL_BUF_SIZE 512 - head = os_zalloc(BEACON_HEAD_BUF_SIZE); - tailpos = tail = os_malloc(BEACON_TAIL_BUF_SIZE); - if (head == NULL || tail == NULL) { - wpa_printf(MSG_ERROR, "Failed to set beacon data"); - os_free(head); - os_free(tail); - return; - } - - head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_BEACON); - head->duration = host_to_le16(0); - os_memset(head->da, 0xff, ETH_ALEN); - - os_memcpy(head->sa, hapd->own_addr, ETH_ALEN); - os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN); - head->u.beacon.beacon_int = - host_to_le16(hapd->iconf->beacon_int); - - /* hardware or low-level driver will setup seq_ctrl and timestamp */ - capab_info = hostapd_own_capab_info(hapd, NULL, 0); - head->u.beacon.capab_info = host_to_le16(capab_info); - pos = &head->u.beacon.variable[0]; - - /* SSID */ - *pos++ = WLAN_EID_SSID; - if (hapd->conf->ignore_broadcast_ssid == 2) { - /* clear the data, but keep the correct length of the SSID */ - *pos++ = hapd->conf->ssid.ssid_len; - os_memset(pos, 0, hapd->conf->ssid.ssid_len); - pos += hapd->conf->ssid.ssid_len; - } else if (hapd->conf->ignore_broadcast_ssid) { - *pos++ = 0; /* empty SSID */ - } else { - *pos++ = hapd->conf->ssid.ssid_len; - os_memcpy(pos, hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len); - pos += hapd->conf->ssid.ssid_len; - } - - /* Supported rates */ - pos = hostapd_eid_supp_rates(hapd, pos); - - /* DS Params */ - pos = hostapd_eid_ds_params(hapd, pos); - - head_len = pos - (u8 *) head; - - tailpos = hostapd_eid_country(hapd, tailpos, - tail + BEACON_TAIL_BUF_SIZE - tailpos); - - /* ERP Information element */ - tailpos = hostapd_eid_erp_info(hapd, tailpos); - - /* Extended supported rates */ - tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); - - tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - - tailpos, NULL); - - /* Wi-Fi Alliance WMM */ - tailpos = hostapd_eid_wmm(hapd, tailpos); - -#ifdef CONFIG_IEEE80211N - if (hapd->iconf->ieee80211n) { - u8 *ht_capab, *ht_oper; - ht_capab = tailpos; - tailpos = hostapd_eid_ht_capabilities_info(hapd, tailpos); - - ht_oper = tailpos; - tailpos = hostapd_eid_ht_operation(hapd, tailpos); - - if (tailpos > ht_oper && ht_oper > ht_capab && - hostapd_set_ht_params(hapd->conf->iface, hapd, - ht_capab + 2, ht_capab[1], - ht_oper + 2, ht_oper[1])) { - wpa_printf(MSG_ERROR, "Could not set HT capabilities " - "for kernel driver"); - } - } -#endif /* CONFIG_IEEE80211N */ - -#ifdef CONFIG_WPS - if (hapd->conf->wps_state && hapd->wps_beacon_ie) { - os_memcpy(tailpos, hapd->wps_beacon_ie, - hapd->wps_beacon_ie_len); - tailpos += hapd->wps_beacon_ie_len; - } -#endif /* CONFIG_WPS */ - - tail_len = tailpos > tail ? tailpos - tail : 0; - - if (hostapd_set_beacon(hapd->conf->iface, hapd, (u8 *) head, head_len, - tail, tail_len)) - wpa_printf(MSG_ERROR, "Failed to set beacon head/tail"); - - os_free(tail); - os_free(head); - - if (hostapd_set_cts_protect(hapd, cts_protection)) - wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel " - "driver"); - - if (hapd->iface->current_mode && - hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && - hostapd_set_short_slot_time(hapd, - hapd->iface->num_sta_no_short_slot_time - > 0 ? 0 : 1)) - wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option " - "in kernel driver"); - - if (hapd->iface->num_sta_no_short_preamble == 0 && - hapd->iconf->preamble == SHORT_PREAMBLE) - preamble = SHORT_PREAMBLE; - else - preamble = LONG_PREAMBLE; - if (hostapd_set_preamble(hapd, preamble)) - wpa_printf(MSG_ERROR, "Could not set preamble for kernel " - "driver"); -} - - -void ieee802_11_set_beacons(struct hostapd_iface *iface) -{ - size_t i; - for (i = 0; i < iface->num_bss; i++) - ieee802_11_set_beacon(iface->bss[i]); -} - -#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/hostapd/beacon.h b/contrib/hostapd/hostapd/beacon.h deleted file mode 100644 index 18e0da2e89..0000000000 --- a/contrib/hostapd/hostapd/beacon.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response - * Copyright (c) 2002-2004, Instant802 Networks, Inc. - * Copyright (c) 2005-2006, Devicescape Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef BEACON_H -#define BEACON_H - -void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, - size_t len); -void ieee802_11_set_beacon(struct hostapd_data *hapd); -void ieee802_11_set_beacons(struct hostapd_iface *iface); - -#endif /* BEACON_H */ diff --git a/contrib/hostapd/hostapd/config.c b/contrib/hostapd/hostapd/config_file.c similarity index 57% rename from contrib/hostapd/hostapd/config.c rename to contrib/hostapd/hostapd/config_file.c index 692b1a412e..54e4af9d38 100644 --- a/contrib/hostapd/hostapd/config.c +++ b/contrib/hostapd/hostapd/config_file.c @@ -1,39 +1,28 @@ /* - * hostapd / Configuration file - * Copyright (c) 2003-2008, Jouni Malinen - * Copyright (c) 2007-2008, Intel Corporation + * hostapd / Configuration file parser + * Copyright (c) 2003-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" #ifndef CONFIG_NATIVE_WINDOWS #include #endif /* CONFIG_NATIVE_WINDOWS */ -#include "hostapd.h" -#include "driver.h" -#include "sha1.h" +#include "utils/common.h" +#include "utils/uuid.h" +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" #include "eap_server/eap.h" #include "radius/radius_client.h" -#include "wpa_common.h" -#include "wpa.h" -#include "uuid.h" -#include "eap_common/eap_wsc_common.h" - - -#define MAX_STA_COUNT 2007 - -extern struct wpa_driver_ops *hostapd_drivers[]; +#include "ap/wpa_auth.h" +#include "ap/ap_config.h" +#include "config_file.h" +#ifndef CONFIG_NO_VLAN static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, const char *fname) { @@ -91,7 +80,7 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, return -1; } - vlan = os_malloc(sizeof(*vlan)); + vlan = os_zalloc(sizeof(*vlan)); if (vlan == NULL) { wpa_printf(MSG_ERROR, "Out of memory while reading " "VLAN interfaces from '%s'", fname); @@ -99,182 +88,17 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, return -1; } - os_memset(vlan, 0, sizeof(*vlan)); vlan->vlan_id = vlan_id; os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); - if (bss->vlan_tail) - bss->vlan_tail->next = vlan; - else - bss->vlan = vlan; - bss->vlan_tail = vlan; + vlan->next = bss->vlan; + bss->vlan = vlan; } fclose(f); return 0; } - - -static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) -{ - struct hostapd_vlan *vlan, *prev; - - vlan = bss->vlan; - prev = NULL; - while (vlan) { - prev = vlan; - vlan = vlan->next; - os_free(prev); - } - - bss->vlan = NULL; -} - - -/* convert floats with one decimal place to value*10 int, i.e., - * "1.5" will return 15 */ -static int hostapd_config_read_int10(const char *value) -{ - int i, d; - char *pos; - - i = atoi(value); - pos = os_strchr(value, '.'); - d = 0; - if (pos) { - pos++; - if (*pos >= '0' && *pos <= '9') - d = *pos - '0'; - } - - return i * 10 + d; -} - - -static void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) -{ - bss->logger_syslog_level = HOSTAPD_LEVEL_INFO; - bss->logger_stdout_level = HOSTAPD_LEVEL_INFO; - bss->logger_syslog = (unsigned int) -1; - bss->logger_stdout = (unsigned int) -1; - - bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED; - - bss->wep_rekeying_period = 300; - /* use key0 in individual key and key1 in broadcast key */ - bss->broadcast_key_idx_min = 1; - bss->broadcast_key_idx_max = 2; - bss->eap_reauth_period = 3600; - - bss->wpa_group_rekey = 600; - bss->wpa_gmk_rekey = 86400; - bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; - bss->wpa_pairwise = WPA_CIPHER_TKIP; - bss->wpa_group = WPA_CIPHER_TKIP; - bss->rsn_pairwise = 0; - - bss->max_num_sta = MAX_STA_COUNT; - - bss->dtim_period = 2; - - bss->radius_server_auth_port = 1812; - bss->ap_max_inactivity = AP_MAX_INACTIVITY; - bss->eapol_version = EAPOL_VERSION; - - bss->max_listen_interval = 65535; - -#ifdef CONFIG_IEEE80211W - bss->assoc_sa_query_max_timeout = 1000; - bss->assoc_sa_query_retry_timeout = 201; -#endif /* CONFIG_IEEE80211W */ -#ifdef EAP_FAST - /* both anonymous and authenticated provisioning */ - bss->eap_fast_prov = 3; - bss->pac_key_lifetime = 7 * 24 * 60 * 60; - bss->pac_key_refresh_time = 1 * 24 * 60 * 60; -#endif /* EAP_FAST */ -} - - -static struct hostapd_config * hostapd_config_defaults(void) -{ - struct hostapd_config *conf; - struct hostapd_bss_config *bss; - int i; - const int aCWmin = 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; - } - - /* set default driver based on configuration */ - conf->driver = hostapd_drivers[0]; - if (conf->driver == NULL) { - wpa_printf(MSG_ERROR, "No driver wrappers registered!"); - os_free(conf); - os_free(bss); - return NULL; - } - - bss->radius = os_zalloc(sizeof(*bss->radius)); - if (bss->radius == NULL) { - os_free(conf); - os_free(bss); - return NULL; - } - - hostapd_config_defaults_bss(bss); - - conf->num_bss = 1; - conf->bss = bss; - - conf->beacon_int = 100; - conf->rts_threshold = -1; /* use driver default: 2347 */ - conf->fragm_threshold = -1; /* user driver default: 2346 */ - conf->send_probe_response = 1; - conf->bridge_packets = INTERNAL_BRIDGE_DO_NOT_CONTROL; - - for (i = 0; i < NUM_TX_QUEUES; i++) - conf->tx_queue[i].aifs = -1; /* use hw default */ - - conf->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; - -#ifdef CONFIG_IEEE80211N - conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; -#endif /* CONFIG_IEEE80211N */ - - return conf; -} - - -int hostapd_mac_comp(const void *a, const void *b) -{ - return os_memcmp(a, b, sizeof(macaddr)); -} - - -int hostapd_mac_comp_empty(const void *a) -{ - macaddr empty = { 0 }; - return os_memcmp(a, empty, sizeof(macaddr)); -} +#endif /* CONFIG_NO_VLAN */ static int hostapd_acl_comp(const void *a, const void *b) @@ -336,7 +160,7 @@ static int hostapd_config_read_maclist(const char *fname, if (*pos != '\0') vlan_id = atoi(pos); - newacl = os_realloc(*acl, (*num + 1) * sizeof(**acl)); + newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); if (newacl == NULL) { wpa_printf(MSG_ERROR, "MAC list reallocation failed"); fclose(f); @@ -357,133 +181,6 @@ static int hostapd_config_read_maclist(const char *fname, } -static int hostapd_config_read_wpa_psk(const char *fname, - struct hostapd_ssid *ssid) -{ - FILE *f; - char buf[128], *pos; - int line = 0, ret = 0, len, ok; - u8 addr[ETH_ALEN]; - struct hostapd_wpa_psk *psk; - - if (!fname) - return 0; - - f = fopen(fname, "r"); - if (!f) { - wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", fname); - return -1; - } - - while (fgets(buf, sizeof(buf), f)) { - line++; - - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - if (buf[0] == '\0') - continue; - - if (hwaddr_aton(buf, addr)) { - wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on " - "line %d in '%s'", buf, line, fname); - ret = -1; - break; - } - - psk = os_zalloc(sizeof(*psk)); - if (psk == NULL) { - wpa_printf(MSG_ERROR, "WPA PSK allocation failed"); - ret = -1; - break; - } - if (is_zero_ether_addr(addr)) - psk->group = 1; - else - os_memcpy(psk->addr, addr, ETH_ALEN); - - pos = buf + 17; - if (*pos == '\0') { - wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'", - line, fname); - os_free(psk); - ret = -1; - break; - } - pos++; - - ok = 0; - len = os_strlen(pos); - if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0) - ok = 1; - else if (len >= 8 && len < 64) { - pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len, - 4096, psk->psk, PMK_LEN); - ok = 1; - } - if (!ok) { - wpa_printf(MSG_ERROR, "Invalid PSK '%s' on line %d in " - "'%s'", pos, line, fname); - os_free(psk); - ret = -1; - break; - } - - psk->next = ssid->wpa_psk; - ssid->wpa_psk = psk; - } - - fclose(f); - - return ret; -} - - -int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf) -{ - struct hostapd_ssid *ssid = &conf->ssid; - - if (ssid->wpa_passphrase != NULL) { - if (ssid->wpa_psk != NULL) { - wpa_printf(MSG_ERROR, "Warning: both WPA PSK and " - "passphrase set. Using passphrase."); - os_free(ssid->wpa_psk); - } - ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); - if (ssid->wpa_psk == NULL) { - wpa_printf(MSG_ERROR, "Unable to alloc space for PSK"); - return -1; - } - wpa_hexdump_ascii(MSG_DEBUG, "SSID", - (u8 *) ssid->ssid, ssid->ssid_len); - wpa_hexdump_ascii(MSG_DEBUG, "PSK (ASCII passphrase)", - (u8 *) ssid->wpa_passphrase, - os_strlen(ssid->wpa_passphrase)); - pbkdf2_sha1(ssid->wpa_passphrase, - ssid->ssid, ssid->ssid_len, - 4096, ssid->wpa_psk->psk, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "PSK (from passphrase)", - ssid->wpa_psk->psk, PMK_LEN); - ssid->wpa_psk->group = 1; - } - - if (ssid->wpa_psk_file) { - if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, - &conf->ssid)) - return -1; - } - - return 0; -} - - #ifdef EAP_SERVER static int hostapd_config_read_eap_user(const char *fname, struct hostapd_bss_config *conf) @@ -496,6 +193,12 @@ static int hostapd_config_read_eap_user(const char *fname, if (!fname) return 0; + if (os_strncmp(fname, "sqlite:", 7) == 0) { + os_free(conf->eap_user_sqlite); + conf->eap_user_sqlite = os_strdup(fname + 7); + return 0; + } + f = fopen(fname, "r"); if (!f) { wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", fname); @@ -620,7 +323,7 @@ static int hostapd_config_read_eap_user(const char *fname, } num_methods++; - if (num_methods >= EAP_USER_MAX_METHODS) + if (num_methods >= EAP_MAX_METHODS) break; skip_eap: if (pos3 == NULL) @@ -761,6 +464,7 @@ static int hostapd_config_read_eap_user(const char *fname, #endif /* EAP_SERVER */ +#ifndef CONFIG_NO_RADIUS static int hostapd_config_read_radius_addr(struct hostapd_radius_server **server, int *num_server, const char *val, int def_port, @@ -770,7 +474,7 @@ hostapd_config_read_radius_addr(struct hostapd_radius_server **server, int ret; static int server_index = 1; - nserv = os_realloc(*server, (*num_server + 1) * sizeof(*nserv)); + nserv = os_realloc_array(*server, *num_server + 1, sizeof(*nserv)); if (nserv == NULL) return -1; @@ -788,6 +492,101 @@ hostapd_config_read_radius_addr(struct hostapd_radius_server **server, } +static struct hostapd_radius_attr * +hostapd_parse_radius_attr(const char *value) +{ + const char *pos; + char syntax; + struct hostapd_radius_attr *attr; + size_t len; + + attr = os_zalloc(sizeof(*attr)); + if (attr == NULL) + return NULL; + + attr->type = atoi(value); + + pos = os_strchr(value, ':'); + if (pos == NULL) { + attr->val = wpabuf_alloc(1); + if (attr->val == NULL) { + os_free(attr); + return NULL; + } + wpabuf_put_u8(attr->val, 0); + return attr; + } + + pos++; + if (pos[0] == '\0' || pos[1] != ':') { + os_free(attr); + return NULL; + } + syntax = *pos++; + pos++; + + switch (syntax) { + case 's': + attr->val = wpabuf_alloc_copy(pos, os_strlen(pos)); + break; + case 'x': + len = os_strlen(pos); + if (len & 1) + break; + len /= 2; + attr->val = wpabuf_alloc(len); + if (attr->val == NULL) + break; + if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) { + wpabuf_free(attr->val); + os_free(attr); + return NULL; + } + break; + case 'd': + attr->val = wpabuf_alloc(4); + if (attr->val) + wpabuf_put_be32(attr->val, atoi(pos)); + break; + default: + os_free(attr); + return NULL; + } + + if (attr->val == NULL) { + os_free(attr); + return NULL; + } + + return attr; +} + + +static int hostapd_parse_das_client(struct hostapd_bss_config *bss, + const char *val) +{ + char *secret; + + secret = os_strchr(val, ' '); + if (secret == NULL) + return -1; + + secret++; + + if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr)) + return -1; + + os_free(bss->radius_das_shared_secret); + bss->radius_das_shared_secret = (u8 *) os_strdup(secret); + if (bss->radius_das_shared_secret == NULL) + return -1; + bss->radius_das_shared_secret_len = os_strlen(secret); + + return 0; +} +#endif /* CONFIG_NO_RADIUS */ + + static int hostapd_config_parse_key_mgmt(int line, const char *value) { int val = 0, last; @@ -824,6 +623,12 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value) else if (os_strcmp(start, "WPA-EAP-SHA256") == 0) val |= WPA_KEY_MGMT_IEEE8021X_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + else if (os_strcmp(start, "SAE") == 0) + val |= WPA_KEY_MGMT_SAE; + else if (os_strcmp(start, "FT-SAE") == 0) + val |= WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -849,47 +654,12 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value) static int hostapd_config_parse_cipher(int line, const char *value) { - int val = 0, last; - char *start, *end, *buf; - - buf = os_strdup(value); - if (buf == NULL) + int val = wpa_parse_cipher(value); + if (val < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, value); 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); @@ -899,113 +669,33 @@ static int hostapd_config_parse_cipher(int line, const char *value) } -static int hostapd_config_check_bss(struct hostapd_bss_config *bss, - struct hostapd_config *conf) +static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, + char *val) { - if (bss->ieee802_1x && !bss->eap_server && - !bss->radius->auth_servers) { - wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no " - "EAP authenticator configured)."); - return -1; - } + size_t len = os_strlen(val); - if (bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && - bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL && - bss->ssid.wpa_psk_file == NULL) { - wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase " - "is not configured."); + if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL) return -1; - } - - if (hostapd_mac_comp_empty(bss->bssid) != 0) { - size_t i; - - for (i = 0; i < conf->num_bss; i++) { - if ((&conf->bss[i] != bss) && - (hostapd_mac_comp(conf->bss[i].bssid, - bss->bssid) == 0)) { - wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR - " on interface '%s' and '%s'.", - MAC2STR(bss->bssid), - conf->bss[i].iface, bss->iface); - return -1; - } - } - } - -#ifdef CONFIG_IEEE80211R - if ((bss->wpa_key_mgmt & - (WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X)) && - (bss->nas_identifier == NULL || - os_strlen(bss->nas_identifier) < 1 || - os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) { - wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires " - "nas_identifier to be configured as a 1..48 octet " - "string"); - return -1; - } -#endif /* CONFIG_IEEE80211R */ - -#ifdef CONFIG_IEEE80211N - if (conf->ieee80211n && bss->wpa && - !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && - !(bss->rsn_pairwise & WPA_CIPHER_CCMP)) { - wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 " - "requires CCMP to be enabled"); - return -1; - } -#endif /* CONFIG_IEEE80211N */ - - return 0; -} - - -static int hostapd_config_check(struct hostapd_config *conf) -{ - size_t i; - - if (conf->ieee80211d && (!conf->country[0] || !conf->country[1])) { - wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without " - "setting the country_code"); - return -1; - } - - for (i = 0; i < conf->num_bss; i++) { - if (hostapd_config_check_bss(&conf->bss[i], conf)) - return -1; - } - - return 0; -} - - -static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, - char *val) -{ - size_t len = os_strlen(val); - - if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL) - return -1; - - if (val[0] == '"') { - if (len < 2 || val[len - 1] != '"') - return -1; - len -= 2; - wep->key[keyidx] = os_malloc(len); - if (wep->key[keyidx] == NULL) - return -1; - os_memcpy(wep->key[keyidx], val + 1, len); - wep->len[keyidx] = len; - } else { - if (len & 1) - return -1; - len /= 2; - wep->key[keyidx] = os_malloc(len); - if (wep->key[keyidx] == NULL) - return -1; - wep->len[keyidx] = len; - if (hexstr2bin(val, wep->key[keyidx], len) < 0) - return -1; + + if (val[0] == '"') { + if (len < 2 || val[len - 1] != '"') + return -1; + len -= 2; + wep->key[keyidx] = os_malloc(len); + if (wep->key[keyidx] == NULL) + return -1; + os_memcpy(wep->key[keyidx], val + 1, len); + wep->len[keyidx] = len; + } else { + if (len & 1) + return -1; + len /= 2; + wep->key[keyidx] = os_malloc(len); + if (wep->key[keyidx] == NULL) + return -1; + wep->len[keyidx] = len; + if (hexstr2bin(val, wep->key[keyidx], len) < 0) + return -1; } wep->keys_set++; @@ -1014,14 +704,14 @@ static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, } -static int hostapd_parse_rates(int **rate_list, char *val) +static int hostapd_parse_intlist(int **int_list, char *val) { int *list; int count; char *pos, *end; - os_free(*rate_list); - *rate_list = NULL; + os_free(*int_list); + *int_list = NULL; pos = val; count = 0; @@ -1048,37 +738,39 @@ static int hostapd_parse_rates(int **rate_list, char *val) } list[count] = -1; - *rate_list = list; + *int_list = list; return 0; } static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname) { - struct hostapd_bss_config *bss; + struct hostapd_bss_config **all, *bss; if (*ifname == '\0') return -1; - bss = os_realloc(conf->bss, (conf->num_bss + 1) * - sizeof(struct hostapd_bss_config)); - if (bss == NULL) { + all = os_realloc_array(conf->bss, conf->num_bss + 1, + sizeof(struct hostapd_bss_config *)); + if (all == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate memory for " "multi-BSS entry"); return -1; } - conf->bss = bss; + conf->bss = all; - bss = &(conf->bss[conf->num_bss]); - os_memset(bss, 0, sizeof(*bss)); + bss = os_zalloc(sizeof(*bss)); + if (bss == NULL) + return -1; bss->radius = os_zalloc(sizeof(*bss->radius)); if (bss->radius == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate memory for " "multi-BSS RADIUS data"); + os_free(bss); return -1; } - conf->num_bss++; + conf->bss[conf->num_bss++] = bss; conf->last_bss = bss; hostapd_config_defaults_bss(bss); @@ -1089,6 +781,26 @@ static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname) } +/* convert floats with one decimal place to value*10 int, i.e., + * "1.5" will return 15 */ +static int hostapd_config_read_int10(const char *value) +{ + int i, d; + char *pos; + + i = atoi(value); + pos = os_strchr(value, '.'); + d = 0; + if (pos) { + pos++; + if (*pos >= '0' && *pos <= '9') + d = *pos - '0'; + } + + return i * 10 + d; +} + + static int valid_cw(int cw) { return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 || @@ -1100,10 +812,7 @@ enum { IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */ IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */ IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */ - IEEE80211_TX_QUEUE_DATA3 = 3, /* used for EDCA AC_BK data */ - IEEE80211_TX_QUEUE_DATA4 = 4, - IEEE80211_TX_QUEUE_AFTER_BEACON = 6, - IEEE80211_TX_QUEUE_BEACON = 7 + IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */ }; static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, @@ -1119,17 +828,21 @@ static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') { num = pos[4] - '0'; pos += 6; - } else if (os_strncmp(pos, "after_beacon_", 13) == 0) { - num = IEEE80211_TX_QUEUE_AFTER_BEACON; - pos += 13; - } else if (os_strncmp(pos, "beacon_", 7) == 0) { - num = IEEE80211_TX_QUEUE_BEACON; - pos += 7; + } else if (os_strncmp(pos, "after_beacon_", 13) == 0 || + os_strncmp(pos, "beacon_", 7) == 0) { + wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name); + return 0; } else { wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos); return -1; } + if (num >= NUM_TX_QUEUES) { + /* for backwards compatibility, do not trigger failure */ + wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name); + return 0; + } + queue = &conf->tx_queue[num]; if (os_strcmp(pos, "aifs") == 0) { @@ -1160,80 +873,6 @@ static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name, return -1; } - queue->configured = 1; - - return 0; -} - - -static int hostapd_config_wmm_ac(struct hostapd_config *conf, char *name, - char *val) -{ - int num, v; - char *pos; - struct hostapd_wmm_ac_params *ac; - - /* skip 'wme_ac_' or 'wmm_ac_' prefix */ - pos = name + 7; - if (os_strncmp(pos, "be_", 3) == 0) { - num = 0; - pos += 3; - } else if (os_strncmp(pos, "bk_", 3) == 0) { - num = 1; - pos += 3; - } else if (os_strncmp(pos, "vi_", 3) == 0) { - num = 2; - pos += 3; - } else if (os_strncmp(pos, "vo_", 3) == 0) { - num = 3; - pos += 3; - } else { - wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); - return -1; - } - - ac = &conf->wmm_ac_params[num]; - - if (os_strcmp(pos, "aifs") == 0) { - v = atoi(val); - if (v < 1 || v > 255) { - wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); - return -1; - } - ac->aifs = v; - } else if (os_strcmp(pos, "cwmin") == 0) { - v = atoi(val); - if (v < 0 || v > 12) { - wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); - return -1; - } - ac->cwmin = v; - } else if (os_strcmp(pos, "cwmax") == 0) { - v = atoi(val); - if (v < 0 || v > 12) { - wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); - return -1; - } - ac->cwmax = v; - } else if (os_strcmp(pos, "txop_limit") == 0) { - v = atoi(val); - if (v < 0 || v > 0xffff) { - wpa_printf(MSG_ERROR, "Invalid txop value %d", v); - return -1; - } - ac->txop_limit = v; - } else if (os_strcmp(pos, "acm") == 0) { - v = atoi(val); - if (v < 0 || v > 1) { - wpa_printf(MSG_ERROR, "Invalid acm value %d", v); - return -1; - } - ac->admission_control_mandatory = v; - } else { - wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); - return -1; - } - return 0; } @@ -1389,76 +1028,588 @@ static int hostapd_config_ht_capab(struct hostapd_config *conf, #endif /* CONFIG_IEEE80211N */ -/** - * hostapd_config_read - Read and parse a configuration file - * @fname: Configuration file name (including path, if needed) - * Returns: Allocated configuration data structure - */ -struct hostapd_config * hostapd_config_read(const char *fname) +#ifdef CONFIG_IEEE80211AC +static int hostapd_config_vht_capab(struct hostapd_config *conf, + const char *capab) { - struct hostapd_config *conf; - struct hostapd_bss_config *bss; - FILE *f; - char buf[256], *pos; - int line = 0; - int errors = 0; - int pairwise; - size_t i; + if (os_strstr(capab, "[MAX-MPDU-7991]")) + conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_7991; + if (os_strstr(capab, "[MAX-MPDU-11454]")) + conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_11454; + if (os_strstr(capab, "[VHT160]")) + conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + if (os_strstr(capab, "[VHT160-80PLUS80]")) + conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + if (os_strstr(capab, "[VHT160-80PLUS80]")) + conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + if (os_strstr(capab, "[RXLDPC]")) + conf->vht_capab |= VHT_CAP_RXLDPC; + if (os_strstr(capab, "[SHORT-GI-80]")) + conf->vht_capab |= VHT_CAP_SHORT_GI_80; + if (os_strstr(capab, "[SHORT-GI-160]")) + conf->vht_capab |= VHT_CAP_SHORT_GI_160; + if (os_strstr(capab, "[TX-STBC-2BY1]")) + conf->vht_capab |= VHT_CAP_TXSTBC; + if (os_strstr(capab, "[RX-STBC-1]")) + conf->vht_capab |= VHT_CAP_RXSTBC_1; + if (os_strstr(capab, "[RX-STBC-12]")) + conf->vht_capab |= VHT_CAP_RXSTBC_2; + if (os_strstr(capab, "[RX-STBC-123]")) + conf->vht_capab |= VHT_CAP_RXSTBC_3; + if (os_strstr(capab, "[RX-STBC-1234]")) + conf->vht_capab |= VHT_CAP_RXSTBC_4; + if (os_strstr(capab, "[SU-BEAMFORMER]")) + conf->vht_capab |= VHT_CAP_SU_BEAMFORMER_CAPABLE; + if (os_strstr(capab, "[SU-BEAMFORMEE]")) + conf->vht_capab |= VHT_CAP_SU_BEAMFORMEE_CAPABLE; + if (os_strstr(capab, "[BF-ANTENNA-2]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET); + if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); + if (os_strstr(capab, "[MU-BEAMFORMER]")) + conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE; + if (os_strstr(capab, "[MU-BEAMFORMEE]")) + conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE; + if (os_strstr(capab, "[VHT-TXOP-PS]")) + conf->vht_capab |= VHT_CAP_VHT_TXOP_PS; + if (os_strstr(capab, "[HTC-VHT]")) + conf->vht_capab |= VHT_CAP_HTC_VHT; + if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP0]")) + conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT; + if (os_strstr(capab, "[VHT-LINK-ADAPT2]") && + (conf->vht_capab & VHT_CAP_HTC_VHT)) + conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB; + if (os_strstr(capab, "[VHT-LINK-ADAPT3]") && + (conf->vht_capab & VHT_CAP_HTC_VHT)) + conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB; + if (os_strstr(capab, "[RX-ANTENNA-PATTERN]")) + conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN; + if (os_strstr(capab, "[TX-ANTENNA-PATTERN]")) + conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN; + return 0; +} +#endif /* CONFIG_IEEE80211AC */ - f = fopen(fname, "r"); - if (f == NULL) { - wpa_printf(MSG_ERROR, "Could not open configuration file '%s' " - "for reading.", fname); - return NULL; + +#ifdef CONFIG_INTERWORKING +static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos, + int line) +{ + size_t len = os_strlen(pos); + u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; + + struct hostapd_roaming_consortium *rc; + + if ((len & 1) || len < 2 * 3 || len / 2 > MAX_ROAMING_CONSORTIUM_LEN || + hexstr2bin(pos, oi, len / 2)) { + wpa_printf(MSG_ERROR, "Line %d: invalid roaming_consortium " + "'%s'", line, pos); + return -1; } + len /= 2; - conf = hostapd_config_defaults(); - if (conf == NULL) { - fclose(f); - return NULL; + rc = os_realloc_array(bss->roaming_consortium, + bss->roaming_consortium_count + 1, + sizeof(struct hostapd_roaming_consortium)); + if (rc == NULL) + return -1; + + os_memcpy(rc[bss->roaming_consortium_count].oi, oi, len); + rc[bss->roaming_consortium_count].len = len; + + bss->roaming_consortium = rc; + bss->roaming_consortium_count++; + + return 0; +} + + +static int parse_lang_string(struct hostapd_lang_string **array, + unsigned int *count, char *pos) +{ + char *sep, *str = NULL; + size_t clen, nlen, slen; + struct hostapd_lang_string *ls; + int ret = -1; + + if (*pos == '"' || (*pos == 'P' && pos[1] == '"')) { + str = wpa_config_parse_string(pos, &slen); + if (!str) + return -1; + pos = str; + } + + sep = os_strchr(pos, ':'); + if (sep == NULL) + goto fail; + *sep++ = '\0'; + + clen = os_strlen(pos); + if (clen < 2 || clen > sizeof(ls->lang)) + goto fail; + nlen = os_strlen(sep); + if (nlen > 252) + goto fail; + + ls = os_realloc_array(*array, *count + 1, + sizeof(struct hostapd_lang_string)); + if (ls == NULL) + goto fail; + + *array = ls; + ls = &(*array)[*count]; + (*count)++; + + os_memset(ls->lang, 0, sizeof(ls->lang)); + os_memcpy(ls->lang, pos, clen); + ls->name_len = nlen; + os_memcpy(ls->name, sep, nlen); + + ret = 0; +fail: + os_free(str); + return ret; +} + + +static int parse_venue_name(struct hostapd_bss_config *bss, char *pos, + int line) +{ + if (parse_lang_string(&bss->venue_name, &bss->venue_name_count, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'", + line, pos); + return -1; } - bss = conf->last_bss = conf->bss; + return 0; +} - while (fgets(buf, sizeof(buf), f)) { - bss = conf->last_bss; - line++; - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; +static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf, + int line) +{ + size_t count; + char *pos; + u8 *info = NULL, *ipos; + + /* format: [;][;...] */ + + count = 1; + for (pos = buf; *pos; pos++) { + if ((*pos < '0' && *pos > '9') && *pos != ';' && *pos != ',') + goto fail; + if (*pos == ';') + count++; + } + if (1 + count * 3 > 0x7f) + goto fail; + + info = os_zalloc(2 + 3 + count * 3); + if (info == NULL) + return -1; + + ipos = info; + *ipos++ = 0; /* GUD - Version 1 */ + *ipos++ = 3 + count * 3; /* User Data Header Length (UDHL) */ + *ipos++ = 0; /* PLMN List IEI */ + /* ext(b8) | Length of PLMN List value contents(b7..1) */ + *ipos++ = 1 + count * 3; + *ipos++ = count; /* Number of PLMNs */ + + pos = buf; + while (pos && *pos) { + char *mcc, *mnc; + size_t mnc_len; + + mcc = pos; + mnc = os_strchr(pos, ','); + if (mnc == NULL) + goto fail; + *mnc++ = '\0'; + pos = os_strchr(mnc, ';'); + if (pos) + *pos++ = '\0'; + + mnc_len = os_strlen(mnc); + if (os_strlen(mcc) != 3 || (mnc_len != 2 && mnc_len != 3)) + goto fail; + + /* BC coded MCC,MNC */ + /* MCC digit 2 | MCC digit 1 */ + *ipos++ = ((mcc[1] - '0') << 4) | (mcc[0] - '0'); + /* MNC digit 3 | MCC digit 3 */ + *ipos++ = (((mnc_len == 2) ? 0xf0 : ((mnc[2] - '0') << 4))) | + (mcc[2] - '0'); + /* MNC digit 2 | MNC digit 1 */ + *ipos++ = ((mnc[1] - '0') << 4) | (mnc[0] - '0'); + } + + os_free(bss->anqp_3gpp_cell_net); + bss->anqp_3gpp_cell_net = info; + bss->anqp_3gpp_cell_net_len = 2 + 3 + 3 * count; + wpa_hexdump(MSG_MSGDUMP, "3GPP Cellular Network information", + bss->anqp_3gpp_cell_net, bss->anqp_3gpp_cell_net_len); + + return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: Invalid anqp_3gpp_cell_net: %s", + line, buf); + os_free(info); + return -1; +} + + +static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line) +{ + struct hostapd_nai_realm_data *realm; + size_t i, j, len; + int *offsets; + char *pos, *end, *rpos; + + offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS, + sizeof(int)); + if (offsets == NULL) + return -1; + + for (i = 0; i < bss->nai_realm_count; i++) { + realm = &bss->nai_realm_data[i]; + for (j = 0; j < MAX_NAI_REALMS; j++) { + offsets[i * MAX_NAI_REALMS + j] = + realm->realm[j] ? + realm->realm[j] - realm->realm_buf : -1; + } + } + + realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1, + sizeof(struct hostapd_nai_realm_data)); + if (realm == NULL) { + os_free(offsets); + return -1; + } + bss->nai_realm_data = realm; + + /* patch the pointers after realloc */ + for (i = 0; i < bss->nai_realm_count; i++) { + realm = &bss->nai_realm_data[i]; + for (j = 0; j < MAX_NAI_REALMS; j++) { + int offs = offsets[i * MAX_NAI_REALMS + j]; + if (offs >= 0) + realm->realm[j] = realm->realm_buf + offs; + else + realm->realm[j] = NULL; + } + } + os_free(offsets); + + realm = &bss->nai_realm_data[bss->nai_realm_count]; + os_memset(realm, 0, sizeof(*realm)); + + pos = buf; + realm->encoding = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + goto fail; + pos++; + + end = os_strchr(pos, ','); + if (end) { + len = end - pos; + *end = '\0'; + } else { + len = os_strlen(pos); + } + + if (len > MAX_NAI_REALMLEN) { + wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d " + "characters)", (int) len, MAX_NAI_REALMLEN); + goto fail; + } + os_memcpy(realm->realm_buf, pos, len); + + if (end) + pos = end + 1; + else + pos = NULL; + + while (pos && *pos) { + struct hostapd_nai_realm_eap *eap; + + if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) { + wpa_printf(MSG_ERROR, "Too many EAP methods"); + goto fail; + } + + eap = &realm->eap_method[realm->eap_method_count]; + realm->eap_method_count++; + + end = os_strchr(pos, ','); + if (end == NULL) + end = pos + os_strlen(pos); + + eap->eap_method = atoi(pos); + for (;;) { + pos = os_strchr(pos, '['); + if (pos == NULL || pos > end) break; - } pos++; + if (eap->num_auths >= MAX_NAI_AUTH_TYPES) { + wpa_printf(MSG_ERROR, "Too many auth params"); + goto fail; + } + eap->auth_id[eap->num_auths] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL || pos > end) + goto fail; + pos++; + eap->auth_val[eap->num_auths] = atoi(pos); + pos = os_strchr(pos, ']'); + if (pos == NULL || pos > end) + goto fail; + pos++; + eap->num_auths++; } - if (buf[0] == '\0') - continue; - pos = os_strchr(buf, '='); - if (pos == NULL) { - wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'", - line, buf); - errors++; - continue; + if (*end != ',') + break; + + pos = end + 1; + } + + /* Split realm list into null terminated realms */ + rpos = realm->realm_buf; + i = 0; + while (*rpos) { + if (i >= MAX_NAI_REALMS) { + wpa_printf(MSG_ERROR, "Too many realms"); + goto fail; } - *pos = '\0'; + realm->realm[i++] = rpos; + rpos = os_strchr(rpos, ';'); + if (rpos == NULL) + break; + *rpos++ = '\0'; + } + + bss->nai_realm_count++; + + return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf); + return -1; +} + + +static int parse_qos_map_set(struct hostapd_bss_config *bss, + char *buf, int line) +{ + u8 qos_map_set[16 + 2 * 21], count = 0; + char *pos = buf; + int val; + + for (;;) { + if (count == sizeof(qos_map_set)) { + wpa_printf(MSG_ERROR, "Line %d: Too many qos_map_set " + "parameters '%s'", line, buf); + return -1; + } + + val = atoi(pos); + if (val > 255 || val < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set " + "'%s'", line, buf); + return -1; + } + + qos_map_set[count++] = val; + pos = os_strchr(pos, ','); + if (!pos) + break; pos++; + } + + if (count < 16 || count & 1) { + wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set '%s'", + line, buf); + return -1; + } + os_memcpy(bss->qos_map_set, qos_map_set, count); + bss->qos_map_set_len = count; + + return 0; +} + +#endif /* CONFIG_INTERWORKING */ + + +#ifdef CONFIG_HS20 +static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf, + int line) +{ + u8 *conn_cap; + char *pos; + + if (bss->hs20_connection_capability_len >= 0xfff0) + return -1; + + conn_cap = os_realloc(bss->hs20_connection_capability, + bss->hs20_connection_capability_len + 4); + if (conn_cap == NULL) + return -1; + + bss->hs20_connection_capability = conn_cap; + conn_cap += bss->hs20_connection_capability_len; + pos = buf; + conn_cap[0] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + pos++; + WPA_PUT_LE16(conn_cap + 1, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + pos++; + conn_cap[3] = atoi(pos); + bss->hs20_connection_capability_len += 4; + + return 0; +} + + +static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf, + int line) +{ + u8 *wan_metrics; + char *pos; + + /* :
:
    :
    :
      : */ + + wan_metrics = os_zalloc(13); + if (wan_metrics == NULL) + return -1; + + pos = buf; + /* WAN Info */ + if (hexstr2bin(pos, wan_metrics, 1) < 0) + goto fail; + pos += 2; + if (*pos != ':') + goto fail; + pos++; + + /* Downlink Speed */ + WPA_PUT_LE32(wan_metrics + 1, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Uplink Speed */ + WPA_PUT_LE32(wan_metrics + 5, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Downlink Load */ + wan_metrics[9] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Uplink Load */ + wan_metrics[10] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* LMD */ + WPA_PUT_LE16(wan_metrics + 11, atoi(pos)); + + os_free(bss->hs20_wan_metrics); + bss->hs20_wan_metrics = wan_metrics; + + return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'", + line, pos); + os_free(wan_metrics); + return -1; +} + + +static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss, + char *pos, int line) +{ + if (parse_lang_string(&bss->hs20_oper_friendly_name, + &bss->hs20_oper_friendly_name_count, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_oper_friendly_name '%s'", line, pos); + return -1; + } + return 0; +} +#endif /* CONFIG_HS20 */ + + +#ifdef CONFIG_WPS_NFC +static struct wpabuf * hostapd_parse_bin(const char *buf) +{ + size_t len; + struct wpabuf *ret; + + len = os_strlen(buf); + if (len & 0x01) + return NULL; + len /= 2; + + ret = wpabuf_alloc(len); + if (ret == NULL) + return NULL; + + if (hexstr2bin(buf, wpabuf_put(ret, len), len)) { + wpabuf_free(ret); + return NULL; + } + + return ret; +} +#endif /* CONFIG_WPS_NFC */ + + +static int hostapd_config_fill(struct hostapd_config *conf, + struct hostapd_bss_config *bss, + char *buf, char *pos, int line) +{ + int errors = 0; + + { if (os_strcmp(buf, "interface") == 0) { - os_strlcpy(conf->bss[0].iface, pos, - sizeof(conf->bss[0].iface)); + os_strlcpy(conf->bss[0]->iface, pos, + sizeof(conf->bss[0]->iface)); } else if (os_strcmp(buf, "bridge") == 0) { os_strlcpy(bss->bridge, pos, sizeof(bss->bridge)); + } else if (os_strcmp(buf, "vlan_bridge") == 0) { + os_strlcpy(bss->vlan_bridge, pos, + sizeof(bss->vlan_bridge)); + } else if (os_strcmp(buf, "wds_bridge") == 0) { + os_strlcpy(bss->wds_bridge, pos, + sizeof(bss->wds_bridge)); } else if (os_strcmp(buf, "driver") == 0) { int j; /* clear to get error below if setting is invalid */ conf->driver = NULL; - for (j = 0; hostapd_drivers[j]; j++) { - if (os_strcmp(pos, hostapd_drivers[j]->name) == - 0) { - conf->driver = hostapd_drivers[j]; + for (j = 0; wpa_drivers[j]; j++) { + if (os_strcmp(pos, wpa_drivers[j]->name) == 0) + { + conf->driver = wpa_drivers[j]; break; } } @@ -1480,7 +1631,8 @@ struct hostapd_config * hostapd_config_read(const char *fname) } else if (os_strcmp(buf, "logger_stdout") == 0) { bss->logger_stdout = atoi(pos); } else if (os_strcmp(buf, "dump_file") == 0) { - bss->dump_log_name = os_strdup(pos); + wpa_printf(MSG_INFO, "Line %d: DEPRECATED: 'dump_file' configuration variable is not used anymore", + line); } else if (os_strcmp(buf, "ssid") == 0) { bss->ssid.ssid_len = os_strlen(pos); if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN || @@ -1491,9 +1643,24 @@ struct hostapd_config * hostapd_config_read(const char *fname) } else { os_memcpy(bss->ssid.ssid, pos, bss->ssid.ssid_len); - bss->ssid.ssid[bss->ssid.ssid_len] = '\0'; bss->ssid.ssid_set = 1; } + } else if (os_strcmp(buf, "ssid2") == 0) { + size_t slen; + char *str = wpa_config_parse_string(pos, &slen); + if (str == NULL || slen < 1 || + slen > HOSTAPD_MAX_SSID_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID " + "'%s'", line, pos); + errors++; + } else { + os_memcpy(bss->ssid.ssid, str, slen); + bss->ssid.ssid_len = slen; + bss->ssid.ssid_set = 1; + } + os_free(str); + } else if (os_strcmp(buf, "utf8_ssid") == 0) { + bss->ssid.utf8_ssid = atoi(pos) > 0; } else if (os_strcmp(buf, "macaddr_acl") == 0) { bss->macaddr_acl = atoi(pos); if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED && @@ -1520,14 +1687,24 @@ struct hostapd_config * hostapd_config_read(const char *fname) line, pos); errors++; } + } else if (os_strcmp(buf, "wds_sta") == 0) { + bss->wds_sta = atoi(pos); + } else if (os_strcmp(buf, "start_disabled") == 0) { + bss->start_disabled = atoi(pos); + } else if (os_strcmp(buf, "ap_isolate") == 0) { + bss->isolate = atoi(pos); } else if (os_strcmp(buf, "ap_max_inactivity") == 0) { bss->ap_max_inactivity = atoi(pos); + } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) { + bss->skip_inactivity_poll = atoi(pos); } else if (os_strcmp(buf, "country_code") == 0) { os_memcpy(conf->country, pos, 2); /* FIX: make this configurable */ conf->country[2] = ' '; } else if (os_strcmp(buf, "ieee80211d") == 0) { conf->ieee80211d = atoi(pos); + } else if (os_strcmp(buf, "ieee80211h") == 0) { + conf->ieee80211h = atoi(pos); } else if (os_strcmp(buf, "ieee8021x") == 0) { bss->ieee802_1x = atoi(pos); } else if (os_strcmp(buf, "eapol_version") == 0) { @@ -1566,10 +1743,15 @@ struct hostapd_config * hostapd_config_read(const char *fname) bss->private_key_passwd = os_strdup(pos); } else if (os_strcmp(buf, "check_crl") == 0) { bss->check_crl = atoi(pos); + } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { + os_free(bss->ocsp_stapling_response); + bss->ocsp_stapling_response = os_strdup(pos); } else if (os_strcmp(buf, "dh_file") == 0) { os_free(bss->dh_file); bss->dh_file = os_strdup(pos); -#ifdef EAP_FAST + } else if (os_strcmp(buf, "fragment_size") == 0) { + bss->fragment_size = atoi(pos); +#ifdef EAP_SERVER_FAST } else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) { os_free(bss->pac_opaque_encr_key); bss->pac_opaque_encr_key = os_malloc(16); @@ -1611,18 +1793,22 @@ struct hostapd_config * hostapd_config_read(const char *fname) bss->pac_key_lifetime = atoi(pos); } else if (os_strcmp(buf, "pac_key_refresh_time") == 0) { bss->pac_key_refresh_time = atoi(pos); -#endif /* EAP_FAST */ -#ifdef EAP_SIM +#endif /* EAP_SERVER_FAST */ +#ifdef EAP_SERVER_SIM } else if (os_strcmp(buf, "eap_sim_db") == 0) { os_free(bss->eap_sim_db); bss->eap_sim_db = os_strdup(pos); } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) { bss->eap_sim_aka_result_ind = atoi(pos); -#endif /* EAP_SIM */ -#ifdef EAP_TNC +#endif /* EAP_SERVER_SIM */ +#ifdef EAP_SERVER_TNC } else if (os_strcmp(buf, "tnc") == 0) { bss->tnc = atoi(pos); -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ +#ifdef EAP_SERVER_PWD + } else if (os_strcmp(buf, "pwd_group") == 0) { + bss->pwd_group = atoi(pos); +#endif /* EAP_SERVER_PWD */ #endif /* EAP_SERVER */ } else if (os_strcmp(buf, "eap_message") == 0) { char *term; @@ -1632,7 +1818,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) "allocate memory for " "eap_req_id_text", line); errors++; - continue; + return errors; } bss->eap_req_id_text_len = os_strlen(bss->eap_req_id_text); @@ -1697,6 +1883,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) } } else if (os_strcmp(buf, "nas_identifier") == 0) { bss->nas_identifier = os_strdup(pos); +#ifndef CONFIG_NO_RADIUS } else if (os_strcmp(buf, "auth_server_addr") == 0) { if (hostapd_config_read_radius_addr( &bss->radius->auth_servers, @@ -1750,7 +1937,53 @@ struct hostapd_config * hostapd_config_read(const char *fname) bss->radius->retry_primary_interval = atoi(pos); } else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) { - bss->radius->acct_interim_interval = atoi(pos); + bss->acct_interim_interval = atoi(pos); + } else if (os_strcmp(buf, "radius_request_cui") == 0) { + bss->radius_request_cui = atoi(pos); + } else if (os_strcmp(buf, "radius_auth_req_attr") == 0) { + struct hostapd_radius_attr *attr, *a; + attr = hostapd_parse_radius_attr(pos); + if (attr == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "radius_auth_req_attr", line); + errors++; + } else if (bss->radius_auth_req_attr == NULL) { + bss->radius_auth_req_attr = attr; + } else { + a = bss->radius_auth_req_attr; + while (a->next) + a = a->next; + a->next = attr; + } + } else if (os_strcmp(buf, "radius_acct_req_attr") == 0) { + struct hostapd_radius_attr *attr, *a; + attr = hostapd_parse_radius_attr(pos); + if (attr == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "radius_acct_req_attr", line); + errors++; + } else if (bss->radius_acct_req_attr == NULL) { + bss->radius_acct_req_attr = attr; + } else { + a = bss->radius_acct_req_attr; + while (a->next) + a = a->next; + a->next = attr; + } + } else if (os_strcmp(buf, "radius_das_port") == 0) { + bss->radius_das_port = atoi(pos); + } else if (os_strcmp(buf, "radius_das_client") == 0) { + if (hostapd_parse_das_client(bss, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "DAS client", line); + errors++; + } + } else if (os_strcmp(buf, "radius_das_time_window") == 0) { + bss->radius_das_time_window = atoi(pos); + } else if (os_strcmp(buf, "radius_das_require_event_timestamp") + == 0) { + bss->radius_das_require_event_timestamp = atoi(pos); +#endif /* CONFIG_NO_RADIUS */ } else if (os_strcmp(buf, "auth_algs") == 0) { bss->auth_algs = atoi(pos); if (bss->auth_algs == 0) { @@ -1789,6 +2022,11 @@ struct hostapd_config * hostapd_config_read(const char *fname) } else { os_free(bss->ssid.wpa_passphrase); bss->ssid.wpa_passphrase = os_strdup(pos); + if (bss->ssid.wpa_passphrase) { + os_free(bss->ssid.wpa_psk); + bss->ssid.wpa_psk = NULL; + bss->ssid.wpa_passphrase_set = 1; + } } } else if (os_strcmp(buf, "wpa_psk") == 0) { os_free(bss->ssid.wpa_psk); @@ -1804,6 +2042,9 @@ struct hostapd_config * hostapd_config_read(const char *fname) errors++; } else { bss->ssid.wpa_psk->group = 1; + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = NULL; + bss->ssid.wpa_psk_set = 1; } } else if (os_strcmp(buf, "wpa_psk_file") == 0) { os_free(bss->ssid.wpa_psk_file); @@ -1818,6 +2059,16 @@ struct hostapd_config * hostapd_config_read(const char *fname) hostapd_config_parse_key_mgmt(line, pos); if (bss->wpa_key_mgmt == -1) errors++; + } else if (os_strcmp(buf, "wpa_psk_radius") == 0) { + bss->wpa_psk_radius = atoi(pos); + if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED && + bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED && + bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) { + wpa_printf(MSG_ERROR, "Line %d: unknown " + "wpa_psk_radius %d", + line, bss->wpa_psk_radius); + errors++; + } } else if (os_strcmp(buf, "wpa_pairwise") == 0) { bss->wpa_pairwise = hostapd_config_parse_cipher(line, pos); @@ -1864,7 +2115,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) wpa_printf(MSG_DEBUG, "Line %d: Invalid " "mobility_domain '%s'", line, pos); errors++; - continue; + return errors; } } else if (os_strcmp(buf, "r1_key_holder") == 0) { if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN || @@ -1873,7 +2124,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) wpa_printf(MSG_DEBUG, "Line %d: Invalid " "r1_key_holder '%s'", line, pos); errors++; - continue; + return errors; } } else if (os_strcmp(buf, "r0_key_lifetime") == 0) { bss->r0_key_lifetime = atoi(pos); @@ -1884,18 +2135,21 @@ struct hostapd_config * hostapd_config_read(const char *fname) wpa_printf(MSG_DEBUG, "Line %d: Invalid " "r0kh '%s'", line, pos); errors++; - continue; + return errors; } } else if (os_strcmp(buf, "r1kh") == 0) { if (add_r1kh(bss, pos) < 0) { wpa_printf(MSG_DEBUG, "Line %d: Invalid " "r1kh '%s'", line, pos); errors++; - continue; + return errors; } } else if (os_strcmp(buf, "pmk_r1_push") == 0) { bss->pmk_r1_push = atoi(pos); + } else if (os_strcmp(buf, "ft_over_ds") == 0) { + bss->ft_over_ds = atoi(pos); #endif /* CONFIG_IEEE80211R */ +#ifndef CONFIG_NO_CTRL_IFACE } else if (os_strcmp(buf, "ctrl_interface") == 0) { os_free(bss->ctrl_interface); bss->ctrl_interface = os_strdup(pos); @@ -1912,7 +2166,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" " (from group name '%s')", bss->ctrl_interface_gid, group); - continue; + return errors; } /* Group name not found - try to parse this as gid */ @@ -1921,12 +2175,13 @@ struct hostapd_config * hostapd_config_read(const char *fname) wpa_printf(MSG_DEBUG, "Line %d: Invalid group " "'%s'", line, group); errors++; - continue; + return errors; } bss->ctrl_interface_gid_set = 1; wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", bss->ctrl_interface_gid); #endif /* CONFIG_NATIVE_WINDOWS */ +#endif /* CONFIG_NO_CTRL_IFACE */ #ifdef RADIUS_SERVER } else if (os_strcmp(buf, "radius_server_clients") == 0) { os_free(bss->radius_server_clients); @@ -1948,13 +2203,38 @@ struct hostapd_config * hostapd_config_read(const char *fname) conf->hw_mode = HOSTAPD_MODE_IEEE80211B; else if (os_strcmp(pos, "g") == 0) conf->hw_mode = HOSTAPD_MODE_IEEE80211G; + else if (os_strcmp(pos, "ad") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211AD; else { wpa_printf(MSG_ERROR, "Line %d: unknown " "hw_mode '%s'", line, pos); errors++; } + } else if (os_strcmp(buf, "wps_rf_bands") == 0) { + if (os_strcmp(pos, "a") == 0) + bss->wps_rf_bands = WPS_RF_50GHZ; + else if (os_strcmp(pos, "g") == 0 || + os_strcmp(pos, "b") == 0) + bss->wps_rf_bands = WPS_RF_24GHZ; + else if (os_strcmp(pos, "ag") == 0 || + os_strcmp(pos, "ga") == 0) + bss->wps_rf_bands = + WPS_RF_24GHZ | WPS_RF_50GHZ; + else { + wpa_printf(MSG_ERROR, "Line %d: unknown " + "wps_rf_band '%s'", line, pos); + errors++; + } } else if (os_strcmp(buf, "channel") == 0) { - conf->channel = atoi(pos); + if (os_strcmp(pos, "acs_survey") == 0) { +#ifndef CONFIG_ACS + wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled", + line); + errors++; +#endif /* CONFIG_ACS */ + conf->channel = 0; + } else + conf->channel = atoi(pos); } else if (os_strcmp(buf, "beacon_int") == 0) { int val = atoi(pos); /* MIB defines range as 1..65535, but very small values @@ -1969,6 +2249,16 @@ struct hostapd_config * hostapd_config_read(const char *fname) errors++; } else conf->beacon_int = val; +#ifdef CONFIG_ACS + } else if (os_strcmp(buf, "acs_num_scans") == 0) { + int val = atoi(pos); + if (val <= 0 || val > 100) { + wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)", + line, val); + errors++; + } else + conf->acs_num_scans = val; +#endif /* CONFIG_ACS */ } else if (os_strcmp(buf, "dtim_period") == 0) { bss->dtim_period = atoi(pos); if (bss->dtim_period < 1 || bss->dtim_period > 255) { @@ -2004,13 +2294,14 @@ struct hostapd_config * hostapd_config_read(const char *fname) } else conf->send_probe_response = val; } else if (os_strcmp(buf, "supported_rates") == 0) { - if (hostapd_parse_rates(&conf->supported_rates, pos)) { + if (hostapd_parse_intlist(&conf->supported_rates, pos)) + { wpa_printf(MSG_ERROR, "Line %d: invalid rate " "list", line); errors++; } } else if (os_strcmp(buf, "basic_rates") == 0) { - if (hostapd_parse_rates(&conf->basic_rates, pos)) { + if (hostapd_parse_intlist(&conf->basic_rates, pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid rate " "list", line); errors++; @@ -2022,8 +2313,6 @@ struct hostapd_config * hostapd_config_read(const char *fname) conf->preamble = LONG_PREAMBLE; } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) { bss->ignore_broadcast_ssid = atoi(pos); - } else if (os_strcmp(buf, "bridge_packets") == 0) { - conf->bridge_packets = atoi(pos); } else if (os_strcmp(buf, "wep_default_key") == 0) { bss->ssid.wep.idx = atoi(pos); if (bss->ssid.wep.idx > 3) { @@ -2042,6 +2331,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) "key '%s'", line, buf); errors++; } +#ifndef CONFIG_NO_VLAN } else if (os_strcmp(buf, "dynamic_vlan") == 0) { bss->ssid.dynamic_vlan = atoi(pos); } else if (os_strcmp(buf, "vlan_file") == 0) { @@ -2050,16 +2340,20 @@ struct hostapd_config * hostapd_config_read(const char *fname) "read VLAN file '%s'", line, pos); errors++; } + } else if (os_strcmp(buf, "vlan_naming") == 0) { + bss->ssid.vlan_naming = atoi(pos); + if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END || + bss->ssid.vlan_naming < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "naming scheme %d", line, + bss->ssid.vlan_naming); + errors++; + } #ifdef CONFIG_FULL_DYNAMIC_VLAN } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) { bss->ssid.vlan_tagged_interface = os_strdup(pos); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ - } else if (os_strcmp(buf, "passive_scan_interval") == 0) { - conf->passive_scan_interval = atoi(pos); - } else if (os_strcmp(buf, "passive_scan_listen") == 0) { - conf->passive_scan_listen = atoi(pos); - } else if (os_strcmp(buf, "passive_scan_mode") == 0) { - conf->passive_scan_mode = atoi(pos); +#endif /* CONFIG_NO_VLAN */ } else if (os_strcmp(buf, "ap_table_max_size") == 0) { conf->ap_table_max_size = atoi(pos); } else if (os_strcmp(buf, "ap_table_expiration_time") == 0) { @@ -2073,9 +2367,12 @@ struct hostapd_config * hostapd_config_read(const char *fname) } else if (os_strcmp(buf, "wme_enabled") == 0 || os_strcmp(buf, "wmm_enabled") == 0) { bss->wmm_enabled = atoi(pos); + } else if (os_strcmp(buf, "uapsd_advertisement_enabled") == 0) { + bss->wmm_uapsd = atoi(pos); } else if (os_strncmp(buf, "wme_ac_", 7) == 0 || os_strncmp(buf, "wmm_ac_", 7) == 0) { - if (hostapd_config_wmm_ac(conf, buf, pos)) { + if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf, + pos)) { wpa_printf(MSG_ERROR, "Line %d: invalid WMM " "ac item", line); errors++; @@ -2087,13 +2384,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) errors++; } } else if (os_strcmp(buf, "bssid") == 0) { - if (bss == conf->bss && - (!conf->driver || !conf->driver->init_bssid)) { - wpa_printf(MSG_ERROR, "Line %d: bssid item " - "not allowed for the default " - "interface and this driver", line); - errors++; - } else if (hwaddr_aton(pos, bss->bssid)) { + if (hwaddr_aton(pos, bss->bssid)) { wpa_printf(MSG_ERROR, "Line %d: invalid bssid " "item", line); errors++; @@ -2127,9 +2418,35 @@ struct hostapd_config * hostapd_config_read(const char *fname) "ht_capab", line); errors++; } + } else if (os_strcmp(buf, "require_ht") == 0) { + conf->require_ht = atoi(pos); + } else if (os_strcmp(buf, "obss_interval") == 0) { + conf->obss_interval = atoi(pos); #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_IEEE80211AC + } else if (os_strcmp(buf, "ieee80211ac") == 0) { + conf->ieee80211ac = atoi(pos); + } else if (os_strcmp(buf, "vht_capab") == 0) { + if (hostapd_config_vht_capab(conf, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "vht_capab", line); + errors++; + } + } else if (os_strcmp(buf, "require_vht") == 0) { + conf->require_vht = atoi(pos); + } else if (os_strcmp(buf, "vht_oper_chwidth") == 0) { + conf->vht_oper_chwidth = atoi(pos); + } else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0) + { + conf->vht_oper_centr_freq_seg0_idx = atoi(pos); + } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) + { + conf->vht_oper_centr_freq_seg1_idx = atoi(pos); +#endif /* CONFIG_IEEE80211AC */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); + } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) { + bss->disable_pmksa_caching = atoi(pos); } else if (os_strcmp(buf, "okc") == 0) { bss->okc = atoi(pos); #ifdef CONFIG_WPS @@ -2140,6 +2457,8 @@ struct hostapd_config * hostapd_config_read(const char *fname) "wps_state", line); errors++; } + } else if (os_strcmp(buf, "wps_independent") == 0) { + bss->wps_independent = atoi(pos); } else if (os_strcmp(buf, "ap_setup_locked") == 0) { bss->ap_setup_locked = atoi(pos); } else if (os_strcmp(buf, "uuid") == 0) { @@ -2192,8 +2511,8 @@ struct hostapd_config * hostapd_config_read(const char *fname) os_free(bss->serial_number); bss->serial_number = os_strdup(pos); } else if (os_strcmp(buf, "device_type") == 0) { - os_free(bss->device_type); - bss->device_type = os_strdup(pos); + if (wps_dev_type_str2bin(pos, bss->device_type)) + errors++; } else if (os_strcmp(buf, "config_methods") == 0) { os_free(bss->config_methods); bss->config_methods = os_strdup(pos); @@ -2247,376 +2566,465 @@ struct hostapd_config * hostapd_config_read(const char *fname) } else if (os_strcmp(buf, "upc") == 0) { os_free(bss->upc); bss->upc = os_strdup(pos); + } else if (os_strcmp(buf, "pbc_in_m1") == 0) { + bss->pbc_in_m1 = atoi(pos); + } else if (os_strcmp(buf, "server_id") == 0) { + os_free(bss->server_id); + bss->server_id = os_strdup(pos); +#ifdef CONFIG_WPS_NFC + } else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) { + bss->wps_nfc_dev_pw_id = atoi(pos); + if (bss->wps_nfc_dev_pw_id < 0x10 || + bss->wps_nfc_dev_pw_id > 0xffff) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "wps_nfc_dev_pw_id value", line); + errors++; + } + bss->wps_nfc_pw_from_config = 1; + } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) { + wpabuf_free(bss->wps_nfc_dh_pubkey); + bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos); + bss->wps_nfc_pw_from_config = 1; + } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) { + wpabuf_free(bss->wps_nfc_dh_privkey); + bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos); + bss->wps_nfc_pw_from_config = 1; + } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) { + wpabuf_free(bss->wps_nfc_dev_pw); + bss->wps_nfc_dev_pw = hostapd_parse_bin(pos); + bss->wps_nfc_pw_from_config = 1; +#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS */ - } else { - wpa_printf(MSG_ERROR, "Line %d: unknown configuration " - "item '%s'", line, buf); - errors++; - } - } - - fclose(f); +#ifdef CONFIG_P2P_MANAGER + } else if (os_strcmp(buf, "manage_p2p") == 0) { + int manage = atoi(pos); + if (manage) + bss->p2p |= P2P_MANAGE; + else + bss->p2p &= ~P2P_MANAGE; + } else if (os_strcmp(buf, "allow_cross_connection") == 0) { + if (atoi(pos)) + bss->p2p |= P2P_ALLOW_CROSS_CONNECTION; + else + bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION; +#endif /* CONFIG_P2P_MANAGER */ + } else if (os_strcmp(buf, "disassoc_low_ack") == 0) { + bss->disassoc_low_ack = atoi(pos); + } else if (os_strcmp(buf, "tdls_prohibit") == 0) { + int val = atoi(pos); + if (val) + bss->tdls |= TDLS_PROHIBIT; + else + bss->tdls &= ~TDLS_PROHIBIT; + } else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) { + int val = atoi(pos); + if (val) + bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH; + else + bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH; +#ifdef CONFIG_RSN_TESTING + } else if (os_strcmp(buf, "rsn_testing") == 0) { + extern int rsn_testing; + rsn_testing = atoi(pos); +#endif /* CONFIG_RSN_TESTING */ + } else if (os_strcmp(buf, "time_advertisement") == 0) { + bss->time_advertisement = atoi(pos); + } else if (os_strcmp(buf, "time_zone") == 0) { + size_t tz_len = os_strlen(pos); + if (tz_len < 4 || tz_len > 255) { + wpa_printf(MSG_DEBUG, "Line %d: invalid " + "time_zone", line); + errors++; + return errors; + } + os_free(bss->time_zone); + bss->time_zone = os_strdup(pos); + if (bss->time_zone == NULL) + errors++; +#ifdef CONFIG_WNM + } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) { + bss->wnm_sleep_mode = atoi(pos); + } else if (os_strcmp(buf, "bss_transition") == 0) { + bss->bss_transition = atoi(pos); +#endif /* CONFIG_WNM */ +#ifdef CONFIG_INTERWORKING + } else if (os_strcmp(buf, "interworking") == 0) { + bss->interworking = atoi(pos); + } else if (os_strcmp(buf, "access_network_type") == 0) { + bss->access_network_type = atoi(pos); + if (bss->access_network_type < 0 || + bss->access_network_type > 15) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "access_network_type", line); + errors++; + } + } else if (os_strcmp(buf, "internet") == 0) { + bss->internet = atoi(pos); + } else if (os_strcmp(buf, "asra") == 0) { + bss->asra = atoi(pos); + } else if (os_strcmp(buf, "esr") == 0) { + bss->esr = atoi(pos); + } else if (os_strcmp(buf, "uesa") == 0) { + bss->uesa = atoi(pos); + } else if (os_strcmp(buf, "venue_group") == 0) { + bss->venue_group = atoi(pos); + bss->venue_info_set = 1; + } else if (os_strcmp(buf, "venue_type") == 0) { + bss->venue_type = atoi(pos); + bss->venue_info_set = 1; + } else if (os_strcmp(buf, "hessid") == 0) { + if (hwaddr_aton(pos, bss->hessid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "hessid", line); + errors++; + } + } else if (os_strcmp(buf, "roaming_consortium") == 0) { + if (parse_roaming_consortium(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "venue_name") == 0) { + if (parse_venue_name(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "network_auth_type") == 0) { + u8 auth_type; + u16 redirect_url_len; + if (hexstr2bin(pos, &auth_type, 1)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "network_auth_type '%s'", + line, pos); + errors++; + return errors; + } + if (auth_type == 0 || auth_type == 2) + redirect_url_len = os_strlen(pos + 2); + else + redirect_url_len = 0; + os_free(bss->network_auth_type); + bss->network_auth_type = + os_malloc(redirect_url_len + 3 + 1); + if (bss->network_auth_type == NULL) { + errors++; + return errors; + } + *bss->network_auth_type = auth_type; + WPA_PUT_LE16(bss->network_auth_type + 1, + redirect_url_len); + if (redirect_url_len) + os_memcpy(bss->network_auth_type + 3, + pos + 2, redirect_url_len); + bss->network_auth_type_len = 3 + redirect_url_len; + } else if (os_strcmp(buf, "ipaddr_type_availability") == 0) { + if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1)) + { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "ipaddr_type_availability '%s'", + line, pos); + bss->ipaddr_type_configured = 0; + errors++; + return errors; + } + bss->ipaddr_type_configured = 1; + } else if (os_strcmp(buf, "domain_name") == 0) { + int j, num_domains, domain_len, domain_list_len = 0; + char *tok_start, *tok_prev; + u8 *domain_list, *domain_ptr; - for (i = 0; i < conf->num_bss; i++) { - bss = &conf->bss[i]; + domain_list_len = os_strlen(pos) + 1; + domain_list = os_malloc(domain_list_len); + if (domain_list == NULL) { + errors++; + return errors; + } + + domain_ptr = domain_list; + tok_prev = pos; + num_domains = 1; + while ((tok_prev = os_strchr(tok_prev, ','))) { + num_domains++; + tok_prev++; + } + tok_prev = pos; + for (j = 0; j < num_domains; j++) { + tok_start = os_strchr(tok_prev, ','); + if (tok_start) { + domain_len = tok_start - tok_prev; + *domain_ptr = domain_len; + os_memcpy(domain_ptr + 1, tok_prev, + domain_len); + domain_ptr += domain_len + 1; + tok_prev = ++tok_start; + } else { + domain_len = os_strlen(tok_prev); + *domain_ptr = domain_len; + os_memcpy(domain_ptr + 1, tok_prev, + domain_len); + domain_ptr += domain_len + 1; + } + } - if (bss->individual_wep_key_len == 0) { - /* individual keys are not use; can use key idx0 for - * broadcast keys */ - bss->broadcast_key_idx_min = 0; - } + os_free(bss->domain_name); + bss->domain_name = domain_list; + bss->domain_name_len = domain_list_len; + } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) { + if (parse_3gpp_cell_net(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "nai_realm") == 0) { + if (parse_nai_realm(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "gas_frag_limit") == 0) { + bss->gas_frag_limit = atoi(pos); + } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { + bss->gas_comeback_delay = atoi(pos); + } else if (os_strcmp(buf, "qos_map_set") == 0) { + if (parse_qos_map_set(bss, pos, line) < 0) + errors++; +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_RADIUS_TEST + } else if (os_strcmp(buf, "dump_msk_file") == 0) { + os_free(bss->dump_msk_file); + bss->dump_msk_file = os_strdup(pos); +#endif /* CONFIG_RADIUS_TEST */ +#ifdef CONFIG_HS20 + } else if (os_strcmp(buf, "hs20") == 0) { + bss->hs20 = atoi(pos); + } else if (os_strcmp(buf, "disable_dgaf") == 0) { + bss->disable_dgaf = atoi(pos); + } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) { + if (hs20_parse_oper_friendly_name(bss, pos, line) < 0) + errors++; + } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) { + if (hs20_parse_wan_metrics(bss, pos, line) < 0) { + errors++; + return errors; + } + } else if (os_strcmp(buf, "hs20_conn_capab") == 0) { + if (hs20_parse_conn_capab(bss, pos, line) < 0) { + errors++; + return errors; + } + } else if (os_strcmp(buf, "hs20_operating_class") == 0) { + u8 *oper_class; + size_t oper_class_len; + oper_class_len = os_strlen(pos); + if (oper_class_len < 2 || (oper_class_len & 0x01)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_operating_class '%s'", + line, pos); + errors++; + return errors; + } + oper_class_len /= 2; + oper_class = os_malloc(oper_class_len); + if (oper_class == NULL) { + errors++; + return errors; + } + if (hexstr2bin(pos, oper_class, oper_class_len)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_operating_class '%s'", + line, pos); + os_free(oper_class); + errors++; + return errors; + } + os_free(bss->hs20_operating_class); + bss->hs20_operating_class = oper_class; + bss->hs20_operating_class_len = oper_class_len; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_TESTING_OPTIONS +#define PARSE_TEST_PROBABILITY(_val) \ + } else if (os_strcmp(buf, #_val) == 0) { \ + char *end; \ + \ + conf->_val = strtod(pos, &end); \ + if (*end || conf->_val < 0.0d || \ + conf->_val > 1.0d) { \ + wpa_printf(MSG_ERROR, \ + "Line %d: Invalid value '%s'", \ + line, pos); \ + errors++; \ + return errors; \ + } + PARSE_TEST_PROBABILITY(ignore_probe_probability) + PARSE_TEST_PROBABILITY(ignore_auth_probability) + PARSE_TEST_PROBABILITY(ignore_assoc_probability) + PARSE_TEST_PROBABILITY(ignore_reassoc_probability) + PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability) + } else if (os_strcmp(buf, "bss_load_test") == 0) { + WPA_PUT_LE16(bss->bss_load_test, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "bss_load_test", line); + return 1; + } + pos++; + bss->bss_load_test[2] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "bss_load_test", line); + return 1; + } + pos++; + WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos)); + bss->bss_load_test_set = 1; +#endif /* CONFIG_TESTING_OPTIONS */ + } else if (os_strcmp(buf, "vendor_elements") == 0) { + struct wpabuf *elems; + size_t len = os_strlen(pos); + if (len & 0x01) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "vendor_elements '%s'", line, pos); + return 1; + } + len /= 2; + if (len == 0) { + wpabuf_free(bss->vendor_elements); + bss->vendor_elements = NULL; + return 0; + } - /* Select group cipher based on the enabled pairwise cipher - * suites */ - pairwise = 0; - if (bss->wpa & 1) - pairwise |= bss->wpa_pairwise; - if (bss->wpa & 2) { - if (bss->rsn_pairwise == 0) - bss->rsn_pairwise = bss->wpa_pairwise; - pairwise |= bss->rsn_pairwise; - } - if (pairwise & WPA_CIPHER_TKIP) - bss->wpa_group = WPA_CIPHER_TKIP; - else - bss->wpa_group = WPA_CIPHER_CCMP; - - bss->radius->auth_server = bss->radius->auth_servers; - bss->radius->acct_server = bss->radius->acct_servers; - - if (bss->wpa && bss->ieee802_1x) { - bss->ssid.security_policy = SECURITY_WPA; - } else if (bss->wpa) { - bss->ssid.security_policy = SECURITY_WPA_PSK; - } else if (bss->ieee802_1x) { - bss->ssid.security_policy = SECURITY_IEEE_802_1X; - bss->ssid.wep.default_len = bss->default_wep_key_len; - } else if (bss->ssid.wep.keys_set) - bss->ssid.security_policy = SECURITY_STATIC_WEP; - else - bss->ssid.security_policy = SECURITY_PLAINTEXT; - } + elems = wpabuf_alloc(len); + if (elems == NULL) + return 1; - if (hostapd_config_check(conf)) - errors++; + if (hexstr2bin(pos, wpabuf_put(elems, len), len)) { + wpabuf_free(elems); + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "vendor_elements '%s'", line, pos); + return 1; + } - if (errors) { - wpa_printf(MSG_ERROR, "%d errors found in configuration file " - "'%s'", errors, fname); - hostapd_config_free(conf); - conf = NULL; + wpabuf_free(bss->vendor_elements); + bss->vendor_elements = elems; + } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) { + bss->sae_anti_clogging_threshold = atoi(pos); + } else if (os_strcmp(buf, "sae_groups") == 0) { + if (hostapd_parse_intlist(&bss->sae_groups, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "sae_groups value '%s'", line, pos); + return 1; + } + } else { + wpa_printf(MSG_ERROR, "Line %d: unknown configuration " + "item '%s'", line, buf); + errors++; + } } - return conf; -} - - -int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b) -{ - int i; - - if (a->idx != b->idx || a->default_len != b->default_len) - return 1; - for (i = 0; i < NUM_WEP_KEYS; i++) - if (a->len[i] != b->len[i] || - os_memcmp(a->key[i], b->key[i], a->len[i]) != 0) - return 1; - return 0; + return errors; } -static void hostapd_config_free_radius(struct hostapd_radius_server *servers, - int num_servers) +/** + * hostapd_config_read - Read and parse a configuration file + * @fname: Configuration file name (including path, if needed) + * Returns: Allocated configuration data structure + */ +struct hostapd_config * hostapd_config_read(const char *fname) { - int i; + struct hostapd_config *conf; + struct hostapd_bss_config *bss; + FILE *f; + char buf[512], *pos; + int line = 0; + int errors = 0; + size_t i; - for (i = 0; i < num_servers; i++) { - os_free(servers[i].shared_secret); + f = fopen(fname, "r"); + if (f == NULL) { + wpa_printf(MSG_ERROR, "Could not open configuration file '%s' " + "for reading.", fname); + return NULL; } - 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; + conf = hostapd_config_defaults(); + if (conf == NULL) { + fclose(f); + return 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); + /* set default driver based on configuration */ + conf->driver = wpa_drivers[0]; + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "No driver wrappers registered!"); + hostapd_config_free(conf); + fclose(f); + return NULL; } - os_free(conf->ssid.wpa_passphrase); - os_free(conf->ssid.wpa_psk_file); -#ifdef CONFIG_FULL_DYNAMIC_VLAN - os_free(conf->ssid.vlan_tagged_interface); -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ - - user = conf->eap_user; - while (user) { - prev_user = user; - user = user->next; - hostapd_config_free_eap_user(prev_user); - } + bss = conf->last_bss = conf->bss[0]; - 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; - } + while (fgets(buf, sizeof(buf), f)) { + bss = conf->last_bss; + line++; -#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); + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; } + if (buf[0] == '\0') + continue; - r1kh = conf->r1kh_list; - conf->r1kh_list = NULL; - while (r1kh) { - r1kh_prev = r1kh; - r1kh = r1kh->next; - os_free(r1kh_prev); + pos = os_strchr(buf, '='); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'", + line, buf); + errors++; + continue; } + *pos = '\0'; + pos++; + errors += hostapd_config_fill(conf, bss, buf, pos, line); } -#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; + fclose(f); 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; + hostapd_set_security_params(conf->bss[i]); - 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 (hostapd_config_check(conf, 1)) + errors++; - if (psk->psk == prev_psk) - next_ok = 1; +#ifndef WPA_IGNORE_CONFIG_ERRORS + if (errors) { + wpa_printf(MSG_ERROR, "%d errors found in configuration file " + "'%s'", errors, fname); + hostapd_config_free(conf); + conf = NULL; } +#endif /* WPA_IGNORE_CONFIG_ERRORS */ - return NULL; + return conf; } -const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, - size_t identity_len, int phase2) +int hostapd_set_iface(struct hostapd_config *conf, + struct hostapd_bss_config *bss, char *field, char *value) { - 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; - } + int errors; + size_t i; - if (conf->wps_state && conf->ap_pin && - identity_len == WSC_ID_REGISTRAR_LEN && - os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { - static struct hostapd_eap_user wsc_registrar; - os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); - wsc_registrar.methods[0].method = eap_server_get_type( - "WSC", &wsc_registrar.methods[0].vendor); - wsc_registrar.password = (u8 *) conf->ap_pin; - wsc_registrar.password_len = os_strlen(conf->ap_pin); - return &wsc_registrar; + errors = hostapd_config_fill(conf, bss, field, value, 0); + if (errors) { + wpa_printf(MSG_INFO, "Failed to set configuration field '%s' " + "to value '%s'", field, value); + return -1; } -#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; - } + for (i = 0; i < conf->num_bss; i++) + hostapd_set_security_params(conf->bss[i]); - if (user->phase2 == !!phase2 && - user->identity_len == identity_len && - os_memcmp(user->identity, identity, identity_len) == 0) - break; - user = user->next; + if (hostapd_config_check(conf, 0)) { + wpa_printf(MSG_ERROR, "Configuration check failed"); + return -1; } - return user; + return 0; } diff --git a/contrib/hostapd/hostapd/config_file.h b/contrib/hostapd/hostapd/config_file.h new file mode 100644 index 0000000000..fba57b87ad --- /dev/null +++ b/contrib/hostapd/hostapd/config_file.h @@ -0,0 +1,17 @@ +/* + * hostapd / Configuration file parser + * Copyright (c) 2003-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef CONFIG_FILE_H +#define CONFIG_FILE_H + +struct hostapd_config * hostapd_config_read(const char *fname); +int hostapd_set_iface(struct hostapd_config *conf, + struct hostapd_bss_config *bss, char *field, + char *value); + +#endif /* CONFIG_FILE_H */ diff --git a/contrib/hostapd/hostapd/ctrl_iface.c b/contrib/hostapd/hostapd/ctrl_iface.c index 9dec7247cd..4a9da5f6a8 100644 --- a/contrib/hostapd/hostapd/ctrl_iface.c +++ b/contrib/hostapd/hostapd/ctrl_iface.c @@ -1,18 +1,12 @@ /* * hostapd / UNIX domain socket -based control interface - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" #ifndef CONFIG_NATIVE_WINDOWS @@ -20,17 +14,28 @@ #include #include -#include "hostapd.h" -#include "eloop.h" -#include "config.h" -#include "ieee802_1x.h" -#include "wpa.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/version.h" +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" #include "radius/radius_client.h" -#include "ieee802_11.h" +#include "radius/radius_server.h" +#include "ap/hostapd.h" +#include "ap/ap_config.h" +#include "ap/ieee802_1x.h" +#include "ap/wpa_auth.h" +#include "ap/ieee802_11.h" +#include "ap/sta_info.h" +#include "ap/wps_hostapd.h" +#include "ap/ctrl_iface_ap.h" +#include "ap/ap_drv_ops.h" +#include "ap/wnm_ap.h" +#include "ap/wpa_auth.h" +#include "wps/wps_defs.h" +#include "wps/wps.h" +#include "config_file.h" #include "ctrl_iface.h" -#include "sta_info.h" -#include "accounting.h" -#include "wps_hostapd.h" struct wpa_ctrl_dst { @@ -79,15 +84,15 @@ static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, os_memcmp(from->sun_path, dst->addr.sun_path, fromlen - offsetof(struct sockaddr_un, sun_path)) == 0) { + wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", + (u8 *) from->sun_path, + fromlen - + offsetof(struct sockaddr_un, sun_path)); if (prev == NULL) hapd->ctrl_dst = dst->next; else prev->next = dst->next; os_free(dst); - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", - (u8 *) from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); return 0; } prev = dst; @@ -125,164 +130,1046 @@ static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, } -static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, - struct sta_info *sta, - char *buf, size_t buflen) +static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd, + const char *txtaddr) { - int len, res, ret; + u8 addr[ETH_ALEN]; + struct sta_info *sta; - if (sta == NULL) { - ret = os_snprintf(buf, buflen, "FAIL\n"); - if (ret < 0 || (size_t) ret >= buflen) - return 0; - return ret; + wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta) + return 0; + + wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface " + "notification", MAC2STR(addr)); + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + + hostapd_new_assoc_sta(hapd, sta, 0); + return 0; +} + + +#ifdef CONFIG_IEEE80211W +#ifdef NEED_AP_MLME +static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr) || + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) + return -1; + + ieee802_11_send_sa_query_req(hapd, addr, trans_id); + + return 0; +} +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_IEEE80211W */ + + +#ifdef CONFIG_WPS +static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt) +{ + char *pin = os_strchr(txt, ' '); + char *timeout_txt; + int timeout; + u8 addr_buf[ETH_ALEN], *addr = NULL; + char *pos; + + if (pin == NULL) + return -1; + *pin++ = '\0'; + + timeout_txt = os_strchr(pin, ' '); + if (timeout_txt) { + *timeout_txt++ = '\0'; + timeout = atoi(timeout_txt); + pos = os_strchr(timeout_txt, ' '); + if (pos) { + *pos++ = '\0'; + if (hwaddr_aton(pos, addr_buf) == 0) + addr = addr_buf; + } + } else + timeout = 0; + + return hostapd_wps_add_pin(hapd, addr, txt, pin, timeout); +} + + +static int hostapd_ctrl_iface_wps_check_pin( + struct hostapd_data *hapd, char *cmd, char *buf, size_t buflen) +{ + char pin[9]; + size_t len; + char *pos; + int ret; + + wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN", + (u8 *) cmd, os_strlen(cmd)); + for (pos = cmd, len = 0; *pos != '\0'; pos++) { + if (*pos < '0' || *pos > '9') + continue; + pin[len++] = *pos; + if (len == 9) { + wpa_printf(MSG_DEBUG, "WPS: Too long PIN"); + return -1; + } + } + if (len != 4 && len != 8) { + wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len); + return -1; + } + pin[len] = '\0'; + + if (len == 8) { + unsigned int pin_val; + pin_val = atoi(pin); + if (!wps_pin_valid(pin_val)) { + wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit"); + ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n"); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; + } + } + + ret = os_snprintf(buf, buflen, "%s", pin); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + + return ret; +} + + +#ifdef CONFIG_WPS_NFC +static int hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd, + char *pos) +{ + size_t len; + struct wpabuf *buf; + int ret; + + len = os_strlen(pos); + if (len & 0x01) + return -1; + len /= 2; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; } - 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; + ret = hostapd_wps_nfc_tag_read(hapd, buf); + wpabuf_free(buf); + + return ret; +} + + +static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd, + char *cmd, char *reply, + size_t max_len) +{ + int ndef; + struct wpabuf *buf; + int res; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + buf = hostapd_wps_nfc_config_token(hapd, ndef); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd, + char *reply, size_t max_len, + int ndef) +{ + struct wpabuf *buf; + int res; + + buf = hostapd_wps_nfc_token_gen(hapd, ndef); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; - 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; + wpabuf_free(buf); - return len; + return res; } -static int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, - char *buf, size_t buflen) +static int hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd, + char *cmd, char *reply, + size_t max_len) { - return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen); + if (os_strcmp(cmd, "WPS") == 0) + return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply, + max_len, 0); + + if (os_strcmp(cmd, "NDEF") == 0) + return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply, + max_len, 1); + + if (os_strcmp(cmd, "enable") == 0) + return hostapd_wps_nfc_token_enable(hapd); + + if (os_strcmp(cmd, "disable") == 0) { + hostapd_wps_nfc_token_disable(hapd); + return 0; + } + + return -1; } -static int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, - const char *txtaddr, - char *buf, size_t buflen) +static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd, + char *cmd, char *reply, + size_t max_len) { - u8 addr[ETH_ALEN]; + struct wpabuf *buf; + int res; + char *pos; + int ndef; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + if (os_strcmp(pos, "WPS-CR") == 0) + buf = hostapd_wps_nfc_hs_cr(hapd, ndef); + else + buf = NULL; + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd, + char *cmd) +{ + size_t len; + struct wpabuf *req, *sel; int ret; + char *pos, *role, *type, *pos2; - if (hwaddr_aton(txtaddr, addr)) { - ret = os_snprintf(buf, buflen, "FAIL\n"); - if (ret < 0 || (size_t) ret >= buflen) - return 0; - return ret; + role = cmd; + pos = os_strchr(role, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + type = pos; + pos = os_strchr(type, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) + return -1; + *pos2++ = '\0'; + + len = os_strlen(pos); + if (len & 0x01) + return -1; + len /= 2; + + req = wpabuf_alloc(len); + if (req == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) { + wpabuf_free(req); + return -1; + } + + len = os_strlen(pos2); + if (len & 0x01) { + wpabuf_free(req); + return -1; + } + len /= 2; + + sel = wpabuf_alloc(len); + if (sel == NULL) { + wpabuf_free(req); + return -1; + } + if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) { + wpabuf_free(req); + wpabuf_free(sel); + return -1; + } + + if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) { + ret = hostapd_wps_nfc_report_handover(hapd, req, sel); + } else { + wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover " + "reported: role=%s type=%s", role, type); + ret = -1; + } + wpabuf_free(req); + wpabuf_free(sel); + + return ret; +} + +#endif /* CONFIG_WPS_NFC */ + + +static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt, + char *buf, size_t buflen) +{ + int timeout = 300; + char *pos; + const char *pin_txt; + + pos = os_strchr(txt, ' '); + if (pos) + *pos++ = '\0'; + + if (os_strcmp(txt, "disable") == 0) { + hostapd_wps_ap_pin_disable(hapd); + return os_snprintf(buf, buflen, "OK\n"); + } + + if (os_strcmp(txt, "random") == 0) { + if (pos) + timeout = atoi(pos); + pin_txt = hostapd_wps_ap_pin_random(hapd, timeout); + if (pin_txt == NULL) + return -1; + return os_snprintf(buf, buflen, "%s", pin_txt); + } + + if (os_strcmp(txt, "get") == 0) { + pin_txt = hostapd_wps_ap_pin_get(hapd); + if (pin_txt == NULL) + return -1; + return os_snprintf(buf, buflen, "%s", pin_txt); + } + + if (os_strcmp(txt, "set") == 0) { + char *pin; + if (pos == NULL) + return -1; + pin = pos; + pos = os_strchr(pos, ' '); + if (pos) { + *pos++ = '\0'; + timeout = atoi(pos); + } + if (os_strlen(pin) > buflen) + return -1; + if (hostapd_wps_ap_pin_set(hapd, pin, timeout) < 0) + return -1; + return os_snprintf(buf, buflen, "%s", pin); + } + + return -1; +} + + +static int hostapd_ctrl_iface_wps_config(struct hostapd_data *hapd, char *txt) +{ + char *pos; + char *ssid, *auth, *encr = NULL, *key = NULL; + + ssid = txt; + pos = os_strchr(txt, ' '); + if (!pos) + return -1; + *pos++ = '\0'; + + auth = pos; + pos = os_strchr(pos, ' '); + if (pos) { + *pos++ = '\0'; + encr = pos; + pos = os_strchr(pos, ' '); + if (pos) { + *pos++ = '\0'; + key = pos; + } + } + + return hostapd_wps_config_ap(hapd, ssid, auth, encr, key); +} + + +static const char * pbc_status_str(enum pbc_status status) +{ + switch (status) { + case WPS_PBC_STATUS_DISABLE: + return "Disabled"; + case WPS_PBC_STATUS_ACTIVE: + return "Active"; + case WPS_PBC_STATUS_TIMEOUT: + return "Timed-out"; + case WPS_PBC_STATUS_OVERLAP: + return "Overlap"; + default: + return "Unknown"; + } +} + + +static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, "PBC Status: %s\n", + pbc_status_str(hapd->wps_stats.pbc_status)); + + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, "Last WPS result: %s\n", + (hapd->wps_stats.status == WPS_STATUS_SUCCESS ? + "Success": + (hapd->wps_stats.status == WPS_STATUS_FAILURE ? + "Failed" : "None"))); + + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + /* If status == Failure - Add possible Reasons */ + if(hapd->wps_stats.status == WPS_STATUS_FAILURE && + hapd->wps_stats.failure_reason > 0) { + ret = os_snprintf(pos, end - pos, + "Failure Reason: %s\n", + wps_ei_str(hapd->wps_stats.failure_reason)); + + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (hapd->wps_stats.status) { + ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n", + MAC2STR(hapd->wps_stats.peer_addr)); + + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + +#endif /* CONFIG_WPS */ + + +#ifdef CONFIG_INTERWORKING + +static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd, + const char *cmd) +{ + u8 qos_map_set[16 + 2 * 21], count = 0; + const char *pos = cmd; + int val, ret; + + for (;;) { + if (count == sizeof(qos_map_set)) { + wpa_printf(MSG_ERROR, "Too many qos_map_set parameters"); + return -1; + } + + val = atoi(pos); + if (val < 0 || val > 255) { + wpa_printf(MSG_INFO, "Invalid QoS Map Set"); + return -1; + } + + qos_map_set[count++] = val; + pos = os_strchr(pos, ','); + if (!pos) + break; + pos++; } - return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr), - buf, buflen); + + if (count < 16 || count & 1) { + wpa_printf(MSG_INFO, "Invalid QoS Map Set"); + return -1; + } + + ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count); + if (ret) { + wpa_printf(MSG_INFO, "Failed to set QoS Map Set"); + return -1; + } + + os_memcpy(hapd->conf->qos_map_set, qos_map_set, count); + hapd->conf->qos_map_set_len = count; + + return 0; } -static int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, - const char *txtaddr, - char *buf, size_t buflen) +static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd, + const char *cmd) { u8 addr[ETH_ALEN]; struct sta_info *sta; + struct wpabuf *buf; + u8 *qos_map_set = hapd->conf->qos_map_set; + u8 qos_map_set_len = hapd->conf->qos_map_set_len; 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); + if (!qos_map_set_len) { + wpa_printf(MSG_INFO, "QoS Map Set is not set"); + return -1; + } + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR " not found " + "for QoS Map Configuration message", + MAC2STR(addr)); + return -1; + } + + if (!sta->qos_map_enabled) { + wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate " + "support for QoS Map", MAC2STR(addr)); + return -1; + } + + buf = wpabuf_alloc(2 + 2 + qos_map_set_len); + if (buf == NULL) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_QOS); + wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG); + + /* QoS Map Set Element */ + wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET); + wpabuf_put_u8(buf, qos_map_set_len); + wpabuf_put_data(buf, qos_map_set, qos_map_set_len); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + + return ret; } +#endif /* CONFIG_INTERWORKING */ -static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd, - const char *txtaddr) + +#ifdef CONFIG_WNM + +static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, + const char *cmd) { u8 addr[ETH_ALEN]; + int disassoc_timer; struct sta_info *sta; - wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr); + if (hwaddr_aton(cmd, addr)) + return -1; + if (cmd[17] != ' ') + return -1; + disassoc_timer = atoi(cmd + 17); - if (hwaddr_aton(txtaddr, addr)) + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for disassociation imminent message", + MAC2STR(addr)); + return -1; + } + + return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer); +} + + +static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *url, *timerstr; + int disassoc_timer; + struct sta_info *sta; + + if (hwaddr_aton(cmd, addr)) return -1; sta = ap_get_sta(hapd, addr); - if (sta) - return 0; + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for ESS disassociation imminent message", + MAC2STR(addr)); + return -1; + } - wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface " - "notification", MAC2STR(addr)); - sta = ap_sta_add(hapd, addr); - if (sta == NULL) + timerstr = cmd + 17; + if (*timerstr != ' ') return -1; + timerstr++; + disassoc_timer = atoi(timerstr); + if (disassoc_timer < 0 || disassoc_timer > 65535) + return -1; + + url = os_strchr(timerstr, ' '); + if (url == NULL) + return -1; + url++; + + return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer); +} + +#endif /* CONFIG_WNM */ + + +static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n" + "ssid=%s\n", + MAC2STR(hapd->own_addr), + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + +#ifdef CONFIG_WPS + ret = os_snprintf(pos, end - pos, "wps_state=%s\n", + hapd->conf->wps_state == 0 ? "disabled" : + (hapd->conf->wps_state == 1 ? "not configured" : + "configured")); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (hapd->conf->wps_state && hapd->conf->wpa && + hapd->conf->ssid.wpa_passphrase) { + ret = os_snprintf(pos, end - pos, "passphrase=%s\n", + hapd->conf->ssid.wpa_passphrase); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (hapd->conf->wps_state && hapd->conf->wpa && + hapd->conf->ssid.wpa_psk && + hapd->conf->ssid.wpa_psk->group) { + char hex[PMK_LEN * 2 + 1]; + wpa_snprintf_hex(hex, sizeof(hex), + hapd->conf->ssid.wpa_psk->psk, PMK_LEN); + ret = os_snprintf(pos, end - pos, "psk=%s\n", hex); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_WPS */ + + if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) { + ret = os_snprintf(pos, end - pos, "key_mgmt="); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + ret = os_snprintf(pos, end - pos, "WPA-PSK "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "WPA-EAP "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#ifdef CONFIG_IEEE80211R + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, "FT-PSK "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "FT-EAP "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 "); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_IEEE80211W */ + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (hapd->conf->wpa) { + ret = os_snprintf(pos, end - pos, "group_cipher=%s\n", + wpa_cipher_txt(hapd->conf->wpa_group)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) { + ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher="); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise, + " "); + if (ret < 0) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, "\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) { + ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher="); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise, + " "); + if (ret < 0) + 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 hostapd_ctrl_iface_set(struct hostapd_data *hapd, 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 (0) { +#ifdef CONFIG_WPS_TESTING + } else if (os_strcasecmp(cmd, "wps_version_number") == 0) { + long int val; + val = strtol(value, NULL, 0); + if (val < 0 || val > 0xff) { + ret = -1; + wpa_printf(MSG_DEBUG, "WPS: Invalid " + "wps_version_number %ld", val); + } else { + wps_version_number = val; + wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS " + "version %u.%u", + (wps_version_number & 0xf0) >> 4, + wps_version_number & 0x0f); + hostapd_wps_update_ie(hapd); + } + } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) { + wps_testing_dummy_cred = atoi(value); + wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d", + wps_testing_dummy_cred); + } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) { + wps_corrupt_pkhash = atoi(value); + wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", + wps_corrupt_pkhash); +#endif /* CONFIG_WPS_TESTING */ +#ifdef CONFIG_INTERWORKING + } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) { + int val = atoi(value); + if (val <= 0) + ret = -1; + else + hapd->gas_frag_limit = val; +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { + hapd->ext_mgmt_frame_handling = atoi(value); +#endif /* CONFIG_TESTING_OPTIONS */ + } else { + ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value); + } + + return ret; +} + + +static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + int res; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd); + + if (os_strcmp(cmd, "version") == 0) { + res = os_snprintf(buf, buflen, "%s", VERSION_STR); + if (res < 0 || (unsigned int) res >= buflen) + return -1; + return res; + } + + return -1; +} + + +static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface) +{ + if (hostapd_enable_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Enabling of interface failed"); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface) +{ + if (hostapd_reload_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Reloading of interface failed"); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface) +{ + if (hostapd_disable_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Disabling of interface failed"); + return -1; + } + return 0; +} + + +#ifdef CONFIG_TESTING_OPTIONS + +static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd) +{ + union wpa_event_data data; + char *pos, *param; + enum wpa_event_type event; + + wpa_printf(MSG_DEBUG, "RADAR TEST: %s", cmd); + + os_memset(&data, 0, sizeof(data)); + + param = os_strchr(cmd, ' '); + if (param == NULL) + return -1; + *param++ = '\0'; + + if (os_strcmp(cmd, "DETECTED") == 0) + event = EVENT_DFS_RADAR_DETECTED; + else if (os_strcmp(cmd, "CAC-FINISHED") == 0) + event = EVENT_DFS_CAC_FINISHED; + else if (os_strcmp(cmd, "CAC-ABORTED") == 0) + event = EVENT_DFS_CAC_ABORTED; + else if (os_strcmp(cmd, "NOP-FINISHED") == 0) + event = EVENT_DFS_NOP_FINISHED; + else { + wpa_printf(MSG_DEBUG, "Unsupported RADAR test command: %s", + cmd); + return -1; + } + + pos = os_strstr(param, "freq="); + if (pos) + data.dfs_event.freq = atoi(pos + 5); + + pos = os_strstr(param, "ht_enabled=1"); + if (pos) + data.dfs_event.ht_enabled = 1; + + pos = os_strstr(param, "chan_offset="); + if (pos) + data.dfs_event.chan_offset = atoi(pos + 12); + + pos = os_strstr(param, "chan_width="); + if (pos) + data.dfs_event.chan_width = atoi(pos + 11); + + pos = os_strstr(param, "cf1="); + if (pos) + data.dfs_event.cf1 = atoi(pos + 4); + + pos = os_strstr(param, "cf2="); + if (pos) + data.dfs_event.cf2 = atoi(pos + 4); + + wpa_supplicant_event(hapd, event, &data); - hostapd_new_assoc_sta(hapd, sta, 0); return 0; } -#ifdef CONFIG_IEEE80211W -static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, - const char *txtaddr) +static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd) { - u8 addr[ETH_ALEN]; - u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + size_t len; + u8 *buf; + int res; - wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr); + wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd); - if (hwaddr_aton(txtaddr, addr)) + len = os_strlen(cmd); + if (len & 1) return -1; + len /= 2; - os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); - ieee802_11_send_sa_query_req(hapd, addr, trans_id); + buf = os_malloc(len); + if (buf == NULL) + return -1; - return 0; + if (hexstr2bin(cmd, buf, len) < 0) { + os_free(buf); + return -1; + } + + res = hostapd_drv_send_mlme(hapd, buf, len, 0); + os_free(buf); + return res; } -#endif /* CONFIG_IEEE80211W */ +#endif /* CONFIG_TESTING_OPTIONS */ -#ifdef CONFIG_WPS -static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt) + +static int hostapd_ctrl_iface_chan_switch(struct hostapd_data *hapd, char *pos) { - char *pin = os_strchr(txt, ' '); - char *timeout_txt; - int timeout; +#ifdef NEED_AP_MLME + struct csa_settings settings; + int ret = hostapd_parse_csa_settings(pos, &settings); - if (pin == NULL) - return -1; - *pin++ = '\0'; + if (ret) + return ret; - timeout_txt = os_strchr(pin, ' '); - if (timeout_txt) { - *timeout_txt++ = '\0'; - timeout = atoi(timeout_txt); - } else - timeout = 0; + return hostapd_switch_channel(hapd, &settings); +#else /* NEED_AP_MLME */ + return -1; +#endif /* NEED_AP_MLME */ +} - return hostapd_wps_add_pin(hapd, txt, pin, timeout); + +static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply, + int reply_size, const char *param) +{ +#ifdef RADIUS_SERVER + if (os_strcmp(param, "radius_server") == 0) { + return radius_server_get_mib(hapd->radius_srv, reply, + reply_size); + } +#endif /* RADIUS_SERVER */ + return -1; } -#endif /* CONFIG_WPS */ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct hostapd_data *hapd = eloop_ctx; - char buf[256]; + char buf[4096]; int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); char *reply; const int reply_size = 4096; int reply_len; + int level = MSG_DEBUG; res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); @@ -291,7 +1178,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, return; } buf[res] = '\0'; - wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res); + if (os_strcmp(buf, "PING") == 0) + level = MSG_EXCESSIVE; + wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res); reply = os_malloc(reply_size); if (reply == NULL) { @@ -306,6 +1195,14 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG\n", 5); reply_len = 5; + } else if (os_strncmp(buf, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; + } else if (os_strcmp(buf, "STATUS") == 0) { + reply_len = hostapd_ctrl_iface_status(hapd, reply, + reply_size); + } else if (os_strcmp(buf, "STATUS-DRIVER") == 0) { + reply_len = hostapd_drv_status(hapd, reply, reply_size); } else if (os_strcmp(buf, "MIB") == 0) { reply_len = ieee802_11_get_mib(hapd, reply, reply_size); if (reply_len >= 0) { @@ -324,6 +1221,7 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, else reply_len += res; } +#ifndef CONFIG_NO_RADIUS if (reply_len >= 0) { res = radius_client_get_mib(hapd->radius, reply + reply_len, @@ -333,6 +1231,10 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, else reply_len += res; } +#endif /* CONFIG_NO_RADIUS */ + } else if (os_strncmp(buf, "MIB ", 4) == 0) { + reply_len = hostapd_ctrl_iface_mib(hapd, reply, reply_size, + buf + 4); } else if (os_strcmp(buf, "STA-FIRST") == 0) { reply_len = hostapd_ctrl_iface_sta_first(hapd, reply, reply_size); @@ -355,19 +1257,104 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) { if (hostapd_ctrl_iface_new_sta(hapd, buf + 8)) reply_len = -1; + } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) { + if (hostapd_ctrl_iface_deauthenticate(hapd, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { + if (hostapd_ctrl_iface_disassociate(hapd, buf + 13)) + reply_len = -1; #ifdef CONFIG_IEEE80211W +#ifdef NEED_AP_MLME } else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) { if (hostapd_ctrl_iface_sa_query(hapd, buf + 9)) reply_len = -1; +#endif /* NEED_AP_MLME */ #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8)) reply_len = -1; + } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) { + reply_len = hostapd_ctrl_iface_wps_check_pin( + hapd, buf + 14, reply, reply_size); } else if (os_strcmp(buf, "WPS_PBC") == 0) { - if (hostapd_wps_button_pushed(hapd)) + if (hostapd_wps_button_pushed(hapd, NULL)) + reply_len = -1; + } else if (os_strcmp(buf, "WPS_CANCEL") == 0) { + if (hostapd_wps_cancel(hapd)) reply_len = -1; + } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) { + reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11, + reply, reply_size); + } else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) { + if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_GET_STATUS", 13) == 0) { + reply_len = hostapd_ctrl_iface_wps_get_status(hapd, reply, + reply_size); +#ifdef CONFIG_WPS_NFC + } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) { + if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) { + reply_len = hostapd_ctrl_iface_wps_nfc_config_token( + hapd, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) { + reply_len = hostapd_ctrl_iface_wps_nfc_token( + hapd, buf + 14, reply, reply_size); + } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) { + reply_len = hostapd_ctrl_iface_nfc_get_handover_sel( + hapd, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) { + if (hostapd_ctrl_iface_nfc_report_handover(hapd, buf + 20)) + reply_len = -1; +#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS */ +#ifdef CONFIG_INTERWORKING + } else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) { + if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16)) + reply_len = -1; + } else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) { + if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18)) + reply_len = -1; +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_WNM + } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) { + if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18)) + reply_len = -1; + } else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) { + if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13)) + reply_len = -1; +#endif /* CONFIG_WNM */ + } else if (os_strcmp(buf, "GET_CONFIG") == 0) { + reply_len = hostapd_ctrl_iface_get_config(hapd, reply, + reply_size); + } else if (os_strncmp(buf, "SET ", 4) == 0) { + if (hostapd_ctrl_iface_set(hapd, buf + 4)) + reply_len = -1; + } else if (os_strncmp(buf, "GET ", 4) == 0) { + reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply, + reply_size); + } else if (os_strncmp(buf, "ENABLE", 6) == 0) { + if (hostapd_ctrl_iface_enable(hapd->iface)) + reply_len = -1; + } else if (os_strncmp(buf, "RELOAD", 6) == 0) { + if (hostapd_ctrl_iface_reload(hapd->iface)) + reply_len = -1; + } else if (os_strncmp(buf, "DISABLE", 7) == 0) { + if (hostapd_ctrl_iface_disable(hapd->iface)) + reply_len = -1; +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strncmp(buf, "RADAR ", 6) == 0) { + if (hostapd_ctrl_iface_radar(hapd, buf + 6)) + reply_len = -1; + } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { + if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8)) + reply_len = -1; +#endif /* CONFIG_TESTING_OPTIONS */ + } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { + if (hostapd_ctrl_iface_chan_switch(hapd, buf + 12)) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -403,7 +1390,7 @@ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) } -static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, +static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, int global, const char *txt, size_t len) { struct hostapd_data *hapd = ctx; @@ -419,7 +1406,10 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) int s = -1; char *fname = NULL; - hapd->ctrl_sock = -1; + if (hapd->ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } if (hapd->conf->ctrl_interface == NULL) return 0; @@ -435,12 +1425,35 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) } if (hapd->conf->ctrl_interface_gid_set && - chown(hapd->conf->ctrl_interface, 0, + chown(hapd->conf->ctrl_interface, -1, hapd->conf->ctrl_interface_gid) < 0) { perror("chown[ctrl_interface]"); return -1; } + if (!hapd->conf->ctrl_interface_gid_set && + hapd->iface->interfaces->ctrl_iface_group && + chown(hapd->conf->ctrl_interface, -1, + hapd->iface->interfaces->ctrl_iface_group) < 0) { + perror("chown[ctrl_interface]"); + return -1; + } + +#ifdef ANDROID + /* + * Android is using umask 0077 which would leave the control interface + * directory without group access. This breaks things since Wi-Fi + * framework assumes that this directory can be accessed by other + * applications in the wifi group. Fix this by adding group access even + * if umask value would prevent this. + */ + if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s", + strerror(errno)); + /* Try to continue anyway */ + } +#endif /* ANDROID */ + if (os_strlen(hapd->conf->ctrl_interface) + 1 + os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path)) goto fail; @@ -476,7 +1489,7 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) } if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + perror("hostapd-ctrl-iface: bind(PF_UNIX)"); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -493,7 +1506,14 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) } if (hapd->conf->ctrl_interface_gid_set && - chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) { + chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { + perror("chown[ctrl_interface/ifname]"); + goto fail; + } + + if (!hapd->conf->ctrl_interface_gid_set && + hapd->iface->interfaces->ctrl_iface_group && + chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { perror("chown[ctrl_interface/ifname]"); goto fail; } @@ -507,6 +1527,7 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) hapd->ctrl_sock = s; eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd, NULL); + hapd->msg_ctx = hapd; wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); return 0; @@ -543,7 +1564,10 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) "directory not empty - leaving it " "behind"); } else { - perror("rmdir[ctrl_interface]"); + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + hapd->conf->ctrl_interface, + strerror(errno)); } } } @@ -557,6 +1581,250 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) } +static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces, + char *buf) +{ + if (hostapd_add_iface(interfaces, buf) < 0) { + wpa_printf(MSG_ERROR, "Adding interface %s failed", buf); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces, + char *buf) +{ + if (hostapd_remove_iface(interfaces, buf) < 0) { + wpa_printf(MSG_ERROR, "Removing interface %s failed", buf); + return -1; + } + return 0; +} + + +static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) +{ +#ifdef CONFIG_WPS_TESTING + wps_version_number = 0x20; + wps_testing_dummy_cred = 0; + wps_corrupt_pkhash = 0; +#endif /* CONFIG_WPS_TESTING */ +} + + +static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + void *interfaces = eloop_ctx; + char buf[256]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + char reply[24]; + int reply_len; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(ctrl_iface)"); + return; + } + buf[res] = '\0'; + wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf); + + 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, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; + } else if (os_strcmp(buf, "FLUSH") == 0) { + hostapd_ctrl_iface_flush(interfaces); + } else if (os_strncmp(buf, "ADD ", 4) == 0) { + if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "REMOVE ", 7) == 0) { + if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0) + reply_len = -1; + } else { + wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " + "ignored"); + reply_len = -1; + } + + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); +} + + +static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface) +{ + char *buf; + size_t len; + + if (interface->global_iface_path == NULL) + return NULL; + + len = os_strlen(interface->global_iface_path) + + os_strlen(interface->global_iface_name) + 2; + buf = os_malloc(len); + if (buf == NULL) + return NULL; + + os_snprintf(buf, len, "%s/%s", interface->global_iface_path, + interface->global_iface_name); + buf[len - 1] = '\0'; + return buf; +} + + +int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) +{ + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; + + if (interface->global_iface_path == NULL) { + wpa_printf(MSG_DEBUG, "ctrl_iface not configured!"); + return 0; + } + + if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + perror("mkdir[ctrl_interface]"); + goto fail; + } + } else if (interface->ctrl_iface_group && + chown(interface->global_iface_path, -1, + interface->ctrl_iface_group) < 0) { + perror("chown[ctrl_interface]"); + goto fail; + } + + if (os_strlen(interface->global_iface_path) + 1 + + os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_UNIX)"); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ + addr.sun_family = AF_UNIX; + fname = hostapd_global_ctrl_iface_path(interface); + if (fname == NULL) + goto fail; + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + perror("unlink[ctrl_iface]"); + wpa_printf(MSG_ERROR, "Could not unlink " + "existing ctrl_iface socket '%s'", + fname); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + perror("bind(PF_UNIX)"); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } + } + + if (interface->ctrl_iface_group && + chown(fname, -1, interface->ctrl_iface_group) < 0) { + perror("chown[ctrl_interface]"); + goto fail; + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + perror("chmod[ctrl_interface/ifname]"); + goto fail; + } + os_free(fname); + + interface->global_ctrl_sock = s; + eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive, + interface, NULL); + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + os_free(fname); + } + return -1; +} + + +void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) +{ + char *fname = NULL; + + if (interfaces->global_ctrl_sock > -1) { + eloop_unregister_read_sock(interfaces->global_ctrl_sock); + close(interfaces->global_ctrl_sock); + interfaces->global_ctrl_sock = -1; + fname = hostapd_global_ctrl_iface_path(interfaces); + if (fname) { + unlink(fname); + os_free(fname); + } + + if (interfaces->global_iface_path && + rmdir(interfaces->global_iface_path) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "Control interface " + "directory not empty - leaving it " + "behind"); + } else { + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + interfaces->global_iface_path, + strerror(errno)); + } + } + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = NULL; + } +} + + static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, const char *buf, size_t len) { diff --git a/contrib/hostapd/hostapd/ctrl_iface.h b/contrib/hostapd/hostapd/ctrl_iface.h index d86de8c940..3341a66bdc 100644 --- a/contrib/hostapd/hostapd/ctrl_iface.h +++ b/contrib/hostapd/hostapd/ctrl_iface.h @@ -2,20 +2,38 @@ * hostapd / UNIX domain socket -based control interface * Copyright (c) 2004, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CTRL_IFACE_H #define CTRL_IFACE_H +#ifndef CONFIG_NO_CTRL_IFACE int hostapd_ctrl_iface_init(struct hostapd_data *hapd); void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd); +int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface); +void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface); +#else /* CONFIG_NO_CTRL_IFACE */ +static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) +{ +} + +static inline int +hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) +{ + return 0; +} + +static inline void +hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface) +{ +} +#endif /* CONFIG_NO_CTRL_IFACE */ #endif /* CTRL_IFACE_H */ diff --git a/contrib/hostapd/hostapd/driver.h b/contrib/hostapd/hostapd/driver.h deleted file mode 100644 index 45f5460873..0000000000 --- a/contrib/hostapd/hostapd/driver.h +++ /dev/null @@ -1,798 +0,0 @@ -/* - * hostapd - driver interface definition - * Copyright (c) 2002-2007, Jouni Malinen - * Copyright (c) 2007-2008, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef DRIVER_H -#define DRIVER_H - -struct hostapd_sta_add_params { - const u8 *addr; - u16 aid; - u16 capability; - const u8 *supp_rates; - size_t supp_rates_len; - int flags; - u16 listen_interval; - const struct ht_cap_ie *ht_capabilities; -}; - -struct hostapd_freq_params { - int mode; - int freq; - int ht_enabled; - int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, - * secondary channel below primary, 1 = HT40 - * enabled, secondary channel above primary */ -}; - -enum hostapd_driver_if_type { - HOSTAPD_IF_VLAN, HOSTAPD_IF_WDS -}; - -struct wpa_driver_ops { - const char *name; /* as appears in the config file */ - - void * (*init)(struct hostapd_data *hapd); - void * (*init_bssid)(struct hostapd_data *hapd, const u8 *bssid); - void (*deinit)(void *priv); - - int (*wireless_event_init)(void *priv); - void (*wireless_event_deinit)(void *priv); - - /** - * set_8021x - enable/disable IEEE 802.1X support - * @ifname: Interface name (for multi-SSID/VLAN support) - * @priv: driver private data - * @enabled: 1 = enable, 0 = disable - * - * Returns: 0 on success, -1 on failure - * - * Configure the kernel driver to enable/disable 802.1X support. - * This may be an empty function if 802.1X support is always enabled. - */ - int (*set_ieee8021x)(const char *ifname, void *priv, int enabled); - - /** - * set_privacy - enable/disable privacy - * @priv: driver private data - * @enabled: 1 = privacy enabled, 0 = disabled - * - * Return: 0 on success, -1 on failure - * - * Configure privacy. - */ - int (*set_privacy)(const char *ifname, void *priv, int enabled); - - int (*set_encryption)(const char *ifname, void *priv, const char *alg, - const u8 *addr, int idx, - const u8 *key, size_t key_len, int txkey); - int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, - int idx, u8 *seq); - int (*get_seqnum_igtk)(const char *ifname, void *priv, const u8 *addr, - int idx, u8 *seq); - int (*flush)(void *priv); - int (*set_generic_elem)(const char *ifname, void *priv, const u8 *elem, - size_t elem_len); - - int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data, - const u8 *addr); - int (*send_eapol)(void *priv, const u8 *addr, const u8 *data, - size_t data_len, int encrypt, const u8 *own_addr); - int (*sta_deauth)(void *priv, const u8 *addr, int reason); - int (*sta_disassoc)(void *priv, const u8 *addr, int reason); - int (*sta_remove)(void *priv, const u8 *addr); - int (*get_ssid)(const char *ifname, void *priv, u8 *buf, int len); - int (*set_ssid)(const char *ifname, void *priv, const u8 *buf, - int len); - int (*set_countermeasures)(void *priv, int enabled); - int (*send_mgmt_frame)(void *priv, const void *msg, size_t len, - int flags); - int (*set_assoc_ap)(void *priv, const u8 *addr); - /* note: sta_add() is deprecated; use sta_add2() instead */ - int (*sta_add)(const char *ifname, void *priv, const u8 *addr, u16 aid, - u16 capability, u8 *supp_rates, size_t supp_rates_len, - int flags, u16 listen_interval); - int (*sta_add2)(const char *ifname, void *priv, - struct hostapd_sta_add_params *params); - int (*get_inact_sec)(void *priv, const u8 *addr); - int (*sta_clear_stats)(void *priv, const u8 *addr); - - /* note: set_freq() is deprecated; use set_freq2() instead */ - int (*set_freq)(void *priv, int mode, int freq); - int (*set_freq2)(void *priv, struct hostapd_freq_params *freq); - int (*set_rts)(void *priv, int rts); - int (*get_rts)(void *priv, int *rts); - int (*set_frag)(void *priv, int frag); - int (*get_frag)(void *priv, int *frag); - int (*set_retry)(void *priv, int short_retry, int long_retry); - int (*get_retry)(void *priv, int *short_retry, int *long_retry); - - int (*sta_set_flags)(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and); - int (*set_rate_sets)(void *priv, int *supp_rates, int *basic_rates, - int mode); - int (*set_regulatory_domain)(void *priv, unsigned int rd); - int (*set_country)(void *priv, const char *country); - int (*set_ieee80211d)(void *priv, int enabled); - int (*set_beacon)(const char *ifname, void *priv, - u8 *head, size_t head_len, - u8 *tail, size_t tail_len); - - /* Configure internal bridge: - * 0 = disabled, i.e., client separation is enabled (no bridging of - * packets between associated STAs - * 1 = enabled, i.e., bridge packets between associated STAs (default) - */ - int (*set_internal_bridge)(void *priv, int value); - int (*set_beacon_int)(void *priv, int value); - int (*set_dtim_period)(const char *ifname, void *priv, int value); - /* Configure broadcast SSID mode: - * 0 = include SSID in Beacon frames and reply to Probe Request frames - * that use broadcast SSID - * 1 = hide SSID from Beacon frames and ignore Probe Request frames for - * broadcast SSID - */ - int (*set_broadcast_ssid)(void *priv, int value); - int (*set_cts_protect)(void *priv, int value); - int (*set_key_tx_rx_threshold)(void *priv, int value); - int (*set_preamble)(void *priv, int value); - int (*set_short_slot_time)(void *priv, int value); - int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min, - int cw_max, int burst_time); - int (*bss_add)(void *priv, const char *ifname, const u8 *bssid); - int (*bss_remove)(void *priv, const char *ifname); - int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask); - int (*passive_scan)(void *priv, int now, int our_mode_only, - int interval, int _listen, int *channel, - int *last_rx); - struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, - u16 *num_modes, - u16 *flags); - int (*if_add)(const char *iface, void *priv, - enum hostapd_driver_if_type type, char *ifname, - const u8 *addr); - int (*if_update)(void *priv, enum hostapd_driver_if_type type, - char *ifname, const u8 *addr); - int (*if_remove)(void *priv, enum hostapd_driver_if_type type, - const char *ifname, const u8 *addr); - int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname, - int vlan_id); - /** - * commit - Optional commit changes handler - * @priv: driver private data - * Returns: 0 on success, -1 on failure - * - * This optional handler function can be registered if the driver - * interface implementation needs to commit changes (e.g., by setting - * network interface up) at the end of initial configuration. If set, - * this handler will be called after initial setup has been completed. - */ - int (*commit)(void *priv); - - int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto, - const u8 *data, size_t data_len); - - int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, - u32 session_timeout); - int (*set_radius_acl_expire)(void *priv, const u8 *mac); - - int (*set_ht_params)(const char *ifname, void *priv, - const u8 *ht_capab, size_t ht_capab_len, - const u8 *ht_oper, size_t ht_oper_len); - - int (*set_wps_beacon_ie)(const char *ifname, void *priv, - const u8 *ie, size_t len); - int (*set_wps_probe_resp_ie)(const char *ifname, void *priv, - const u8 *ie, size_t len); -}; - -static inline void * -hostapd_driver_init(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || hapd->driver->init == NULL) - return NULL; - return hapd->driver->init(hapd); -} - -static inline void * -hostapd_driver_init_bssid(struct hostapd_data *hapd, const u8 *bssid) -{ - if (hapd->driver == NULL || hapd->driver->init_bssid == NULL) - return NULL; - return hapd->driver->init_bssid(hapd, bssid); -} - -static inline void -hostapd_driver_deinit(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || hapd->driver->deinit == NULL) - return; - hapd->driver->deinit(hapd->drv_priv); -} - -static inline int -hostapd_wireless_event_init(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || - hapd->driver->wireless_event_init == NULL) - return 0; - return hapd->driver->wireless_event_init(hapd->drv_priv); -} - -static inline void -hostapd_wireless_event_deinit(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || - hapd->driver->wireless_event_deinit == NULL) - return; - hapd->driver->wireless_event_deinit(hapd->drv_priv); -} - -static inline int -hostapd_set_ieee8021x(const char *ifname, struct hostapd_data *hapd, - int enabled) -{ - if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL) - return 0; - return hapd->driver->set_ieee8021x(ifname, hapd->drv_priv, enabled); -} - -static inline int -hostapd_set_privacy(struct hostapd_data *hapd, int enabled) -{ - if (hapd->driver == NULL || hapd->driver->set_privacy == NULL) - return 0; - return hapd->driver->set_privacy(hapd->conf->iface, hapd->drv_priv, - enabled); -} - -static inline int -hostapd_set_encryption(const char *ifname, struct hostapd_data *hapd, - const char *alg, const u8 *addr, int idx, - u8 *key, size_t key_len, int txkey) -{ - if (hapd->driver == NULL || hapd->driver->set_encryption == NULL) - return 0; - return hapd->driver->set_encryption(ifname, hapd->drv_priv, alg, addr, - idx, key, key_len, txkey); -} - -static inline int -hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, - const u8 *addr, int idx, u8 *seq) -{ - if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL) - return 0; - return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx, - seq); -} - -static inline int -hostapd_get_seqnum_igtk(const char *ifname, struct hostapd_data *hapd, - const u8 *addr, int idx, u8 *seq) -{ - if (hapd->driver == NULL || hapd->driver->get_seqnum_igtk == NULL) - return -1; - return hapd->driver->get_seqnum_igtk(ifname, hapd->drv_priv, addr, idx, - seq); -} - -static inline int -hostapd_flush(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || hapd->driver->flush == NULL) - return 0; - return hapd->driver->flush(hapd->drv_priv); -} - -static inline int -hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, - size_t elem_len) -{ - if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL) - return 0; - return hapd->driver->set_generic_elem(hapd->conf->iface, - hapd->drv_priv, elem, elem_len); -} - -static inline int -hostapd_read_sta_data(struct hostapd_data *hapd, - struct hostap_sta_driver_data *data, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) - return -1; - return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); -} - -static inline int -hostapd_send_eapol(struct hostapd_data *hapd, const u8 *addr, const u8 *data, - size_t data_len, int encrypt) -{ - if (hapd->driver == NULL || hapd->driver->send_eapol == NULL) - return 0; - return hapd->driver->send_eapol(hapd->drv_priv, addr, data, data_len, - encrypt, hapd->own_addr); -} - -static inline int -hostapd_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason) -{ - if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) - return 0; - return hapd->driver->sta_deauth(hapd->drv_priv, addr, reason); -} - -static inline int -hostapd_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason) -{ - if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) - return 0; - return hapd->driver->sta_disassoc(hapd->drv_priv, addr, reason); -} - -static inline int -hostapd_sta_remove(struct hostapd_data *hapd, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) - return 0; - return hapd->driver->sta_remove(hapd->drv_priv, addr); -} - -static inline int -hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len) -{ - if (hapd->driver == NULL || hapd->driver->get_ssid == NULL) - return 0; - return hapd->driver->get_ssid(hapd->conf->iface, hapd->drv_priv, buf, - len); -} - -static inline int -hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len) -{ - if (hapd->driver == NULL || hapd->driver->set_ssid == NULL) - return 0; - return hapd->driver->set_ssid(hapd->conf->iface, hapd->drv_priv, buf, - len); -} - -static inline int -hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg, size_t len, - int flags) -{ - if (hapd->driver == NULL || hapd->driver->send_mgmt_frame == NULL) - return 0; - return hapd->driver->send_mgmt_frame(hapd->drv_priv, msg, len, flags); -} - -static inline int -hostapd_set_assoc_ap(struct hostapd_data *hapd, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->set_assoc_ap == NULL) - return 0; - return hapd->driver->set_assoc_ap(hapd->drv_priv, addr); -} - -static inline int -hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) -{ - if (hapd->driver == NULL || hapd->driver->set_countermeasures == NULL) - return 0; - return hapd->driver->set_countermeasures(hapd->drv_priv, enabled); -} - -static inline int -hostapd_sta_add(const char *ifname, struct hostapd_data *hapd, const u8 *addr, - u16 aid, u16 capability, const u8 *supp_rates, - size_t supp_rates_len, int flags, u16 listen_interval, - const struct ht_cap_ie *ht_capabilities) -{ - if (hapd->driver == NULL) - return 0; - - if (hapd->driver->sta_add2) { - struct hostapd_sta_add_params params; - os_memset(¶ms, 0, sizeof(params)); - params.addr = addr; - params.aid = aid; - params.capability = capability; - params.supp_rates = supp_rates; - params.supp_rates_len = supp_rates_len; - params.flags = flags; - params.listen_interval = listen_interval; - params.ht_capabilities = ht_capabilities; - return hapd->driver->sta_add2(ifname, hapd->drv_priv, ¶ms); - } - - if (hapd->driver->sta_add == NULL) - return 0; - return hapd->driver->sta_add(ifname, hapd->drv_priv, addr, aid, - capability, (u8 *) supp_rates, - supp_rates_len, - flags, listen_interval); -} - -static inline int -hostapd_get_inact_sec(struct hostapd_data *hapd, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) - return 0; - return hapd->driver->get_inact_sec(hapd->drv_priv, addr); -} - -static inline int -hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, int ht_enabled, - int sec_channel_offset) -{ - if (hapd->driver == NULL) - return 0; - if (hapd->driver->set_freq2) { - struct hostapd_freq_params data; - os_memset(&data, 0, sizeof(data)); - data.mode = mode; - data.freq = freq; - data.ht_enabled = ht_enabled; - data.sec_channel_offset = sec_channel_offset; - return hapd->driver->set_freq2(hapd->drv_priv, &data); - } - - if (hapd->driver->set_freq == NULL) - return 0; - return hapd->driver->set_freq(hapd->drv_priv, mode, freq); -} - -static inline int -hostapd_set_rts(struct hostapd_data *hapd, int rts) -{ - if (hapd->driver == NULL || hapd->driver->set_rts == NULL) - return 0; - return hapd->driver->set_rts(hapd->drv_priv, rts); -} - -static inline int -hostapd_get_rts(struct hostapd_data *hapd, int *rts) -{ - if (hapd->driver == NULL || hapd->driver->get_rts == NULL) - return 0; - return hapd->driver->get_rts(hapd->drv_priv, rts); -} - -static inline int -hostapd_set_frag(struct hostapd_data *hapd, int frag) -{ - if (hapd->driver == NULL || hapd->driver->set_frag == NULL) - return 0; - return hapd->driver->set_frag(hapd->drv_priv, frag); -} - -static inline int -hostapd_get_frag(struct hostapd_data *hapd, int *frag) -{ - if (hapd->driver == NULL || hapd->driver->get_frag == NULL) - return 0; - return hapd->driver->get_frag(hapd->drv_priv, frag); -} - -static inline int -hostapd_set_retry(struct hostapd_data *hapd, int short_retry, int long_retry) -{ - if (hapd->driver == NULL || hapd->driver->set_retry == NULL) - return 0; - return hapd->driver->set_retry(hapd->drv_priv, short_retry, - long_retry); -} - -static inline int -hostapd_get_retry(struct hostapd_data *hapd, int *short_retry, int *long_retry) -{ - if (hapd->driver == NULL || hapd->driver->get_retry == NULL) - return 0; - return hapd->driver->get_retry(hapd->drv_priv, short_retry, - long_retry); -} - -static inline int -hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, - int total_flags, int flags_or, int flags_and) -{ - if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL) - return 0; - return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags, - flags_or, flags_and); -} - -static inline int -hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, - int *basic_rates, int mode) -{ - if (hapd->driver == NULL || hapd->driver->set_rate_sets == NULL) - return 0; - return hapd->driver->set_rate_sets(hapd->drv_priv, supp_rates, - basic_rates, mode); -} - -static inline int -hostapd_set_regulatory_domain(struct hostapd_data *hapd, unsigned int rd) -{ - if (hapd->driver == NULL || - hapd->driver->set_regulatory_domain == NULL) - return 0; - return hapd->driver->set_regulatory_domain(hapd->drv_priv, rd); -} - -static inline int -hostapd_set_country(struct hostapd_data *hapd, const char *country) -{ - if (hapd->driver == NULL || - hapd->driver->set_country == NULL) - return 0; - return hapd->driver->set_country(hapd->drv_priv, country); -} - -static inline int -hostapd_set_ieee80211d(struct hostapd_data *hapd, int enabled) -{ - if (hapd->driver == NULL || - hapd->driver->set_ieee80211d == NULL) - return 0; - return hapd->driver->set_ieee80211d(hapd->drv_priv, enabled); -} - -static inline int -hostapd_sta_clear_stats(struct hostapd_data *hapd, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) - return 0; - return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); -} - -static inline int -hostapd_set_beacon(const char *ifname, struct hostapd_data *hapd, - u8 *head, size_t head_len, - u8 *tail, size_t tail_len) -{ - if (hapd->driver == NULL || hapd->driver->set_beacon == NULL) - return 0; - return hapd->driver->set_beacon(ifname, hapd->drv_priv, head, head_len, - tail, tail_len); -} - -static inline int -hostapd_set_internal_bridge(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_internal_bridge == NULL) - return 0; - return hapd->driver->set_internal_bridge(hapd->drv_priv, value); -} - -static inline int -hostapd_set_beacon_int(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_beacon_int == NULL) - return 0; - return hapd->driver->set_beacon_int(hapd->drv_priv, value); -} - -static inline int -hostapd_set_dtim_period(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_dtim_period == NULL) - return 0; - return hapd->driver->set_dtim_period(hapd->conf->iface, hapd->drv_priv, - value); -} - -static inline int -hostapd_set_broadcast_ssid(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_broadcast_ssid == NULL) - return 0; - return hapd->driver->set_broadcast_ssid(hapd->drv_priv, value); -} - -static inline int -hostapd_set_cts_protect(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL) - return 0; - return hapd->driver->set_cts_protect(hapd->drv_priv, value); -} - -static inline int -hostapd_set_key_tx_rx_threshold(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || - hapd->driver->set_key_tx_rx_threshold == NULL) - return 0; - return hapd->driver->set_key_tx_rx_threshold(hapd->drv_priv, value); -} - -static inline int -hostapd_set_preamble(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_preamble == NULL) - return 0; - return hapd->driver->set_preamble(hapd->drv_priv, value); -} - -static inline int -hostapd_set_short_slot_time(struct hostapd_data *hapd, int value) -{ - if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL) - return 0; - return hapd->driver->set_short_slot_time(hapd->drv_priv, value); -} - -static inline int -hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, - int cw_min, int cw_max, int burst_time) -{ - if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL) - return 0; - return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs, - cw_min, cw_max, burst_time); -} - -static inline int -hostapd_bss_add(struct hostapd_data *hapd, const char *ifname, const u8 *bssid) -{ - if (hapd->driver == NULL || hapd->driver->bss_add == NULL) - return 0; - return hapd->driver->bss_add(hapd->drv_priv, ifname, bssid); -} - -static inline int -hostapd_bss_remove(struct hostapd_data *hapd, const char *ifname) -{ - if (hapd->driver == NULL || hapd->driver->bss_remove == NULL) - return 0; - return hapd->driver->bss_remove(hapd->drv_priv, ifname); -} - -static inline int -hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr, - const u8 *mask) -{ - if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL) - return 1; - return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask); -} - -static inline int -hostapd_if_add(struct hostapd_data *hapd, enum hostapd_driver_if_type type, - char *ifname, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->if_add == NULL) - return -1; - return hapd->driver->if_add(hapd->conf->iface, hapd->drv_priv, type, - ifname, addr); -} - -static inline int -hostapd_if_update(struct hostapd_data *hapd, enum hostapd_driver_if_type type, - char *ifname, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->if_update == NULL) - return -1; - return hapd->driver->if_update(hapd->drv_priv, type, ifname, addr); -} - -static inline int -hostapd_if_remove(struct hostapd_data *hapd, enum hostapd_driver_if_type type, - char *ifname, const u8 *addr) -{ - if (hapd->driver == NULL || hapd->driver->if_remove == NULL) - return -1; - return hapd->driver->if_remove(hapd->drv_priv, type, ifname, addr); -} - -static inline int -hostapd_passive_scan(struct hostapd_data *hapd, int now, int our_mode_only, - int interval, int _listen, int *channel, - int *last_rx) -{ - if (hapd->driver == NULL || hapd->driver->passive_scan == NULL) - return -1; - return hapd->driver->passive_scan(hapd->drv_priv, now, our_mode_only, - interval, _listen, channel, last_rx); -} - -static inline struct hostapd_hw_modes * -hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, - u16 *flags) -{ - if (hapd->driver == NULL || hapd->driver->get_hw_feature_data == NULL) - return NULL; - return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, - flags); -} - -static inline int -hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd, - const u8 *addr, int vlan_id) -{ - if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) - return 0; - return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, vlan_id); -} - -static inline int -hostapd_driver_commit(struct hostapd_data *hapd) -{ - if (hapd->driver == NULL || hapd->driver->commit == NULL) - return 0; - return hapd->driver->commit(hapd->drv_priv); -} - -static inline int -hostapd_set_radius_acl_auth(struct hostapd_data *hapd, const u8 *mac, - int accepted, u32 session_timeout) -{ - if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) - return 0; - return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, - session_timeout); -} - -static inline int -hostapd_set_radius_acl_expire(struct hostapd_data *hapd, const u8 *mac) -{ - if (hapd->driver == NULL || - hapd->driver->set_radius_acl_expire == NULL) - return 0; - return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); -} - -#ifdef CONFIG_IEEE80211N -static inline int -hostapd_set_ht_params(const char *ifname, struct hostapd_data *hapd, - const u8 *ht_capab, size_t ht_capab_len, - const u8 *ht_oper, size_t ht_oper_len) -{ - if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL || - ht_capab == NULL || ht_oper == NULL) - return 0; - return hapd->driver->set_ht_params( - ifname, hapd->drv_priv, ht_capab, ht_capab_len, - ht_oper, ht_oper_len); -} -#endif /* CONFIG_IEEE80211N */ - -static inline int -hostapd_drv_none(struct hostapd_data *hapd) -{ - return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0; -} - -static inline int -hostapd_set_wps_beacon_ie(struct hostapd_data *hapd, const u8 *ie, size_t len) -{ - if (hapd->driver == NULL || hapd->driver->set_wps_beacon_ie == NULL) - return 0; - return hapd->driver->set_wps_beacon_ie(hapd->conf->iface, - hapd->drv_priv, ie, len); -} - -static inline int -hostapd_set_wps_probe_resp_ie(struct hostapd_data *hapd, const u8 *ie, - size_t len) -{ - if (hapd->driver == NULL || - hapd->driver->set_wps_probe_resp_ie == NULL) - return 0; - return hapd->driver->set_wps_probe_resp_ie(hapd->conf->iface, - hapd->drv_priv, ie, len); -} - -#endif /* DRIVER_H */ diff --git a/contrib/hostapd/hostapd/driver_atheros.c b/contrib/hostapd/hostapd/driver_atheros.c deleted file mode 100644 index 558a8bbf2b..0000000000 --- a/contrib/hostapd/hostapd/driver_atheros.c +++ /dev/null @@ -1,1457 +0,0 @@ -/* - * hostapd / Driver interaction with Atheros driver - * Copyright (c) 2004, Sam Leffler - * Copyright (c) 2004, Video54 Technologies - * Copyright (c) 2005-2007, Jouni Malinen - * 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 -#include - -#include "common.h" -#ifndef _BYTE_ORDER -#ifdef WORDS_BIGENDIAN -#define _BYTE_ORDER _BIG_ENDIAN -#else -#define _BYTE_ORDER _LITTLE_ENDIAN -#endif -#endif /* _BYTE_ORDER */ - -#include -#include -#include - -/* - * 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 - -#ifdef CONFIG_WPS -#ifdef IEEE80211_IOCTL_FILTERFRAME -#include - -#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 WPA_OUI_TYPE -#undef WME_OUI_TYPE - -#include "wireless_copy.h" - -#include "hostapd.h" -#include "driver.h" -#include "eloop.h" -#include "priv_netlink.h" -#include "l2_packet/l2_packet.h" - -#include "wps_hostapd.h" -#include "ieee802_11_defs.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 */ - int wext_sock; /* socket for wireless events */ - 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 *addr, int reason_code); - -/* hostapd 0.7.x compatibility - START */ -#include "ieee802_1x.h" -#include "sta_info.h" -#include "wpa.h" -#include "radius/radius.h" -#include "ieee802_11.h" -#include "accounting.h" - -static 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; - - hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "associated"); - - 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) { - wpa_printf(MSG_DEBUG, "WPA/RSN information element " - "rejected? (res %u)", res); - wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); - 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; -} - - -static 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_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); -} - - -static void hostapd_eapol_receive(struct hostapd_data *hapd, const u8 *sa, - const u8 *buf, size_t len) -{ - ieee802_1x_receive(hapd, sa, buf, len); -} - - -static void hostapd_michael_mic_failure(struct hostapd_data *hapd, - const u8 *addr) -{ - ieee80211_michael_mic_failure(hapd, addr, 1); -} -/* hostapd 0.7.x compatibility - END */ - -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) { - int first = IEEE80211_IOCTL_SETPARAM; - static const char *opnames[] = { - "ioctl[IEEE80211_IOCTL_SETPARAM]", - "ioctl[IEEE80211_IOCTL_GETPARAM]", - "ioctl[IEEE80211_IOCTL_SETKEY]", - "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", - "ioctl[IEEE80211_IOCTL_DELKEY]", - "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", - "ioctl[IEEE80211_IOCTL_SETMLME]", - "ioctl[IEEE80211_IOCTL_GETCHANINFO]", - "ioctl[IEEE80211_IOCTL_SETOPTIE]", - "ioctl[IEEE80211_IOCTL_GETOPTIE]", - "ioctl[IEEE80211_IOCTL_ADDMAC]", - "ioctl[IEEE80211_IOCTL_DELMAC]", - "ioctl[IEEE80211_IOCTL_GETCHANLIST]", - "ioctl[IEEE80211_IOCTL_SETCHANLIST]", - "ioctl[IEEE80211_IOCTL_KICKMAC]", - "ioctl[IEEE80211_IOCTL_CHANSWITCH]", - "ioctl[IEEE80211_IOCTL_GETMODE]", - "ioctl[IEEE80211_IOCTL_SETMODE]", - "ioctl[IEEE80211_IOCTL_GET_APPIEBUF]", - "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", - NULL, - "ioctl[IEEE80211_IOCTL_FILTERFRAME]", - }; - int idx = op - first; - if (first <= op && - idx < (int) (sizeof(opnames) / sizeof(opnames[0])) && - opnames[idx]) - perror(opnames[idx]); - else { - perror("ioctl[unknown???]"); - wpa_printf(MSG_DEBUG, "Failed ioctl: 0x%x", op); - } - 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 hostapd_data *hapd = drv->hapd; - struct hostapd_bss_config *conf = hapd->conf; - int v; - - switch (conf->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", - conf->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 = (conf->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 (conf->wpa_pairwise & WPA_CIPHER_CCMP) - v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) - v |= 1<wpa_pairwise & WPA_CIPHER_NONE) - v |= 1<wpa_key_mgmt); - if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, conf->wpa_key_mgmt)) { - printf("Unable to set key management algorithms to 0x%x\n", - conf->wpa_key_mgmt); - return -1; - } - - v = 0; - if (conf->rsn_preauth) - v |= BIT(0); - wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", - __func__, conf->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__, conf->wpa); - if (set80211param(drv, IEEE80211_PARAM_WPA, conf->wpa)) { - printf("Unable to set WPA to %u\n", conf->wpa); - return -1; - } - return 0; -} - - -static int -madwifi_set_iface_flags(void *priv, int dev_up) -{ - struct madwifi_driver_data *drv = priv; - struct ifreq ifr; - - wpa_printf(MSG_DEBUG, "%s: dev_up=%d", __func__, dev_up); - - if (drv->ioctl_sock < 0) - return -1; - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCGIFFLAGS]"); - return -1; - } - - if (dev_up) - ifr.ifr_flags |= IFF_UP; - else - ifr.ifr_flags &= ~IFF_UP; - - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCSIFFLAGS]"); - return -1; - } - - if (dev_up) { - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, 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 -madwifi_set_ieee8021x(const char *ifname, void *priv, int enabled) -{ - struct madwifi_driver_data *drv = priv; - struct hostapd_data *hapd = drv->hapd; - struct hostapd_bss_config *conf = hapd->conf; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - - if (!enabled) { - /* XXX restore state */ - return set80211param(priv, IEEE80211_PARAM_AUTHMODE, - IEEE80211_AUTH_AUTO); - } - if (!conf->wpa && !conf->ieee802_1x) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); - return -1; - } - if (conf->wpa && madwifi_configure_wpa(drv) != 0) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); - return -1; - } - if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, - (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); - return -1; - } - - return 0; -} - -static int -madwifi_set_privacy(const char *ifname, 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 & WLAN_STA_AUTHORIZED) - return madwifi_set_sta_authorized(priv, addr, 1); - if (!(flags_and & WLAN_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, const char *alg, - const u8 *addr, int key_idx, - const u8 *key, size_t key_len, int txkey) -{ - struct madwifi_driver_data *drv = priv; - struct ieee80211req_key wk; - u_int8_t cipher; - int ret; - - if (strcmp(alg, "none") == 0) - return madwifi_del_key(drv, addr, key_idx); - - wpa_printf(MSG_DEBUG, "%s: alg=%s addr=%s key_idx=%d", - __func__, alg, ether_sprintf(addr), key_idx); - - if (strcmp(alg, "WEP") == 0) - cipher = IEEE80211_CIPHER_WEP; - else if (strcmp(alg, "TKIP") == 0) - cipher = IEEE80211_CIPHER_TKIP; - else if (strcmp(alg, "CCMP") == 0) - cipher = IEEE80211_CIPHER_AES_CCM; - else { - printf("%s: unknown/unsupported algorithm %s\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 '%s' key_len %lu txkey %d)", - __func__, ether_sprintf(wk.ik_macaddr), key_idx, - alg, (unsigned long) key_len, txkey); - } - - 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) -{ - u8 allsta[IEEE80211_ADDR_LEN]; - memset(allsta, 0xff, IEEE80211_ADDR_LEN); - return madwifi_sta_deauth(priv, 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(const char *ifname, 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 *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 *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; - const u8 *end, *ie; - u16 fc; - size_t ie_len; - - /* 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; - - end = buf + len; - ie = mgmt->u.probe_req.variable; - ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); - - hostapd_wps_probe_req_rx(drv->hapd, mgmt->sa, ie, ie_len); -} -#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_wps_beacon_ie(const char *ifname, void *priv, const u8 *ie, - size_t len) -{ - return madwifi_set_wps_ie(priv, ie, len, IEEE80211_APPIE_FRAME_BEACON); -} - -static int -madwifi_set_wps_probe_resp_ie(const char *ifname, void *priv, const u8 *ie, - size_t len) -{ - return madwifi_set_wps_ie(priv, ie, len, - IEEE80211_APPIE_FRAME_PROBE_RESP); -} -#else /* CONFIG_WPS */ -#define madwifi_set_wps_beacon_ie NULL -#define madwifi_set_wps_probe_resp_ie NULL -#endif /* CONFIG_WPS */ - -static int -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, res; - 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: - res = hostapd_notif_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)); - } - - return res; -} - -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) { - hostapd_michael_mic_failure(drv->hapd, addr); - } 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. - */ - hostapd_wps_button_pushed(drv->hapd); - } 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 " | 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: - hostapd_notif_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(struct madwifi_driver_data *drv, - struct nlmsghdr *h, int len) -{ - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr * attr; - - if (len < (int) sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - - if (ifi->ifi_index != drv->ifindex) - return; - - 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)) { - 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 void -madwifi_wireless_event_receive(int sock, void *eloop_ctx, void *sock_ctx) -{ - char buf[256]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - struct madwifi_driver_data *drv = eloop_ctx; - - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - perror("recvfrom(netlink)"); - return; - } - - h = (struct nlmsghdr *) buf; - while (left >= (int) sizeof(*h)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - printf("Malformed netlink message: " - "len=%d left=%d plen=%d\n", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - madwifi_wireless_event_rtm_newlink(drv, h, plen); - break; - } - - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); - } - - if (left > 0) { - printf("%d extra bytes in the end of netlink message\n", left); - } -} - - -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(void *priv) -{ - struct madwifi_driver_data *drv = priv; - int s; - struct sockaddr_nl local; - - madwifi_get_we_version(drv); - - drv->wext_sock = -1; - - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - return -1; - } - - memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); - return -1; - } - - eloop_register_read_sock(s, madwifi_wireless_event_receive, drv, NULL); - drv->wext_sock = s; - - return 0; -} - - -static void -madwifi_wireless_event_deinit(void *priv) -{ - struct madwifi_driver_data *drv = priv; - - if (drv != NULL) { - if (drv->wext_sock < 0) - return; - eloop_unregister_read_sock(drv->wext_sock); - close(drv->wext_sock); - } -} - - -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; - hostapd_eapol_receive(drv->hapd, src_addr, - buf + sizeof(struct l2_ethhdr), - len - sizeof(struct l2_ethhdr)); -} - -static void * -madwifi_init(struct hostapd_data *hapd) -{ - struct madwifi_driver_data *drv; - struct ifreq ifr; - struct iwreq iwr; - - 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, hapd->conf->iface, 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, hapd->own_addr)) - goto bad; - if (hapd->conf->bridge[0] != '\0') { - wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", - hapd->conf->bridge); - drv->sock_recv = l2_packet_init(hapd->conf->bridge, 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; - } - - madwifi_set_iface_flags(drv, 0); /* mark down during setup */ - madwifi_set_privacy(drv->iface, drv, 0); /* default to no privacy */ - - madwifi_receive_probe_req(drv); - - 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; - - (void) madwifi_set_iface_flags(drv, 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(const char *ifname, 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(const char *ifname, 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) -{ - return madwifi_set_iface_flags(priv, 1); -} - -const struct wpa_driver_ops wpa_driver_atheros_ops = { - .name = "atheros", - .init = madwifi_init, - .deinit = madwifi_deinit, - .set_ieee8021x = madwifi_set_ieee8021x, - .set_privacy = madwifi_set_privacy, - .set_encryption = madwifi_set_key, - .get_seqnum = madwifi_get_seqnum, - .flush = madwifi_flush, - .set_generic_elem = madwifi_set_opt_ie, - .wireless_event_init = madwifi_wireless_event_init, - .wireless_event_deinit = madwifi_wireless_event_deinit, - .sta_set_flags = madwifi_sta_set_flags, - .read_sta_data = madwifi_read_sta_driver_data, - .send_eapol = madwifi_send_eapol, - .sta_disassoc = madwifi_sta_disassoc, - .sta_deauth = madwifi_sta_deauth, - .set_ssid = madwifi_set_ssid, - .get_ssid = madwifi_get_ssid, - .set_countermeasures = madwifi_set_countermeasures, - .sta_clear_stats = madwifi_sta_clear_stats, - .commit = madwifi_commit, - .set_wps_beacon_ie = madwifi_set_wps_beacon_ie, - .set_wps_probe_resp_ie = madwifi_set_wps_probe_resp_ie, -}; diff --git a/contrib/hostapd/hostapd/driver_bsd.c b/contrib/hostapd/hostapd/driver_bsd.c deleted file mode 100644 index 43d57d9091..0000000000 --- a/contrib/hostapd/hostapd/driver_bsd.c +++ /dev/null @@ -1,839 +0,0 @@ -/* - * hostapd / Driver interaction with BSD net80211 layer - * Copyright (c) 2004, Sam Leffler - * 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 - -#include - -#include -#include -#include - -/* - * Avoid conflicts with hostapd definitions by undefining couple of defines - * from net80211 header files. - */ -#undef RSN_VERSION -#undef WPA_VERSION -#undef WPA_OUI_TYPE -#undef WME_OUI_TYPE - -#include "hostapd.h" -#include "driver.h" -#include "ieee802_1x.h" -#include "eloop.h" -#include "sta_info.h" -#include "l2_packet/l2_packet.h" - -#include "eapol_sm.h" -#include "wpa.h" -#include "radius/radius.h" -#include "ieee802_11.h" -#include "common.h" - -struct bsd_driver_data { - struct hostapd_data *hapd; /* back pointer */ - - char iface[IFNAMSIZ + 1]; - struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ - int ioctl_sock; /* socket for ioctl() use */ - int wext_sock; /* socket for wireless events */ -}; - -static int bsd_sta_deauth(void *priv, const u8 *addr, int reason_code); - -static int -set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len) -{ - struct ieee80211req ireq; - - memset(&ireq, 0, sizeof(ireq)); - os_strlcpy(ireq.i_name, drv->iface, IFNAMSIZ); - ireq.i_type = op; - ireq.i_len = arg_len; - ireq.i_data = (void *) arg; - - if (ioctl(drv->ioctl_sock, SIOCS80211, &ireq) < 0) { - perror("ioctl[SIOCS80211]"); - return -1; - } - return 0; -} - -static int -get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len) -{ - struct ieee80211req ireq; - - memset(&ireq, 0, sizeof(ireq)); - os_strlcpy(ireq.i_name, drv->iface, IFNAMSIZ); - ireq.i_type = op; - ireq.i_len = arg_len; - ireq.i_data = arg; - - if (ioctl(drv->ioctl_sock, SIOCG80211, &ireq) < 0) { - perror("ioctl[SIOCG80211]"); - return -1; - } - return ireq.i_len; -} - -static int -set80211param(struct bsd_driver_data *drv, int op, int arg) -{ - struct ieee80211req ireq; - - memset(&ireq, 0, sizeof(ireq)); - os_strlcpy(ireq.i_name, drv->iface, IFNAMSIZ); - ireq.i_type = op; - ireq.i_val = arg; - - if (ioctl(drv->ioctl_sock, SIOCS80211, &ireq) < 0) { - perror("ioctl[SIOCS80211]"); - return -1; - } - return 0; -} - -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; -} - -/* - * Configure WPA parameters. - */ -static int -bsd_configure_wpa(struct bsd_driver_data *drv) -{ - static const char *ciphernames[] = - { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" }; - struct hostapd_data *hapd = drv->hapd; - struct hostapd_bss_config *conf = hapd->conf; - int v; - - switch (conf->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", - conf->wpa_group); - return -1; - } - wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)", - __func__, ciphernames[v], v); - if (set80211param(drv, 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 = (conf->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); - if (set80211param(drv, IEEE80211_IOC_MCASTKEYLEN, v)) { - printf("Unable to set group key length to %u\n", v); - return -1; - } - } - - v = 0; - if (conf->wpa_pairwise & WPA_CIPHER_CCMP) - v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) - v |= 1<wpa_pairwise & WPA_CIPHER_NONE) - v |= 1<wpa_key_mgmt); - if (set80211param(drv, IEEE80211_IOC_KEYMGTALGS, conf->wpa_key_mgmt)) { - printf("Unable to set key management algorithms to 0x%x\n", - conf->wpa_key_mgmt); - return -1; - } - - v = 0; - if (conf->rsn_preauth) - v |= BIT(0); - wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", - __func__, conf->rsn_preauth); - if (set80211param(drv, IEEE80211_IOC_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__, conf->wpa); - if (set80211param(drv, IEEE80211_IOC_WPA, conf->wpa)) { - printf("Unable to set WPA to %u\n", conf->wpa); - return -1; - } - return 0; -} - - -static int -bsd_set_iface_flags(void *priv, int dev_up) -{ - struct bsd_driver_data *drv = priv; - struct ifreq ifr; - - wpa_printf(MSG_DEBUG, "%s: dev_up=%d", __func__, dev_up); - - if (drv->ioctl_sock < 0) - return -1; - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCGIFFLAGS]"); - return -1; - } - - if (dev_up) - ifr.ifr_flags |= IFF_UP; - else - ifr.ifr_flags &= ~IFF_UP; - - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCSIFFLAGS]"); - return -1; - } - - if (dev_up) { - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, 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 -bsd_set_ieee8021x(const char *ifname, void *priv, int enabled) -{ - struct bsd_driver_data *drv = priv; - struct hostapd_data *hapd = drv->hapd; - struct hostapd_bss_config *conf = hapd->conf; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - - if (!enabled) { - /* XXX restore state */ - return set80211param(priv, IEEE80211_IOC_AUTHMODE, - IEEE80211_AUTH_AUTO); - } - if (!conf->wpa && !conf->ieee802_1x) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); - return -1; - } - if (conf->wpa && bsd_configure_wpa(drv) != 0) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); - return -1; - } - if (set80211param(priv, IEEE80211_IOC_AUTHMODE, - (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); - return -1; - } - return bsd_set_iface_flags(priv, 1); -} - -static int -bsd_set_privacy(const char *ifname, void *priv, int enabled) -{ - struct bsd_driver_data *drv = priv; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - - return set80211param(drv, IEEE80211_IOC_PRIVACY, enabled); -} - -static int -bsd_set_sta_authorized(void *priv, const u8 *addr, int authorized) -{ - struct bsd_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - - 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); - return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); -} - -static int -bsd_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 & WLAN_STA_AUTHORIZED) - return bsd_set_sta_authorized(priv, addr, 1); - if (!(flags_and & WLAN_STA_AUTHORIZED)) - return bsd_set_sta_authorized(priv, addr, 0); - return 0; -} - -static int -bsd_del_key(void *priv, const u8 *addr, int key_idx) -{ - struct bsd_driver_data *drv = priv; - struct ieee80211req_del_key wk; - - 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 = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */ - } else { - wk.idk_keyix = key_idx; - } - - return set80211var(drv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); -} - -static int -bsd_set_key(const char *ifname, void *priv, const char *alg, - const u8 *addr, int key_idx, - const u8 *key, size_t key_len, int txkey) -{ - struct bsd_driver_data *drv = priv; - struct ieee80211req_key wk; - u_int8_t cipher; - - if (strcmp(alg, "none") == 0) - return bsd_del_key(drv, addr, key_idx); - - wpa_printf(MSG_DEBUG, "%s: alg=%s addr=%s key_idx=%d", - __func__, alg, ether_sprintf(addr), key_idx); - - if (strcmp(alg, "WEP") == 0) - cipher = IEEE80211_CIPHER_WEP; - else if (strcmp(alg, "TKIP") == 0) - cipher = IEEE80211_CIPHER_TKIP; - else if (strcmp(alg, "CCMP") == 0) - cipher = IEEE80211_CIPHER_AES_CCM; - else { - printf("%s: unknown/unsupported algorithm %s\n", - __func__, alg); - return -1; - } - - if (key_len > sizeof(wk.ik_keydata)) { - printf("%s: key length %d too big\n", __func__, 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); - - return set80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); -} - - -static int -bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, - u8 *seq) -{ - struct bsd_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 (get80211var(drv, 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, allsta, IEEE80211_REASON_AUTH_LEAVE); -} - - -static int -bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, - const u8 *addr) -{ - struct bsd_driver_data *drv = priv; - struct ieee80211req_sta_stats stats; - - memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); - if (get80211var(drv, 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_set_opt_ie(const char *ifname, 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 -bsd_sta_deauth(void *priv, const u8 *addr, int reason_code) -{ - struct bsd_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - - 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); - return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); -} - -static int -bsd_sta_disassoc(void *priv, const u8 *addr, int reason_code) -{ - struct bsd_driver_data *drv = priv; - struct ieee80211req_mlme mlme; - - 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); - return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); -} - -static int -bsd_del_sta(struct bsd_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) -{ - struct hostapd_data *hapd = drv->hapd; - struct hostapd_bss_config *conf = hapd->conf; - struct sta_info *sta; - - hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "deassociated"); - - sta = ap_get_sta(hapd, addr); - if (sta != NULL) { - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - if (conf->wpa) - 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); - } - return 0; -} - -static int -bsd_new_sta(struct bsd_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) -{ - struct hostapd_data *hapd = drv->hapd; - struct hostapd_bss_config *conf = hapd->conf; - struct sta_info *sta; - struct ieee80211req_wpaie ie; - int new_assoc, ielen, res; - - hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "associated"); - - sta = ap_sta_add(hapd, addr); - if (sta == NULL) - return -1; - /* - * Fetch and validate any negotiated WPA/RSN parameters. - */ - if (conf->wpa) { - memset(&ie, 0, sizeof(ie)); - memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); - if (get80211var(drv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) { - printf("Failed to get WPA/RSN information element.\n"); - return -1; /* XXX not right */ - } - ielen = ie.wpa_ie[1]; - if (ielen == 0) { - printf("No WPA/RSN information element for station!\n"); - return -1; /* XXX not right */ - } - ielen += 2; - if (sta->wpa_sm == NULL) - sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr); - if (sta->wpa_sm == NULL) { - printf("Failed to initialize WPA state machine\n"); - return -1; - } - res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, - ie.wpa_ie, ielen, NULL, 0); - if (res != WPA_IE_OK) { - printf("WPA/RSN information element rejected? " - "(res %u)\n", res); - return -1; - } - } - - /* - * Now that the internal station state is setup - * kick the authenticator into action. - */ - 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; -} - -#include -#include - -static void -bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) -{ - struct bsd_driver_data *drv = ctx; - struct hostapd_data *hapd = drv->hapd; - 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; - - 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]; - bsd_del_sta(drv, 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, 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)); - ieee80211_michael_mic_failure(hapd, mic->iev_src, 1); - break; - } - break; - } -} - -static int -bsd_wireless_event_init(void *priv) -{ - struct bsd_driver_data *drv = priv; - int s; - - drv->wext_sock = -1; - - s = socket(PF_ROUTE, SOCK_RAW, 0); - if (s < 0) { - perror("socket(PF_ROUTE,SOCK_RAW)"); - return -1; - } - eloop_register_read_sock(s, bsd_wireless_event_receive, drv, NULL); - drv->wext_sock = s; - - return 0; -} - -static void -bsd_wireless_event_deinit(void *priv) -{ - struct bsd_driver_data *drv = priv; - - if (drv != NULL) { - if (drv->wext_sock < 0) - return; - eloop_unregister_read_sock(drv->wext_sock); - close(drv->wext_sock); - } -} - - -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; - unsigned char buf[3000]; - unsigned char *bp = buf; - struct l2_ethhdr *eth; - size_t len; - int status; - - /* - * Prepend the Etherent 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 %u!\n", 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 = htons(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 bsd_driver_data *drv = ctx; - struct hostapd_data *hapd = drv->hapd; - struct sta_info *sta; - - sta = ap_get_sta(hapd, src_addr); - if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { - printf("Data frame from not associated STA %s\n", - ether_sprintf(src_addr)); - /* XXX cannot happen */ - return; - } - ieee802_1x_receive(hapd, src_addr, buf + sizeof(struct l2_ethhdr), - len - sizeof(struct l2_ethhdr)); -} - -static int -bsd_get_ssid(const char *ifname, void *priv, u8 *buf, int len) -{ - struct bsd_driver_data *drv = priv; - int ssid_len = get80211var(drv, IEEE80211_IOC_SSID, buf, len); - - wpa_printf(MSG_DEBUG, "%s: ssid=\"%.*s\"", __func__, ssid_len, buf); - - return ssid_len; -} - -static int -bsd_set_ssid(const char *ifname, void *priv, const u8 *buf, int len) -{ - struct bsd_driver_data *drv = priv; - - wpa_printf(MSG_DEBUG, "%s: ssid=\"%.*s\"", __func__, len, buf); - - return set80211var(drv, IEEE80211_IOC_SSID, buf, len); -} - -static void * -bsd_init(struct hostapd_data *hapd) -{ - 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->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->ioctl_sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); - goto bad; - } - memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); - - 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, hapd->own_addr)) - goto bad; - - bsd_set_iface_flags(drv, 0); /* mark down during setup */ - - 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 -bsd_deinit(void *priv) -{ - struct bsd_driver_data *drv = priv; - - (void) bsd_set_iface_flags(drv, 0); - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); - if (drv->sock_xmit != NULL) - l2_packet_deinit(drv->sock_xmit); - free(drv); -} - -const struct wpa_driver_ops wpa_driver_bsd_ops = { - .name = "bsd", - .init = bsd_init, - .deinit = bsd_deinit, - .set_ieee8021x = bsd_set_ieee8021x, - .set_privacy = bsd_set_privacy, - .set_encryption = bsd_set_key, - .get_seqnum = bsd_get_seqnum, - .flush = bsd_flush, - .set_generic_elem = bsd_set_opt_ie, - .wireless_event_init = bsd_wireless_event_init, - .wireless_event_deinit = bsd_wireless_event_deinit, - .sta_set_flags = bsd_sta_set_flags, - .read_sta_data = bsd_read_sta_driver_data, - .send_eapol = bsd_send_eapol, - .sta_disassoc = bsd_sta_disassoc, - .sta_deauth = bsd_sta_deauth, - .set_ssid = bsd_set_ssid, - .get_ssid = bsd_get_ssid, -}; diff --git a/contrib/hostapd/hostapd/driver_hostap.c b/contrib/hostapd/hostapd/driver_hostap.c deleted file mode 100644 index ceff099891..0000000000 --- a/contrib/hostapd/hostapd/driver_hostap.c +++ /dev/null @@ -1,1279 +0,0 @@ -/* - * hostapd / Kernel driver communication with Linux Host AP driver - * Copyright (c) 2002-2007, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include - -#ifdef USE_KERNEL_HEADERS -/* compat-wireless does not include linux/compiler.h to define __user, so - * define it here */ -#ifndef __user -#define __user -#endif /* __user */ -#include -#include -#include /* The L2 protocols */ -#include -#include -#else /* USE_KERNEL_HEADERS */ -#include -#include -#include "wireless_copy.h" -#endif /* USE_KERNEL_HEADERS */ - -#include "hostapd.h" -#include "driver.h" -#include "ieee802_1x.h" -#include "eloop.h" -#include "priv_netlink.h" -#include "ieee802_11.h" -#include "sta_info.h" -#include "hostap_common.h" -#include "hw_features.h" - - -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 */ - int wext_sock; /* socket for wireless events */ - - 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 hostapd_data *hapd, u8 *buf, size_t len, - u16 stype) -{ - struct ieee80211_hdr *hdr; - u16 fc, ethertype; - u8 *pos, *sa; - size_t left; - struct sta_info *sta; - - 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; - sta = ap_get_sta(hapd, sa); - if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { - printf("Data frame from not associated STA " MACSTR "\n", - MAC2STR(sa)); - if (sta && (sta->flags & WLAN_STA_AUTH)) - hostapd_sta_disassoc( - hapd, sa, - WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); - else - hostapd_sta_deauth( - hapd, sa, - WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); - return; - } - - 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: - ieee802_1x_receive(hapd, sa, pos, left); - break; - - default: - printf("Unknown ethertype 0x%04x in data frame\n", ethertype); - break; - } -} - - -static void handle_tx_callback(struct hostapd_data *hapd, u8 *buf, size_t len, - int ok) -{ - struct ieee80211_hdr *hdr; - u16 fc, type, stype; - struct sta_info *sta; - - hdr = (struct ieee80211_hdr *) buf; - 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_MGMT: - wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s", - ok ? "ACK" : "fail"); - ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); - break; - case WLAN_FC_TYPE_CTRL: - wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s", - ok ? "ACK" : "fail"); - break; - case WLAN_FC_TYPE_DATA: - wpa_printf(MSG_DEBUG, "DATA (TX callback) %s", - ok ? "ACK" : "fail"); - sta = ap_get_sta(hapd, hdr->addr1); - if (sta && sta->flags & WLAN_STA_PENDING_POLL) { - wpa_printf(MSG_DEBUG, "STA " MACSTR - " %s pending activity poll", - MAC2STR(sta->addr), - ok ? "ACKed" : "did not ACK"); - if (ok) - sta->flags &= ~WLAN_STA_PENDING_POLL; - } - if (sta) - ieee802_1x_tx_status(hapd, sta, buf, len, ok); - break; - default: - printf("unknown TX callback frame type %d\n", type); - break; - } -} - - -static void handle_frame(struct hostapd_data *hapd, 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; - - /* 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(hapd, 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: - if (stype != WLAN_FC_STYPE_BEACON) - wpa_printf(MSG_MSGDUMP, "MGMT"); - ieee802_11_mgmt(hapd, buf, data_len, stype, NULL); - break; - case WLAN_FC_TYPE_CTRL: - wpa_printf(MSG_DEBUG, "CTRL"); - break; - case WLAN_FC_TYPE_DATA: - wpa_printf(MSG_DEBUG, "DATA"); - handle_data(hapd, 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 hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; - int len; - unsigned char buf[3000]; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - perror("recv"); - return; - } - - handle_frame(hapd, buf, len); -} - - -static int hostap_init_sockets(struct hostap_driver_data *drv) -{ - 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->hapd, 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; - } - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, 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; - } - memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - - return 0; -} - - -static int hostap_send_mgmt_frame(void *priv, const void *msg, size_t len, - int flags) -{ - 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, flags); - 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_mgmt_frame(drv, (u8 *) hdr, len, 0); - free(hdr); - - if (res < 0) { - perror("hostapd_send_eapol: send"); - printf("hostapd_send_eapol - packet len: %lu - failed\n", - (unsigned long) len); - } - - 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; - - memset(¶m, 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, ¶m, sizeof(param)); -} - - -static int hostap_set_iface_flags(void *priv, int dev_up) -{ - struct hostap_driver_data *drv = priv; - struct ifreq ifr; - - if (drv->ioctl_sock < 0) - return -1; - - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, IFNAMSIZ, "%sap", drv->iface); - - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCGIFFLAGS]"); - return -1; - } - - if (dev_up) - ifr.ifr_flags |= IFF_UP; - else - ifr.ifr_flags &= ~IFF_UP; - - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCSIFFLAGS]"); - return -1; - } - - if (dev_up) { - memset(&ifr, 0, sizeof(ifr)); - snprintf(ifr.ifr_name, IFNAMSIZ, "%sap", drv->iface); - 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 hostap_set_encryption(const char *ifname, void *priv, - const char *alg, const u8 *addr, - int idx, const u8 *key, size_t key_len, - int txkey) -{ - 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); - os_strlcpy((char *) param->u.crypt.alg, alg, - HOSTAP_CRYPT_ALG_NAME_LEN); - param->u.crypt.flags = txkey ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; - param->u.crypt.idx = 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(const char *ifname, void *priv, int enabled) -{ - struct hostap_driver_data *drv = priv; - - /* 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(const char *ifname, 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(const char *ifname, 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(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_FLUSH; - return hostapd_ioctl(drv, ¶m, 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(const char *ifname, void *priv, const u8 *addr, - u16 aid, u16 capability, u8 *supp_rates, - size_t supp_rates_len, int flags, - u16 listen_interval) -{ - 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 < supp_rates_len; i++) { - if ((supp_rates[i] & 0x7f) == 2) - tx_supp_rates |= WLAN_RATE_1M; - if ((supp_rates[i] & 0x7f) == 4) - tx_supp_rates |= WLAN_RATE_2M; - if ((supp_rates[i] & 0x7f) == 11) - tx_supp_rates |= WLAN_RATE_5M5; - if ((supp_rates[i] & 0x7f) == 22) - tx_supp_rates |= WLAN_RATE_11M; - } - - memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_ADD_STA; - memcpy(param.sta_addr, addr, ETH_ALEN); - param.u.add_sta.aid = aid; - param.u.add_sta.capability = capability; - param.u.add_sta.tx_supp_rates = tx_supp_rates; - return hostapd_ioctl(drv, ¶m, 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, ~WLAN_STA_AUTHORIZED); - - memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_REMOVE_STA; - memcpy(param.sta_addr, addr, ETH_ALEN); - if (hostapd_ioctl(drv, ¶m, 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(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_GET_INFO_STA; - memcpy(param.sta_addr, addr, ETH_ALEN); - if (hostapd_ioctl(drv, ¶m, 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(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS; - memcpy(param.sta_addr, addr, ETH_ALEN); - if (hostapd_ioctl(drv, ¶m, sizeof(param))) { - return -1; - } - - return 0; -} - - -static int hostap_set_assoc_ap(void *priv, const u8 *addr) -{ - struct hostap_driver_data *drv = priv; - struct prism2_hostapd_param param; - - memset(¶m, 0, sizeof(param)); - param.cmd = PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR; - memcpy(param.sta_addr, addr, ETH_ALEN); - if (hostapd_ioctl(drv, ¶m, 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(¶m->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(const char *ifname, 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_wps_beacon_ie(const char *ifname, void *priv, - const u8 *ie, size_t len) -{ - /* Host AP driver supports only one set of extra IEs, so we need to - * use the ProbeResp IEs also for Beacon frames since they include more - * information. */ - return 0; -} - - -static int hostap_set_wps_probe_resp_ie(const char *ifname, void *priv, - const u8 *ie, size_t len) -{ - struct hostap_driver_data *drv = priv; - - os_free(drv->wps_ie); - drv->wps_ie = NULL; - drv->wps_ie_len = 0; - if (ie) { - drv->wps_ie = os_malloc(len); - if (drv->wps_ie == NULL) - return -1; - os_memcpy(drv->wps_ie, ie, len); - drv->wps_ie_len = len; - } - - 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) { - ieee80211_michael_mic_failure(drv->hapd, addr, 1); - } 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(struct hostap_driver_data *drv, - struct nlmsghdr *h, int len) -{ - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr * attr; - - if (len < (int) sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - - /* TODO: use ifi->ifi_index to filter out wireless events from other - * interfaces */ - - 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)) { - 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 void hostapd_wireless_event_receive(int sock, void *eloop_ctx, - void *sock_ctx) -{ - char buf[256]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - struct hostap_driver_data *drv = eloop_ctx; - - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - perror("recvfrom(netlink)"); - return; - } - - h = (struct nlmsghdr *) buf; - while (left >= (int) sizeof(*h)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - printf("Malformed netlink message: " - "len=%d left=%d plen=%d\n", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - hostapd_wireless_event_rtm_newlink(drv, h, plen); - break; - } - - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); - } - - if (left > 0) { - printf("%d extra bytes in the end of netlink message\n", left); - } -} - - -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(void *priv) -{ - struct hostap_driver_data *drv = priv; - int s; - struct sockaddr_nl local; - - hostap_get_we_version(drv); - - drv->wext_sock = -1; - - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - return -1; - } - - memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); - return -1; - } - - eloop_register_read_sock(s, hostapd_wireless_event_receive, drv, - NULL); - drv->wext_sock = s; - - return 0; -} - - -static void hostap_wireless_event_deinit(void *priv) -{ - struct hostap_driver_data *drv = priv; - if (drv->wext_sock < 0) - return; - eloop_unregister_read_sock(drv->wext_sock); - close(drv->wext_sock); -} - - -static void * hostap_init(struct hostapd_data *hapd) -{ - 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, hapd->conf->iface, 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)) { - close(drv->ioctl_sock); - free(drv); - return NULL; - } - - return drv; -} - - -static void hostap_driver_deinit(void *priv) -{ - struct hostap_driver_data *drv = priv; - - (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 *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, drv->hapd->own_addr, ETH_ALEN); - memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); - mgmt.u.deauth.reason_code = host_to_le16(reason); - return hostap_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth), 0); -} - - -static int hostap_sta_disassoc(void *priv, 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, drv->hapd->own_addr, ETH_ALEN); - memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); - mgmt.u.disassoc.reason_code = host_to_le16(reason); - return hostap_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.disassoc), 0); -} - - -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(struct hostapd_rate_data); - - mode->channels = os_zalloc(clen); - mode->rates = os_zalloc(rlen); - if (mode->channels == NULL || mode->rates == NULL) { - hostapd_free_hw_features(mode, *num_modes); - 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].rate = 10; - mode->rates[0].flags = HOSTAPD_RATE_CCK; - mode->rates[1].rate = 20; - mode->rates[1].flags = HOSTAPD_RATE_CCK; - mode->rates[2].rate = 55; - mode->rates[2].flags = HOSTAPD_RATE_CCK; - mode->rates[3].rate = 110; - mode->rates[3].flags = HOSTAPD_RATE_CCK; - - return mode; -} - - -const struct wpa_driver_ops wpa_driver_hostap_ops = { - .name = "hostap", - .init = hostap_init, - .deinit = hostap_driver_deinit, - .wireless_event_init = hostap_wireless_event_init, - .wireless_event_deinit = hostap_wireless_event_deinit, - .set_ieee8021x = hostap_set_ieee8021x, - .set_privacy = hostap_set_privacy, - .set_encryption = hostap_set_encryption, - .get_seqnum = hostap_get_seqnum, - .flush = hostap_flush, - .set_generic_elem = hostap_set_generic_elem, - .read_sta_data = hostap_read_sta_data, - .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, - .set_ssid = hostap_set_ssid, - .send_mgmt_frame = hostap_send_mgmt_frame, - .set_assoc_ap = hostap_set_assoc_ap, - .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_wps_beacon_ie = hostap_set_wps_beacon_ie, - .set_wps_probe_resp_ie = hostap_set_wps_probe_resp_ie, -}; diff --git a/contrib/hostapd/hostapd/driver_madwifi.c b/contrib/hostapd/hostapd/driver_madwifi.c deleted file mode 100644 index 4083fda29b..0000000000 --- a/contrib/hostapd/hostapd/driver_madwifi.c +++ /dev/null @@ -1,1483 +0,0 @@ -/* - * hostapd / Driver interaction with MADWIFI 802.11 driver - * Copyright (c) 2004, Sam Leffler - * Copyright (c) 2004, Video54 Technologies - * Copyright (c) 2005-2007, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include -#include - -#include -#include -#ifdef WME_NUM_AC -/* Assume this is built against BSD branch of madwifi driver. */ -#define MADWIFI_BSD -#include -#endif /* WME_NUM_AC */ -#include -#include - -#ifdef CONFIG_WPS -#ifdef IEEE80211_IOCTL_FILTERFRAME -#include - -#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 */ - -#include "wireless_copy.h" - -#include "hostapd.h" -#include "driver.h" -#include "ieee802_1x.h" -#include "eloop.h" -#include "priv_netlink.h" -#include "sta_info.h" -#include "l2_packet/l2_packet.h" - -#include "wpa.h" -#include "radius/radius.h" -#include "ieee802_11.h" -#include "accounting.h" -#include "common.h" -#include "wps_hostapd.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 */ - int wext_sock; /* socket for wireless events */ - 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 *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; -} - -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; -} - -/* - * Configure WPA parameters. - */ -static int -madwifi_configure_wpa(struct madwifi_driver_data *drv) -{ - struct hostapd_data *hapd = drv->hapd; - struct hostapd_bss_config *conf = hapd->conf; - int v; - - switch (conf->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", - conf->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 = (conf->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 (conf->wpa_pairwise & WPA_CIPHER_CCMP) - v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) - v |= 1<wpa_pairwise & WPA_CIPHER_NONE) - v |= 1<wpa_key_mgmt); - if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, conf->wpa_key_mgmt)) { - printf("Unable to set key management algorithms to 0x%x\n", - conf->wpa_key_mgmt); - return -1; - } - - v = 0; - if (conf->rsn_preauth) - v |= BIT(0); - wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", - __func__, conf->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__, conf->wpa); - if (set80211param(drv, IEEE80211_PARAM_WPA, conf->wpa)) { - printf("Unable to set WPA to %u\n", conf->wpa); - return -1; - } - return 0; -} - - -static int -madwifi_set_iface_flags(void *priv, int dev_up) -{ - struct madwifi_driver_data *drv = priv; - struct ifreq ifr; - - wpa_printf(MSG_DEBUG, "%s: dev_up=%d", __func__, dev_up); - - if (drv->ioctl_sock < 0) - return -1; - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCGIFFLAGS]"); - return -1; - } - - if (dev_up) - ifr.ifr_flags |= IFF_UP; - else - ifr.ifr_flags &= ~IFF_UP; - - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCSIFFLAGS]"); - return -1; - } - - if (dev_up) { - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, 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 -madwifi_set_ieee8021x(const char *ifname, void *priv, int enabled) -{ - struct madwifi_driver_data *drv = priv; - struct hostapd_data *hapd = drv->hapd; - struct hostapd_bss_config *conf = hapd->conf; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - - if (!enabled) { - /* XXX restore state */ - return set80211param(priv, IEEE80211_PARAM_AUTHMODE, - IEEE80211_AUTH_AUTO); - } - if (!conf->wpa && !conf->ieee802_1x) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); - return -1; - } - if (conf->wpa && madwifi_configure_wpa(drv) != 0) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); - return -1; - } - if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, - (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, - HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); - return -1; - } - - return 0; -} - -static int -madwifi_set_privacy(const char *ifname, 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 & WLAN_STA_AUTHORIZED) - return madwifi_set_sta_authorized(priv, addr, 1); - if (!(flags_and & WLAN_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, const char *alg, - const u8 *addr, int key_idx, - const u8 *key, size_t key_len, int txkey) -{ - struct madwifi_driver_data *drv = priv; - struct ieee80211req_key wk; - u_int8_t cipher; - int ret; - - if (strcmp(alg, "none") == 0) - return madwifi_del_key(drv, addr, key_idx); - - wpa_printf(MSG_DEBUG, "%s: alg=%s addr=%s key_idx=%d", - __func__, alg, ether_sprintf(addr), key_idx); - - if (strcmp(alg, "WEP") == 0) - cipher = IEEE80211_CIPHER_WEP; - else if (strcmp(alg, "TKIP") == 0) - cipher = IEEE80211_CIPHER_TKIP; - else if (strcmp(alg, "CCMP") == 0) - cipher = IEEE80211_CIPHER_AES_CCM; - else { - printf("%s: unknown/unsupported algorithm %s\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 '%s' key_len %lu txkey %d)", - __func__, ether_sprintf(wk.ik_macaddr), key_idx, - alg, (unsigned long) key_len, txkey); - } - - 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, 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(const char *ifname, 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 *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 *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; - const u8 *end, *ie; - u16 fc; - size_t ie_len; - - /* 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; - - end = buf + len; - ie = mgmt->u.probe_req.variable; - ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); - - hostapd_wps_probe_req_rx(drv->hapd, mgmt->sa, ie, ie_len); -} -#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; -} - -static int -madwifi_del_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) -{ - struct hostapd_data *hapd = drv->hapd; - struct sta_info *sta; - - hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "disassociated"); - - sta = ap_get_sta(hapd, addr); - if (sta != NULL) { - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - 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); - } - return 0; -} - -#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_wps_beacon_ie(const char *ifname, void *priv, const u8 *ie, - size_t len) -{ - return madwifi_set_wps_ie(priv, ie, len, IEEE80211_APPIE_FRAME_BEACON); -} - -static int -madwifi_set_wps_probe_resp_ie(const char *ifname, void *priv, const u8 *ie, - size_t len) -{ - return madwifi_set_wps_ie(priv, ie, len, - IEEE80211_APPIE_FRAME_PROBE_RESP); -} -#else /* CONFIG_WPS */ -#define madwifi_set_wps_beacon_ie NULL -#define madwifi_set_wps_probe_resp_ie NULL -#endif /* CONFIG_WPS */ - -static int -madwifi_process_wpa_ie(struct madwifi_driver_data *drv, struct sta_info *sta) -{ - struct hostapd_data *hapd = drv->hapd; - struct ieee80211req_wpaie ie; - int ielen, res; - u8 *iebuf; - - /* - * Fetch negotiated WPA/RSN parameters from the system. - */ - memset(&ie, 0, sizeof(ie)); - memcpy(ie.wpa_macaddr, sta->addr, IEEE80211_ADDR_LEN); - if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { - wpa_printf(MSG_ERROR, "%s: Failed to get WPA/RSN IE", - __func__); - printf("Failed to get WPA/RSN information element.\n"); - return -1; /* XXX not right */ - } - 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) { -#ifdef CONFIG_WPS - 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; - return 0; - } -#endif /* CONFIG_WPS */ - printf("No WPA/RSN information element for station!?\n"); - return -1; /* XXX not right */ - } - ielen += 2; - if (sta->wpa_sm == NULL) - sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); - if (sta->wpa_sm == NULL) { - printf("Failed to initialize WPA state machine\n"); - return -1; - } - res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, - iebuf, ielen, NULL, 0); - if (res != WPA_IE_OK) { - printf("WPA/RSN information element rejected? (res %u)\n", res); - return -1; - } - return 0; -} - -static int -madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) -{ - struct hostapd_data *hapd = drv->hapd; - struct sta_info *sta; - int new_assoc; - - hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "associated"); - - 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; - } - - 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)); - } - - if (hapd->conf->wpa) { - if (madwifi_process_wpa_ie(drv, sta)) - return -1; - } - - /* - * Now that the internal station state is setup - * kick the authenticator into action. - */ - 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; -} - -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) { - ieee80211_michael_mic_failure(drv->hapd, addr, 1); - } 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: - madwifi_del_sta(drv, (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(struct madwifi_driver_data *drv, - struct nlmsghdr *h, int len) -{ - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr * attr; - - if (len < (int) sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - - if (ifi->ifi_index != drv->ifindex) - return; - - 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)) { - 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 void -madwifi_wireless_event_receive(int sock, void *eloop_ctx, void *sock_ctx) -{ - char buf[256]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - struct madwifi_driver_data *drv = eloop_ctx; - - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - perror("recvfrom(netlink)"); - return; - } - - h = (struct nlmsghdr *) buf; - while (left >= (int) sizeof(*h)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - printf("Malformed netlink message: " - "len=%d left=%d plen=%d\n", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - madwifi_wireless_event_rtm_newlink(drv, h, plen); - break; - } - - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); - } - - if (left > 0) { - printf("%d extra bytes in the end of netlink message\n", left); - } -} - - -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(void *priv) -{ - struct madwifi_driver_data *drv = priv; - int s; - struct sockaddr_nl local; - - madwifi_get_we_version(drv); - - drv->wext_sock = -1; - - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - return -1; - } - - memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); - return -1; - } - - eloop_register_read_sock(s, madwifi_wireless_event_receive, drv, NULL); - drv->wext_sock = s; - - return 0; -} - - -static void -madwifi_wireless_event_deinit(void *priv) -{ - struct madwifi_driver_data *drv = priv; - - if (drv != NULL) { - if (drv->wext_sock < 0) - return; - eloop_unregister_read_sock(drv->wext_sock); - close(drv->wext_sock); - } -} - - -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 = htons(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; - struct hostapd_data *hapd = drv->hapd; - struct sta_info *sta; - - sta = ap_get_sta(hapd, src_addr); - if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { - printf("Data frame from not associated STA %s\n", - ether_sprintf(src_addr)); - /* XXX cannot happen */ - return; - } - ieee802_1x_receive(hapd, src_addr, buf + sizeof(struct l2_ethhdr), - len - sizeof(struct l2_ethhdr)); -} - -static void * -madwifi_init(struct hostapd_data *hapd) -{ - struct madwifi_driver_data *drv; - struct ifreq ifr; - struct iwreq iwr; - - 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, hapd->conf->iface, 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, hapd->own_addr)) - goto bad; - if (hapd->conf->bridge[0] != '\0') { - wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", - hapd->conf->bridge); - drv->sock_recv = l2_packet_init(hapd->conf->bridge, 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; - } - - madwifi_set_iface_flags(drv, 0); /* mark down during setup */ - madwifi_set_privacy(drv->iface, drv, 0); /* default to no privacy */ - - madwifi_receive_probe_req(drv); - - 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; - - (void) madwifi_set_iface_flags(drv, 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(const char *ifname, 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(const char *ifname, 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) -{ - return madwifi_set_iface_flags(priv, 1); -} - -const struct wpa_driver_ops wpa_driver_madwifi_ops = { - .name = "madwifi", - .init = madwifi_init, - .deinit = madwifi_deinit, - .set_ieee8021x = madwifi_set_ieee8021x, - .set_privacy = madwifi_set_privacy, - .set_encryption = madwifi_set_key, - .get_seqnum = madwifi_get_seqnum, - .flush = madwifi_flush, - .set_generic_elem = madwifi_set_opt_ie, - .wireless_event_init = madwifi_wireless_event_init, - .wireless_event_deinit = madwifi_wireless_event_deinit, - .sta_set_flags = madwifi_sta_set_flags, - .read_sta_data = madwifi_read_sta_driver_data, - .send_eapol = madwifi_send_eapol, - .sta_disassoc = madwifi_sta_disassoc, - .sta_deauth = madwifi_sta_deauth, - .set_ssid = madwifi_set_ssid, - .get_ssid = madwifi_get_ssid, - .set_countermeasures = madwifi_set_countermeasures, - .sta_clear_stats = madwifi_sta_clear_stats, - .commit = madwifi_commit, - .set_wps_beacon_ie = madwifi_set_wps_beacon_ie, - .set_wps_probe_resp_ie = madwifi_set_wps_probe_resp_ie, -}; diff --git a/contrib/hostapd/hostapd/driver_nl80211.c b/contrib/hostapd/hostapd/driver_nl80211.c deleted file mode 100644 index 4599e99ead..0000000000 --- a/contrib/hostapd/hostapd/driver_nl80211.c +++ /dev/null @@ -1,2708 +0,0 @@ -/* - * hostapd / Kernel driver communication via nl80211 - * Copyright (c) 2002-2007, Jouni Malinen - * Copyright (c) 2003-2004, Instant802 Networks, Inc. - * Copyright (c) 2005-2006, Devicescape Software, Inc. - * Copyright (c) 2007, Johannes Berg - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by 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 -#include -#include -#include -#include -#include -#include "nl80211_copy.h" -#include -#include -#include "wireless_copy.h" -#include -#include - -#include "hostapd.h" -#include "driver.h" -#include "ieee802_1x.h" -#include "eloop.h" -#include "ieee802_11.h" -#include "sta_info.h" -#include "hw_features.h" -#include "mlme.h" -#include "radiotap.h" -#include "radiotap_iter.h" - -#ifdef CONFIG_LIBNL20 -/* libnl 2.0 compatibility code */ -#define nl_handle_alloc_cb nl_socket_alloc_cb -#define nl_handle_destroy nl_socket_free -#endif /* CONFIG_LIBNL20 */ - -enum ieee80211_msg_type { - ieee80211_msg_normal = 0, - ieee80211_msg_tx_callback_ack = 1, - ieee80211_msg_tx_callback_fail = 2, -}; - -struct i802_driver_data { - struct hostapd_data *hapd; - - char iface[IFNAMSIZ + 1]; - int bridge; - int ioctl_sock; /* socket for ioctl() use */ - int wext_sock; /* socket for wireless events */ - int eapol_sock; /* socket for EAPOL frames */ - int monitor_sock; /* socket for monitor */ - int monitor_ifidx; - - int default_if_indices[16]; - int *if_indices; - int num_if_indices; - - int we_version; - struct nl_handle *nl_handle; - struct nl_cache *nl_cache; - struct nl_cb *nl_cb; - struct genl_family *nl80211; - int dtim_period, beacon_int; - unsigned int beacon_set:1; - unsigned int ieee802_1x_active:1; - - int last_freq; - int last_freq_ht; -}; - - -static void add_ifidx(struct i802_driver_data *drv, int ifidx) -{ - int i; - int *old; - - 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 = 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; - } - drv->if_indices[drv->num_if_indices] = ifidx; - drv->num_if_indices++; -} - - -static void del_ifidx(struct i802_driver_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 i802_driver_data *drv, int ifidx) -{ - int i; - - if (ifidx == drv->bridge) - return 1; - - for (i = 0; i < drv->num_if_indices; i++) - if (drv->if_indices[i] == ifidx) - return 1; - - return 0; -} - - -/* 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 send_and_recv_msgs(struct i802_driver_data *drv, - 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(drv->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(drv->nl_handle, cb); - out: - nl_cb_put(cb); - nlmsg_free(msg); - return err; -} - -static int hostapd_set_iface_flags(struct i802_driver_data *drv, - const char *ifname, int dev_up) -{ - struct ifreq ifr; - - if (drv->ioctl_sock < 0) - return -1; - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCGIFFLAGS]"); - wpa_printf(MSG_DEBUG, "Could not read interface flags (%s)", - drv->iface); - return -1; - } - - if (dev_up) - ifr.ifr_flags |= IFF_UP; - else - ifr.ifr_flags &= ~IFF_UP; - - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCSIFFLAGS]"); - return -1; - } - - return 0; -} - - -static int nl_set_encr(int ifindex, struct i802_driver_data *drv, - const char *alg, const u8 *addr, int idx, const u8 *key, - size_t key_len, int txkey) -{ - struct nl_msg *msg; - int ret; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - if (strcmp(alg, "none") == 0) { - 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); - if (strcmp(alg, "WEP") == 0) { - if (key_len == 5) - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - 0x000FAC01); - else - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - 0x000FAC05); - } else if (strcmp(alg, "TKIP") == 0) - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); - else if (strcmp(alg, "CCMP") == 0) - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); - else if (strcmp(alg, "IGTK") == 0) - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06); - else { - wpa_printf(MSG_ERROR, "%s: Unsupported encryption " - "algorithm '%s'", __func__, alg); - nlmsg_free(msg); - return -1; - } - } - - 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, ifindex); - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret == -ENOENT) - ret = 0; - - /* - * If we failed or don't need to set the default TX key (below), - * we're done here. - */ - if (ret || !txkey || addr) - return ret; - - 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, idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - if (strcmp(alg, "IGTK") == 0) - 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; - return ret; - nla_put_failure: - return -ENOBUFS; -} - - -static int i802_set_encryption(const char *iface, void *priv, const char *alg, - const u8 *addr, int idx, const u8 *key, - size_t key_len, int txkey) -{ - struct i802_driver_data *drv = priv; - int ret; - - ret = nl_set_encr(if_nametoindex(iface), drv, alg, addr, idx, key, - key_len, txkey); - if (ret < 0) - return ret; - - return ret; -} - - -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_driver_data *drv = priv; - 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_driver_data *drv = priv; - 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); - - /* TODO: multi-BSS support */ - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - - -static int i802_send_frame(void *priv, const void *data, size_t len, - int encrypt, int flags) -{ - __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 i802_driver_data *drv = priv; - 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, flags); -} - -static int i802_send_mgmt_frame(void *priv, const void *data, size_t len, - int flags) -{ - struct ieee80211_mgmt *mgmt; - int do_not_encrypt = 0; - 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_OPEN || - (auth_alg == WLAN_AUTH_SHARED_KEY && auth_trans != 3)) - do_not_encrypt = 1; - } - - return i802_send_frame(priv, data, len, !do_not_encrypt, flags); -} - -/* Set kernel driver on given frequency (MHz) */ -static int i802_set_freq2(void *priv, struct hostapd_freq_params *freq) -{ - struct i802_driver_data *drv = priv; - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - drv->last_freq = freq->freq; - drv->last_freq_ht = freq->ht_enabled; - - 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(drv->iface)); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); - if (freq->ht_enabled) { - switch (freq->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; - } - } - - if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) - return 0; - nla_put_failure: - return -1; -} - - -static int i802_set_rts(void *priv, int rts) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); - iwr.u.rts.value = rts; - iwr.u.rts.fixed = 1; - - if (ioctl(drv->ioctl_sock, SIOCSIWRTS, &iwr) < 0) { - perror("ioctl[SIOCSIWRTS]"); - return -1; - } - - return 0; -} - - -static int i802_get_rts(void *priv, int *rts) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIWRTS, &iwr) < 0) { - perror("ioctl[SIOCGIWRTS]"); - return -1; - } - - *rts = iwr.u.rts.value; - - return 0; -} - - -static int i802_set_frag(void *priv, int frag) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); - iwr.u.frag.value = frag; - iwr.u.frag.fixed = 1; - - if (ioctl(drv->ioctl_sock, SIOCSIWFRAG, &iwr) < 0) { - perror("ioctl[SIOCSIWFRAG]"); - return -1; - } - - return 0; -} - - -static int i802_get_frag(void *priv, int *frag) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); - - if (ioctl(drv->ioctl_sock, SIOCGIWFRAG, &iwr) < 0) { - perror("ioctl[SIOCGIWFRAG]"); - return -1; - } - - *frag = iwr.u.frag.value; - - return 0; -} - - -static int i802_set_retry(void *priv, int short_retry, int long_retry) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); - - iwr.u.retry.value = short_retry; - iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; - if (ioctl(drv->ioctl_sock, SIOCSIWRETRY, &iwr) < 0) { - perror("ioctl[SIOCSIWRETRY(short)]"); - return -1; - } - - iwr.u.retry.value = long_retry; - iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; - if (ioctl(drv->ioctl_sock, SIOCSIWRETRY, &iwr) < 0) { - perror("ioctl[SIOCSIWRETRY(long)]"); - return -1; - } - - return 0; -} - - -static int i802_get_retry(void *priv, int *short_retry, int *long_retry) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); - - iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; - if (ioctl(drv->ioctl_sock, SIOCGIWRETRY, &iwr) < 0) { - perror("ioctl[SIOCGIWFRAG(short)]"); - return -1; - } - *short_retry = iwr.u.retry.value; - - iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; - if (ioctl(drv->ioctl_sock, SIOCGIWRETRY, &iwr) < 0) { - perror("ioctl[SIOCGIWFRAG(long)]"); - return -1; - } - *long_retry = iwr.u.retry.value; - - return 0; -} - - -static int i802_flush(void *priv) -{ - struct i802_driver_data *drv = priv; - 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(drv->iface)); - - 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_driver_data *drv = priv; - 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(drv->iface)); - - return send_and_recv_msgs(drv, msg, get_sta_handler, data); - nla_put_failure: - return -ENOBUFS; -} - - -static int i802_send_eapol(void *priv, const u8 *addr, const u8 *data, - size_t data_len, int encrypt, const u8 *own_addr) -{ - struct i802_driver_data *drv = priv; - struct ieee80211_hdr *hdr; - size_t len; - u8 *pos; - int res; -#if 0 /* FIX */ - int qos = sta->flags & WLAN_STA_WME; -#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 = i802_send_frame(drv, (u8 *) hdr, len, encrypt, 0); - free(hdr); - - if (res < 0) { - perror("i802_send_eapol: send"); - printf("i802_send_eapol - packet len: %lu - failed\n", - (unsigned long) len); - } - - return res; -} - - -static int i802_sta_add2(const char *ifname, void *priv, - struct hostapd_sta_add_params *params) -{ - struct i802_driver_data *drv = priv; - 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(drv->iface)); - 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); - -#ifdef CONFIG_IEEE80211N - if (params->ht_capabilities) { - NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, - params->ht_capabilities->length, - ¶ms->ht_capabilities->data); - } -#endif /* CONFIG_IEEE80211N */ - - 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 i802_sta_remove(void *priv, const u8 *addr) -{ - struct i802_driver_data *drv = priv; - 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(drv->iface)); - 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 int i802_sta_set_flags(void *priv, const u8 *addr, - int total_flags, int flags_or, int flags_and) -{ - struct i802_driver_data *drv = priv; - struct nl_msg *msg, *flags = NULL; - - 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(drv->iface)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - - if (total_flags & WLAN_STA_AUTHORIZED || !drv->ieee802_1x_active) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED); - - if (total_flags & WLAN_STA_WMM) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME); - - if (total_flags & WLAN_STA_SHORT_PREAMBLE) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE); - - if (total_flags & WLAN_STA_MFP) - NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP); - - if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) - goto nla_put_failure; - - nlmsg_free(flags); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - nlmsg_free(flags); - return -ENOBUFS; -} - - -static int i802_set_regulatory_domain(void *priv, unsigned int rd) -{ - return -1; -} - - -static int i802_set_tx_queue_params(void *priv, int queue, int aifs, - int cw_min, int cw_max, int burst_time) -{ - struct i802_driver_data *drv = priv; - 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(drv->iface)); - - 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 void nl80211_remove_iface(struct i802_driver_data *drv, int ifidx) -{ - struct nl_msg *msg; - - /* stop listening for EAPOL on this interface */ - del_ifidx(drv, ifidx); - - 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: - printf("Failed to remove interface.\n"); -} - - -static int nl80211_create_iface(struct i802_driver_data *drv, - const char *ifname, - enum nl80211_iftype iftype, - const u8 *addr) -{ - struct nl_msg *msg, *flags = NULL; - int ifidx; - struct ifreq ifreq; - struct iwreq iwr; - 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, - if_nametoindex(drv->hapd->conf->iface)); - 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; - } - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret) { - nla_put_failure: - printf("Failed to create interface %s.\n", ifname); - return ret; - } - - ifidx = if_nametoindex(ifname); - - if (ifidx <= 0) - return -1; - - /* start listening for EAPOL on this interface */ - add_ifidx(drv, ifidx); - - if (addr) { - switch (iftype) { - case NL80211_IFTYPE_AP: - os_strlcpy(ifreq.ifr_name, ifname, IFNAMSIZ); - memcpy(ifreq.ifr_hwaddr.sa_data, addr, ETH_ALEN); - ifreq.ifr_hwaddr.sa_family = ARPHRD_ETHER; - - if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifreq)) { - nl80211_remove_iface(drv, ifidx); - return -1; - } - break; - case NL80211_IFTYPE_WDS: - memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); - iwr.u.addr.sa_family = ARPHRD_ETHER; - memcpy(iwr.u.addr.sa_data, addr, ETH_ALEN); - if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr)) - return -1; - break; - default: - /* nothing */ - break; - } - } - - return ifidx; -} - - -static int i802_bss_add(void *priv, const char *ifname, const u8 *bssid) -{ - int ifidx; - - /* - * The kernel supports that when the low-level driver does, - * but we currently don't because we need per-BSS data that - * currently we can't handle easily. - */ - return -1; - - ifidx = nl80211_create_iface(priv, ifname, NL80211_IFTYPE_AP, bssid); - if (ifidx < 0) - return -1; - if (hostapd_set_iface_flags(priv, ifname, 1)) { - nl80211_remove_iface(priv, ifidx); - return -1; - } - return 0; -} - - -static int i802_bss_remove(void *priv, const char *ifname) -{ - nl80211_remove_iface(priv, if_nametoindex(ifname)); - return 0; -} - - -static int i802_set_beacon(const char *iface, void *priv, - u8 *head, size_t head_len, - u8 *tail, size_t tail_len) -{ - struct i802_driver_data *drv = priv; - struct nl_msg *msg; - u8 cmd = NL80211_CMD_NEW_BEACON; - int ret; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - if (drv->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, if_nametoindex(iface)); - NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, drv->beacon_int); - - if (!drv->dtim_period) - drv->dtim_period = 2; - NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (!ret) - drv->beacon_set = 1; - return ret; - nla_put_failure: - return -ENOBUFS; -} - - -static int i802_del_beacon(struct i802_driver_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, if_nametoindex(drv->iface)); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - - -static int i802_set_ieee8021x(const char *ifname, void *priv, int enabled) -{ - struct i802_driver_data *drv = priv; - - /* - * FIXME: This needs to be per interface (BSS) - */ - drv->ieee802_1x_active = enabled; - return 0; -} - - -static int i802_set_privacy(const char *ifname, void *priv, int enabled) -{ - struct i802_driver_data *drv = priv; - struct iwreq iwr; - - memset(&iwr, 0, sizeof(iwr)); - - os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); - iwr.u.param.flags = IW_AUTH_PRIVACY_INVOKED; - iwr.u.param.value = enabled; - - ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr); - - /* ignore errors, the kernel/driver might not care */ - return 0; -} - - -static int i802_set_internal_bridge(void *priv, int value) -{ - return -1; -} - - -static int i802_set_beacon_int(void *priv, int value) -{ - struct i802_driver_data *drv = priv; - struct nl_msg *msg; - - drv->beacon_int = value; - - if (!drv->beacon_set) - return 0; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_SET_BEACON, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); - - NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, value); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - - -static int i802_set_dtim_period(const char *iface, void *priv, int value) -{ - struct i802_driver_data *drv = priv; - 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_BEACON, 0); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); - - drv->dtim_period = value; - NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - - -static int i802_set_bss(void *priv, int cts, int preamble, int slot) -{ - struct i802_driver_data *drv = priv; - 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); - - /* TODO: multi-BSS support */ - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); - - 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 enum nl80211_iftype i802_if_type(enum hostapd_driver_if_type type) -{ - switch (type) { - case HOSTAPD_IF_VLAN: - return NL80211_IFTYPE_AP_VLAN; - case HOSTAPD_IF_WDS: - return NL80211_IFTYPE_WDS; - } - return -1; -} - - -static int i802_if_add(const char *iface, void *priv, - enum hostapd_driver_if_type type, char *ifname, - const u8 *addr) -{ - if (nl80211_create_iface(priv, ifname, i802_if_type(type), addr) < 0) - return -1; - return 0; -} - - -static int i802_if_update(void *priv, enum hostapd_driver_if_type type, - char *ifname, const u8 *addr) -{ - /* unused at the moment */ - return -1; -} - - -static int i802_if_remove(void *priv, enum hostapd_driver_if_type type, - const char *ifname, const u8 *addr) -{ - nl80211_remove_iface(priv, if_nametoindex(ifname)); - return 0; -} - - -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 = 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]); - } - - 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 = calloc(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 = calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); - 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].rate = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]); - - /* crude heuristic */ - if (mode->mode == HOSTAPD_MODE_IEEE80211B && - mode->rates[idx].rate > 200) - mode->mode = HOSTAPD_MODE_IEEE80211G; - - if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) - mode->rates[idx].flags |= HOSTAPD_RATE_PREAMBLE2; - - idx++; - } - } - - return NL_SKIP; -} - -static struct hostapd_hw_modes *i802_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(struct hostapd_rate_data)); - 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].rate > 110 || - mode11g->rates[i].flags & - (HOSTAPD_RATE_ERP | HOSTAPD_RATE_OFDM)) - 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 *i802_get_hw_feature_data(void *priv, - u16 *num_modes, - u16 *flags) -{ - struct i802_driver_data *drv = priv; - 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, if_nametoindex(drv->iface)); - - if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) - return i802_add_11b(result.modes, num_modes); - nla_put_failure: - return NULL; -} - - -static int i802_set_sta_vlan(void *priv, const u8 *addr, - const char *ifname, int vlan_id) -{ - struct i802_driver_data *drv = priv; - 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_STATION, 0); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(drv->iface)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(ifname)); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - - -static int i802_set_country(void *priv, const char *country) -{ - struct i802_driver_data *drv = priv; - struct nl_msg *msg; - char alpha2[3]; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_REQ_SET_REG, 0); - - alpha2[0] = country[0]; - alpha2[1] = country[1]; - alpha2[2] = '\0'; - NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); - - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - return -ENOBUFS; -} - - -static void handle_unknown_sta(struct hostapd_data *hapd, u8 *ta) -{ - struct sta_info *sta; - - sta = ap_get_sta(hapd, ta); - if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { - printf("Data/PS-poll frame from not associated STA " - MACSTR "\n", MAC2STR(ta)); - if (sta && (sta->flags & WLAN_STA_AUTH)) - hostapd_sta_disassoc( - hapd, ta, - WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); - else - hostapd_sta_deauth( - hapd, ta, - WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); - } -} - - -static void handle_tx_callback(struct hostapd_data *hapd, u8 *buf, size_t len, - int ok) -{ - struct ieee80211_hdr *hdr; - u16 fc, type, stype; - struct sta_info *sta; - - hdr = (struct ieee80211_hdr *) buf; - 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_MGMT: - wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s", - ok ? "ACK" : "fail"); - ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); - break; - case WLAN_FC_TYPE_CTRL: - wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s", - ok ? "ACK" : "fail"); - break; - case WLAN_FC_TYPE_DATA: - sta = ap_get_sta(hapd, hdr->addr1); - if (sta && sta->flags & WLAN_STA_PENDING_POLL) { - wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " - "activity poll", MAC2STR(sta->addr), - ok ? "ACKed" : "did not ACK"); - if (ok) - sta->flags &= ~WLAN_STA_PENDING_POLL; - } - if (sta) - ieee802_1x_tx_status(hapd, sta, buf, len, ok); - break; - default: - printf("unknown TX callback frame type %d\n", type); - break; - } -} - - -static void handle_frame(struct hostapd_iface *iface, u8 *buf, size_t len, - struct hostapd_frame_info *hfi, - enum ieee80211_msg_type msg_type) -{ - struct ieee80211_hdr *hdr; - u16 fc, type, stype; - size_t data_len = len; - struct hostapd_data *hapd = NULL; - int broadcast_bssid = 0; - size_t i; - u8 *bssid; - - /* - * PS-Poll frames are 16 bytes. All other frames are - * 24 bytes or longer. - */ - if (len < 16) - 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); - - switch (type) { - case WLAN_FC_TYPE_DATA: - if (len < 24) - return; - switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { - case WLAN_FC_TODS: - bssid = hdr->addr1; - break; - case WLAN_FC_FROMDS: - bssid = hdr->addr2; - break; - default: - /* discard */ - return; - } - break; - case WLAN_FC_TYPE_CTRL: - /* discard non-ps-poll frames */ - if (stype != WLAN_FC_STYPE_PSPOLL) - return; - bssid = hdr->addr1; - break; - case WLAN_FC_TYPE_MGMT: - bssid = hdr->addr3; - break; - default: - /* discard */ - return; - } - - /* find interface frame belongs to */ - for (i = 0; i < iface->num_bss; i++) { - if (memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0) { - hapd = iface->bss[i]; - break; - } - } - - if (hapd == NULL) { - hapd = iface->bss[0]; - - if (bssid[0] != 0xff || bssid[1] != 0xff || - bssid[2] != 0xff || bssid[3] != 0xff || - bssid[4] != 0xff || bssid[5] != 0xff) { - /* - * Unknown BSSID - drop frame if this is not from - * passive scanning or a beacon (at least ProbeReq - * frames to other APs may be allowed through RX - * filtering in the wlan hw/driver) - */ - if ((type != WLAN_FC_TYPE_MGMT || - stype != WLAN_FC_STYPE_BEACON)) - return; - } else - broadcast_bssid = 1; - } - - switch (msg_type) { - case ieee80211_msg_normal: - /* continue processing */ - break; - case ieee80211_msg_tx_callback_ack: - handle_tx_callback(hapd, buf, data_len, 1); - return; - case ieee80211_msg_tx_callback_fail: - handle_tx_callback(hapd, buf, data_len, 0); - return; - } - - switch (type) { - case WLAN_FC_TYPE_MGMT: - if (stype != WLAN_FC_STYPE_BEACON && - stype != WLAN_FC_STYPE_PROBE_REQ) - wpa_printf(MSG_MSGDUMP, "MGMT"); - if (broadcast_bssid) { - for (i = 0; i < iface->num_bss; i++) - ieee802_11_mgmt(iface->bss[i], buf, data_len, - stype, hfi); - } else - ieee802_11_mgmt(hapd, buf, data_len, stype, hfi); - break; - case WLAN_FC_TYPE_CTRL: - /* can only get here with PS-Poll frames */ - wpa_printf(MSG_DEBUG, "CTRL"); - handle_unknown_sta(hapd, hdr->addr2); - break; - case WLAN_FC_TYPE_DATA: - handle_unknown_sta(hapd, hdr->addr2); - break; - } -} - - -static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct i802_driver_data *drv = eloop_ctx; - struct hostapd_data *hapd = drv->hapd; - 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)) - ieee802_1x_receive(hapd, lladdr.sll_addr, buf, len); -} - - -static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct i802_driver_data *drv = eloop_ctx; - int len; - unsigned char buf[3000]; - struct hostapd_data *hapd = drv->hapd; - struct ieee80211_radiotap_iterator iter; - int ret; - struct hostapd_frame_info hfi; - int injected = 0, failed = 0, msg_type, rxflags = 0; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - perror("recv"); - return; - } - - if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { - printf("received invalid radiotap frame\n"); - return; - } - - memset(&hfi, 0, sizeof(hfi)); - - 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 - hfi.channel = XXX; - hfi.phytype = XXX; - */ - break; - case IEEE80211_RADIOTAP_RATE: - hfi.datarate = *iter.this_arg * 5; - break; - case IEEE80211_RADIOTAP_DB_ANTSIGNAL: - hfi.ssi_signal = *iter.this_arg; - break; - } - } - - if (rxflags && injected) - return; - - if (!injected) - msg_type = ieee80211_msg_normal; - else if (failed) - msg_type = ieee80211_msg_tx_callback_fail; - else - msg_type = ieee80211_msg_tx_callback_ack; - - handle_frame(hapd->iface, buf + iter.max_length, - len - iter.max_length, &hfi, msg_type); -} - - -/* - * 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, WDS 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), - /* load the upper byte of the frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), - /* mask off toDS/fromDS */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), - /* drop WDS frames */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, FAIL, 0), -#endif - - /* - * 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 int nl80211_create_monitor_interface(struct i802_driver_data *drv) -{ - char buf[IFNAMSIZ]; - struct sockaddr_ll ll; - int optval; - socklen_t optlen; - - snprintf(buf, IFNAMSIZ, "mon.%s", drv->iface); - buf[IFNAMSIZ - 1] = '\0'; - - drv->monitor_ifidx = - nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL); - - if (drv->monitor_ifidx < 0) - return -1; - - if (hostapd_set_iface_flags(drv, 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_iface(drv, drv->monitor_ifidx); - return -1; -} - - -static int nl80211_set_master_mode(struct i802_driver_data *drv, - const char *ifname) -{ - 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, - if_nametoindex(ifname)); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP); - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (!ret) - return 0; - nla_put_failure: - wpa_printf(MSG_ERROR, "Failed to set interface %s to master " - "mode.", ifname); - return ret; -} - - -static int i802_init_sockets(struct i802_driver_data *drv, const u8 *bssid) -{ - struct ifreq ifr; - struct sockaddr_ll addr; - - drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->ioctl_sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); - return -1; - } - - /* start listening for EAPOL on the default AP interface */ - add_ifidx(drv, if_nametoindex(drv->iface)); - - if (hostapd_set_iface_flags(drv, drv->iface, 0)) - return -1; - - if (bssid) { - os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); - memcpy(ifr.ifr_hwaddr.sa_data, bssid, ETH_ALEN); - ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; - - if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) { - perror("ioctl(SIOCSIFHWADDR)"); - return -1; - } - } - - /* - * initialise generic netlink and nl80211 - */ - drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); - if (!drv->nl_cb) { - printf("Failed to allocate netlink callbacks.\n"); - return -1; - } - - drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); - if (!drv->nl_handle) { - printf("Failed to allocate netlink handle.\n"); - return -1; - } - - if (genl_connect(drv->nl_handle)) { - printf("Failed to connect to generic netlink.\n"); - return -1; - } - -#ifdef CONFIG_LIBNL20 - if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) { - printf("Failed to allocate generic netlink cache.\n"); - return -1; - } -#else /* CONFIG_LIBNL20 */ - drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); - if (!drv->nl_cache) { - printf("Failed to allocate generic netlink cache.\n"); - return -1; - } -#endif /* CONFIG_LIBNL20 */ - - drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); - if (!drv->nl80211) { - printf("nl80211 not found.\n"); - return -1; - } - - /* Initialise a monitor interface */ - if (nl80211_create_monitor_interface(drv)) - return -1; - - if (nl80211_set_master_mode(drv, drv->iface)) - goto fail1; - - if (hostapd_set_iface_flags(drv, drv->iface, 1)) - goto fail1; - - 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); - - 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 fail1; - } - - if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) - { - printf("Could not register read socket for eapol\n"); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); - if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFHWADDR)"); - goto fail1; - } - - if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { - printf("Invalid HW-addr family 0x%04x\n", - ifr.ifr_hwaddr.sa_family); - goto fail1; - } - memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - - return 0; - -fail1: - nl80211_remove_iface(drv, drv->monitor_ifidx); - return -1; -} - - -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 void -hostapd_wireless_event_wireless_custom(struct i802_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) { - ieee80211_michael_mic_failure(drv->hapd, addr, 1); - } else { - wpa_printf(MSG_DEBUG, - "MLME-MICHAELMICFAILURE.indication " - "with invalid MAC address"); - } - } -} - - -static void hostapd_wireless_event_wireless(struct i802_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(struct i802_driver_data *drv, - struct nlmsghdr *h, int len) -{ - struct ifinfomsg *ifi; - int attrlen, _nlmsg_len, rta_len; - struct rtattr *attr; - - if (len < (int) sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - - /* TODO: use ifi->ifi_index to filter out wireless events from other - * interfaces */ - - _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)) { - 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 void hostapd_wireless_event_receive(int sock, void *eloop_ctx, - void *sock_ctx) -{ - char buf[256]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - struct i802_driver_data *drv = eloop_ctx; - - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - perror("recvfrom(netlink)"); - return; - } - - h = (struct nlmsghdr *) buf; - while (left >= (int) sizeof(*h)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - printf("Malformed netlink message: " - "len=%d left=%d plen=%d\n", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - hostapd_wireless_event_rtm_newlink(drv, h, plen); - break; - } - - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); - } - - if (left > 0) { - printf("%d extra bytes in the end of netlink message\n", left); - } -} - - -static int hostap_get_we_version(struct i802_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 i802_wireless_event_init(void *priv) -{ - struct i802_driver_data *drv = priv; - int s; - struct sockaddr_nl local; - - hostap_get_we_version(drv); - - drv->wext_sock = -1; - - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - return -1; - } - - memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); - return -1; - } - - eloop_register_read_sock(s, hostapd_wireless_event_receive, drv, - NULL); - drv->wext_sock = s; - - return 0; -} - - -static void i802_wireless_event_deinit(void *priv) -{ - struct i802_driver_data *drv = priv; - if (drv->wext_sock < 0) - return; - eloop_unregister_read_sock(drv->wext_sock); - close(drv->wext_sock); -} - - -static int i802_sta_deauth(void *priv, const u8 *addr, int reason) -{ - struct i802_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, drv->hapd->own_addr, ETH_ALEN); - memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); - mgmt.u.deauth.reason_code = host_to_le16(reason); - return i802_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth), 0); -} - - -static int i802_sta_disassoc(void *priv, const u8 *addr, int reason) -{ - struct i802_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, drv->hapd->own_addr, ETH_ALEN); - memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); - mgmt.u.disassoc.reason_code = host_to_le16(reason); - return i802_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.disassoc), 0); -} - - -static void *i802_init_bssid(struct hostapd_data *hapd, const u8 *bssid) -{ - struct i802_driver_data *drv; - - drv = os_zalloc(sizeof(struct i802_driver_data)); - if (drv == NULL) { - printf("Could not allocate memory for i802 driver data\n"); - return NULL; - } - - drv->hapd = hapd; - memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); - - drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); - drv->if_indices = drv->default_if_indices; - drv->bridge = if_nametoindex(hapd->conf->bridge); - - if (i802_init_sockets(drv, bssid)) - goto failed; - - return drv; - -failed: - free(drv); - return NULL; -} - - -static void *i802_init(struct hostapd_data *hapd) -{ - return i802_init_bssid(hapd, NULL); -} - - -static void i802_deinit(void *priv) -{ - struct i802_driver_data *drv = priv; - - 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_freq2(priv, &freq); - } - - i802_del_beacon(drv); - - /* remove monitor interface */ - nl80211_remove_iface(drv, drv->monitor_ifidx); - - (void) hostapd_set_iface_flags(drv, drv->iface, 0); - - if (drv->monitor_sock >= 0) { - eloop_unregister_read_sock(drv->monitor_sock); - close(drv->monitor_sock); - } - if (drv->ioctl_sock >= 0) - close(drv->ioctl_sock); - if (drv->eapol_sock >= 0) { - eloop_unregister_read_sock(drv->eapol_sock); - close(drv->eapol_sock); - } - - genl_family_put(drv->nl80211); - nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); - nl_cb_put(drv->nl_cb); - - if (drv->if_indices != drv->default_if_indices) - free(drv->if_indices); - - free(drv); -} - - -const struct wpa_driver_ops wpa_driver_nl80211_ops = { - .name = "nl80211", - .init = i802_init, - .init_bssid = i802_init_bssid, - .deinit = i802_deinit, - .wireless_event_init = i802_wireless_event_init, - .wireless_event_deinit = i802_wireless_event_deinit, - .set_ieee8021x = i802_set_ieee8021x, - .set_privacy = i802_set_privacy, - .set_encryption = i802_set_encryption, - .get_seqnum = i802_get_seqnum, - .flush = i802_flush, - .read_sta_data = i802_read_sta_data, - .send_eapol = i802_send_eapol, - .sta_set_flags = i802_sta_set_flags, - .sta_deauth = i802_sta_deauth, - .sta_disassoc = i802_sta_disassoc, - .sta_remove = i802_sta_remove, - .send_mgmt_frame = i802_send_mgmt_frame, - .sta_add2 = i802_sta_add2, - .get_inact_sec = i802_get_inact_sec, - .sta_clear_stats = i802_sta_clear_stats, - .set_freq2 = i802_set_freq2, - .set_rts = i802_set_rts, - .get_rts = i802_get_rts, - .set_frag = i802_set_frag, - .get_frag = i802_get_frag, - .set_retry = i802_set_retry, - .get_retry = i802_get_retry, - .set_rate_sets = i802_set_rate_sets, - .set_regulatory_domain = i802_set_regulatory_domain, - .set_beacon = i802_set_beacon, - .set_internal_bridge = i802_set_internal_bridge, - .set_beacon_int = i802_set_beacon_int, - .set_dtim_period = i802_set_dtim_period, - .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, - .bss_add = i802_bss_add, - .bss_remove = i802_bss_remove, - .if_add = i802_if_add, - .if_update = i802_if_update, - .if_remove = i802_if_remove, - .get_hw_feature_data = i802_get_hw_feature_data, - .set_sta_vlan = i802_set_sta_vlan, - .set_country = i802_set_country, -}; diff --git a/contrib/hostapd/hostapd/driver_none.c b/contrib/hostapd/hostapd/driver_none.c deleted file mode 100644 index 96e7e644e5..0000000000 --- a/contrib/hostapd/hostapd/driver_none.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * hostapd / Driver interface for RADIUS server 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 "hostapd.h" -#include "driver.h" - - -struct none_driver_data { - struct hostapd_data *hapd; -}; - - -static void * none_driver_init(struct hostapd_data *hapd) -{ - 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_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; -} - - -const struct wpa_driver_ops wpa_driver_none_ops = { - .name = "none", - .init = none_driver_init, - .deinit = none_driver_deinit, - .send_ether = none_driver_send_ether, -}; diff --git a/contrib/hostapd/hostapd/driver_prism54.c b/contrib/hostapd/hostapd/driver_prism54.c deleted file mode 100644 index 4e2189aeb5..0000000000 --- a/contrib/hostapd/hostapd/driver_prism54.c +++ /dev/null @@ -1,1091 +0,0 @@ -/* - * hostapd / Driver interaction with Prism54 PIMFOR interface - * Copyright (c) 2004, Bell Kin - * based on hostap driver.c, ieee802_11.c - * Copyright (c) 2002-2007, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include -#include - -#ifdef USE_KERNEL_HEADERS -/* compat-wireless does not include linux/compiler.h to define __user, so - * define it here */ -#ifndef __user -#define __user -#endif /* __user */ -#include -#include -#include /* The L2 protocols */ -#include -#include -#else /* USE_KERNEL_HEADERS */ -#include -#include -#include "wireless_copy.h" -#endif /* USE_KERNEL_HEADERS */ - -#include "hostapd.h" -#include "driver.h" -#include "ieee802_1x.h" -#include "eloop.h" -#include "ieee802_11.h" -#include "prism54.h" -#include "wpa.h" -#include "radius/radius.h" -#include "sta_info.h" -#include "accounting.h" - -const int PIM_BUF_SIZE = 4096; - -struct prism54_driver_data { - struct hostapd_data *hapd; - char iface[IFNAMSIZ + 1]; - int sock; /* raw packet socket for 802.3 access */ - int pim_sock; /* socket for pimfor packet */ - char macs[2007][6]; -}; - - -static int mac_id_refresh(struct prism54_driver_data *data, int id, char *mac) -{ - if (id < 0 || id > 2006) { - return -1; - } - memcpy(&data->macs[id][0], mac, ETH_ALEN); - return 0; -} - - -static char * mac_id_get(struct prism54_driver_data *data, int id) -{ - if (id < 0 || id > 2006) { - return NULL; - } - return &data->macs[id][0]; -} - - -/* wait for a specific pimfor, timeout in 10ms resolution */ -/* pim_sock must be non-block to prevent dead lock from no response */ -/* or same response type in series */ -static int prism54_waitpim(void *priv, unsigned long oid, void *buf, int len, - int timeout) -{ - struct prism54_driver_data *drv = priv; - struct timeval tv, stv, ctv; - fd_set pfd; - int rlen; - pimdev_hdr *pkt; - - pkt = malloc(8192); - if (pkt == NULL) - return -1; - - FD_ZERO(&pfd); - gettimeofday(&stv, NULL); - do { - FD_SET(drv->pim_sock, &pfd); - tv.tv_sec = 0; - tv.tv_usec = 10000; - if (select(drv->pim_sock + 1, &pfd, NULL, NULL, &tv)) { - rlen = recv(drv->pim_sock, pkt, 8192, 0); - if (rlen > 0) { - if (pkt->oid == htonl(oid)) { - if (rlen <= len) { - if (buf != NULL) { - memcpy(buf, pkt, rlen); - } - free(pkt); - return rlen; - } else { - printf("buffer too small\n"); - free(pkt); - return -1; - } - } else { - gettimeofday(&ctv, NULL); - continue; - } - } - } - gettimeofday(&ctv, NULL); - } while (((ctv.tv_sec - stv.tv_sec) * 100 + - (ctv.tv_usec - stv.tv_usec) / 10000) > timeout); - free(pkt); - return 0; -} - - -/* send an eapol packet */ -static int prism54_send_eapol(void *priv, const u8 *addr, - const u8 *data, size_t data_len, int encrypt, - const u8 *own_addr) -{ - struct prism54_driver_data *drv = priv; - ieee802_3_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 prism54_send_data(len=%lu)\n", - (unsigned long) len); - return -1; - } - - memcpy(&hdr->da[0], addr, ETH_ALEN); - memcpy(&hdr->sa[0], own_addr, ETH_ALEN); - hdr->type = htons(ETH_P_PAE); - pos = (u8 *) (hdr + 1); - memcpy(pos, data, data_len); - - res = send(drv->sock, hdr, len, 0); - free(hdr); - - if (res < 0) { - perror("hostapd_send_eapol: send"); - printf("hostapd_send_eapol - packet len: %lu - failed\n", - (unsigned long) len); - } - - return res; -} - - -/* open data channel(auth-1) or eapol only(unauth-0) */ -static int prism54_set_sta_authorized(void *priv, const u8 *addr, - int authorized) -{ - struct prism54_driver_data *drv = priv; - pimdev_hdr *hdr; - char *pos; - - hdr = malloc(sizeof(*hdr) + ETH_ALEN); - if (hdr == NULL) - return -1; - hdr->op = htonl(PIMOP_SET); - if (authorized) { - hdr->oid = htonl(DOT11_OID_EAPAUTHSTA); - } else { - hdr->oid = htonl(DOT11_OID_EAPUNAUTHSTA); - } - pos = (char *) (hdr + 1); - memcpy(pos, addr, ETH_ALEN); - send(drv->pim_sock, hdr, sizeof(*hdr) + ETH_ALEN, 0); - prism54_waitpim(priv, hdr->oid, hdr, sizeof(*hdr) + ETH_ALEN, 10); - free(hdr); - return 0; -} - - -static int -prism54_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 & WLAN_STA_AUTHORIZED) - return prism54_set_sta_authorized(priv, addr, 1); - if (flags_and & WLAN_STA_AUTHORIZED) - return prism54_set_sta_authorized(priv, addr, 0); - return 0; -} - - -/* set per station key */ -static int prism54_set_encryption(const char *ifname, void *priv, - const char *alg, const u8 *addr, - int idx, const u8 *key, size_t key_len, - int txkey) -{ - struct prism54_driver_data *drv = priv; - pimdev_hdr *hdr; - struct obj_stakey *keys; - u8 *buf; - size_t blen; - int ret = 0; - - blen = sizeof(struct obj_stakey) + sizeof(pimdev_hdr); - hdr = malloc(blen); - if (hdr == NULL) { - printf("memory low\n"); - return -1; - } - keys = (struct obj_stakey *) &hdr[1]; - if (!addr) { - memset(&keys->address[0], 0xff, ETH_ALEN); - } else { - memcpy(&keys->address[0], addr, ETH_ALEN); - } - if (!strcmp(alg, "WEP")) { - keys->type = DOT11_PRIV_WEP; - } else if (!strcmp(alg, "TKIP")) { - keys->type = DOT11_PRIV_TKIP; - } else if (!strcmp(alg, "none")) { - /* the only way to clear the key is to deauth it */ - /* and prism54 is capable to receive unencrypted packet */ - /* so we do nothing here */ - free(hdr); - return 0; - } else { - printf("bad auth type: %s\n", alg); - } - buf = (u8 *) &keys->key[0]; - keys->length = key_len; - keys->keyid = idx; - keys->options = htons(DOT11_STAKEY_OPTION_DEFAULTKEY); - keys->reserved = 0; - - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_STAKEY); - - memcpy(buf, key, key_len); - - ret = send(drv->pim_sock, hdr, blen, 0); - if (ret < 0) { - free(hdr); - return ret; - } - prism54_waitpim(priv, hdr->oid, hdr, blen, 10); - - free(hdr); - - return 0; -} - - -/* get TKIP station sequence counter, prism54 is only 6 bytes */ -static int prism54_get_seqnum(const char *ifname, void *priv, const u8 *addr, - int idx, u8 *seq) -{ - struct prism54_driver_data *drv = priv; - struct obj_stasc *stasc; - pimdev_hdr *hdr; - size_t blen; - int ret = 0; - - blen = sizeof(*stasc) + sizeof(*hdr); - hdr = malloc(blen); - if (hdr == NULL) - return -1; - - stasc = (struct obj_stasc *) &hdr[1]; - - if (addr == NULL) - memset(&stasc->address[0], 0xff, ETH_ALEN); - else - memcpy(&stasc->address[0], addr, ETH_ALEN); - - hdr->oid = htonl(DOT11_OID_STASC); - hdr->op = htonl(PIMOP_GET); - stasc->keyid = idx; - if (send(drv->pim_sock,hdr,blen,0) <= 0) { - free(hdr); - return -1; - } - if (prism54_waitpim(priv, DOT11_OID_STASC, hdr, blen, 10) <= 0) { - ret = -1; - } else { - if (hdr->op == (int) htonl(PIMOP_RESPONSE)) { - memcpy(seq + 2, &stasc->sc_high, ETH_ALEN); - memset(seq, 0, 2); - } else { - ret = -1; - } - } - free(hdr); - - return ret; -} - - -/* include unencrypted, set mlme autolevel to extended */ -static int prism54_init_1x(void *priv) -{ - struct prism54_driver_data *drv = priv; - pimdev_hdr *hdr; - unsigned long *ul; - int blen = sizeof(*hdr) + sizeof(*ul); - - hdr = malloc(blen); - if (hdr == NULL) - return -1; - - ul = (unsigned long *) &hdr[1]; - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_EXUNENCRYPTED); - *ul = htonl(DOT11_BOOL_TRUE); /* not accept */ - send(drv->pim_sock, hdr, blen, 0); - prism54_waitpim(priv, DOT11_OID_EXUNENCRYPTED, hdr, blen, 10); - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_MLMEAUTOLEVEL); - *ul = htonl(DOT11_MLME_EXTENDED); - send(drv->pim_sock, hdr, blen, 0); - prism54_waitpim(priv, DOT11_OID_MLMEAUTOLEVEL, hdr, blen, 10); - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_DOT1XENABLE); - *ul = htonl(DOT11_BOOL_TRUE); - send(drv->pim_sock, hdr, blen, 0); - prism54_waitpim(priv, DOT11_OID_DOT1XENABLE, hdr, blen, 10); - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_AUTHENABLE); - *ul = htonl(DOT11_AUTH_OS); /* OS */ - send(drv->pim_sock, hdr, blen, 0); - prism54_waitpim(priv, DOT11_OID_AUTHENABLE, hdr, blen, 10); - free(hdr); - return 0; -} - - -static int prism54_set_privacy_invoked(const char *ifname, void *priv, - int flag) -{ - struct prism54_driver_data *drv = priv; - pimdev_hdr *hdr; - unsigned long *ul; - int ret; - int blen = sizeof(*hdr) + sizeof(*ul); - hdr = malloc(blen); - if (hdr == NULL) - return -1; - ul = (unsigned long *) &hdr[1]; - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_PRIVACYINVOKED); - if (flag) { - *ul = htonl(DOT11_BOOL_TRUE); /* has privacy */ - } else { - *ul = 0; - } - ret = send(drv->pim_sock, hdr, blen, 0); - if (ret >= 0) { - ret = prism54_waitpim(priv, DOT11_OID_PRIVACYINVOKED, hdr, - blen, 10); - } - free(hdr); - return ret; -} - - -static int prism54_ioctl_setiwessid(const char *ifname, void *priv, - const u8 *buf, int len) -{ -#if 0 - struct prism54_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->pim_sock, SIOCSIWESSID, &iwr) < 0) { - perror("ioctl[SIOCSIWESSID]"); - printf("len=%d\n", len); - return -1; - } -#endif - return 0; -} - - -/* kick all stations */ -/* does not work during init, but at least it won't crash firmware */ -static int prism54_flush(void *priv) -{ - struct prism54_driver_data *drv = priv; - struct obj_mlmeex *mlme; - pimdev_hdr *hdr; - int ret; - unsigned int i; - long *nsta; - int blen = sizeof(*hdr) + sizeof(*mlme); - char *mac_id; - - hdr = os_zalloc(blen); - if (hdr == NULL) - return -1; - - mlme = (struct obj_mlmeex *) &hdr[1]; - nsta = (long *) &hdr[1]; - hdr->op = htonl(PIMOP_GET); - hdr->oid = htonl(DOT11_OID_CLIENTS); - ret = send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(long), 0); - ret = prism54_waitpim(priv, DOT11_OID_CLIENTS, hdr, blen, 10); - if ((ret < 0) || (hdr->op != (int) htonl(PIMOP_RESPONSE)) || - (le_to_host32(*nsta) > 2007)) { - free(hdr); - return 0; - } - for (i = 0; i < le_to_host32(*nsta); i++) { - mlme->id = -1; - mac_id = mac_id_get(drv, i); - if (mac_id) - memcpy(&mlme->address[0], mac_id, ETH_ALEN); - mlme->code = host_to_le16(WLAN_REASON_UNSPECIFIED); - mlme->state = htons(DOT11_STATE_NONE); - mlme->size = 0; - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_DISASSOCIATEEX); - ret = send(drv->pim_sock, hdr, blen, 0); - prism54_waitpim(priv, DOT11_OID_DISASSOCIATEEX, hdr, blen, - 100); - } - for (i = 0; i < le_to_host32(*nsta); i++) { - mlme->id = -1; - mac_id = mac_id_get(drv, i); - if (mac_id) - memcpy(&mlme->address[0], mac_id, ETH_ALEN); - mlme->code = host_to_le16(WLAN_REASON_UNSPECIFIED); - mlme->state = htons(DOT11_STATE_NONE); - mlme->size = 0; - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_DEAUTHENTICATEEX); - ret = send(drv->pim_sock, hdr, blen, 0); - prism54_waitpim(priv, DOT11_OID_DEAUTHENTICATEEX, hdr, blen, - 100); - } - free(hdr); - return 0; -} - - -static int prism54_sta_deauth(void *priv, const u8 *addr, int reason) -{ - struct prism54_driver_data *drv = priv; - pimdev_hdr *hdr; - struct obj_mlmeex *mlme; - int ret; - int blen = sizeof(*hdr) + sizeof(*mlme); - hdr = malloc(blen); - if (hdr == NULL) - return -1; - mlme = (struct obj_mlmeex *) &hdr[1]; - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_DEAUTHENTICATEEX); - memcpy(&mlme->address[0], addr, ETH_ALEN); - mlme->id = -1; - mlme->state = htons(DOT11_STATE_NONE); - mlme->code = host_to_le16(reason); - mlme->size = 0; - ret = send(drv->pim_sock, hdr, blen, 0); - prism54_waitpim(priv, DOT11_OID_DEAUTHENTICATEEX, hdr, blen, 10); - free(hdr); - return ret; -} - - -static int prism54_sta_disassoc(void *priv, const u8 *addr, int reason) -{ - struct prism54_driver_data *drv = priv; - pimdev_hdr *hdr; - struct obj_mlmeex *mlme; - int ret; - int blen = sizeof(*hdr) + sizeof(*mlme); - hdr = malloc(blen); - if (hdr == NULL) - return -1; - mlme = (struct obj_mlmeex *) &hdr[1]; - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_DISASSOCIATEEX); - memcpy(&mlme->address[0], addr, ETH_ALEN); - mlme->id = -1; - mlme->state = htons(DOT11_STATE_NONE); - mlme->code = host_to_le16(reason); - mlme->size = 0; - ret = send(drv->pim_sock, hdr, blen, 0); - prism54_waitpim(priv, DOT11_OID_DISASSOCIATEEX, hdr, blen, 10); - free(hdr); - return ret; -} - - -static int prism54_get_inact_sec(void *priv, const u8 *addr) -{ - struct prism54_driver_data *drv = priv; - pimdev_hdr *hdr; - struct obj_sta *sta; - int blen = sizeof(*hdr) + sizeof(*sta); - int ret; - - hdr = malloc(blen); - if (hdr == NULL) - return -1; - hdr->op = htonl(PIMOP_GET); - hdr->oid = htonl(DOT11_OID_CLIENTFIND); - sta = (struct obj_sta *) &hdr[1]; - memcpy(&sta->address[0], addr, ETH_ALEN); - ret = send(drv->pim_sock, hdr, blen, 0); - ret = prism54_waitpim(priv, DOT11_OID_CLIENTFIND, hdr, blen, 10); - if (ret != blen) { - printf("get_inact_sec: bad return %d\n", ret); - free(hdr); - return -1; - } - if (hdr->op != (int) htonl(PIMOP_RESPONSE)) { - printf("get_inact_sec: bad resp\n"); - free(hdr); - return -1; - } - free(hdr); - return le_to_host16(sta->age); -} - - -/* set attachments */ -static int prism54_set_generic_elem(const char *ifname, void *priv, - const u8 *elem, size_t elem_len) -{ - struct prism54_driver_data *drv = priv; - pimdev_hdr *hdr; - char *pos; - struct obj_attachment_hdr *attach; - size_t blen = sizeof(*hdr) + sizeof(*attach) + elem_len; - hdr = os_zalloc(blen); - if (hdr == NULL) { - printf("%s: memory low\n", __func__); - return -1; - } - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_ATTACHMENT); - attach = (struct obj_attachment_hdr *)&hdr[1]; - attach->type = DOT11_PKT_BEACON; - attach->id = -1; - attach->size = host_to_le16((short)elem_len); - pos = ((char*) attach) + sizeof(*attach); - if (elem) - memcpy(pos, elem, elem_len); - send(drv->pim_sock, hdr, blen, 0); - attach->type = DOT11_PKT_PROBE_RESP; - send(drv->pim_sock, hdr, blen, 0); - free(hdr); - return 0; -} - - -/* tell the card to auth the sta */ -static void prism54_handle_probe(struct prism54_driver_data *drv, - void *buf, size_t len) -{ - struct obj_mlmeex *mlme; - pimdev_hdr *hdr; - struct sta_info *sta; - hdr = (pimdev_hdr *)buf; - mlme = (struct obj_mlmeex *) &hdr[1]; - sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); - if (sta != NULL) { - if (sta->flags & (WLAN_STA_AUTH | WLAN_STA_ASSOC)) - return; - } - if (len < sizeof(*mlme)) { - printf("bad probe packet\n"); - return; - } - mlme->state = htons(DOT11_STATE_AUTHING); - mlme->code = 0; - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_AUTHENTICATEEX); - mlme->size = 0; - send(drv->pim_sock, hdr, sizeof(*hdr)+sizeof(*mlme), 0); -} - - -static void prism54_handle_deauth(struct prism54_driver_data *drv, - void *buf, size_t len) -{ - struct obj_mlme *mlme; - pimdev_hdr *hdr; - struct sta_info *sta; - char *mac_id; - - hdr = (pimdev_hdr *) buf; - mlme = (struct obj_mlme *) &hdr[1]; - sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); - mac_id = mac_id_get(drv, mlme->id); - if (sta == NULL || mac_id == NULL) - return; - memcpy(&mlme->address[0], mac_id, ETH_ALEN); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); - sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; - ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); - ap_free_sta(drv->hapd, sta); -} - - -static void prism54_handle_disassoc(struct prism54_driver_data *drv, - void *buf, size_t len) -{ - struct obj_mlme *mlme; - pimdev_hdr *hdr; - struct sta_info *sta; - char *mac_id; - - hdr = (pimdev_hdr *) buf; - mlme = (struct obj_mlme *) &hdr[1]; - mac_id = mac_id_get(drv, mlme->id); - if (mac_id == NULL) - return; - memcpy(&mlme->address[0], mac_id, ETH_ALEN); - sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); - if (sta == NULL) { - return; - } - sta->flags &= ~WLAN_STA_ASSOC; - 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); - accounting_sta_stop(drv->hapd, sta); - ieee802_1x_free_station(sta); -} - - -/* to auth it, just allow it now, later for os/sk */ -static void prism54_handle_auth(struct prism54_driver_data *drv, - void *buf, size_t len) -{ - struct obj_mlmeex *mlme; - pimdev_hdr *hdr; - struct sta_info *sta; - int resp; - - hdr = (pimdev_hdr *) buf; - mlme = (struct obj_mlmeex *) &hdr[1]; - if (len < sizeof(*mlme)) { - printf("bad auth packet\n"); - return; - } - - if (mlme->state == htons(DOT11_STATE_AUTHING)) { - sta = ap_sta_add(drv->hapd, (u8 *) &mlme->address[0]); - if (drv->hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; - goto fail; - } - mac_id_refresh(drv, mlme->id, &mlme->address[0]); - if (!sta) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - sta->flags &= ~WLAN_STA_PREAUTH; - - ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); - sta->flags |= WLAN_STA_AUTH; - wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); - mlme->code = 0; - mlme->state=htons(DOT11_STATE_AUTH); - hdr->op = htonl(PIMOP_SET); - hdr->oid = htonl(DOT11_OID_AUTHENTICATEEX); - mlme->size = 0; - sta->timeout_next = STA_NULLFUNC; - send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(*mlme), 0); - } - return; - -fail: - printf("auth fail: %x\n", resp); - mlme->code = host_to_le16(resp); - mlme->size = 0; - if (sta) - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - hdr->oid = htonl(DOT11_OID_DEAUTHENTICATEEX); - hdr->op = htonl(PIMOP_SET); - send(drv->pim_sock, hdr, sizeof(*hdr)+sizeof(*mlme), 0); -} - - -/* do the wpa thing */ -static void prism54_handle_assoc(struct prism54_driver_data *drv, - void *buf, size_t len) -{ - pimdev_hdr *hdr; - struct obj_mlmeex *mlme; - struct ieee802_11_elems elems; - struct sta_info *sta; - u8 *wpa_ie; - u8 *cb; - int ieofs = 0; - size_t wpa_ie_len; - int resp, new_assoc; - char *mac_id; - - resp = 0; - hdr = (pimdev_hdr *) buf; - mlme = (struct obj_mlmeex *) &hdr[1]; - switch (ntohl(hdr->oid)) { - case DOT11_OID_ASSOCIATE: - case DOT11_OID_REASSOCIATE: - mlme->size = 0; - default: - break; - } - if ((mlme->state == (int) htonl(DOT11_STATE_ASSOCING)) || - (mlme->state == (int) htonl(DOT11_STATE_REASSOCING))) { - if (len < sizeof(pimdev_hdr) + sizeof(struct obj_mlme)) { - printf("bad assoc packet\n"); - return; - } - mac_id = mac_id_get(drv, mlme->id); - if (mac_id == NULL) - return; - memcpy(&mlme->address[0], mac_id, ETH_ALEN); - sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); - if (sta == NULL) { - printf("cannot get sta\n"); - return; - } - cb = (u8 *) &mlme->data[0]; - if (hdr->oid == htonl(DOT11_OID_ASSOCIATEEX)) { - ieofs = 4; - } else if (hdr->oid == htonl(DOT11_OID_REASSOCIATEEX)) { - ieofs = 10; - } - if (le_to_host16(mlme->size) <= ieofs) { - printf("attach too small\n"); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - if (ieee802_11_parse_elems(cb + ieofs, - le_to_host16(mlme->size) - ieofs, - &elems, 1) == ParseFailed) { - printf("STA " MACSTR " sent invalid association " - "request\n", MAC2STR(sta->addr)); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - if ((drv->hapd->conf->wpa & WPA_PROTO_RSN) && - elems.rsn_ie) { - wpa_ie = elems.rsn_ie; - wpa_ie_len = elems.rsn_ie_len; - } else if ((drv->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; - } - if (drv->hapd->conf->wpa && wpa_ie == NULL) { - printf("STA " MACSTR ": No WPA/RSN IE in association " - "request\n", MAC2STR(sta->addr)); - resp = WLAN_STATUS_INVALID_IE; - goto fail; - } - if (drv->hapd->conf->wpa) { - int res; - wpa_ie -= 2; - wpa_ie_len += 2; - if (sta->wpa_sm == NULL) - sta->wpa_sm = wpa_auth_sta_init( - drv->hapd->wpa_auth, sta->addr); - if (sta->wpa_sm == NULL) { - printf("Failed to initialize WPA state " - "machine\n"); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - res = wpa_validate_wpa_ie(drv->hapd->wpa_auth, - sta->wpa_sm, - wpa_ie, wpa_ie_len, - NULL, 0); - 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; - else if (res != WPA_IE_OK) - resp = WLAN_STATUS_INVALID_IE; - if (resp != WLAN_STATUS_SUCCESS) - goto fail; - } - hdr->oid = (hdr->oid == htonl(DOT11_OID_ASSOCIATEEX)) ? - htonl(DOT11_OID_ASSOCIATEEX) : - htonl(DOT11_OID_REASSOCIATEEX); - hdr->op = htonl(PIMOP_SET); - mlme->code = 0; - mlme->state = htons(DOT11_STATE_ASSOC); - mlme->size = 0; - send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(*mlme), 0); - return; - } else if (mlme->state==htons(DOT11_STATE_ASSOC)) { - if (len < sizeof(pimdev_hdr) + sizeof(struct obj_mlme)) { - printf("bad assoc packet\n"); - return; - } - mac_id = mac_id_get(drv, mlme->id); - if (mac_id == NULL) - return; - memcpy(&mlme->address[0], mac_id, ETH_ALEN); - sta = ap_get_sta(drv->hapd, (u8 *) &mlme->address[0]); - if (sta == NULL) { - printf("cannot get sta\n"); - return; - } - 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(drv->hapd, sta, !new_assoc); - ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); - sta->timeout_next = STA_NULLFUNC; - return; - } - return; - -fail: - printf("Prism54: assoc fail: %x\n", resp); - mlme->code = host_to_le16(resp); - mlme->size = 0; - mlme->state = htons(DOT11_STATE_ASSOCING); - hdr->oid = htonl(DOT11_OID_DISASSOCIATEEX); - hdr->op = htonl(PIMOP_SET); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - send(drv->pim_sock, hdr, sizeof(*hdr) + sizeof(*mlme), 0); -} - - -static void handle_pim(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct prism54_driver_data *drv = eloop_ctx; - int len; - pimdev_hdr *hdr; - - hdr = malloc(PIM_BUF_SIZE); - if (hdr == NULL) - return; - len = recv(sock, hdr, PIM_BUF_SIZE, 0); - if (len < 0) { - perror("recv"); - free(hdr); - return; - } - if (len < 8) { - printf("handle_pim: too short (%d)\n", len); - free(hdr); - return; - } - - if (hdr->op != (int) htonl(PIMOP_TRAP)) { - free(hdr); - return; - } - switch (ntohl(hdr->oid)) { - case DOT11_OID_PROBE: - prism54_handle_probe(drv, hdr, len); - break; - case DOT11_OID_DEAUTHENTICATEEX: - case DOT11_OID_DEAUTHENTICATE: - prism54_handle_deauth(drv, hdr, len); - break; - case DOT11_OID_DISASSOCIATEEX: - case DOT11_OID_DISASSOCIATE: - prism54_handle_disassoc(drv, hdr, len); - break; - case DOT11_OID_AUTHENTICATEEX: - case DOT11_OID_AUTHENTICATE: - prism54_handle_auth(drv, hdr, len); - break; - case DOT11_OID_ASSOCIATEEX: - case DOT11_OID_REASSOCIATEEX: - case DOT11_OID_ASSOCIATE: - case DOT11_OID_REASSOCIATE: - prism54_handle_assoc(drv, hdr, len); - default: - break; - } - - free(hdr); -} - - -static void handle_802_3(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; - int len; - ieee802_3_hdr *hdr; - - hdr = malloc(PIM_BUF_SIZE); - if (hdr == NULL) - return; - len = recv(sock, hdr, PIM_BUF_SIZE, 0); - if (len < 0) { - perror("recv"); - free(hdr); - return; - } - if (len < 14) { - wpa_printf(MSG_MSGDUMP, "handle_802_3: too short (%d)", len); - free(hdr); - return; - } - if (hdr->type == htons(ETH_P_PAE)) { - ieee802_1x_receive(hapd, (u8 *) &hdr->sa[0], (u8 *) &hdr[1], - len - sizeof(*hdr)); - } - free(hdr); -} - - -static int prism54_init_sockets(struct prism54_driver_data *drv) -{ - struct hostapd_data *hapd = drv->hapd; - struct ifreq ifr; - struct sockaddr_ll addr; - - 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_802_3, drv->hapd, NULL)) - { - printf("Could not register read socket\n"); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - if (hapd->conf->bridge[0] != '\0') { - printf("opening bridge: %s\n", hapd->conf->bridge); - os_strlcpy(ifr.ifr_name, hapd->conf->bridge, - sizeof(ifr.ifr_name)); - } else { - os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); - } - if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sll_family = AF_PACKET; - addr.sll_ifindex = ifr.ifr_ifindex; - addr.sll_protocol = htons(ETH_P_PAE); - 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; - } - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->iface, 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; - } - memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - - drv->pim_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (drv->pim_sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); - return -1; - } - - if (eloop_register_read_sock(drv->pim_sock, handle_pim, 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->pim_sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sll_family = AF_PACKET; - addr.sll_ifindex = ifr.ifr_ifindex; - addr.sll_protocol = htons(ETH_P_ALL); - wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", - addr.sll_ifindex); - - if (bind(drv->pim_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); - return -1; - } - - return 0; -} - - -static void * prism54_driver_init(struct hostapd_data *hapd) -{ - struct prism54_driver_data *drv; - - drv = os_zalloc(sizeof(struct prism54_driver_data)); - if (drv == NULL) { - printf("Could not allocate memory for hostapd Prism54 driver " - "data\n"); - return NULL; - } - - drv->hapd = hapd; - drv->pim_sock = drv->sock = -1; - memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); - - if (prism54_init_sockets(drv)) { - free(drv); - return NULL; - } - prism54_init_1x(drv); - /* must clean previous elems */ - prism54_set_generic_elem(drv->iface, drv, NULL, 0); - - return drv; -} - - -static void prism54_driver_deinit(void *priv) -{ - struct prism54_driver_data *drv = priv; - - if (drv->pim_sock >= 0) - close(drv->pim_sock); - - if (drv->sock >= 0) - close(drv->sock); - - free(drv); -} - - -const struct wpa_driver_ops wpa_driver_prism54_ops = { - .name = "prism54", - .init = prism54_driver_init, - .deinit = prism54_driver_deinit, - /* .set_ieee8021x = prism54_init_1x, */ - .set_privacy = prism54_set_privacy_invoked, - .set_encryption = prism54_set_encryption, - .get_seqnum = prism54_get_seqnum, - .flush = prism54_flush, - .set_generic_elem = prism54_set_generic_elem, - .send_eapol = prism54_send_eapol, - .sta_set_flags = prism54_sta_set_flags, - .sta_deauth = prism54_sta_deauth, - .sta_disassoc = prism54_sta_disassoc, - .set_ssid = prism54_ioctl_setiwessid, - .get_inact_sec = prism54_get_inact_sec, -}; diff --git a/contrib/hostapd/hostapd/driver_test.c b/contrib/hostapd/hostapd/driver_test.c deleted file mode 100644 index 9930a82847..0000000000 --- a/contrib/hostapd/hostapd/driver_test.c +++ /dev/null @@ -1,1300 +0,0 @@ -/* - * hostapd / Driver interface for development testing - * Copyright (c) 2004-2008, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include -#include - -#include "hostapd.h" -#include "driver.h" -#include "sha1.h" -#include "eloop.h" -#include "ieee802_1x.h" -#include "sta_info.h" -#include "wpa.h" -#include "accounting.h" -#include "radius/radius.h" -#include "l2_packet/l2_packet.h" -#include "ieee802_11.h" -#include "hw_features.h" -#include "wps_hostapd.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 test_driver_bss *next; - char ifname[IFNAMSIZ + 1]; - 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 test_driver_data { - struct hostapd_data *hapd; - struct test_client_socket *cli; - int test_socket; - struct test_driver_bss *bss; - char *socket_dir; - char *own_socket_path; - int udp_port; -}; - - -static void test_driver_free_bss(struct test_driver_bss *bss) -{ - free(bss->ie); - free(bss->wps_beacon_ie); - free(bss->wps_probe_resp_ie); - free(bss); -} - - -static void test_driver_free_priv(struct test_driver_data *drv) -{ - struct test_driver_bss *bss, *prev; - - if (drv == NULL) - return; - - bss = drv->bss; - while (bss) { - prev = bss; - bss = bss->next; - test_driver_free_bss(prev); - } - free(drv->own_socket_path); - free(drv->socket_dir); - free(drv); -} - - -static struct test_client_socket * -test_driver_get_cli(struct test_driver_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_data *drv = priv; - 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 = ð - 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_data *drv = priv; - 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->socket_dir == NULL) { - wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d " - "socket_dir=%p)", - __func__, drv->test_socket, drv->socket_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 = ð - 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->socket_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->socket_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 test_driver_send_mgmt_frame(void *priv, const void *buf, - size_t len, int flags) -{ - struct test_driver_data *drv = priv; - struct msghdr msg; - struct iovec io[2]; - const u8 *dest; - int ret = 0, broadcast = 0; - char desttxt[30]; - struct sockaddr_un addr; - struct dirent *dent; - DIR *dir; - struct ieee80211_hdr *hdr; - u16 fc; - - if (drv->test_socket < 0 || len < 10 || drv->socket_dir == NULL) { - wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%lu" - " socket_dir=%p)", - __func__, drv->test_socket, (unsigned long) len, - drv->socket_dir); - return -1; - } - - dest = buf; - dest += 4; - broadcast = memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; - snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest)); - - io[0].iov_base = "MLME "; - io[0].iov_len = 5; - io[1].iov_base = (void *) buf; - io[1].iov_len = len; - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = io; - msg.msg_iovlen = 2; - - dir = opendir(drv->socket_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->socket_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 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"); - } - closedir(dir); - - hdr = (struct ieee80211_hdr *) buf; - fc = le_to_host16(hdr->frame_control); - ieee802_11_mgmt_cb(drv->hapd, (u8 *) buf, len, WLAN_FC_GET_STYPE(fc), - ret >= 0); - - return ret; -} - - -static void test_driver_scan(struct test_driver_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; - - /* 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); - - hostapd_wps_probe_req_rx(drv->hapd, sa, ie, ielen); - } - - for (bss = drv->bss; bss; bss = bss->next) { - 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 struct hostapd_data * test_driver_get_hapd(struct test_driver_data *drv, - struct test_driver_bss *bss) -{ - struct hostapd_iface *iface = drv->hapd->iface; - struct hostapd_data *hapd = NULL; - size_t i; - - if (bss == NULL) { - wpa_printf(MSG_DEBUG, "%s: bss == NULL", __func__); - return NULL; - } - - for (i = 0; i < iface->num_bss; i++) { - hapd = iface->bss[i]; - if (memcmp(hapd->own_addr, bss->bssid, ETH_ALEN) == 0) - break; - } - if (i == iface->num_bss) { - wpa_printf(MSG_DEBUG, "%s: no matching interface entry found " - "for BSSID " MACSTR, __func__, MAC2STR(bss->bssid)); - return NULL; - } - - return hapd; -} - - -static int test_driver_new_sta(struct test_driver_data *drv, - struct test_driver_bss *bss, const u8 *addr, - const u8 *ie, size_t ielen) -{ - struct hostapd_data *hapd; - struct sta_info *sta; - int new_assoc, res; - - hapd = test_driver_get_hapd(drv, bss); - if (hapd == NULL) - return -1; - - hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "associated"); - - 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) { - sta->flags |= WLAN_STA_WPS; - goto skip_wpa_check; - } - - printf("test_driver: no IE from STA\n"); - 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) { - printf("test_driver: Failed to initialize WPA state " - "machine\n"); - return -1; - } - res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, - ie, ielen, NULL, 0); - if (res != WPA_IE_OK) { - printf("WPA/RSN information element rejected? " - "(res %u)\n", res); - wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); - return -1; - } - } -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; -} - - -static void test_driver_assoc(struct test_driver_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; - - /* 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); - 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__); - 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; - } - - for (bss = drv->bss; bss; bss = bss->next) { - if (bss->ssid_len == ssid_len && - memcmp(bss->ssid, ssid, ssid_len) == 0) - break; - } - if (bss == NULL) { - wpa_printf(MSG_DEBUG, "%s: No matching SSID found from " - "configured BSSes", __func__); - 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); - - if (test_driver_new_sta(drv, bss, cli->addr, ie, ielen) < 0) { - wpa_printf(MSG_DEBUG, "test_driver: failed to add new STA"); - } -} - - -static void test_driver_disassoc(struct test_driver_data *drv, - struct sockaddr_un *from, socklen_t fromlen) -{ - struct test_client_socket *cli; - struct sta_info *sta; - - cli = test_driver_get_cli(drv, from, fromlen); - if (!cli) - return; - - hostapd_logger(drv->hapd, cli->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "disassociated"); - - sta = ap_get_sta(drv->hapd, cli->addr); - if (sta != NULL) { - sta->flags &= ~WLAN_STA_ASSOC; - 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(drv->hapd, sta); - } -} - - -static void test_driver_eapol(struct test_driver_data *drv, - struct sockaddr_un *from, socklen_t fromlen, - u8 *data, size_t datalen) -{ - struct test_client_socket *cli; - if (datalen > 14) { - /* Skip Ethernet header */ - wpa_printf(MSG_DEBUG, "test_driver: dst=" MACSTR " src=" - MACSTR " proto=%04x", - MAC2STR(data), MAC2STR(data + ETH_ALEN), - WPA_GET_BE16(data + 2 * ETH_ALEN)); - data += 14; - datalen -= 14; - } - cli = test_driver_get_cli(drv, from, fromlen); - if (cli) { - struct hostapd_data *hapd; - hapd = test_driver_get_hapd(drv, cli->bss); - if (hapd == NULL) - return; - ieee802_1x_receive(hapd, cli->addr, data, datalen); - } else { - wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown " - "client"); - } -} - - -static void test_driver_ether(struct test_driver_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) { - wpa_ft_rrb_rx(drv->hapd->wpa_auth, eth->h_source, - data + sizeof(*eth), datalen - sizeof(*eth)); - } -#endif /* CONFIG_IEEE80211R */ -} - - -static void test_driver_mlme(struct test_driver_data *drv, - struct sockaddr_un *from, socklen_t fromlen, - u8 *data, size_t datalen) -{ - struct ieee80211_hdr *hdr; - u16 fc; - - 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; - } - ieee802_11_mgmt(drv->hapd, data, datalen, WLAN_FC_GET_STYPE(fc), NULL); -} - - -static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct test_driver_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 struct test_driver_bss * -test_driver_get_bss(struct test_driver_data *drv, const char *ifname) -{ - struct test_driver_bss *bss; - - for (bss = drv->bss; bss; bss = bss->next) { - if (strcmp(bss->ifname, ifname) == 0) - return bss; - } - return NULL; -} - - -static int test_driver_set_generic_elem(const char *ifname, void *priv, - const u8 *elem, size_t elem_len) -{ - struct test_driver_data *drv = priv; - struct test_driver_bss *bss; - - bss = test_driver_get_bss(drv, ifname); - if (bss == NULL) - return -1; - - free(bss->ie); - - if (elem == NULL) { - bss->ie = NULL; - bss->ielen = 0; - return 0; - } - - bss->ie = 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_wps_beacon_ie(const char *ifname, void *priv, - const u8 *ie, size_t len) -{ - struct test_driver_data *drv = priv; - struct test_driver_bss *bss; - - wpa_hexdump(MSG_DEBUG, "test_driver: Beacon WPS IE", ie, len); - bss = test_driver_get_bss(drv, ifname); - if (bss == NULL) - return -1; - - free(bss->wps_beacon_ie); - - if (ie == NULL) { - bss->wps_beacon_ie = NULL; - bss->wps_beacon_ie_len = 0; - return 0; - } - - bss->wps_beacon_ie = malloc(len); - if (bss->wps_beacon_ie == NULL) { - bss->wps_beacon_ie_len = 0; - return -1; - } - - memcpy(bss->wps_beacon_ie, ie, len); - bss->wps_beacon_ie_len = len; - return 0; -} - - -static int test_driver_set_wps_probe_resp_ie(const char *ifname, void *priv, - const u8 *ie, size_t len) -{ - struct test_driver_data *drv = priv; - struct test_driver_bss *bss; - - wpa_hexdump(MSG_DEBUG, "test_driver: ProbeResp WPS IE", ie, len); - bss = test_driver_get_bss(drv, ifname); - if (bss == NULL) - return -1; - - free(bss->wps_probe_resp_ie); - - if (ie == NULL) { - bss->wps_probe_resp_ie = NULL; - bss->wps_probe_resp_ie_len = 0; - return 0; - } - - bss->wps_probe_resp_ie = malloc(len); - if (bss->wps_probe_resp_ie == NULL) { - bss->wps_probe_resp_ie_len = 0; - return -1; - } - - memcpy(bss->wps_probe_resp_ie, ie, len); - bss->wps_probe_resp_ie_len = len; - return 0; -} - - -static int test_driver_sta_deauth(void *priv, const u8 *addr, int reason) -{ - struct test_driver_data *drv = priv; - 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 *addr, int reason) -{ - struct test_driver_data *drv = priv; - 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 struct hostapd_hw_modes * -test_driver_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) -{ - struct hostapd_hw_modes *modes; - - *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 = 1; - modes[0].num_rates = 1; - modes[0].channels = os_zalloc(sizeof(struct hostapd_channel_data)); - modes[0].rates = os_zalloc(sizeof(struct hostapd_rate_data)); - if (modes[0].channels == NULL || modes[0].rates == NULL) { - hostapd_free_hw_features(modes, *num_modes); - return NULL; - } - modes[0].channels[0].chan = 1; - modes[0].channels[0].freq = 2412; - modes[0].channels[0].flag = 0; - modes[0].rates[0].rate = 10; - modes[0].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | - HOSTAPD_RATE_CCK | HOSTAPD_RATE_MANDATORY; - - modes[1].mode = HOSTAPD_MODE_IEEE80211B; - modes[1].num_channels = 1; - modes[1].num_rates = 1; - modes[1].channels = os_zalloc(sizeof(struct hostapd_channel_data)); - modes[1].rates = os_zalloc(sizeof(struct hostapd_rate_data)); - if (modes[1].channels == NULL || modes[1].rates == NULL) { - hostapd_free_hw_features(modes, *num_modes); - return NULL; - } - modes[1].channels[0].chan = 1; - modes[1].channels[0].freq = 2412; - modes[1].channels[0].flag = 0; - modes[1].rates[0].rate = 10; - modes[1].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | - HOSTAPD_RATE_CCK | HOSTAPD_RATE_MANDATORY; - - modes[2].mode = HOSTAPD_MODE_IEEE80211A; - modes[2].num_channels = 1; - modes[2].num_rates = 1; - modes[2].channels = os_zalloc(sizeof(struct hostapd_channel_data)); - modes[2].rates = os_zalloc(sizeof(struct hostapd_rate_data)); - if (modes[2].channels == NULL || modes[2].rates == NULL) { - hostapd_free_hw_features(modes, *num_modes); - return NULL; - } - modes[2].channels[0].chan = 60; - modes[2].channels[0].freq = 5300; - modes[2].channels[0].flag = 0; - modes[2].rates[0].rate = 60; - modes[2].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | - HOSTAPD_RATE_MANDATORY; - - return modes; -} - - -static int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid) -{ - struct test_driver_data *drv = priv; - 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; - - os_strlcpy(bss->ifname, ifname, IFNAMSIZ); - memcpy(bss->bssid, bssid, ETH_ALEN); - - bss->next = drv->bss; - drv->bss = bss; - - return 0; -} - - -static int test_driver_bss_remove(void *priv, const char *ifname) -{ - struct test_driver_data *drv = priv; - struct test_driver_bss *bss, *prev; - struct test_client_socket *cli, *prev_c; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname); - - for (prev = NULL, bss = drv->bss; bss; prev = bss, bss = bss->next) { - if (strcmp(bss->ifname, ifname) != 0) - continue; - - if (prev) - prev->next = bss->next; - else - drv->bss = bss->next; - - 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; - free(cli); - break; - } - - test_driver_free_bss(bss); - return 0; - } - - return -1; -} - - -static int test_driver_if_add(const char *iface, void *priv, - enum hostapd_driver_if_type type, char *ifname, - const u8 *addr) -{ - wpa_printf(MSG_DEBUG, "%s(iface=%s type=%d ifname=%s)", - __func__, iface, type, ifname); - return 0; -} - - -static int test_driver_if_update(void *priv, enum hostapd_driver_if_type type, - char *ifname, const u8 *addr) -{ - wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); - return 0; -} - - -static int test_driver_if_remove(void *priv, enum hostapd_driver_if_type type, - const char *ifname, const u8 *addr) -{ - wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, 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(const char *ifname, void *priv, const u8 *buf, - int len) -{ - struct test_driver_data *drv = priv; - struct test_driver_bss *bss; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname); - wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len); - - for (bss = drv->bss; bss; bss = bss->next) { - if (strcmp(bss->ifname, ifname) != 0) - continue; - - if (len < 0 || (size_t) len > sizeof(bss->ssid)) - return -1; - - memcpy(bss->ssid, buf, len); - bss->ssid_len = len; - - return 0; - } - - return -1; -} - - -static int test_driver_set_privacy(const char *ifname, void *priv, int enabled) -{ - struct test_driver_data *drv = priv; - struct test_driver_bss *bss; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s enabled=%d)", - __func__, ifname, enabled); - - for (bss = drv->bss; bss; bss = bss->next) { - if (strcmp(bss->ifname, ifname) != 0) - continue; - - bss->privacy = enabled; - - return 0; - } - - return -1; -} - - -static int test_driver_set_encryption(const char *iface, void *priv, - const char *alg, const u8 *addr, int idx, - const u8 *key, size_t key_len, int txkey) -{ - wpa_printf(MSG_DEBUG, "%s(iface=%s alg=%s idx=%d txkey=%d)", - __func__, iface, alg, idx, txkey); - if (addr) - wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); - if (key) - wpa_hexdump_key(MSG_DEBUG, " key", key, key_len); - 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(const char *ifname, void *priv, const u8 *addr, - u16 aid, u16 capability, u8 *supp_rates, - size_t supp_rates_len, int flags, - u16 listen_interval) -{ - struct test_driver_data *drv = priv; - struct test_client_socket *cli; - struct test_driver_bss *bss; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s addr=" MACSTR " aid=%d " - "capability=0x%x flags=0x%x listen_interval=%d)", - __func__, ifname, MAC2STR(addr), aid, capability, flags, - listen_interval); - wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates", - supp_rates, supp_rates_len); - - 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 matching client entry", - __func__); - return -1; - } - - for (bss = drv->bss; bss; bss = bss->next) { - if (strcmp(ifname, bss->ifname) == 0) - break; - } - if (bss == NULL) { - wpa_printf(MSG_DEBUG, "%s: No matching interface found from " - "configured BSSes", __func__); - return -1; - } - - cli->bss = bss; - - return 0; -} - - -static void * test_driver_init(struct hostapd_data *hapd) -{ - struct test_driver_data *drv; - struct sockaddr_un addr_un; - struct sockaddr_in addr_in; - struct sockaddr *addr; - socklen_t alen; - - drv = os_zalloc(sizeof(struct test_driver_data)); - if (drv == NULL) { - printf("Could not allocate memory for test driver data\n"); - return NULL; - } - drv->bss = os_zalloc(sizeof(*drv->bss)); - if (drv->bss == NULL) { - printf("Could not allocate memory for test driver BSS data\n"); - free(drv); - return NULL; - } - - drv->hapd = hapd; - - /* Generate a MAC address to help testing with multiple APs */ - hapd->own_addr[0] = 0x02; /* locally administered */ - sha1_prf((const u8 *) hapd->conf->iface, strlen(hapd->conf->iface), - "hostapd test bssid generation", - (const u8 *) hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len, - hapd->own_addr + 1, ETH_ALEN - 1); - - os_strlcpy(drv->bss->ifname, hapd->conf->iface, IFNAMSIZ); - memcpy(drv->bss->bssid, hapd->own_addr, ETH_ALEN); - - if (hapd->conf->test_socket) { - if (strlen(hapd->conf->test_socket) >= - sizeof(addr_un.sun_path)) { - printf("Too long test_socket path\n"); - test_driver_free_priv(drv); - return NULL; - } - if (strncmp(hapd->conf->test_socket, "DIR:", 4) == 0) { - size_t len = strlen(hapd->conf->test_socket) + 30; - drv->socket_dir = strdup(hapd->conf->test_socket + 4); - drv->own_socket_path = malloc(len); - if (drv->own_socket_path) { - snprintf(drv->own_socket_path, len, - "%s/AP-" MACSTR, - hapd->conf->test_socket + 4, - MAC2STR(hapd->own_addr)); - } - } else if (strncmp(hapd->conf->test_socket, "UDP:", 4) == 0) { - drv->udp_port = atoi(hapd->conf->test_socket + 4); - } else { - drv->own_socket_path = strdup(hapd->conf->test_socket); - } - if (drv->own_socket_path == NULL && drv->udp_port == 0) { - test_driver_free_priv(drv); - return NULL; - } - - drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX, - SOCK_DGRAM, 0); - if (drv->test_socket < 0) { - perror("socket"); - test_driver_free_priv(drv); - 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); - test_driver_free_priv(drv); - return NULL; - } - eloop_register_read_sock(drv->test_socket, - test_driver_receive_unix, drv, NULL); - } else - drv->test_socket = -1; - - return drv; -} - - -static void test_driver_deinit(void *priv) -{ - struct test_driver_data *drv = priv; - struct test_client_socket *cli, *prev; - - cli = drv->cli; - while (cli) { - prev = cli; - cli = cli->next; - free(prev); - } - - if (drv->test_socket >= 0) { - eloop_unregister_read_sock(drv->test_socket); - close(drv->test_socket); - if (drv->own_socket_path) - unlink(drv->own_socket_path); - } - - /* There should be only one BSS remaining at this point. */ - if (drv->bss == NULL) - wpa_printf(MSG_ERROR, "%s: drv->bss == NULL", __func__); - else if (drv->bss->next) - wpa_printf(MSG_ERROR, "%s: drv->bss->next != NULL", __func__); - - test_driver_free_priv(drv); -} - - -const struct wpa_driver_ops wpa_driver_test_ops = { - .name = "test", - .init = test_driver_init, - .deinit = test_driver_deinit, - .send_eapol = test_driver_send_eapol, - .send_mgmt_frame = test_driver_send_mgmt_frame, - .set_generic_elem = test_driver_set_generic_elem, - .sta_deauth = test_driver_sta_deauth, - .sta_disassoc = test_driver_sta_disassoc, - .get_hw_feature_data = test_driver_get_hw_feature_data, - .bss_add = test_driver_bss_add, - .bss_remove = test_driver_bss_remove, - .if_add = test_driver_if_add, - .if_update = test_driver_if_update, - .if_remove = test_driver_if_remove, - .valid_bss_mask = test_driver_valid_bss_mask, - .set_ssid = test_driver_set_ssid, - .set_privacy = test_driver_set_privacy, - .set_encryption = test_driver_set_encryption, - .set_sta_vlan = test_driver_set_sta_vlan, - .sta_add = test_driver_sta_add, - .send_ether = test_driver_send_ether, - .set_wps_beacon_ie = test_driver_set_wps_beacon_ie, - .set_wps_probe_resp_ie = test_driver_set_wps_probe_resp_ie, -}; diff --git a/contrib/hostapd/hostapd/driver_wired.c b/contrib/hostapd/hostapd/driver_wired.c deleted file mode 100644 index 61cb667cca..0000000000 --- a/contrib/hostapd/hostapd/driver_wired.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * hostapd / Kernel driver communication for wired (Ethernet) drivers - * Copyright (c) 2002-2007, Jouni Malinen - * Copyright (c) 2004, Gunter Burchardt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include - -#ifdef USE_KERNEL_HEADERS -#include -#include -#include /* The L2 protocols */ -#include -#include -#else /* USE_KERNEL_HEADERS */ -#include -#include -#include -#endif /* USE_KERNEL_HEADERS */ - -#include "hostapd.h" -#include "ieee802_1x.h" -#include "eloop.h" -#include "sta_info.h" -#include "driver.h" -#include "accounting.h" - - -struct wired_driver_data { - struct hostapd_data *hapd; - - int sock; /* raw packet socket for driver access */ - int dhcp_sock; /* socket for dhcp packets */ - int use_pae_group_addr; -}; - - -#define WIRED_EAPOL_MULTICAST_GROUP {0x01,0x80,0xc2,0x00,0x00,0x03} - - -/* TODO: detecting new devices should eventually be changed from using DHCP - * snooping to trigger on any packet from a new layer 2 MAC address, e.g., - * based on ebtables, etc. */ - -struct dhcp_message { - u_int8_t op; - u_int8_t htype; - u_int8_t hlen; - u_int8_t hops; - u_int32_t xid; - u_int16_t secs; - u_int16_t flags; - u_int32_t ciaddr; - u_int32_t yiaddr; - u_int32_t siaddr; - u_int32_t giaddr; - u_int8_t chaddr[16]; - u_int8_t sname[64]; - u_int8_t file[128]; - u_int32_t cookie; - u_int8_t options[308]; /* 312 - cookie */ -}; - - -static void wired_possible_new_sta(struct hostapd_data *hapd, u8 *addr) -{ - struct sta_info *sta; - - sta = ap_get_sta(hapd, addr); - if (sta) - return; - - 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)); - } -} - - -static void handle_data(struct hostapd_data *hapd, unsigned char *buf, - size_t len) -{ - struct ieee8023_hdr *hdr; - u8 *pos, *sa; - size_t left; - - /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest, - * 2 byte ethertype */ - if (len < 14) { - 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; - wired_possible_new_sta(hapd, sa); - - pos = (u8 *) (hdr + 1); - left = len - sizeof(*hdr); - - ieee802_1x_receive(hapd, sa, pos, left); - break; - - default: - wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame", - ntohs(hdr->ethertype)); - break; - } -} - - -static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; - int len; - unsigned char buf[3000]; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - perror("recv"); - return; - } - - handle_data(hapd, buf, len); -} - - -static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct hostapd_data *hapd = (struct hostapd_data *) eloop_ctx; - int len; - unsigned char buf[3000]; - struct dhcp_message *msg; - u8 *mac_address; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - perror("recv"); - return; - } - - /* must contain at least dhcp_message->chaddr */ - if (len < 44) { - 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)); - - wired_possible_new_sta(hapd, mac_address); -} - - -static int wired_init_sockets(struct wired_driver_data *drv) -{ - struct hostapd_data *hapd = drv->hapd; - struct ifreq ifr; - struct sockaddr_ll addr; - struct sockaddr_in addr2; - struct packet_mreq mreq; - u8 multicastgroup_eapol[6] = WIRED_EAPOL_MULTICAST_GROUP; - int n = 1; - - drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); - if (drv->sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); - return -1; - } - - if (eloop_register_read_sock(drv->sock, handle_read, hapd, NULL)) { - printf("Could not register read socket\n"); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, hapd->conf->iface, sizeof(ifr.ifr_name)); - if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); - return -1; - } - - - memset(&addr, 0, sizeof(addr)); - addr.sll_family = AF_PACKET; - addr.sll_ifindex = ifr.ifr_ifindex; - 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 */ - memset(&mreq, 0, sizeof(mreq)); - mreq.mr_ifindex = ifr.ifr_ifindex; - mreq.mr_type = PACKET_MR_MULTICAST; - mreq.mr_alen = 6; - memcpy(mreq.mr_address, multicastgroup_eapol, mreq.mr_alen); - - if (setsockopt(drv->sock, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, - sizeof(mreq)) < 0) { - perror("setsockopt[SOL_SOCKET,PACKET_ADD_MEMBERSHIP]"); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, hapd->conf->iface, 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; - } - memcpy(hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); - - /* setup dhcp listen socket for sta detection */ - if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - perror("socket call failed for dhcp"); - return -1; - } - - if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, hapd, NULL)) - { - printf("Could not register read socket\n"); - return -1; - } - - memset(&addr2, 0, sizeof(addr2)); - addr2.sin_family = AF_INET; - addr2.sin_port = htons(67); - addr2.sin_addr.s_addr = INADDR_ANY; - - if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n, - sizeof(n)) == -1) { - perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]"); - return -1; - } - if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n, - sizeof(n)) == -1) { - perror("setsockopt[SOL_SOCKET,SO_BROADCAST]"); - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_ifrn.ifrn_name, hapd->conf->iface, IFNAMSIZ); - if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, - (char *) &ifr, sizeof(ifr)) < 0) { - perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]"); - return -1; - } - - if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2, - sizeof(struct sockaddr)) == -1) { - perror("bind"); - return -1; - } - - return 0; -} - - -static int wired_send_eapol(void *priv, const u8 *addr, - const u8 *data, size_t data_len, int encrypt, - const u8 *own_addr) -{ - struct wired_driver_data *drv = priv; - u8 pae_group_addr[ETH_ALEN] = WIRED_EAPOL_MULTICAST_GROUP; - struct ieee8023_hdr *hdr; - size_t len; - u8 *pos; - int res; - - len = sizeof(*hdr) + data_len; - hdr = os_zalloc(len); - if (hdr == NULL) { - printf("malloc() failed for wired_send_eapol(len=%lu)\n", - (unsigned long) len); - return -1; - } - - memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, - ETH_ALEN); - memcpy(hdr->src, own_addr, ETH_ALEN); - hdr->ethertype = htons(ETH_P_PAE); - - pos = (u8 *) (hdr + 1); - memcpy(pos, data, data_len); - - res = send(drv->sock, (u8 *) hdr, len, 0); - free(hdr); - - if (res < 0) { - perror("wired_send_eapol: send"); - printf("wired_send_eapol - packet len: %lu - failed\n", - (unsigned long) len); - } - - return res; -} - - -static void * wired_driver_init(struct hostapd_data *hapd) -{ - struct wired_driver_data *drv; - - drv = os_zalloc(sizeof(struct wired_driver_data)); - if (drv == NULL) { - printf("Could not allocate memory for wired driver data\n"); - return NULL; - } - - drv->hapd = hapd; - drv->use_pae_group_addr = hapd->conf->use_pae_group_addr; - - if (wired_init_sockets(drv)) { - free(drv); - return NULL; - } - - return drv; -} - - -static void wired_driver_deinit(void *priv) -{ - struct wired_driver_data *drv = priv; - - if (drv->sock >= 0) - close(drv->sock); - - if (drv->dhcp_sock >= 0) - close(drv->dhcp_sock); - - free(drv); -} - - -const struct wpa_driver_ops wpa_driver_wired_ops = { - .name = "wired", - .init = wired_driver_init, - .deinit = wired_driver_deinit, - .send_eapol = wired_send_eapol, -}; diff --git a/contrib/hostapd/hostapd/drivers.c b/contrib/hostapd/hostapd/drivers.c deleted file mode 100644 index bde6e609f3..0000000000 --- a/contrib/hostapd/hostapd/drivers.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * hostapd / driver interface list - * Copyright (c) 2007, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - - -#ifdef CONFIG_DRIVER_HOSTAP -extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ -#endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_NL80211 -extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ -#endif /* CONFIG_DRIVER_NL80211 */ -#ifdef CONFIG_DRIVER_PRISM54 -extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */ -#endif /* CONFIG_DRIVER_PRISM54 */ -#ifdef CONFIG_DRIVER_MADWIFI -extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ -#endif /* CONFIG_DRIVER_MADWIFI */ -#ifdef CONFIG_DRIVER_ATHEROS -extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */ -#endif /* CONFIG_DRIVER_ATHEROS */ -#ifdef CONFIG_DRIVER_BSD -extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ -#endif /* CONFIG_DRIVER_BSD */ -#ifdef CONFIG_DRIVER_WIRED -extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ -#endif /* CONFIG_DRIVER_WIRED */ -#ifdef CONFIG_DRIVER_TEST -extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ -#endif /* CONFIG_DRIVER_TEST */ -#ifdef CONFIG_DRIVER_NONE -extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ -#endif /* CONFIG_DRIVER_NONE */ - - -struct wpa_driver_ops *hostapd_drivers[] = -{ -#ifdef CONFIG_DRIVER_HOSTAP - &wpa_driver_hostap_ops, -#endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_NL80211 - &wpa_driver_nl80211_ops, -#endif /* CONFIG_DRIVER_NL80211 */ -#ifdef CONFIG_DRIVER_PRISM54 - &wpa_driver_prism54_ops, -#endif /* CONFIG_DRIVER_PRISM54 */ -#ifdef CONFIG_DRIVER_MADWIFI - &wpa_driver_madwifi_ops, -#endif /* CONFIG_DRIVER_MADWIFI */ -#ifdef CONFIG_DRIVER_ATHEROS - &wpa_driver_atheros_ops, -#endif /* CONFIG_DRIVER_ATHEROS */ -#ifdef CONFIG_DRIVER_BSD - &wpa_driver_bsd_ops, -#endif /* CONFIG_DRIVER_BSD */ -#ifdef CONFIG_DRIVER_WIRED - &wpa_driver_wired_ops, -#endif /* CONFIG_DRIVER_WIRED */ -#ifdef CONFIG_DRIVER_TEST - &wpa_driver_test_ops, -#endif /* CONFIG_DRIVER_TEST */ -#ifdef CONFIG_DRIVER_NONE - &wpa_driver_none_ops, -#endif /* CONFIG_DRIVER_NONE */ - NULL -}; diff --git a/contrib/hostapd/hostapd/eap_register.c b/contrib/hostapd/hostapd/eap_register.c new file mode 100644 index 0000000000..981e53946a --- /dev/null +++ b/contrib/hostapd/hostapd/eap_register.c @@ -0,0 +1,143 @@ +/* + * EAP method registration + * Copyright (c) 2004-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_server/eap_methods.h" +#include "eap_register.h" + + +/** + * eap_server_register_methods - Register statically linked EAP server 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_server_register_methods(void) +{ + int ret = 0; + +#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_UNAUTH_TLS + if (ret == 0) + ret = eap_server_unauth_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 */ + +#ifdef EAP_SERVER_PWD + if (ret == 0) + ret = eap_server_pwd_register(); +#endif /* EAP_SERVER_PWD */ + +#ifdef EAP_SERVER_EKE + if (ret == 0) + ret = eap_server_eke_register(); +#endif /* EAP_SERVER_EKE */ + + return ret; +} diff --git a/contrib/hostapd/hostapd/eap_register.h b/contrib/hostapd/hostapd/eap_register.h new file mode 100644 index 0000000000..c342351a1a --- /dev/null +++ b/contrib/hostapd/hostapd/eap_register.h @@ -0,0 +1,14 @@ +/* + * EAP method registration + * Copyright (c) 2004-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_REGISTER_H +#define EAP_REGISTER_H + +int eap_server_register_methods(void); + +#endif /* EAP_REGISTER_H */ diff --git a/contrib/hostapd/hostapd/hlr_auc_gw.txt b/contrib/hostapd/hostapd/hlr_auc_gw.txt new file mode 100644 index 0000000000..097bbce362 --- /dev/null +++ b/contrib/hostapd/hostapd/hlr_auc_gw.txt @@ -0,0 +1,104 @@ +HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator + +hlr_auc_gw is an example implementation of the EAP-SIM/AKA/AKA' +database/authentication gateway interface to HLR/AuC. It could be +replaced with an implementation of SS7 gateway to GSM/UMTS +authentication center (HLR/AuC). hostapd will send SIM/AKA +authentication queries over a UNIX domain socket to and external +program, e.g., hlr_auc_gw. + +hlr_auc_gw can be configured with GSM and UMTS authentication data with +text files: GSM triplet file (see hostapd.sim_db) and Milenage file (see +hlr_auc_gw.milenage_db). Milenage parameters can be used to generate +dynamic authentication data for EAP-SIM, EAP-AKA, and EAP-AKA' while the +GSM triplet data is used for a more static configuration (e.g., triplets +extracted from a SIM card). + +Alternatively, hlr_auc_gw can be built with support for an SQLite +database for more dynamic operations. This is enabled by adding +"CONFIG_SQLITE=y" into hostapd/.config before building hlr_auc_gw ("make +clean; make hlr_auc_gw" in this directory). + +hostapd is configured to use hlr_auc_gw with the eap_sim_db parameter in +hostapd.conf (e.g., "eap_sim_db=unix:/tmp/hlr_auc_gw.sock"). hlr_auc_gw +is configured with command line parameters: + +hlr_auc_gw [-hu] [-s] [-g] [-m] \ + [-D] [-i] + +options: + -h = show this usage help + -u = update SQN in Milenage file on exit + -s = path for UNIX domain socket + (default: /tmp/hlr_auc_gw.sock) + -g = path for GSM authentication triplets + -m = path for Milenage keys + -D = path to SQLite database + -i = IND length for SQN (default: 5) + + +The SQLite database can be initialized with sqlite, e.g., by running +following commands in "sqlite3 /path/to/hlr_auc_gw.db": + +CREATE TABLE milenage( + imsi INTEGER PRIMARY KEY NOT NULL, + ki CHAR(32) NOT NULL, + opc CHAR(32) NOT NULL, + amf CHAR(4) NOT NULL, + sqn CHAR(12) NOT NULL +); +INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES( + 232010000000000, + '90dca4eda45b53cf0f12d7c9c3bc6a89', + 'cb9cccc4b9258e6dca4760379fb82581', + '61df', + '000000000000' +); +INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES( + 555444333222111, + '5122250214c33e723a5dd523fc145fc0', + '981d464c7c52eb6e5036234984ad0bcf', + 'c3ab', + '16f3b3f70fc1' +); + + +hostapd (EAP server) can also be configured to store the EAP-SIM/AKA +pseudonyms and reauth information into a SQLite database. This is +configured with the db parameter within the eap_sim_db configuration +option. + + +"hlr_auc_gw -D /path/to/hlr_auc_gw.db" can then be used to fetch +Milenage parameters based on IMSI from the database. The database can be +updated dynamically while hlr_auc_gw is running to add/remove/modify +entries. + + +Example configuration files for hostapd to operate as a RADIUS +authentication server for EAP-SIM/AKA/AKA': + +hostapd.conf: + +driver=none +radius_server_clients=hostapd.radius_clients +eap_server=1 +eap_user_file=hostapd.eap_user +eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/eap_sim.db +eap_sim_aka_result_ind=1 + +hostapd.radius_clients: + +0.0.0.0/0 radius + +hostapd.eap_user: + +"0"* AKA +"1"* SIM +"2"* AKA +"3"* SIM +"4"* AKA +"5"* SIM +"6"* AKA' +"7"* AKA' +"8"* AKA' diff --git a/contrib/hostapd/hostapd/hostap_common.h b/contrib/hostapd/hostapd/hostap_common.h deleted file mode 100644 index 5a57dca46d..0000000000 --- a/contrib/hostapd/hostapd/hostap_common.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * hostapd / Kernel driver communication with Linux Host AP driver - * Copyright (c) 2002-2006, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef HOSTAP_COMMON_H -#define HOSTAP_COMMON_H - -/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ - -/* New wireless extensions API - SET/GET convention (even ioctl numbers are - * root only) - */ -#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) -#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) -#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) -#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) -#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) -#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) -#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) -#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) -#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) -#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) -#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) -#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) -#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) -#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) - -/* following are not in SIOCGIWPRIV list; check permission in the driver code - */ -#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) -#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) - - -/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ -enum { - /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ - PRISM2_PARAM_TXRATECTRL = 2, - PRISM2_PARAM_BEACON_INT = 3, - PRISM2_PARAM_PSEUDO_IBSS = 4, - PRISM2_PARAM_ALC = 5, - /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ - PRISM2_PARAM_DUMP = 7, - PRISM2_PARAM_OTHER_AP_POLICY = 8, - PRISM2_PARAM_AP_MAX_INACTIVITY = 9, - PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, - PRISM2_PARAM_DTIM_PERIOD = 11, - PRISM2_PARAM_AP_NULLFUNC_ACK = 12, - PRISM2_PARAM_MAX_WDS = 13, - PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, - PRISM2_PARAM_AP_AUTH_ALGS = 15, - PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, - PRISM2_PARAM_HOST_ENCRYPT = 17, - PRISM2_PARAM_HOST_DECRYPT = 18, - PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, - PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, - PRISM2_PARAM_HOST_ROAMING = 21, - PRISM2_PARAM_BCRX_STA_KEY = 22, - PRISM2_PARAM_IEEE_802_1X = 23, - PRISM2_PARAM_ANTSEL_TX = 24, - PRISM2_PARAM_ANTSEL_RX = 25, - PRISM2_PARAM_MONITOR_TYPE = 26, - PRISM2_PARAM_WDS_TYPE = 27, - PRISM2_PARAM_HOSTSCAN = 28, - PRISM2_PARAM_AP_SCAN = 29, - PRISM2_PARAM_ENH_SEC = 30, - PRISM2_PARAM_IO_DEBUG = 31, - PRISM2_PARAM_BASIC_RATES = 32, - PRISM2_PARAM_OPER_RATES = 33, - PRISM2_PARAM_HOSTAPD = 34, - PRISM2_PARAM_HOSTAPD_STA = 35, - PRISM2_PARAM_WPA = 36, - PRISM2_PARAM_PRIVACY_INVOKED = 37, - PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, - PRISM2_PARAM_DROP_UNENCRYPTED = 39, - PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, -}; - -enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, - HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; - - -/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ -enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, - AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, - AP_MAC_CMD_KICKALL = 4 }; - - -/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ -enum { - PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, - /* Note! Old versions of prism2_srec have a fatal error in CRC-16 - * calculation, which will corrupt all non-volatile downloads. - * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to - * prevent use of old versions of prism2_srec for non-volatile - * download. */ - PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, - PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, - /* Persistent versions of volatile download commands (keep firmware - * data in memory and automatically re-download after hw_reset */ - PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, - PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, -}; - -struct prism2_download_param { - u32 dl_cmd; - u32 start_addr; - u32 num_areas; - struct prism2_download_area { - u32 addr; /* wlan card address */ - u32 len; - caddr_t ptr; /* pointer to data in user space */ - } data[0]; -}; - -#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 -#define PRISM2_MAX_DOWNLOAD_LEN 262144 - - -/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ -enum { - PRISM2_HOSTAPD_FLUSH = 1, - PRISM2_HOSTAPD_ADD_STA = 2, - PRISM2_HOSTAPD_REMOVE_STA = 3, - PRISM2_HOSTAPD_GET_INFO_STA = 4, - /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ - PRISM2_SET_ENCRYPTION = 6, - PRISM2_GET_ENCRYPTION = 7, - PRISM2_HOSTAPD_SET_FLAGS_STA = 8, - PRISM2_HOSTAPD_GET_RID = 9, - PRISM2_HOSTAPD_SET_RID = 10, - PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, - PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, - PRISM2_HOSTAPD_MLME = 13, - PRISM2_HOSTAPD_SCAN_REQ = 14, - PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, -}; - -#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 -#define PRISM2_HOSTAPD_RID_HDR_LEN \ -((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) -#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ -((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) - -/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() - */ -#define HOSTAP_CRYPT_ALG_NAME_LEN 16 - - -struct prism2_hostapd_param { - u32 cmd; - u8 sta_addr[ETH_ALEN]; - union { - struct { - u16 aid; - u16 capability; - u8 tx_supp_rates; - } add_sta; - struct { - u32 inactive_sec; - } get_info_sta; - struct { - u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; - u32 flags; - u32 err; - u8 idx; - u8 seq[8]; /* sequence counter (set: RX, get: TX) */ - u16 key_len; - u8 key[0]; - } crypt; - struct { - u32 flags_and; - u32 flags_or; - } set_flags_sta; - struct { - u16 rid; - u16 len; - u8 data[0]; - } rid; - struct { - u8 len; - u8 data[0]; - } generic_elem; - struct { -#define MLME_STA_DEAUTH 0 -#define MLME_STA_DISASSOC 1 - u16 cmd; - u16 reason_code; - } mlme; - struct { - u8 ssid_len; - u8 ssid[32]; - } scan_req; - } u; -}; - -#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) -#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) - -#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 -#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 -#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 -#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 -#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 -#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 - -#endif /* HOSTAP_COMMON_H */ diff --git a/contrib/hostapd/hostapd/hostapd.c b/contrib/hostapd/hostapd/hostapd.c deleted file mode 100644 index 3fbd3d0b03..0000000000 --- a/contrib/hostapd/hostapd/hostapd.c +++ /dev/null @@ -1,2039 +0,0 @@ -/* - * hostapd / Initialization and configuration - * Copyright (c) 2002-2009, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#ifndef CONFIG_NATIVE_WINDOWS -#include -#endif /* CONFIG_NATIVE_WINDOWS */ - -#include "eloop.h" -#include "hostapd.h" -#include "ieee802_1x.h" -#include "ieee802_11.h" -#include "beacon.h" -#include "hw_features.h" -#include "accounting.h" -#include "eapol_sm.h" -#include "iapp.h" -#include "ap.h" -#include "ieee802_11_auth.h" -#include "ap_list.h" -#include "sta_info.h" -#include "driver.h" -#include "radius/radius_client.h" -#include "radius/radius_server.h" -#include "wpa.h" -#include "preauth.h" -#include "wme.h" -#include "vlan_init.h" -#include "ctrl_iface.h" -#include "tls.h" -#include "eap_server/eap_sim_db.h" -#include "eap_server/eap.h" -#include "eap_server/tncs.h" -#include "version.h" -#include "l2_packet/l2_packet.h" -#include "wps_hostapd.h" - - -static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, - size_t identity_len, int phase2, - struct eap_user *user); -static int hostapd_flush_old_stations(struct hostapd_data *hapd); -static int hostapd_setup_wpa(struct hostapd_data *hapd); -static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); - -struct hapd_interfaces { - size_t count; - struct hostapd_iface **iface; -}; - -unsigned char rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; - - -extern int wpa_debug_level; -extern int wpa_debug_show_keys; -extern int wpa_debug_timestamp; - - -static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, - int level, const char *txt, size_t len) -{ - struct hostapd_data *hapd = ctx; - char *format, *module_str; - int maxlen; - int conf_syslog_level, conf_stdout_level; - unsigned int conf_syslog, conf_stdout; - - maxlen = len + 100; - format = os_malloc(maxlen); - if (!format) - return; - - if (hapd && hapd->conf) { - conf_syslog_level = hapd->conf->logger_syslog_level; - conf_stdout_level = hapd->conf->logger_stdout_level; - conf_syslog = hapd->conf->logger_syslog; - conf_stdout = hapd->conf->logger_stdout; - } else { - conf_syslog_level = conf_stdout_level = 0; - conf_syslog = conf_stdout = (unsigned int) -1; - } - - switch (module) { - case HOSTAPD_MODULE_IEEE80211: - module_str = "IEEE 802.11"; - break; - case HOSTAPD_MODULE_IEEE8021X: - module_str = "IEEE 802.1X"; - break; - case HOSTAPD_MODULE_RADIUS: - module_str = "RADIUS"; - break; - case HOSTAPD_MODULE_WPA: - module_str = "WPA"; - break; - case HOSTAPD_MODULE_DRIVER: - module_str = "DRIVER"; - break; - case HOSTAPD_MODULE_IAPP: - module_str = "IAPP"; - break; - case HOSTAPD_MODULE_MLME: - module_str = "MLME"; - break; - default: - module_str = NULL; - break; - } - - if (hapd && hapd->conf && addr) - os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s", - hapd->conf->iface, MAC2STR(addr), - module_str ? " " : "", module_str, txt); - else if (hapd && hapd->conf) - os_snprintf(format, maxlen, "%s:%s%s %s", - hapd->conf->iface, module_str ? " " : "", - module_str, txt); - else if (addr) - os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", - MAC2STR(addr), module_str ? " " : "", - module_str, txt); - else - os_snprintf(format, maxlen, "%s%s%s", - module_str, module_str ? ": " : "", txt); - - if ((conf_stdout & module) && level >= conf_stdout_level) { - wpa_debug_print_timestamp(); - printf("%s\n", format); - } - -#ifndef CONFIG_NATIVE_WINDOWS - if ((conf_syslog & module) && level >= conf_syslog_level) { - int priority; - switch (level) { - case HOSTAPD_LEVEL_DEBUG_VERBOSE: - case HOSTAPD_LEVEL_DEBUG: - priority = LOG_DEBUG; - break; - case HOSTAPD_LEVEL_INFO: - priority = LOG_INFO; - break; - case HOSTAPD_LEVEL_NOTICE: - priority = LOG_NOTICE; - break; - case HOSTAPD_LEVEL_WARNING: - priority = LOG_WARNING; - break; - default: - priority = LOG_INFO; - break; - } - syslog(priority, "%s", format); - } -#endif /* CONFIG_NATIVE_WINDOWS */ - - os_free(format); -} - - -static void hostapd_deauth_all_stas(struct hostapd_data *hapd) -{ - u8 addr[ETH_ALEN]; - - /* New Prism2.5/3 STA firmware versions seem to have issues with this - * broadcast deauth frame. This gets the firmware in odd state where - * nothing works correctly, so let's skip sending this for the hostap - * driver. */ - - if (hapd->driver && os_strcmp(hapd->driver->name, "hostap") != 0) { - os_memset(addr, 0xff, ETH_ALEN); - hostapd_sta_deauth(hapd, addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); - } -} - - -/** - * hostapd_prune_associations - Remove extraneous associations - * @hapd: Pointer to BSS data for the most recent association - * @sta: Pointer to the associated STA data - * - * This function looks through all radios and BSS's for previous - * (stale) associations of STA. If any are found they are removed. - */ -static void hostapd_prune_associations(struct hostapd_data *hapd, - struct sta_info *sta) -{ - struct sta_info *osta; - struct hostapd_data *ohapd; - size_t i, j; - struct hapd_interfaces *interfaces = eloop_get_user_data(); - - for (i = 0; i < interfaces->count; i++) { - for (j = 0; j < interfaces->iface[i]->num_bss; j++) { - ohapd = interfaces->iface[i]->bss[j]; - if (ohapd == hapd) - continue; - osta = ap_get_sta(ohapd, sta->addr); - if (!osta) - continue; - - ap_sta_disassociate(ohapd, osta, - WLAN_REASON_UNSPECIFIED); - } - } -} - - -/** - * hostapd_new_assoc_sta - Notify that a new station associated with the AP - * @hapd: Pointer to BSS data - * @sta: Pointer to the associated STA data - * @reassoc: 1 to indicate this was a re-association; 0 = first association - * - * This function will be called whenever a station associates with the AP. It - * can be called for ieee802_11.c for drivers that export MLME to hostapd and - * from driver_*.c for drivers that take care of management frames (IEEE 802.11 - * authentication and association) internally. - */ -void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, - int reassoc) -{ - if (hapd->tkip_countermeasures) { - hostapd_sta_deauth(hapd, sta->addr, - WLAN_REASON_MICHAEL_MIC_FAILURE); - return; - } - - hostapd_prune_associations(hapd, sta); - - /* IEEE 802.11F (IAPP) */ - if (hapd->conf->ieee802_11f) - iapp_new_station(hapd->iapp, sta); - - /* Start accounting here, if IEEE 802.1X and WPA are not used. - * IEEE 802.1X/WPA code will start accounting after the station has - * been authorized. */ - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) - accounting_sta_start(hapd, sta); - - hostapd_wmm_sta_config(hapd, sta); - - /* Start IEEE 802.1X authentication process for new stations */ - ieee802_1x_new_station(hapd, sta); - if (reassoc) { - if (sta->auth_alg != WLAN_AUTH_FT && - !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) - wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); - } else - wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); -} - - -#ifdef EAP_SERVER -static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd, - struct sta_info *sta, void *ctx) -{ - if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0) - return 1; - return 0; -} - - -static void hostapd_sim_db_cb(void *ctx, void *session_ctx) -{ - struct hostapd_data *hapd = ctx; - if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) - radius_server_eap_pending_cb(hapd->radius_srv, session_ctx); -} -#endif /* EAP_SERVER */ - - -/** - * handle_term - SIGINT and SIGTERM handler to terminate hostapd process - */ -static void handle_term(int sig, void *eloop_ctx, void *signal_ctx) -{ - wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig); - eloop_terminate(); -} - - -static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, - struct wpa_auth_config *wconf) -{ - wconf->wpa = conf->wpa; - wconf->wpa_key_mgmt = conf->wpa_key_mgmt; - wconf->wpa_pairwise = conf->wpa_pairwise; - wconf->wpa_group = conf->wpa_group; - wconf->wpa_group_rekey = conf->wpa_group_rekey; - wconf->wpa_strict_rekey = conf->wpa_strict_rekey; - wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; - wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; - wconf->rsn_pairwise = conf->rsn_pairwise; - wconf->rsn_preauth = conf->rsn_preauth; - wconf->eapol_version = conf->eapol_version; - wconf->peerkey = conf->peerkey; - wconf->wmm_enabled = conf->wmm_enabled; - wconf->okc = conf->okc; -#ifdef CONFIG_IEEE80211W - wconf->ieee80211w = conf->ieee80211w; -#endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R - wconf->ssid_len = conf->ssid.ssid_len; - if (wconf->ssid_len > SSID_LEN) - wconf->ssid_len = SSID_LEN; - os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); - os_memcpy(wconf->mobility_domain, conf->mobility_domain, - MOBILITY_DOMAIN_ID_LEN); - if (conf->nas_identifier && - os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) { - wconf->r0_key_holder_len = os_strlen(conf->nas_identifier); - os_memcpy(wconf->r0_key_holder, conf->nas_identifier, - wconf->r0_key_holder_len); - } - os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); - wconf->r0_key_lifetime = conf->r0_key_lifetime; - wconf->reassociation_deadline = conf->reassociation_deadline; - wconf->r0kh_list = conf->r0kh_list; - wconf->r1kh_list = conf->r1kh_list; - wconf->pmk_r1_push = conf->pmk_r1_push; -#endif /* CONFIG_IEEE80211R */ -} - - -int hostapd_reload_config(struct hostapd_iface *iface) -{ - struct hostapd_data *hapd = iface->bss[0]; - struct hostapd_config *newconf, *oldconf; - struct wpa_auth_config wpa_auth_conf; - size_t j; - - newconf = hostapd_config_read(iface->config_fname); - if (newconf == NULL) - return -1; - - /* - * Deauthenticate all stations since the new configuration may not - * allow them to use the BSS anymore. - */ - for (j = 0; j < iface->num_bss; j++) - hostapd_flush_old_stations(iface->bss[j]); - - /* TODO: update dynamic data based on changed configuration - * items (e.g., open/close sockets, etc.) */ - radius_client_flush(hapd->radius, 0); - - oldconf = hapd->iconf; - hapd->iconf = newconf; - hapd->conf = &newconf->bss[0]; - iface->conf = newconf; - - if (hostapd_setup_wpa_psk(hapd->conf)) { - wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " - "after reloading configuration"); - } - - if (hapd->conf->wpa && hapd->wpa_auth == NULL) - hostapd_setup_wpa(hapd); - else if (hapd->conf->wpa) { - hostapd_wpa_auth_conf(&newconf->bss[0], &wpa_auth_conf); - wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); - } else if (hapd->wpa_auth) { - wpa_deinit(hapd->wpa_auth); - hapd->wpa_auth = NULL; - hostapd_set_privacy(hapd, 0); - hostapd_setup_encryption(hapd->conf->iface, hapd); - } - - ieee802_11_set_beacon(hapd); - - 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 */ - } - - if (hapd->conf->ieee802_1x || hapd->conf->wpa) - hostapd_set_ieee8021x(hapd->conf->iface, hapd, 1); - - hostapd_config_free(oldconf); - - wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); - - return 0; -} - - -#ifndef CONFIG_NATIVE_WINDOWS -/** - * handle_reload - SIGHUP handler to reload configuration - */ -static void handle_reload(int sig, void *eloop_ctx, void *signal_ctx) -{ - struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; - size_t i; - - wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration", - sig); - - for (i = 0; i < hapds->count; i++) { - if (hostapd_reload_config(hapds->iface[i]) < 0) { - wpa_printf(MSG_WARNING, "Failed to read new " - "configuration file - continuing with " - "old."); - continue; - } - } -} - - -#ifdef HOSTAPD_DUMP_STATE -/** - * hostapd_dump_state - SIGUSR1 handler to dump hostapd state to a text file - */ -static void hostapd_dump_state(struct hostapd_data *hapd) -{ - FILE *f; - time_t now; - struct sta_info *sta; - int i; - char *buf; - - if (!hapd->conf->dump_log_name) { - wpa_printf(MSG_DEBUG, "Dump file not defined - ignoring dump " - "request"); - return; - } - - wpa_printf(MSG_DEBUG, "Dumping hostapd state to '%s'", - hapd->conf->dump_log_name); - f = fopen(hapd->conf->dump_log_name, "w"); - if (f == NULL) { - wpa_printf(MSG_WARNING, "Could not open dump file '%s' for " - "writing.", hapd->conf->dump_log_name); - return; - } - - time(&now); - fprintf(f, "hostapd state dump - %s", ctime(&now)); - fprintf(f, "num_sta=%d num_sta_non_erp=%d " - "num_sta_no_short_slot_time=%d\n" - "num_sta_no_short_preamble=%d\n", - hapd->num_sta, hapd->iface->num_sta_non_erp, - hapd->iface->num_sta_no_short_slot_time, - hapd->iface->num_sta_no_short_preamble); - - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr)); - - fprintf(f, - " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" - " capability=0x%x listen_interval=%d\n", - sta->aid, - sta->flags, - (sta->flags & WLAN_STA_AUTH ? "[AUTH]" : ""), - (sta->flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), - (sta->flags & WLAN_STA_PS ? "[PS]" : ""), - (sta->flags & WLAN_STA_TIM ? "[TIM]" : ""), - (sta->flags & WLAN_STA_PERM ? "[PERM]" : ""), - (sta->flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : - ""), - (sta->flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" : - ""), - (sta->flags & WLAN_STA_SHORT_PREAMBLE ? - "[SHORT_PREAMBLE]" : ""), - (sta->flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""), - (sta->flags & WLAN_STA_WMM ? "[WMM]" : ""), - (sta->flags & WLAN_STA_MFP ? "[MFP]" : ""), - (sta->flags & WLAN_STA_WPS ? "[WPS]" : ""), - (sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""), - (sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""), - sta->capability, - sta->listen_interval); - - fprintf(f, " supported_rates="); - for (i = 0; i < sta->supported_rates_len; i++) - fprintf(f, "%02x ", sta->supported_rates[i]); - fprintf(f, "\n"); - - fprintf(f, - " timeout_next=%s\n", - (sta->timeout_next == STA_NULLFUNC ? "NULLFUNC POLL" : - (sta->timeout_next == STA_DISASSOC ? "DISASSOC" : - "DEAUTH"))); - - ieee802_1x_dump_state(f, " ", sta); - } - - buf = os_malloc(4096); - if (buf) { - int count = radius_client_get_mib(hapd->radius, buf, 4096); - if (count < 0) - count = 0; - else if (count > 4095) - count = 4095; - buf[count] = '\0'; - fprintf(f, "%s", buf); - - count = radius_server_get_mib(hapd->radius_srv, buf, 4096); - if (count < 0) - count = 0; - else if (count > 4095) - count = 4095; - buf[count] = '\0'; - fprintf(f, "%s", buf); - os_free(buf); - } - fclose(f); -} -#endif /* HOSTAPD_DUMP_STATE */ - - -static void handle_dump_state(int sig, void *eloop_ctx, void *signal_ctx) -{ -#ifdef HOSTAPD_DUMP_STATE - struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; - size_t i, j; - - for (i = 0; i < hapds->count; i++) { - struct hostapd_iface *hapd_iface = hapds->iface[i]; - for (j = 0; j < hapd_iface->num_bss; j++) - hostapd_dump_state(hapd_iface->bss[j]); - } -#endif /* HOSTAPD_DUMP_STATE */ -} -#endif /* CONFIG_NATIVE_WINDOWS */ - -static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, - char *ifname) -{ - int i; - - for (i = 0; i < NUM_WEP_KEYS; i++) { - if (hostapd_set_encryption(ifname, hapd, "none", NULL, i, NULL, - 0, i == 0 ? 1 : 0)) { - wpa_printf(MSG_DEBUG, "Failed to clear default " - "encryption keys (ifname=%s keyidx=%d)", - ifname, i); - } - } -#ifdef CONFIG_IEEE80211W - if (hapd->conf->ieee80211w) { - for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) { - if (hostapd_set_encryption(ifname, hapd, "none", NULL, - i, NULL, 0, - i == 0 ? 1 : 0)) { - wpa_printf(MSG_DEBUG, "Failed to clear " - "default mgmt encryption keys " - "(ifname=%s keyidx=%d)", ifname, i); - } - } - } -#endif /* CONFIG_IEEE80211W */ -} - - -static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd) -{ - hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface); - return 0; -} - - -static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) -{ - int errors = 0, idx; - struct hostapd_ssid *ssid = &hapd->conf->ssid; - - idx = ssid->wep.idx; - if (ssid->wep.default_len && - hostapd_set_encryption(hapd->conf->iface, - hapd, "WEP", NULL, idx, - ssid->wep.key[idx], - ssid->wep.len[idx], - idx == ssid->wep.idx)) { - wpa_printf(MSG_WARNING, "Could not set WEP encryption."); - errors++; - } - - if (ssid->dyn_vlan_keys) { - size_t i; - for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { - const char *ifname; - struct hostapd_wep_keys *key = ssid->dyn_vlan_keys[i]; - if (key == NULL) - continue; - ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, - i); - if (ifname == NULL) - continue; - - idx = key->idx; - if (hostapd_set_encryption(ifname, hapd, "WEP", NULL, - idx, key->key[idx], - key->len[idx], - idx == key->idx)) { - wpa_printf(MSG_WARNING, "Could not set " - "dynamic VLAN WEP encryption."); - errors++; - } - } - } - - return errors; -} - -/** - * hostapd_cleanup - Per-BSS cleanup (deinitialization) - * @hapd: Pointer to BSS data - * - * This function is used to free all per-BSS data structures and resources. - * This gets called in a loop for each BSS between calls to - * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface - * is deinitialized. Most of the modules that are initialized in - * hostapd_setup_bss() are deinitialized here. - */ -static void hostapd_cleanup(struct hostapd_data *hapd) -{ - hostapd_ctrl_iface_deinit(hapd); - - os_free(hapd->default_wep_key); - hapd->default_wep_key = NULL; - iapp_deinit(hapd->iapp); - hapd->iapp = NULL; - accounting_deinit(hapd); - rsn_preauth_iface_deinit(hapd); - if (hapd->wpa_auth) { - wpa_deinit(hapd->wpa_auth); - hapd->wpa_auth = NULL; - - if (hostapd_set_privacy(hapd, 0)) { - wpa_printf(MSG_DEBUG, "Could not disable " - "PrivacyInvoked for interface %s", - hapd->conf->iface); - } - - if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { - wpa_printf(MSG_DEBUG, "Could not remove generic " - "information element from interface %s", - hapd->conf->iface); - } - } - ieee802_1x_deinit(hapd); - vlan_deinit(hapd); - hostapd_acl_deinit(hapd); - radius_client_deinit(hapd->radius); - hapd->radius = NULL; - radius_server_deinit(hapd->radius_srv); - hapd->radius_srv = NULL; - -#ifdef CONFIG_IEEE80211R - l2_packet_deinit(hapd->l2); -#endif /* CONFIG_IEEE80211R */ - - hostapd_deinit_wps(hapd); - - hostapd_wireless_event_deinit(hapd); - -#ifdef EAP_TLS_FUNCS - if (hapd->ssl_ctx) { - tls_deinit(hapd->ssl_ctx); - hapd->ssl_ctx = NULL; - } -#endif /* EAP_TLS_FUNCS */ - -#ifdef EAP_SERVER - if (hapd->eap_sim_db_priv) { - eap_sim_db_deinit(hapd->eap_sim_db_priv); - hapd->eap_sim_db_priv = NULL; - } -#endif /* EAP_SERVER */ - - if (hapd->interface_added && - hostapd_bss_remove(hapd, hapd->conf->iface)) { - wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s", - hapd->conf->iface); - } -} - - -/** - * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup - * @iface: Pointer to interface data - * - * This function is called before per-BSS data structures are deinitialized - * with hostapd_cleanup(). - */ -static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface) -{ -} - - -/** - * hostapd_cleanup_iface - Complete per-interface cleanup - * @iface: Pointer to interface data - * - * This function is called after per-BSS data structures are deinitialized - * with hostapd_cleanup(). - */ -static void hostapd_cleanup_iface(struct hostapd_iface *iface) -{ - hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); - iface->hw_features = NULL; - os_free(iface->current_rates); - iface->current_rates = NULL; - ap_list_deinit(iface); - hostapd_config_free(iface->conf); - iface->conf = NULL; - - os_free(iface->config_fname); - os_free(iface->bss); - os_free(iface); -} - - -static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) -{ - int i; - - hostapd_broadcast_wep_set(hapd); - - if (hapd->conf->ssid.wep.default_len) - return 0; - - for (i = 0; i < 4; i++) { - if (hapd->conf->ssid.wep.key[i] && - hostapd_set_encryption(iface, hapd, "WEP", NULL, - i, hapd->conf->ssid.wep.key[i], - hapd->conf->ssid.wep.len[i], - i == hapd->conf->ssid.wep.idx)) { - wpa_printf(MSG_WARNING, "Could not set WEP " - "encryption."); - return -1; - } - if (hapd->conf->ssid.wep.key[i] && - i == hapd->conf->ssid.wep.idx) - hostapd_set_privacy(hapd, 1); - } - - return 0; -} - - -static int hostapd_flush_old_stations(struct hostapd_data *hapd) -{ - int ret = 0; - - if (hostapd_drv_none(hapd)) - return 0; - - wpa_printf(MSG_DEBUG, "Flushing old station entries"); - if (hostapd_flush(hapd)) { - wpa_printf(MSG_WARNING, "Could not connect to kernel driver."); - ret = -1; - } - wpa_printf(MSG_DEBUG, "Deauthenticate all stations"); - hostapd_deauth_all_stas(hapd); - - return ret; -} - - -static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr, - logger_level level, const char *txt) -{ - struct hostapd_data *hapd = ctx; - int hlevel; - - switch (level) { - case LOGGER_WARNING: - hlevel = HOSTAPD_LEVEL_WARNING; - break; - case LOGGER_INFO: - hlevel = HOSTAPD_LEVEL_INFO; - break; - case LOGGER_DEBUG: - default: - hlevel = HOSTAPD_LEVEL_DEBUG; - break; - } - - hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt); -} - - -static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr, - u16 reason) -{ - struct hostapd_data *hapd = ctx; - struct sta_info *sta; - - wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: " - "STA " MACSTR " reason %d", - __func__, MAC2STR(addr), reason); - - sta = ap_get_sta(hapd, addr); - hostapd_sta_deauth(hapd, addr, reason); - if (sta == NULL) - return; - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta); - sta->timeout_next = STA_REMOVE; -} - - -static void hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) -{ - struct hostapd_data *hapd = ctx; - ieee80211_michael_mic_failure(hapd, addr, 0); -} - - -static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, - wpa_eapol_variable var, int value) -{ - struct hostapd_data *hapd = ctx; - struct sta_info *sta = ap_get_sta(hapd, addr); - if (sta == NULL) - return; - switch (var) { - case WPA_EAPOL_portEnabled: - ieee802_1x_notify_port_enabled(sta->eapol_sm, value); - break; - case WPA_EAPOL_portValid: - ieee802_1x_notify_port_valid(sta->eapol_sm, value); - break; - case WPA_EAPOL_authorized: - ieee802_1x_set_sta_authorized(hapd, sta, value); - break; - case WPA_EAPOL_portControl_Auto: - if (sta->eapol_sm) - sta->eapol_sm->portControl = Auto; - break; - case WPA_EAPOL_keyRun: - if (sta->eapol_sm) - sta->eapol_sm->keyRun = value ? TRUE : FALSE; - break; - case WPA_EAPOL_keyAvailable: - if (sta->eapol_sm) - sta->eapol_sm->eap_if->eapKeyAvailable = - value ? TRUE : FALSE; - break; - case WPA_EAPOL_keyDone: - if (sta->eapol_sm) - sta->eapol_sm->keyDone = value ? TRUE : FALSE; - break; - case WPA_EAPOL_inc_EapolFramesTx: - if (sta->eapol_sm) - sta->eapol_sm->dot1xAuthEapolFramesTx++; - break; - } -} - - -static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, - wpa_eapol_variable var) -{ - struct hostapd_data *hapd = ctx; - struct sta_info *sta = ap_get_sta(hapd, addr); - if (sta == NULL || sta->eapol_sm == NULL) - return -1; - switch (var) { - case WPA_EAPOL_keyRun: - return sta->eapol_sm->keyRun; - case WPA_EAPOL_keyAvailable: - return sta->eapol_sm->eap_if->eapKeyAvailable; - default: - return -1; - } -} - - -static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, - const u8 *prev_psk) -{ - struct hostapd_data *hapd = ctx; - return hostapd_get_psk(hapd->conf, addr, prev_psk); -} - - -static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk, - size_t *len) -{ - struct hostapd_data *hapd = ctx; - const u8 *key; - size_t keylen; - struct sta_info *sta; - - sta = ap_get_sta(hapd, addr); - if (sta == NULL) - return -1; - - key = ieee802_1x_get_key(sta->eapol_sm, &keylen); - if (key == NULL) - return -1; - - if (keylen > *len) - keylen = *len; - os_memcpy(msk, key, keylen); - *len = keylen; - - return 0; -} - - -static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, const char *alg, - const u8 *addr, int idx, u8 *key, - size_t key_len) -{ - struct hostapd_data *hapd = ctx; - const char *ifname = hapd->conf->iface; - - if (vlan_id > 0) { - ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); - if (ifname == NULL) - return -1; - } - - return hostapd_set_encryption(ifname, hapd, alg, addr, idx, - key, key_len, 1); -} - - -static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx, - u8 *seq) -{ - struct hostapd_data *hapd = ctx; - return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq); -} - - -static int hostapd_wpa_auth_get_seqnum_igtk(void *ctx, const u8 *addr, int idx, - u8 *seq) -{ - struct hostapd_data *hapd = ctx; - return hostapd_get_seqnum_igtk(hapd->conf->iface, hapd, addr, idx, - seq); -} - - -static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, - const u8 *data, size_t data_len, - int encrypt) -{ - struct hostapd_data *hapd = ctx; - return hostapd_send_eapol(hapd, addr, data, data_len, encrypt); -} - - -static int hostapd_wpa_auth_for_each_sta( - void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), - void *cb_ctx) -{ - struct hostapd_data *hapd = ctx; - struct sta_info *sta; - - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx)) - return 1; - } - return 0; -} - - -static int hostapd_wpa_auth_for_each_auth( - void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx), - void *cb_ctx) -{ - struct hostapd_data *ohapd; - size_t i, j; - struct hapd_interfaces *interfaces = eloop_get_user_data(); - - for (i = 0; i < interfaces->count; i++) { - for (j = 0; j < interfaces->iface[i]->num_bss; j++) { - ohapd = interfaces->iface[i]->bss[j]; - if (cb(ohapd->wpa_auth, cb_ctx)) - return 1; - } - } - - return 0; -} - - -static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, - const u8 *data, size_t data_len) -{ - struct hostapd_data *hapd = ctx; - - if (hapd->driver && hapd->driver->send_ether) - return hapd->driver->send_ether(hapd->drv_priv, dst, - hapd->own_addr, proto, - data, data_len); - if (hapd->l2 == NULL) - return -1; - return l2_packet_send(hapd->l2, dst, proto, data, data_len); -} - - -#ifdef CONFIG_IEEE80211R - -static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, - const u8 *data, size_t data_len) -{ - struct hostapd_data *hapd = ctx; - int res; - struct ieee80211_mgmt *m; - size_t mlen; - struct sta_info *sta; - - sta = ap_get_sta(hapd, dst); - if (sta == NULL || sta->wpa_sm == NULL) - return -1; - - m = os_zalloc(sizeof(*m) + data_len); - if (m == NULL) - return -1; - mlen = ((u8 *) &m->u - (u8 *) m) + data_len; - m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_ACTION); - os_memcpy(m->da, dst, ETH_ALEN); - os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); - os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); - os_memcpy(&m->u, data, data_len); - - res = hostapd_send_mgmt_frame(hapd, (u8 *) m, mlen, 0); - os_free(m); - return res; -} - - -static struct wpa_state_machine * -hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) -{ - struct hostapd_data *hapd = ctx; - struct sta_info *sta; - - sta = ap_sta_add(hapd, sta_addr); - if (sta == NULL) - return NULL; - if (sta->wpa_sm) - return sta->wpa_sm; - - sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); - if (sta->wpa_sm == NULL) { - ap_free_sta(hapd, sta); - return NULL; - } - sta->auth_alg = WLAN_AUTH_FT; - - return sta->wpa_sm; -} - - -static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ - struct hostapd_data *hapd = ctx; - wpa_ft_rrb_rx(hapd->wpa_auth, src_addr, buf, len); -} - -#endif /* CONFIG_IEEE80211R */ - - -/** - * hostapd_validate_bssid_configuration - Validate BSSID configuration - * @iface: Pointer to interface data - * Returns: 0 on success, -1 on failure - * - * This function is used to validate that the configured BSSIDs are valid. - */ -static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) -{ - u8 mask[ETH_ALEN] = { 0 }; - struct hostapd_data *hapd = iface->bss[0]; - unsigned int i = iface->conf->num_bss, bits = 0, j; - int res; - - if (hostapd_drv_none(hapd)) - return 0; - - /* Generate BSSID mask that is large enough to cover the BSSIDs. */ - - /* Determine the bits necessary to cover the number of BSSIDs. */ - for (i--; i; i >>= 1) - bits++; - - /* Determine the bits necessary to any configured BSSIDs, - if they are higher than the number of BSSIDs. */ - for (j = 0; j < iface->conf->num_bss; j++) { - if (hostapd_mac_comp_empty(iface->conf->bss[j].bssid) == 0) - continue; - - for (i = 0; i < ETH_ALEN; i++) { - mask[i] |= - iface->conf->bss[j].bssid[i] ^ - hapd->own_addr[i]; - } - } - - for (i = 0; i < ETH_ALEN && mask[i] == 0; i++) - ; - j = 0; - if (i < ETH_ALEN) { - j = (5 - i) * 8; - - while (mask[i] != 0) { - mask[i] >>= 1; - j++; - } - } - - if (bits < j) - bits = j; - - if (bits > 40) - return -1; - - os_memset(mask, 0xff, ETH_ALEN); - j = bits / 8; - for (i = 5; i > 5 - j; i--) - mask[i] = 0; - j = bits % 8; - while (j--) - mask[i] <<= 1; - - wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", - (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits); - - res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask); - if (res == 0) - return 0; - - if (res < 0) { - wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask " - MACSTR " for start address " MACSTR ".", - MAC2STR(mask), MAC2STR(hapd->own_addr)); - return -1; - } - - for (i = 0; i < ETH_ALEN; i++) { - if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) { - wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR - " for start address " MACSTR ".", - MAC2STR(mask), MAC2STR(hapd->own_addr)); - wpa_printf(MSG_ERROR, "Start address must be the " - "first address in the block (i.e., addr " - "AND mask == addr)."); - return -1; - } - } - - return 0; -} - - -static int mac_in_conf(struct hostapd_config *conf, const void *a) -{ - size_t i; - - for (i = 0; i < conf->num_bss; i++) { - if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) { - return 1; - } - } - - return 0; -} - - -static int hostapd_setup_wpa(struct hostapd_data *hapd) -{ - struct wpa_auth_config _conf; - struct wpa_auth_callbacks cb; - const u8 *wpa_ie; - size_t wpa_ie_len; - - hostapd_wpa_auth_conf(hapd->conf, &_conf); - os_memset(&cb, 0, sizeof(cb)); - cb.ctx = hapd; - cb.logger = hostapd_wpa_auth_logger; - cb.disconnect = hostapd_wpa_auth_disconnect; - cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; - cb.set_eapol = hostapd_wpa_auth_set_eapol; - cb.get_eapol = hostapd_wpa_auth_get_eapol; - cb.get_psk = hostapd_wpa_auth_get_psk; - cb.get_msk = hostapd_wpa_auth_get_msk; - cb.set_key = hostapd_wpa_auth_set_key; - cb.get_seqnum = hostapd_wpa_auth_get_seqnum; - cb.get_seqnum_igtk = hostapd_wpa_auth_get_seqnum_igtk; - cb.send_eapol = hostapd_wpa_auth_send_eapol; - cb.for_each_sta = hostapd_wpa_auth_for_each_sta; - cb.for_each_auth = hostapd_wpa_auth_for_each_auth; - cb.send_ether = hostapd_wpa_auth_send_ether; -#ifdef CONFIG_IEEE80211R - cb.send_ft_action = hostapd_wpa_auth_send_ft_action; - cb.add_sta = hostapd_wpa_auth_add_sta; -#endif /* CONFIG_IEEE80211R */ - hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); - if (hapd->wpa_auth == NULL) { - wpa_printf(MSG_ERROR, "WPA initialization failed."); - return -1; - } - - if (hostapd_set_privacy(hapd, 1)) { - wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked " - "for interface %s", hapd->conf->iface); - return -1; - } - - wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len); - if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) { - wpa_printf(MSG_ERROR, "Failed to configure WPA IE for " - "the kernel driver."); - return -1; - } - - if (rsn_preauth_iface_init(hapd)) { - wpa_printf(MSG_ERROR, "Initialization of RSN " - "pre-authentication failed."); - return -1; - } - - return 0; - -} - - -static int hostapd_setup_radius_srv(struct hostapd_data *hapd, - struct hostapd_bss_config *conf) -{ - struct radius_server_conf srv; - os_memset(&srv, 0, sizeof(srv)); - srv.client_file = conf->radius_server_clients; - srv.auth_port = conf->radius_server_auth_port; - srv.conf_ctx = conf; - srv.eap_sim_db_priv = hapd->eap_sim_db_priv; - srv.ssl_ctx = hapd->ssl_ctx; - srv.pac_opaque_encr_key = conf->pac_opaque_encr_key; - srv.eap_fast_a_id = conf->eap_fast_a_id; - srv.eap_fast_a_id_len = conf->eap_fast_a_id_len; - srv.eap_fast_a_id_info = conf->eap_fast_a_id_info; - srv.eap_fast_prov = conf->eap_fast_prov; - srv.pac_key_lifetime = conf->pac_key_lifetime; - srv.pac_key_refresh_time = conf->pac_key_refresh_time; - srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; - srv.tnc = conf->tnc; - srv.wps = hapd->wps; - srv.ipv6 = conf->radius_server_ipv6; - srv.get_eap_user = hostapd_radius_get_eap_user; - srv.eap_req_id_text = conf->eap_req_id_text; - srv.eap_req_id_text_len = conf->eap_req_id_text_len; - - hapd->radius_srv = radius_server_init(&srv); - if (hapd->radius_srv == NULL) { - wpa_printf(MSG_ERROR, "RADIUS server initialization failed."); - return -1; - } - - return 0; -} - - -/** - * hostapd_setup_bss - Per-BSS setup (initialization) - * @hapd: Pointer to BSS data - * @first: Whether this BSS is the first BSS of an interface - * - * This function is used to initialize all per-BSS data structures and - * resources. This gets called in a loop for each BSS when an interface is - * initialized. Most of the modules that are initialized here will be - * deinitialized in hostapd_cleanup(). - */ -static int hostapd_setup_bss(struct hostapd_data *hapd, int first) -{ - struct hostapd_bss_config *conf = hapd->conf; - u8 ssid[HOSTAPD_MAX_SSID_LEN + 1]; - int ssid_len, set_ssid; - - if (!first) { - if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) { - /* Allocate the next available BSSID. */ - do { - inc_byte_array(hapd->own_addr, ETH_ALEN); - } while (mac_in_conf(hapd->iconf, hapd->own_addr)); - } else { - /* Allocate the configured BSSID. */ - os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN); - - if (hostapd_mac_comp(hapd->own_addr, - hapd->iface->bss[0]->own_addr) == - 0) { - wpa_printf(MSG_ERROR, "BSS '%s' may not have " - "BSSID set to the MAC address of " - "the radio", hapd->conf->iface); - return -1; - } - } - - hapd->interface_added = 1; - if (hostapd_bss_add(hapd->iface->bss[0], hapd->conf->iface, - hapd->own_addr)) { - wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" - MACSTR ")", MAC2STR(hapd->own_addr)); - return -1; - } - } - - 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 flag for whether SSID is broadcast in beacons */ - if (hostapd_set_broadcast_ssid(hapd, - !!hapd->conf->ignore_broadcast_ssid)) { - wpa_printf(MSG_ERROR, "Could not set broadcast SSID flag for " - "kernel driver"); - return -1; - } - - if (hostapd_set_dtim_period(hapd, hapd->conf->dtim_period)) { - wpa_printf(MSG_ERROR, "Could not set DTIM period for kernel " - "driver"); - return -1; - } - - /* Set SSID for the kernel driver (to be used in beacon and probe - * response frames) */ - if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid, - conf->ssid.ssid_len)) { - wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); - return -1; - } - - if (wpa_debug_level == MSG_MSGDUMP) - conf->radius->msg_dumps = 1; - hapd->radius = radius_client_init(hapd, conf->radius); - if (hapd->radius == NULL) { - wpa_printf(MSG_ERROR, "RADIUS client initialization failed."); - return -1; - } - - if (hostapd_acl_init(hapd)) { - wpa_printf(MSG_ERROR, "ACL initialization failed."); - return -1; - } - if (hostapd_init_wps(hapd, conf)) - return -1; - - if (ieee802_1x_init(hapd)) { - wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed."); - return -1; - } - - if (hapd->conf->wpa && hostapd_setup_wpa(hapd)) - return -1; - - if (accounting_init(hapd)) { - wpa_printf(MSG_ERROR, "Accounting initialization failed."); - return -1; - } - - if (hapd->conf->ieee802_11f && - (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) { - wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization " - "failed."); - return -1; - } - - if (hostapd_ctrl_iface_init(hapd)) { - wpa_printf(MSG_ERROR, "Failed to setup control interface"); - return -1; - } - - if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { - wpa_printf(MSG_ERROR, "VLAN initialization failed."); - return -1; - } - -#ifdef CONFIG_IEEE80211R - if (!hostapd_drv_none(hapd)) { - hapd->l2 = l2_packet_init(hapd->conf->iface, NULL, ETH_P_RRB, - hostapd_rrb_receive, hapd, 0); - if (hapd->l2 == NULL && - (hapd->driver == NULL || - hapd->driver->send_ether == NULL)) { - wpa_printf(MSG_ERROR, "Failed to open l2_packet " - "interface"); - return -1; - } - } -#endif /* CONFIG_IEEE80211R */ - - ieee802_11_set_beacon(hapd); - - if (conf->radius_server_clients && - hostapd_setup_radius_srv(hapd, conf)) - return -1; - - return 0; -} - - -static void hostapd_tx_queue_params(struct hostapd_iface *iface) -{ - struct hostapd_data *hapd = iface->bss[0]; - int i; - struct hostapd_tx_queue_params *p; - - for (i = 0; i < NUM_TX_QUEUES; i++) { - p = &iface->conf->tx_queue[i]; - - if (!p->configured) - continue; - - if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin, - p->cwmax, p->burst)) { - wpa_printf(MSG_DEBUG, "Failed to set TX queue " - "parameters for queue %d.", i); - /* Continue anyway */ - } - } -} - - -static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, - size_t identity_len, int phase2, - struct eap_user *user) -{ - const struct hostapd_eap_user *eap_user; - int i, count; - - eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2); - if (eap_user == NULL) - return -1; - - if (user == NULL) - return 0; - - os_memset(user, 0, sizeof(*user)); - count = EAP_USER_MAX_METHODS; - if (count > EAP_MAX_METHODS) - count = EAP_MAX_METHODS; - for (i = 0; i < count; i++) { - user->methods[i].vendor = eap_user->methods[i].vendor; - user->methods[i].method = eap_user->methods[i].method; - } - - if (eap_user->password) { - user->password = os_malloc(eap_user->password_len); - if (user->password == NULL) - return -1; - os_memcpy(user->password, eap_user->password, - eap_user->password_len); - user->password_len = eap_user->password_len; - user->password_hash = eap_user->password_hash; - } - user->force_version = eap_user->force_version; - user->ttls_auth = eap_user->ttls_auth; - - return 0; -} - - -static int setup_interface(struct hostapd_iface *iface) -{ - struct hostapd_data *hapd = iface->bss[0]; - struct hostapd_bss_config *conf = hapd->conf; - size_t i; - char country[4]; - u8 *b = conf->bssid; - int freq; - size_t j; - u8 *prev_addr; - - /* - * Initialize the driver interface and make sure that all BSSes get - * configured with a pointer to this driver interface. - */ - if (b[0] | b[1] | b[2] | b[3] | b[4] | b[5]) { - hapd->drv_priv = hostapd_driver_init_bssid(hapd, b); - } else { - hapd->drv_priv = hostapd_driver_init(hapd); - } - - if (hapd->drv_priv == NULL) { - wpa_printf(MSG_ERROR, "%s driver initialization failed.", - hapd->driver ? hapd->driver->name : "Unknown"); - hapd->driver = NULL; - return -1; - } - for (i = 0; i < iface->num_bss; i++) { - iface->bss[i]->driver = hapd->driver; - iface->bss[i]->drv_priv = hapd->drv_priv; - } - - if (hostapd_validate_bssid_configuration(iface)) - return -1; - -#ifdef CONFIG_IEEE80211N - SET_2BIT_LE16(&iface->ht_op_mode, - HT_INFO_OPERATION_MODE_OP_MODE_OFFSET, - OP_MODE_PURE); -#endif /* CONFIG_IEEE80211N */ - - if (hapd->iconf->country[0] && hapd->iconf->country[1]) { - os_memcpy(country, hapd->iconf->country, 3); - country[3] = '\0'; - if (hostapd_set_country(hapd, country) < 0) { - wpa_printf(MSG_ERROR, "Failed to set country code"); - return -1; - } - } - - if (hapd->iconf->ieee80211d && - hostapd_set_ieee80211d(hapd, 1) < 0) { - wpa_printf(MSG_ERROR, "Failed to set ieee80211d (%d)", - hapd->iconf->ieee80211d); - return -1; - } - - if (hapd->iconf->bridge_packets != INTERNAL_BRIDGE_DO_NOT_CONTROL && - hostapd_set_internal_bridge(hapd, hapd->iconf->bridge_packets)) { - wpa_printf(MSG_ERROR, "Failed to set bridge_packets for " - "kernel driver"); - return -1; - } - - /* TODO: merge with hostapd_driver_init() ? */ - if (hostapd_wireless_event_init(hapd) < 0) - return -1; - - if (hostapd_get_hw_features(iface)) { - /* Not all drivers support this yet, so continue without hw - * feature data. */ - } else { - int ret = hostapd_select_hw_mode(iface); - if (ret < 0) { - wpa_printf(MSG_ERROR, "Could not select hw_mode and " - "channel. (%d)", ret); - return -1; - } - } - - if (hapd->iconf->channel) { - freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel); - wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d " - "Frequency: %d MHz", - hostapd_hw_mode_txt(hapd->iconf->hw_mode), - hapd->iconf->channel, freq); - - if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, freq, - hapd->iconf->ieee80211n, - hapd->iconf->secondary_channel)) { - wpa_printf(MSG_ERROR, "Could not set channel for " - "kernel driver"); - return -1; - } - } - - hostapd_set_beacon_int(hapd, hapd->iconf->beacon_int); - - if (hapd->iconf->rts_threshold > -1 && - hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { - wpa_printf(MSG_ERROR, "Could not set RTS threshold for " - "kernel driver"); - return -1; - } - - if (hapd->iconf->fragm_threshold > -1 && - hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { - wpa_printf(MSG_ERROR, "Could not set fragmentation threshold " - "for kernel driver"); - return -1; - } - - prev_addr = hapd->own_addr; - - for (j = 0; j < iface->num_bss; j++) { - hapd = iface->bss[j]; - if (j) - os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN); - if (hostapd_setup_bss(hapd, j == 0)) - return -1; - if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) - prev_addr = hapd->own_addr; - } - - hostapd_tx_queue_params(iface); - - ap_list_init(iface); - - if (hostapd_driver_commit(hapd) < 0) { - wpa_printf(MSG_ERROR, "%s: Failed to commit driver " - "configuration", __func__); - return -1; - } - - return 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. - */ -static int hostapd_setup_interface(struct hostapd_iface *iface) -{ - int ret; - - ret = setup_interface(iface); - if (ret) { - wpa_printf(MSG_DEBUG, "%s: Unable to setup interface.", - iface->bss[0]->conf->iface); - eloop_terminate(); - return -1; - } else if (!hostapd_drv_none(iface->bss[0])) { - wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", - iface->bss[0]->conf->iface); - } - - return 0; -} - - -static void show_version(void) -{ - fprintf(stderr, - "hostapd v" VERSION_STR "\n" - "User space daemon for IEEE 802.11 AP management,\n" - "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" - "Copyright (c) 2002-2009, Jouni Malinen " - "and contributors\n"); -} - - -static void usage(void) -{ - show_version(); - fprintf(stderr, - "\n" - "usage: hostapd [-hdBKtv] [-P ] " - "\n" - "\n" - "options:\n" - " -h show this usage\n" - " -d show more debug messages (-dd for even more)\n" - " -B run daemon in the background\n" - " -P PID file\n" - " -K include key data in debug messages\n" - " -t include timestamps in some debug messages\n" - " -v show hostapd version\n"); - - exit(1); -} - - -/** - * hostapd_alloc_bss_data - Allocate and initialize per-BSS data - * @hapd_iface: Pointer to interface data - * @conf: Pointer to per-interface configuration - * @bss: Pointer to per-BSS configuration for this BSS - * Returns: Pointer to allocated BSS data - * - * This function is used to allocate per-BSS data structure. This data will be - * freed after hostapd_cleanup() is called for it during interface - * deinitialization. - */ -static struct hostapd_data * -hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, - struct hostapd_config *conf, - struct hostapd_bss_config *bss) -{ - struct hostapd_data *hapd; - - hapd = os_zalloc(sizeof(*hapd)); - if (hapd == NULL) - return NULL; - - hapd->iconf = conf; - hapd->conf = bss; - hapd->iface = hapd_iface; - - if (hapd->conf->individual_wep_key_len > 0) { - /* use key0 in individual key and key1 in broadcast key */ - hapd->default_wep_key_idx = 1; - } - -#ifdef EAP_TLS_FUNCS - if (hapd->conf->eap_server && - (hapd->conf->ca_cert || hapd->conf->server_cert || - hapd->conf->dh_file)) { - struct tls_connection_params params; - - hapd->ssl_ctx = tls_init(NULL); - if (hapd->ssl_ctx == NULL) { - wpa_printf(MSG_ERROR, "Failed to initialize TLS"); - goto fail; - } - - os_memset(¶ms, 0, sizeof(params)); - params.ca_cert = hapd->conf->ca_cert; - params.client_cert = hapd->conf->server_cert; - params.private_key = hapd->conf->private_key; - params.private_key_passwd = hapd->conf->private_key_passwd; - params.dh_file = hapd->conf->dh_file; - - if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { - wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); - goto fail; - } - - if (tls_global_set_verify(hapd->ssl_ctx, - hapd->conf->check_crl)) { - wpa_printf(MSG_ERROR, "Failed to enable check_crl"); - goto fail; - } - } -#endif /* EAP_TLS_FUNCS */ - -#ifdef EAP_SERVER - if (hapd->conf->eap_sim_db) { - hapd->eap_sim_db_priv = - eap_sim_db_init(hapd->conf->eap_sim_db, - hostapd_sim_db_cb, hapd); - if (hapd->eap_sim_db_priv == NULL) { - wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM " - "database interface"); - goto fail; - } - } -#endif /* EAP_SERVER */ - - hapd->driver = hapd->iconf->driver; - - return hapd; - -#if defined(EAP_TLS_FUNCS) || defined(EAP_SERVER) -fail: -#endif - /* TODO: cleanup allocated resources(?) */ - os_free(hapd); - return NULL; -} - - -/** - * hostapd_init - Allocate and initialize per-interface data - * @config_file: Path to the configuration file - * Returns: Pointer to the allocated interface data or %NULL on failure - * - * This function is used to allocate main data structures for per-interface - * data. The allocated data buffer will be freed by calling - * hostapd_cleanup_iface(). - */ -static struct hostapd_iface * hostapd_init(const char *config_file) -{ - struct hostapd_iface *hapd_iface = NULL; - struct hostapd_config *conf = NULL; - struct hostapd_data *hapd; - size_t i; - - hapd_iface = os_zalloc(sizeof(*hapd_iface)); - if (hapd_iface == NULL) - goto fail; - - hapd_iface->config_fname = os_strdup(config_file); - if (hapd_iface->config_fname == NULL) - goto fail; - - conf = hostapd_config_read(hapd_iface->config_fname); - if (conf == NULL) - goto fail; - hapd_iface->conf = conf; - - hapd_iface->num_bss = conf->num_bss; - hapd_iface->bss = os_zalloc(conf->num_bss * - sizeof(struct hostapd_data *)); - if (hapd_iface->bss == NULL) - goto fail; - - for (i = 0; i < conf->num_bss; i++) { - hapd = hapd_iface->bss[i] = - hostapd_alloc_bss_data(hapd_iface, conf, - &conf->bss[i]); - if (hapd == NULL) - goto fail; - } - - return hapd_iface; - -fail: - if (conf) - hostapd_config_free(conf); - if (hapd_iface) { - for (i = 0; hapd_iface->bss && i < hapd_iface->num_bss; i++) { - hapd = hapd_iface->bss[i]; - if (hapd && hapd->ssl_ctx) - tls_deinit(hapd->ssl_ctx); - } - - os_free(hapd_iface->config_fname); - os_free(hapd_iface->bss); - os_free(hapd_iface); - } - return NULL; -} - - -int main(int argc, char *argv[]) -{ - struct hapd_interfaces interfaces; - int ret = 1, k; - size_t i, j; - int c, debug = 0, daemonize = 0, tnc = 0; - char *pid_file = NULL; - - hostapd_logger_register_cb(hostapd_logger_cb); - - for (;;) { - c = getopt(argc, argv, "BdhKP:tv"); - if (c < 0) - break; - switch (c) { - case 'h': - usage(); - break; - case 'd': - debug++; - if (wpa_debug_level > 0) - wpa_debug_level--; - break; - case 'B': - daemonize++; - break; - case 'K': - wpa_debug_show_keys++; - break; - case 'P': - os_free(pid_file); - pid_file = os_rel2abs_path(optarg); - break; - case 't': - wpa_debug_timestamp++; - break; - case 'v': - show_version(); - exit(1); - break; - - default: - usage(); - break; - } - } - - if (optind == argc) - usage(); - - if (eap_server_register_methods()) { - wpa_printf(MSG_ERROR, "Failed to register EAP methods"); - return -1; - } - - interfaces.count = argc - optind; - - interfaces.iface = os_malloc(interfaces.count * - sizeof(struct hostapd_iface *)); - if (interfaces.iface == NULL) { - wpa_printf(MSG_ERROR, "malloc failed\n"); - return -1; - } - - if (eloop_init(&interfaces)) { - wpa_printf(MSG_ERROR, "Failed to initialize event loop"); - return -1; - } - -#ifndef CONFIG_NATIVE_WINDOWS - eloop_register_signal(SIGHUP, handle_reload, NULL); - eloop_register_signal(SIGUSR1, handle_dump_state, NULL); -#endif /* CONFIG_NATIVE_WINDOWS */ - eloop_register_signal_terminate(handle_term, NULL); - - /* Initialize interfaces */ - for (i = 0; i < interfaces.count; i++) { - wpa_printf(MSG_ERROR, "Configuration file: %s", - argv[optind + i]); - interfaces.iface[i] = hostapd_init(argv[optind + i]); - if (!interfaces.iface[i]) - goto out; - for (k = 0; k < debug; k++) { - if (interfaces.iface[i]->bss[0]->conf-> - logger_stdout_level > 0) - interfaces.iface[i]->bss[0]->conf-> - logger_stdout_level--; - } - - ret = hostapd_setup_interface(interfaces.iface[i]); - if (ret) - goto out; - - for (k = 0; k < (int) interfaces.iface[i]->num_bss; k++) { - if (interfaces.iface[i]->bss[0]->conf->tnc) - tnc++; - } - } - -#ifdef EAP_TNC - if (tnc && tncs_global_init() < 0) { - wpa_printf(MSG_ERROR, "Failed to initialize TNCS"); - goto out; - } -#endif /* EAP_TNC */ - - if (daemonize && os_daemonize(pid_file)) { - perror("daemon"); - goto out; - } - -#ifndef CONFIG_NATIVE_WINDOWS - openlog("hostapd", 0, LOG_DAEMON); -#endif /* CONFIG_NATIVE_WINDOWS */ - - eloop_run(); - - /* Disconnect associated stations from all interfaces and BSSes */ - for (i = 0; i < interfaces.count; i++) { - for (j = 0; j < interfaces.iface[i]->num_bss; j++) { - struct hostapd_data *hapd = - interfaces.iface[i]->bss[j]; - hostapd_free_stas(hapd); - hostapd_flush_old_stations(hapd); - } - } - - ret = 0; - - out: - /* Deinitialize all interfaces */ - for (i = 0; i < interfaces.count; i++) { - if (!interfaces.iface[i]) - continue; - hostapd_cleanup_iface_pre(interfaces.iface[i]); - for (j = 0; j < interfaces.iface[i]->num_bss; j++) { - struct hostapd_data *hapd = - interfaces.iface[i]->bss[j]; - hostapd_cleanup(hapd); - if (j == interfaces.iface[i]->num_bss - 1 && - hapd->driver) - hostapd_driver_deinit(hapd); - } - for (j = 0; j < interfaces.iface[i]->num_bss; j++) - os_free(interfaces.iface[i]->bss[j]); - hostapd_cleanup_iface(interfaces.iface[i]); - } - os_free(interfaces.iface); - -#ifdef EAP_TNC - tncs_global_deinit(); -#endif /* EAP_TNC */ - - eloop_destroy(); - -#ifndef CONFIG_NATIVE_WINDOWS - closelog(); -#endif /* CONFIG_NATIVE_WINDOWS */ - - eap_server_unregister_methods(); - - os_daemonize_terminate(pid_file); - os_free(pid_file); - - return ret; -} diff --git a/contrib/hostapd/hostapd/hostapd.h b/contrib/hostapd/hostapd/hostapd.h deleted file mode 100644 index 26f30d7016..0000000000 --- a/contrib/hostapd/hostapd/hostapd.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - * hostapd / Initialization and configuration - * Host AP kernel driver - * Copyright (c) 2002-2008, Jouni Malinen - * Copyright (c) 2007-2008, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef HOSTAPD_H -#define HOSTAPD_H - -#include "common.h" -#include "ap.h" - -#ifndef ETH_ALEN -#define ETH_ALEN 6 -#endif -#ifndef IFNAMSIZ -#define IFNAMSIZ 16 -#endif -#ifndef ETH_P_ALL -#define ETH_P_ALL 0x0003 -#endif -#ifndef ETH_P_PAE -#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ -#endif /* ETH_P_PAE */ -#ifndef ETH_P_EAPOL -#define ETH_P_EAPOL ETH_P_PAE -#endif /* ETH_P_EAPOL */ - -#ifndef ETH_P_RRB -#define ETH_P_RRB 0x890D -#endif /* ETH_P_RRB */ - -#include "config.h" - -#ifdef _MSC_VER -#pragma pack(push, 1) -#endif /* _MSC_VER */ - -#define MAX_VLAN_ID 4094 - -struct ieee8023_hdr { - u8 dest[6]; - u8 src[6]; - u16 ethertype; -} STRUCT_PACKED; - - -struct ieee80211_hdr { - le16 frame_control; - le16 duration_id; - u8 addr1[6]; - u8 addr2[6]; - u8 addr3[6]; - le16 seq_ctrl; - /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame - */ -} STRUCT_PACKED; - -#ifdef _MSC_VER -#pragma pack(pop) -#endif /* _MSC_VER */ - -#define IEEE80211_DA_FROMDS addr1 -#define IEEE80211_BSSID_FROMDS addr2 -#define IEEE80211_SA_FROMDS addr3 - -#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr)) - -#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) - -/* MTU to be set for the wlan#ap device; this is mainly needed for IEEE 802.1X - * frames that might be longer than normal default MTU and they are not - * fragmented */ -#define HOSTAPD_MTU 2290 - -extern unsigned char rfc1042_header[6]; - -struct hostap_sta_driver_data { - unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; - unsigned long current_tx_rate; - unsigned long inactive_msec; - unsigned long flags; - unsigned long num_ps_buf_frames; - unsigned long tx_retry_failed; - unsigned long tx_retry_count; - int last_rssi; - int last_ack_rssi; -}; - -struct wpa_driver_ops; -struct wpa_ctrl_dst; -struct radius_server_data; -struct upnp_wps_device_sm; - -#ifdef CONFIG_FULL_DYNAMIC_VLAN -struct full_dynamic_vlan; -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ - -/** - * struct hostapd_data - hostapd per-BSS data structure - */ -struct hostapd_data { - struct hostapd_iface *iface; - struct hostapd_config *iconf; - struct hostapd_bss_config *conf; - int interface_added; /* virtual interface added for this BSS */ - - u8 own_addr[ETH_ALEN]; - - int num_sta; /* number of entries in sta_list */ - struct sta_info *sta_list; /* STA info list head */ - struct sta_info *sta_hash[STA_HASH_SIZE]; - - /* pointers to STA info; based on allocated AID or NULL if AID free - * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1 - * and so on - */ - struct sta_info *sta_aid[MAX_AID_TABLE_SIZE]; - - const struct wpa_driver_ops *driver; - void *drv_priv; - - u8 *default_wep_key; - u8 default_wep_key_idx; - - struct radius_client_data *radius; - int radius_client_reconfigured; - u32 acct_session_id_hi, acct_session_id_lo; - - struct iapp_data *iapp; - - struct hostapd_cached_radius_acl *acl_cache; - struct hostapd_acl_query_data *acl_queries; - - struct wpa_authenticator *wpa_auth; - struct eapol_authenticator *eapol_auth; - - struct rsn_preauth_interface *preauth_iface; - time_t michael_mic_failure; - int michael_mic_failures; - int tkip_countermeasures; - - int ctrl_sock; - struct wpa_ctrl_dst *ctrl_dst; - - void *ssl_ctx; - void *eap_sim_db_priv; - struct radius_server_data *radius_srv; - - int parameter_set_count; - -#ifdef CONFIG_FULL_DYNAMIC_VLAN - struct full_dynamic_vlan *full_dynamic_vlan; -#endif /* CONFIG_FULL_DYNAMIC_VLAN */ - - struct l2_packet_data *l2; - struct wps_context *wps; - -#ifdef CONFIG_WPS - u8 *wps_beacon_ie; - size_t wps_beacon_ie_len; - u8 *wps_probe_resp_ie; - size_t wps_probe_resp_ie_len; - unsigned int ap_pin_failures; - struct upnp_wps_device_sm *wps_upnp; -#endif /* CONFIG_WPS */ -}; - - -/** - * struct hostapd_iface - hostapd per-interface data structure - */ -struct hostapd_iface { - char *config_fname; - struct hostapd_config *conf; - - size_t num_bss; - struct hostapd_data **bss; - - int num_ap; /* number of entries in ap_list */ - struct ap_info *ap_list; /* AP info list head */ - struct ap_info *ap_hash[STA_HASH_SIZE]; - struct ap_info *ap_iter_list; - - struct hostapd_hw_modes *hw_features; - int num_hw_features; - struct hostapd_hw_modes *current_mode; - /* Rates that are currently used (i.e., filtered copy of - * current_mode->channels */ - int num_rates; - struct hostapd_rate_data *current_rates; - - u16 hw_flags; - - /* Number of associated Non-ERP stations (i.e., stations using 802.11b - * in 802.11g BSS) */ - int num_sta_non_erp; - - /* Number of associated stations that do not support Short Slot Time */ - int num_sta_no_short_slot_time; - - /* Number of associated stations that do not support Short Preamble */ - int num_sta_no_short_preamble; - - int olbc; /* Overlapping Legacy BSS Condition */ - - /* Number of HT associated stations that do not support greenfield */ - int num_sta_ht_no_gf; - - /* Number of associated non-HT stations */ - int num_sta_no_ht; - - /* Number of HT associated stations 20 MHz */ - int num_sta_ht_20mhz; - - /* Overlapping BSS information */ - int olbc_ht; - -#ifdef CONFIG_IEEE80211N - u16 ht_op_mode; -#endif /* CONFIG_IEEE80211N */ -}; - -void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, - int reassoc); -int hostapd_reload_config(struct hostapd_iface *iface); - -#endif /* HOSTAPD_H */ diff --git a/contrib/hostapd/hostapd/hostapd_cli.c b/contrib/hostapd/hostapd/hostapd_cli.c index c2ecd4e23c..eee85041b6 100644 --- a/contrib/hostapd/hostapd/hostapd_cli.c +++ b/contrib/hostapd/hostapd/hostapd_cli.c @@ -1,53 +1,32 @@ /* * hostapd - command line interface for hostapd daemon - * Copyright (c) 2004-2009, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include -#include "wpa_ctrl.h" -#include "common.h" -#include "version.h" +#include "common/wpa_ctrl.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/edit.h" +#include "common/version.h" static const char *hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2009, Jouni Malinen and contributors"; +"Copyright (c) 2004-2014, Jouni Malinen and contributors"; static const char *hostapd_cli_license = -"This program is free software. You can distribute it and/or modify it\n" -"under the terms of the GNU General Public License version 2.\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license. See README and COPYING for more details.\n"; +"This software may be distributed under the terms of the BSD license.\n" +"See README for more details.\n"; static const char *hostapd_cli_full_license = -"This program is free software; you can redistribute it and/or modify\n" -"it under the terms of the GNU General Public License version 2 as\n" -"published by the Free Software Foundation.\n" -"\n" -"This program is distributed in the hope that it will be useful,\n" -"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" -"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" -"GNU General Public License for more details.\n" -"\n" -"You should have received a copy of the GNU General Public License\n" -"along with this program; if not, write to the Free Software\n" -"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license.\n" +"This software may be distributed under the terms of the 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" @@ -83,13 +62,26 @@ static const char *commands_help = " sta get MIB variables for one station\n" " all_sta get MIB variables for all stations\n" " new_sta add a new station\n" +" deauthenticate deauthenticate a station\n" +" disassociate disassociate a station\n" #ifdef CONFIG_IEEE80211W " sa_query send SA Query to a station\n" #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS -" wps_pin [timeout] add WPS Enrollee PIN (Device Password)\n" +" wps_pin [timeout] [addr] add WPS Enrollee PIN\n" +" wps_check_pin verify PIN checksum\n" " wps_pbc indicate button pushed to initiate PBC\n" +" wps_cancel cancel the pending WPS operation\n" +#ifdef CONFIG_WPS_NFC +" wps_nfc_tag_read report read NFC tag with WPS data\n" +" wps_nfc_config_token build NFC configuration token\n" +" wps_nfc_token manager NFC password token\n" +#endif /* CONFIG_WPS_NFC */ +" wps_ap_pin [params..] enable/disable AP PIN\n" +" wps_config configure AP\n" +" wps_get_status show current WPS status\n" #endif /* CONFIG_WPS */ +" get_config show current configuration\n" " help show this usage help\n" " interface [ifname] show interfaces/select interface\n" " level change debug level\n" @@ -99,25 +91,37 @@ static const char *commands_help = static struct wpa_ctrl *ctrl_conn; static int hostapd_cli_quit = 0; static int hostapd_cli_attached = 0; -static const char *ctrl_iface_dir = "/var/run/hostapd"; + +#ifndef CONFIG_CTRL_IFACE_DIR +#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd" +#endif /* CONFIG_CTRL_IFACE_DIR */ +static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; + 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 usage(void) { fprintf(stderr, "%s\n", hostapd_cli_version); - fprintf(stderr, - "\n" - "usage: hostapd_cli [-p] [-i] [-hv] " - "[-G] \\\n" - " [command..]\n" + fprintf(stderr, + "\n" + "usage: hostapd_cli [-p] [-i] [-hvB] " + "[-a] \\\n" + " [-G] [command..]\n" "\n" "Options:\n" " -h help (show this usage text)\n" " -v shown version information\n" " -p path to find control sockets (default: " "/var/run/hostapd)\n" + " -a run in daemon mode executing the action file " + "based on events\n" + " from hostapd\n" + " -B run a daemon in the background\n" " -i Interface to listen on (default: first " "interface found in the\n" " socket path)\n\n" @@ -206,21 +210,88 @@ static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOG"); +} + + +static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc > 0 && os_strcmp(argv[0], "driver") == 0) + return wpa_ctrl_command(ctrl, "STATUS-DRIVER"); + return wpa_ctrl_command(ctrl, "STATUS"); +} + + static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) { + if (argc > 0) { + char buf[100]; + os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); + } return wpa_ctrl_command(ctrl, "MIB"); } +static int hostapd_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 hostapd_cli_action_process(char *msg, size_t len) +{ + const char *pos; + + pos = msg; + if (*pos == '<') { + pos = os_strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + hostapd_cli_exec(action_file, ctrl_ifname, pos); +} + + static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char buf[64]; - if (argc != 1) { - printf("Invalid 'sta' command - exactly one argument, STA " + if (argc < 1) { + printf("Invalid 'sta' command - at least one argument, STA " "address, is required.\n"); return -1; } - snprintf(buf, sizeof(buf), "STA %s", argv[0]); + if (argc > 1) + snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]); + else + snprintf(buf, sizeof(buf), "STA %s", argv[0]); return wpa_ctrl_command(ctrl, buf); } @@ -239,6 +310,42 @@ static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc < 1) { + printf("Invalid 'deauthenticate' command - exactly one " + "argument, STA address, is required.\n"); + return -1; + } + if (argc > 1) + os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s", + argv[0], argv[1]); + else + os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc < 1) { + printf("Invalid 'disassociate' command - exactly one " + "argument, STA address, is required.\n"); + return -1; + } + if (argc > 1) + os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s", + argv[0], argv[1]); + else + os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + #ifdef CONFIG_IEEE80211W static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -259,13 +366,16 @@ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char buf[64]; + char buf[256]; if (argc < 2) { printf("Invalid 'wps_pin' command - at least two arguments, " "UUID and PIN, are required.\n"); return -1; } - if (argc > 2) + if (argc > 3) + snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else if (argc > 2) snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s", argv[0], argv[1], argv[2]); else @@ -274,14 +384,260 @@ static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1 && argc != 2) { + printf("Invalid WPS_CHECK_PIN command: needs one argument:\n" + "- PIN to be verified\n"); + return -1; + } + + if (argc == 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_CHECK_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "WPS_PBC"); } + + +static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_CANCEL"); +} + + +#ifdef CONFIG_WPS_NFC +static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + int ret; + char *buf; + size_t buflen; + + if (argc != 1) { + printf("Invalid 'wps_nfc_tag_read' command - one argument " + "is required.\n"); + return -1; + } + + buflen = 18 + os_strlen(argv[0]); + buf = os_malloc(buflen); + if (buf == NULL) + return -1; + os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]); + + ret = wpa_ctrl_command(ctrl, buf); + os_free(buf); + + return ret; +} + + +static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 1) { + printf("Invalid 'wps_nfc_config_token' command - one argument " + "is required.\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_NFC_CONFIG_TOKEN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 1) { + printf("Invalid 'wps_nfc_token' command - one argument is " + "required.\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_NFC_TOKEN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 2) { + printf("Invalid 'nfc_get_handover_sel' command - two arguments " + "are required.\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long NFC_GET_HANDOVER_SEL command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + +#endif /* CONFIG_WPS_NFC */ + + +static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc < 1) { + printf("Invalid 'wps_ap_pin' command - at least one argument " + "is required.\n"); + return -1; + } + if (argc > 2) + snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else if (argc > 1) + snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s", + argv[0], argv[1]); + else + snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_GET_STATUS"); +} + + +static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[256]; + char ssid_hex[2 * 32 + 1]; + char key_hex[2 * 64 + 1]; + int i; + + if (argc < 1) { + printf("Invalid 'wps_config' command - at least two arguments " + "are required.\n"); + return -1; + } + + ssid_hex[0] = '\0'; + for (i = 0; i < 32; i++) { + if (argv[0][i] == '\0') + break; + os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]); + } + + key_hex[0] = '\0'; + if (argc > 3) { + for (i = 0; i < 64; i++) { + if (argv[3][i] == '\0') + break; + os_snprintf(&key_hex[i * 2], 3, "%02x", + argv[3][i]); + } + } + + if (argc > 3) + snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s", + ssid_hex, argv[1], argv[2], key_hex); + else if (argc > 2) + snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s", + ssid_hex, argv[1], argv[2]); + else + snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s", + ssid_hex, argv[1]); + return wpa_ctrl_command(ctrl, buf); +} #endif /* CONFIG_WPS */ +static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 2) { + printf("Invalid 'disassoc_imminent' command - two arguments " + "(STA addr and Disassociation Timer) are needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s", + argv[0], argv[1]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 3) { + printf("Invalid 'ess_disassoc' command - three arguments (STA " + "addr, disassoc timer, and URL) are needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s", + argv[0], argv[1], argv[2]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "GET_CONFIG"); +} + + static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, char *addr, size_t addr_len) { @@ -348,9 +704,50 @@ static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[200]; + int res; + + if (argc != 1) { + printf("Invalid 'set_qos_map_set' command - " + "one argument (comma delimited QoS map set) " + "is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[50]; + int res; + + if (argc != 1) { + printf("Invalid 'send_qos_map_conf' command - " + "one argument (STA addr) is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]); + if (res < 0 || res >= (int) sizeof(buf)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) { hostapd_cli_quit = 1; + if (interactive) + eloop_terminate(); return 0; } @@ -419,6 +816,85 @@ static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + 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 hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid GET command: needs one argument (variable " + "name)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long GET command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[256]; + int res; + int i; + char *tmp; + int total; + + if (argc < 2) { + printf("Invalid chan_switch command: needs at least two " + "arguments (count and freq)\n" + "usage: [sec_channel_offset=] " + "[center_freq1=] [center_freq2=] [bandwidth=] " + "[blocktx] [ht|vht]\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long CHAN_SWITCH command.\n"); + return -1; + } + + total = res; + for (i = 2; i < argc; i++) { + tmp = cmd + total; + res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]); + if (res < 0 || (size_t) res >= sizeof(cmd) - total - 1) { + printf("Too long CHAN_SWITCH command.\n"); + return -1; + } + total += res; + } + return wpa_ctrl_command(ctrl, cmd); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -427,21 +903,44 @@ struct hostapd_cli_cmd { static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "ping", hostapd_cli_cmd_ping }, { "mib", hostapd_cli_cmd_mib }, + { "relog", hostapd_cli_cmd_relog }, + { "status", hostapd_cli_cmd_status }, { "sta", hostapd_cli_cmd_sta }, { "all_sta", hostapd_cli_cmd_all_sta }, { "new_sta", hostapd_cli_cmd_new_sta }, + { "deauthenticate", hostapd_cli_cmd_deauthenticate }, + { "disassociate", hostapd_cli_cmd_disassociate }, #ifdef CONFIG_IEEE80211W { "sa_query", hostapd_cli_cmd_sa_query }, #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS { "wps_pin", hostapd_cli_cmd_wps_pin }, + { "wps_check_pin", hostapd_cli_cmd_wps_check_pin }, { "wps_pbc", hostapd_cli_cmd_wps_pbc }, + { "wps_cancel", hostapd_cli_cmd_wps_cancel }, +#ifdef CONFIG_WPS_NFC + { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read }, + { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token }, + { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token }, + { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel }, +#endif /* CONFIG_WPS_NFC */ + { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin }, + { "wps_config", hostapd_cli_cmd_wps_config }, + { "wps_get_status", hostapd_cli_cmd_wps_get_status }, #endif /* CONFIG_WPS */ + { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent }, + { "ess_disassoc", hostapd_cli_cmd_ess_disassoc }, + { "get_config", hostapd_cli_cmd_get_config }, { "help", hostapd_cli_cmd_help }, { "interface", hostapd_cli_cmd_interface }, { "level", hostapd_cli_cmd_level }, { "license", hostapd_cli_cmd_license }, { "quit", hostapd_cli_cmd_quit }, + { "set", hostapd_cli_cmd_set }, + { "get", hostapd_cli_cmd_get }, + { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set }, + { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf }, + { "chan_switch", hostapd_cli_cmd_chan_switch }, { NULL, NULL } }; @@ -456,6 +955,11 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) while (cmd->cmd) { if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) { match = cmd; + if (os_strcasecmp(cmd->cmd, argv[0]) == 0) { + /* we have an exact match */ + count = 1; + break; + } count++; } cmd++; @@ -480,7 +984,8 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) } -static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read) +static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, + int action_monitor) { int first = 1; if (ctrl_conn == NULL) @@ -490,10 +995,14 @@ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read) size_t len = sizeof(buf) - 1; if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { buf[len] = '\0'; - if (in_read && first) - printf("\n"); - first = 0; - printf("%s\n", buf); + if (action_monitor) + hostapd_cli_action_process(buf, len); + else { + if (in_read && first) + printf("\n"); + first = 0; + printf("%s\n", buf); + } } else { printf("Could not read pending message.\n"); break; @@ -502,60 +1011,39 @@ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read) } -static void hostapd_cli_interactive(void) -{ - const int max_args = 10; - char cmd[256], *res, *argv[max_args], *pos; - int argc; +#define max_args 10 - printf("\nInteractive mode\n\n"); +static int tokenize_cmd(char *cmd, char *argv[]) +{ + char *pos; + int argc = 0; - do { - hostapd_cli_recv_pending(ctrl_conn, 0); - printf("> "); - alarm(ping_interval); - res = fgets(cmd, sizeof(cmd), stdin); - alarm(0); - if (res == NULL) - break; - pos = cmd; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } + pos = 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; } - argc = 0; - pos = cmd; - for (;;) { - while (*pos == ' ') - pos++; - if (*pos == '\0') - break; - argv[argc] = pos; - argc++; - if (argc == max_args) - break; - while (*pos != '\0' && *pos != ' ') - pos++; - if (*pos == ' ') - *pos++ = '\0'; - } - if (argc) - wpa_request(ctrl_conn, argc, argv); - } while (!hostapd_cli_quit); -} - + while (*pos != '\0' && *pos != ' ') + pos++; + if (*pos == ' ') + *pos++ = '\0'; + } -static void hostapd_cli_terminate(int sig) -{ - hostapd_cli_close_connection(); - exit(0); + return argc; } -static void hostapd_cli_alarm(int sig) +static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx) { if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { printf("Connection to hostapd lost - trying to reconnect\n"); @@ -574,22 +1062,116 @@ static void hostapd_cli_alarm(int sig) } } if (ctrl_conn) - hostapd_cli_recv_pending(ctrl_conn, 1); - alarm(ping_interval); + hostapd_cli_recv_pending(ctrl_conn, 1, 0); + eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); +} + + +static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx) +{ + eloop_terminate(); +} + + +static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd) +{ + char *argv[max_args]; + int argc; + argc = tokenize_cmd(cmd, argv); + if (argc) + wpa_request(ctrl_conn, argc, argv); +} + + +static void hostapd_cli_edit_eof_cb(void *ctx) +{ + eloop_terminate(); +} + + +static void hostapd_cli_interactive(void) +{ + printf("\nInteractive mode\n\n"); + + eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); + edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, + NULL, NULL, NULL, NULL); + eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); + + eloop_run(); + + edit_deinit(NULL, NULL); + eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL); +} + + +static void hostapd_cli_cleanup(void) +{ + hostapd_cli_close_connection(); + if (pid_file) + os_daemonize_terminate(pid_file); + + os_program_deinit(); +} + + +static void hostapd_cli_action(struct wpa_ctrl *ctrl) +{ + fd_set rfds; + int fd, res; + struct timeval tv; + char buf[256]; + size_t len; + + fd = wpa_ctrl_get_fd(ctrl); + + while (!hostapd_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)) + hostapd_cli_recv_pending(ctrl, 0, 1); + else { + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len, + hostapd_cli_action_process) < 0 || + len < 4 || os_memcmp(buf, "PONG", 4) != 0) { + printf("hostapd did not reply to PING " + "command - exiting\n"); + break; + } + } + } } int main(int argc, char *argv[]) { - int interactive; int warning_displayed = 0; int c; + int daemonize = 0; + + if (os_program_init()) + return -1; for (;;) { - c = getopt(argc, argv, "hG:i:p:v"); + c = getopt(argc, argv, "a:BhG:i:p:v"); if (c < 0) break; switch (c) { + case 'a': + action_file = optarg; + break; + case 'B': + daemonize = 1; + break; case 'G': ping_interval = atoi(optarg); break; @@ -600,8 +1182,8 @@ int main(int argc, char *argv[]) printf("%s\n", hostapd_cli_version); return 0; case 'i': - free(ctrl_ifname); - ctrl_ifname = strdup(optarg); + os_free(ctrl_ifname); + ctrl_ifname = os_strdup(optarg); break; case 'p': ctrl_iface_dir = optarg; @@ -612,25 +1194,29 @@ int main(int argc, char *argv[]) } } - interactive = argc == optind; + interactive = (argc == optind) && (action_file == NULL); if (interactive) { printf("%s\n\n%s\n\n", hostapd_cli_version, hostapd_cli_license); } + if (eloop_init()) + return -1; + for (;;) { if (ctrl_ifname == NULL) { struct dirent *dent; DIR *dir = opendir(ctrl_iface_dir); if (dir) { while ((dent = readdir(dir))) { - if (strcmp(dent->d_name, ".") == 0 || - strcmp(dent->d_name, "..") == 0) + if (os_strcmp(dent->d_name, ".") == 0 + || + os_strcmp(dent->d_name, "..") == 0) continue; printf("Selected interface '%s'\n", dent->d_name); - ctrl_ifname = strdup(dent->d_name); + ctrl_ifname = os_strdup(dent->d_name); break; } closedir(dir); @@ -653,25 +1239,32 @@ int main(int argc, char *argv[]) printf("Could not connect to hostapd - re-trying\n"); warning_displayed = 1; } - sleep(1); + os_sleep(1, 0); continue; } - signal(SIGINT, hostapd_cli_terminate); - signal(SIGTERM, hostapd_cli_terminate); - signal(SIGALRM, hostapd_cli_alarm); - - if (interactive) { + if (interactive || action_file) { if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; } else { printf("Warning: Failed to attach to hostapd.\n"); + if (action_file) + return -1; } + } + + if (daemonize && os_daemonize(pid_file)) + return -1; + + if (interactive) hostapd_cli_interactive(); - } else + else if (action_file) + hostapd_cli_action(ctrl_conn); + else wpa_request(ctrl_conn, argc - optind, &argv[optind]); - free(ctrl_ifname); - hostapd_cli_close_connection(); + os_free(ctrl_ifname); + eloop_destroy(); + hostapd_cli_cleanup(); return 0; } diff --git a/contrib/hostapd/hostapd/hw_features.c b/contrib/hostapd/hostapd/hw_features.c deleted file mode 100644 index 1d6299e625..0000000000 --- a/contrib/hostapd/hostapd/hw_features.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * hostapd / Hardware feature query and different modes - * Copyright 2002-2003, Instant802 Networks, Inc. - * Copyright 2005-2006, Devicescape Software, Inc. - * Copyright (c) 2008, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "hostapd.h" -#include "hw_features.h" -#include "driver.h" -#include "config.h" - - -void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, - size_t num_hw_features) -{ - size_t i; - - if (hw_features == NULL) - return; - - for (i = 0; i < num_hw_features; i++) { - os_free(hw_features[i].channels); - os_free(hw_features[i].rates); - } - - os_free(hw_features); -} - - -int hostapd_get_hw_features(struct hostapd_iface *iface) -{ - struct hostapd_data *hapd = iface->bss[0]; - int ret = 0, i, j; - u16 num_modes, flags; - struct hostapd_hw_modes *modes; - - if (hostapd_drv_none(hapd)) - return -1; - modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); - if (modes == NULL) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Fetching hardware channel/rate support not " - "supported."); - return -1; - } - - iface->hw_flags = flags; - - hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); - iface->hw_features = modes; - iface->num_hw_features = num_modes; - - for (i = 0; i < num_modes; i++) { - struct hostapd_hw_modes *feature = &modes[i]; - /* set flag for channels we can use in current regulatory - * domain */ - for (j = 0; j < feature->num_channels; j++) { - /* - * Disable all channels that are marked not to allow - * IBSS operation or active scanning. In addition, - * disable all channels that require radar detection, - * since that (in addition to full DFS) is not yet - * supported. - */ - if (feature->channels[j].flag & - (HOSTAPD_CHAN_NO_IBSS | - HOSTAPD_CHAN_PASSIVE_SCAN | - HOSTAPD_CHAN_RADAR)) - feature->channels[j].flag |= - HOSTAPD_CHAN_DISABLED; - if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) - continue; - wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " - "chan=%d freq=%d MHz max_tx_power=%d dBm", - feature->mode, - feature->channels[j].chan, - feature->channels[j].freq, - feature->channels[j].max_tx_power); - } - } - - return ret; -} - - -static int hostapd_prepare_rates(struct hostapd_data *hapd, - struct hostapd_hw_modes *mode) -{ - int i, num_basic_rates = 0; - int basic_rates_a[] = { 60, 120, 240, -1 }; - int basic_rates_b[] = { 10, 20, -1 }; - int basic_rates_g[] = { 10, 20, 55, 110, -1 }; - int *basic_rates; - - if (hapd->iconf->basic_rates) - basic_rates = hapd->iconf->basic_rates; - else switch (mode->mode) { - case HOSTAPD_MODE_IEEE80211A: - basic_rates = basic_rates_a; - break; - case HOSTAPD_MODE_IEEE80211B: - basic_rates = basic_rates_b; - break; - case HOSTAPD_MODE_IEEE80211G: - basic_rates = basic_rates_g; - break; - default: - return -1; - } - - if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates, - basic_rates, mode->mode)) { - wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel " - "module"); - } - - os_free(hapd->iface->current_rates); - hapd->iface->num_rates = 0; - - hapd->iface->current_rates = - os_malloc(mode->num_rates * sizeof(struct hostapd_rate_data)); - if (!hapd->iface->current_rates) { - wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " - "table."); - return -1; - } - - for (i = 0; i < mode->num_rates; i++) { - struct hostapd_rate_data *rate; - - if (hapd->iconf->supported_rates && - !hostapd_rate_found(hapd->iconf->supported_rates, - mode->rates[i].rate)) - continue; - - rate = &hapd->iface->current_rates[hapd->iface->num_rates]; - os_memcpy(rate, &mode->rates[i], - sizeof(struct hostapd_rate_data)); - if (hostapd_rate_found(basic_rates, rate->rate)) { - rate->flags |= HOSTAPD_RATE_BASIC; - num_basic_rates++; - } else - rate->flags &= ~HOSTAPD_RATE_BASIC; - wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", - hapd->iface->num_rates, rate->rate, rate->flags); - hapd->iface->num_rates++; - } - - if (hapd->iface->num_rates == 0 || num_basic_rates == 0) { - wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " - "rate sets (%d,%d).", - hapd->iface->num_rates, num_basic_rates); - return -1; - } - - return 0; -} - - -#ifdef CONFIG_IEEE80211N -static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) -{ - int sec_chan, ok, j, first; - int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, - 184, 192 }; - size_t k; - - if (!iface->conf->secondary_channel) - return 1; /* HT40 not used */ - - sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4; - wpa_printf(MSG_DEBUG, "HT40: control channel: %d " - "secondary channel: %d", - iface->conf->channel, sec_chan); - - /* Verify that HT40 secondary channel is an allowed 20 MHz - * channel */ - ok = 0; - for (j = 0; j < iface->current_mode->num_channels; j++) { - struct hostapd_channel_data *chan = - &iface->current_mode->channels[j]; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && - chan->chan == sec_chan) { - ok = 1; - break; - } - } - if (!ok) { - wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", - sec_chan); - return 0; - } - - /* - * Verify that HT40 primary,secondary channel pair is allowed per - * IEEE 802.11n Annex J. This is only needed for 5 GHz band since - * 2.4 GHz rules allow all cases where the secondary channel fits into - * the list of allowed channels (already checked above). - */ - if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) - return 1; - - if (iface->conf->secondary_channel > 0) - first = iface->conf->channel; - else - first = sec_chan; - - ok = 0; - for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) { - if (first == allowed[k]) { - ok = 1; - break; - } - } - if (!ok) { - wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", - iface->conf->channel, - iface->conf->secondary_channel); - return 0; - } - - return 1; -} - - -static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) -{ - u16 hw = iface->current_mode->ht_capab; - u16 conf = iface->conf->ht_capab; - - if (!iface->conf->ieee80211n) - return 1; - - if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && - !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [LDPC]"); - return 0; - } - - if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && - !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [HT40*]"); - return 0; - } - - if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) && - (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [SMPS-*]"); - return 0; - } - - if ((conf & HT_CAP_INFO_GREEN_FIELD) && - !(hw & HT_CAP_INFO_GREEN_FIELD)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [GF]"); - return 0; - } - - if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) && - !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [SHORT-GI-20]"); - return 0; - } - - if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) && - !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [SHORT-GI-40]"); - return 0; - } - - if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [TX-STBC]"); - return 0; - } - - if ((conf & HT_CAP_INFO_RX_STBC_MASK) > - (hw & HT_CAP_INFO_RX_STBC_MASK)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [RX-STBC*]"); - return 0; - } - - if ((conf & HT_CAP_INFO_DELAYED_BA) && - !(hw & HT_CAP_INFO_DELAYED_BA)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [DELAYED-BA]"); - return 0; - } - - if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) && - !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [MAX-AMSDU-7935]"); - return 0; - } - - if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) && - !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [DSSS_CCK-40]"); - return 0; - } - - if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [PSMP]"); - return 0; - } - - if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && - !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [LSIG-TXOP-PROT]"); - return 0; - } - - return 1; -} -#endif /* CONFIG_IEEE80211N */ - - -/** - * hostapd_select_hw_mode - Select the hardware mode - * @iface: Pointer to interface data. - * Returns: 0 on success, -1 on failure - * - * Sets up the hardware mode, channel, rates, and passive scanning - * based on the configuration. - */ -int hostapd_select_hw_mode(struct hostapd_iface *iface) -{ - int i, j, ok, ret; - - if (iface->num_hw_features < 1) - return -1; - - iface->current_mode = NULL; - for (i = 0; i < iface->num_hw_features; i++) { - struct hostapd_hw_modes *mode = &iface->hw_features[i]; - if (mode->mode == (int) iface->conf->hw_mode) { - iface->current_mode = mode; - break; - } - } - - if (iface->current_mode == NULL) { - wpa_printf(MSG_ERROR, "Hardware does not support configured " - "mode"); - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Hardware does not support configured mode " - "(%d)", (int) iface->conf->hw_mode); - return -1; - } - - ok = 0; - for (j = 0; j < iface->current_mode->num_channels; j++) { - struct hostapd_channel_data *chan = - &iface->current_mode->channels[j]; - if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && - (chan->chan == iface->conf->channel)) { - ok = 1; - break; - } - } - if (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; - } - -#ifdef CONFIG_IEEE80211N - if (!ieee80211n_allowed_ht40_channel_pair(iface)) - return -1; - if (!ieee80211n_supported_ht_capab(iface)) - return -1; -#endif /* CONFIG_IEEE80211N */ - - if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) { - wpa_printf(MSG_ERROR, "Failed to prepare rates table."); - hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_WARNING, - "Failed to prepare rates table."); - return -1; - } - - ret = hostapd_passive_scan(iface->bss[0], 0, - iface->conf->passive_scan_mode, - iface->conf->passive_scan_interval, - iface->conf->passive_scan_listen, - NULL, NULL); - if (ret) { - if (ret == -1) { - wpa_printf(MSG_DEBUG, "Passive scanning not " - "supported"); - } else { - wpa_printf(MSG_ERROR, "Could not set passive " - "scanning: %s", strerror(ret)); - } - ret = 0; - } - - return ret; -} - - -const char * hostapd_hw_mode_txt(int mode) -{ - switch (mode) { - case HOSTAPD_MODE_IEEE80211A: - return "IEEE 802.11a"; - case HOSTAPD_MODE_IEEE80211B: - return "IEEE 802.11b"; - case HOSTAPD_MODE_IEEE80211G: - return "IEEE 802.11g"; - default: - return "UNKNOWN"; - } -} - - -int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) -{ - int i; - - if (!hapd->iface->current_mode) - return 0; - - for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { - struct hostapd_channel_data *ch = - &hapd->iface->current_mode->channels[i]; - if (ch->chan == chan) - return ch->freq; - } - - return 0; -} - - -int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) -{ - int i; - - if (!hapd->iface->current_mode) - return 0; - - for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { - struct hostapd_channel_data *ch = - &hapd->iface->current_mode->channels[i]; - if (ch->freq == freq) - return ch->chan; - } - - return 0; -} diff --git a/contrib/hostapd/hostapd/hw_features.h b/contrib/hostapd/hostapd/hw_features.h deleted file mode 100644 index 7d43c89b66..0000000000 --- a/contrib/hostapd/hostapd/hw_features.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * hostapd / Hardware feature query and different modes - * Copyright 2002-2003, Instant802 Networks, Inc. - * Copyright 2005-2006, Devicescape Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef HW_FEATURES_H -#define HW_FEATURES_H - -#define HOSTAPD_CHAN_DISABLED 0x00000001 -#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 -#define HOSTAPD_CHAN_NO_IBSS 0x00000004 -#define HOSTAPD_CHAN_RADAR 0x00000008 - -struct hostapd_channel_data { - short chan; /* channel number (IEEE 802.11) */ - short freq; /* frequency in MHz */ - int flag; /* flag for hostapd use (HOSTAPD_CHAN_*) */ - u8 max_tx_power; /* maximum transmit power in dBm */ -}; - -#define HOSTAPD_RATE_ERP 0x00000001 -#define HOSTAPD_RATE_BASIC 0x00000002 -#define HOSTAPD_RATE_PREAMBLE2 0x00000004 -#define HOSTAPD_RATE_SUPPORTED 0x00000010 -#define HOSTAPD_RATE_OFDM 0x00000020 -#define HOSTAPD_RATE_CCK 0x00000040 -#define HOSTAPD_RATE_MANDATORY 0x00000100 - -struct hostapd_rate_data { - int rate; /* rate in 100 kbps */ - int flags; /* HOSTAPD_RATE_ flags */ -}; - -struct hostapd_hw_modes { - int mode; - int num_channels; - struct hostapd_channel_data *channels; - int num_rates; - struct hostapd_rate_data *rates; - u16 ht_capab; -}; - - -void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, - size_t num_hw_features); -int hostapd_get_hw_features(struct hostapd_iface *iface); -int hostapd_select_hw_mode(struct hostapd_iface *iface); -const char * hostapd_hw_mode_txt(int mode); -int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); -int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); - -#endif /* HW_FEATURES_H */ diff --git a/contrib/hostapd/hostapd/iapp.h b/contrib/hostapd/hostapd/iapp.h deleted file mode 100644 index 86de592560..0000000000 --- a/contrib/hostapd/hostapd/iapp.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef IAPP_H -#define IAPP_H - -struct iapp_data; - -#ifdef CONFIG_IAPP - -void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta); -struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface); -void iapp_deinit(struct iapp_data *iapp); -int iapp_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, - struct hostapd_bss_config *oldbss); - -#else /* CONFIG_IAPP */ - -static inline void iapp_new_station(struct iapp_data *iapp, - struct sta_info *sta) -{ -} - -static inline struct iapp_data * iapp_init(struct hostapd_data *hapd, - const char *iface) -{ - return NULL; -} - -static inline void iapp_deinit(struct iapp_data *iapp) -{ -} - -static inline int -iapp_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, - struct hostapd_bss_config *oldbss) -{ - return 0; -} - -#endif /* CONFIG_IAPP */ - -#endif /* IAPP_H */ diff --git a/contrib/hostapd/hostapd/ieee802_11.c b/contrib/hostapd/hostapd/ieee802_11.c deleted file mode 100644 index 70491b4fa2..0000000000 --- a/contrib/hostapd/hostapd/ieee802_11.c +++ /dev/null @@ -1,1833 +0,0 @@ -/* - * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2008, Jouni Malinen - * Copyright (c) 2007-2008, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#ifndef CONFIG_NATIVE_WINDOWS - -#include - -#include "eloop.h" -#include "hostapd.h" -#include "ieee802_11.h" -#include "beacon.h" -#include "hw_features.h" -#include "radius/radius.h" -#include "radius/radius_client.h" -#include "ieee802_11_auth.h" -#include "sta_info.h" -#include "rc4.h" -#include "ieee802_1x.h" -#include "wpa.h" -#include "wme.h" -#include "ap_list.h" -#include "accounting.h" -#include "driver.h" -#include "mlme.h" - - -u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) -{ - u8 *pos = eid; - int i, num, count; - - if (hapd->iface->current_rates == NULL) - return eid; - - *pos++ = WLAN_EID_SUPP_RATES; - num = hapd->iface->num_rates; - if (num > 8) { - /* rest of the rates are encoded in Extended supported - * rates element */ - num = 8; - } - - *pos++ = num; - count = 0; - for (i = 0, count = 0; i < hapd->iface->num_rates && count < num; - i++) { - count++; - *pos = hapd->iface->current_rates[i].rate / 5; - if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) - *pos |= 0x80; - pos++; - } - - return pos; -} - - -u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) -{ - u8 *pos = eid; - int i, num, count; - - if (hapd->iface->current_rates == NULL) - return eid; - - num = hapd->iface->num_rates; - if (num <= 8) - return eid; - num -= 8; - - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = num; - count = 0; - for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8; - i++) { - count++; - if (count <= 8) - continue; /* already in SuppRates IE */ - *pos = hapd->iface->current_rates[i].rate / 5; - if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) - *pos |= 0x80; - pos++; - } - - return pos; -} - - -u8 * hostapd_eid_ht_capabilities_info(struct hostapd_data *hapd, u8 *eid) -{ -#ifdef CONFIG_IEEE80211N - struct ieee80211_ht_capability *cap; - u8 *pos = eid; - - if (!hapd->iconf->ieee80211n) - return eid; - - *pos++ = WLAN_EID_HT_CAP; - *pos++ = sizeof(*cap); - - cap = (struct ieee80211_ht_capability *) pos; - os_memset(cap, 0, sizeof(*cap)); - SET_2BIT_U8(&cap->mac_ht_params_info, - MAC_HT_PARAM_INFO_MAX_RX_AMPDU_FACTOR_OFFSET, - MAX_RX_AMPDU_FACTOR_64KB); - - cap->capabilities_info = host_to_le16(hapd->iconf->ht_capab); - - cap->supported_mcs_set[0] = 0xff; - cap->supported_mcs_set[1] = 0xff; - - pos += sizeof(*cap); - - return pos; -#else /* CONFIG_IEEE80211N */ - return eid; -#endif /* CONFIG_IEEE80211N */ -} - - -u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) -{ -#ifdef CONFIG_IEEE80211N - struct ieee80211_ht_operation *oper; - u8 *pos = eid; - - if (!hapd->iconf->ieee80211n) - return eid; - - *pos++ = WLAN_EID_HT_OPERATION; - *pos++ = sizeof(*oper); - - oper = (struct ieee80211_ht_operation *) pos; - os_memset(oper, 0, sizeof(*oper)); - - oper->control_chan = hapd->iconf->channel; - oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode); - if (hapd->iconf->secondary_channel == 1) - oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | - HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; - if (hapd->iconf->secondary_channel == -1) - oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | - HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; - - pos += sizeof(*oper); - - return pos; -#else /* CONFIG_IEEE80211N */ - return eid; -#endif /* CONFIG_IEEE80211N */ -} - - -#ifdef CONFIG_IEEE80211N - -/* -op_mode -Set to 0 (HT pure) under the followign conditions - - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or - - all STAs in the BSS are 20 MHz HT in 20 MHz BSS -Set to 1 (HT non-member protection) if there may be non-HT STAs - in both the primary and the secondary channel -Set to 2 if only HT STAs are associated in BSS, - however and at least one 20 MHz HT STA is associated -Set to 3 (HT mixed mode) when one or more non-HT STAs are associated - (currently non-GF HT station is considered as non-HT STA also) -*/ -int hostapd_ht_operation_update(struct hostapd_iface *iface) -{ - u16 cur_op_mode, new_op_mode; - int op_mode_changes = 0; - - if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed) - return 0; - - wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X", - __func__, iface->ht_op_mode); - - if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) - && iface->num_sta_ht_no_gf) { - iface->ht_op_mode |= - HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; - op_mode_changes++; - } else if ((iface->ht_op_mode & - HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) && - iface->num_sta_ht_no_gf == 0) { - iface->ht_op_mode &= - ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; - op_mode_changes++; - } - - if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && - (iface->num_sta_no_ht || iface->olbc_ht)) { - iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; - op_mode_changes++; - } else if ((iface->ht_op_mode & - HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && - (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) { - iface->ht_op_mode &= - ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; - op_mode_changes++; - } - - /* Note: currently we switch to the MIXED op mode if HT non-greenfield - * station is associated. Probably it's a theoretical case, since - * it looks like all known HT STAs support greenfield. - */ - new_op_mode = 0; - if (iface->num_sta_no_ht || - (iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)) - new_op_mode = OP_MODE_MIXED; - else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) - && iface->num_sta_ht_20mhz) - new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED; - else if (iface->olbc_ht) - new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS; - else - new_op_mode = OP_MODE_PURE; - - cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK; - if (cur_op_mode != new_op_mode) { - iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK; - iface->ht_op_mode |= new_op_mode; - op_mode_changes++; - } - - wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d", - __func__, iface->ht_op_mode, op_mode_changes); - - return op_mode_changes; -} - -#endif /* CONFIG_IEEE80211N */ - - -u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, - int probe) -{ - int capab = WLAN_CAPABILITY_ESS; - int privacy; - - if (hapd->iface->num_sta_no_short_preamble == 0 && - hapd->iconf->preamble == SHORT_PREAMBLE) - capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; - - privacy = hapd->conf->ssid.wep.keys_set; - - if (hapd->conf->ieee802_1x && - (hapd->conf->default_wep_key_len || - hapd->conf->individual_wep_key_len)) - privacy = 1; - - if (hapd->conf->wpa) - privacy = 1; - - if (sta) { - int policy, def_klen; - if (probe && sta->ssid_probe) { - policy = sta->ssid_probe->security_policy; - def_klen = sta->ssid_probe->wep.default_len; - } else { - policy = sta->ssid->security_policy; - def_klen = sta->ssid->wep.default_len; - } - privacy = policy != SECURITY_PLAINTEXT; - if (policy == SECURITY_IEEE_802_1X && def_klen == 0) - privacy = 0; - } - - if (privacy) - capab |= WLAN_CAPABILITY_PRIVACY; - - if (hapd->iface->current_mode && - hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && - hapd->iface->num_sta_no_short_slot_time == 0) - capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; - - return capab; -} - - -#ifdef CONFIG_IEEE80211W -static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, - struct sta_info *sta, u8 *eid) -{ - u8 *pos = eid; - u32 timeout, tu; - struct os_time now, passed; - - *pos++ = WLAN_EID_TIMEOUT_INTERVAL; - *pos++ = 5; - *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; - os_get_time(&now); - os_time_sub(&now, &sta->sa_query_start, &passed); - tu = (passed.sec * 1000000 + passed.usec) / 1024; - if (hapd->conf->assoc_sa_query_max_timeout > tu) - timeout = hapd->conf->assoc_sa_query_max_timeout - tu; - else - timeout = 0; - if (timeout < hapd->conf->assoc_sa_query_max_timeout) - timeout++; /* add some extra time for local timers */ - WPA_PUT_LE32(pos, timeout); - pos += 4; - - return pos; -} -#endif /* CONFIG_IEEE80211W */ - - -void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len) -{ - int i; - if (len > HOSTAPD_MAX_SSID_LEN) - len = HOSTAPD_MAX_SSID_LEN; - for (i = 0; i < len; i++) { - if (ssid[i] >= 32 && ssid[i] < 127) - buf[i] = ssid[i]; - else - buf[i] = '.'; - } - buf[len] = '\0'; -} - - -/** - * ieee802_11_send_deauth - Send Deauthentication frame - * @hapd: hostapd BSS data - * @addr: Address of the destination STA - * @reason: Reason code for Deauthentication - */ -void ieee802_11_send_deauth(struct hostapd_data *hapd, u8 *addr, u16 reason) -{ - struct ieee80211_mgmt mgmt; - - hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "deauthenticate - reason %d", reason); - os_memset(&mgmt, 0, sizeof(mgmt)); - mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, - WLAN_FC_STYPE_DEAUTH); - os_memcpy(mgmt.da, addr, ETH_ALEN); - os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); - os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); - mgmt.u.deauth.reason_code = host_to_le16(reason); - if (hostapd_send_mgmt_frame(hapd, &mgmt, IEEE80211_HDRLEN + - sizeof(mgmt.u.deauth), 0) < 0) - perror("ieee802_11_send_deauth: send"); -} - - -static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, - u16 auth_transaction, u8 *challenge, int iswep) -{ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "authentication (shared key, transaction %d)", - auth_transaction); - - if (auth_transaction == 1) { - if (!sta->challenge) { - /* Generate a pseudo-random challenge */ - u8 key[8]; - time_t now; - int r; - sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); - if (sta->challenge == NULL) - return WLAN_STATUS_UNSPECIFIED_FAILURE; - - now = time(NULL); - r = random(); - os_memcpy(key, &now, 4); - os_memcpy(key + 4, &r, 4); - rc4_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 (hostapd_send_mgmt_frame(hapd, reply, rlen, 0) < 0) - perror("send_auth_reply: send"); - - os_free(buf); -} - - -#ifdef CONFIG_IEEE80211R -static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, - u16 auth_transaction, u16 status, - const u8 *ies, size_t ies_len) -{ - struct hostapd_data *hapd = ctx; - struct sta_info *sta; - - send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, - status, ies, ies_len); - - if (status != WLAN_STATUS_SUCCESS) - return; - - sta = ap_get_sta(hapd, dst); - if (sta == NULL) - return; - - hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); - sta->flags |= WLAN_STA_AUTH; - mlme_authenticate_indication(hapd, sta); -} -#endif /* CONFIG_IEEE80211R */ - - -static void handle_auth(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, - size_t len) -{ - u16 auth_alg, auth_transaction, status_code; - u16 resp = WLAN_STATUS_SUCCESS; - struct sta_info *sta = NULL; - int res; - u16 fc; - u8 *challenge = NULL; - u32 session_timeout, acct_interim_interval; - int vlan_id = 0; - u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; - size_t resp_ies_len = 0; - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - printf("handle_auth - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - auth_alg = le_to_host16(mgmt->u.auth.auth_alg); - auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); - status_code = le_to_host16(mgmt->u.auth.status_code); - fc = le_to_host16(mgmt->frame_control); - - if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + - 2 + WLAN_AUTH_CHALLENGE_LEN && - mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE && - mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN) - challenge = &mgmt->u.auth.variable[2]; - - wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " - "auth_transaction=%d status_code=%d wep=%d%s", - MAC2STR(mgmt->sa), auth_alg, auth_transaction, - status_code, !!(fc & WLAN_FC_ISWEP), - challenge ? " challenge" : ""); - - if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; - goto fail; - } - - if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && - auth_alg == WLAN_AUTH_OPEN) || -#ifdef CONFIG_IEEE80211R - (hapd->conf->wpa && - (hapd->conf->wpa_key_mgmt & - (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) && - auth_alg == WLAN_AUTH_FT) || -#endif /* CONFIG_IEEE80211R */ - ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && - auth_alg == WLAN_AUTH_SHARED_KEY))) { - printf("Unsupported authentication algorithm (%d)\n", - auth_alg); - resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; - goto fail; - } - - if (!(auth_transaction == 1 || - (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { - printf("Unknown authentication transaction number (%d)\n", - auth_transaction); - resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; - goto fail; - } - - if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { - printf("Station " MACSTR " not allowed to authenticate.\n", - MAC2STR(mgmt->sa)); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, - &session_timeout, - &acct_interim_interval, &vlan_id); - if (res == HOSTAPD_ACL_REJECT) { - printf("Station " MACSTR " not allowed to authenticate.\n", - MAC2STR(mgmt->sa)); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - if (res == HOSTAPD_ACL_PENDING) { - wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR - " waiting for an external authentication", - MAC2STR(mgmt->sa)); - /* Authentication code will re-send the authentication frame - * after it has received (and cached) information from the - * external source. */ - return; - } - - sta = ap_sta_add(hapd, mgmt->sa); - if (!sta) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - if (vlan_id > 0) { - if (hostapd_get_vlan_id_ifname(hapd->conf->vlan, - vlan_id) == NULL) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " - "%d received from RADIUS server", - vlan_id); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - sta->vlan_id = vlan_id; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); - } - - sta->flags &= ~WLAN_STA_PREAUTH; - ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); - - if (hapd->conf->radius->acct_interim_interval == 0 && - acct_interim_interval) - sta->acct_interim_interval = acct_interim_interval; - if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) - ap_sta_session_timeout(hapd, sta, session_timeout); - else - ap_sta_no_session_timeout(hapd, sta); - - switch (auth_alg) { - case WLAN_AUTH_OPEN: - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "authentication OK (open system)"); -#ifdef IEEE80211_REQUIRE_AUTH_ACK - /* Station will be marked authenticated if it ACKs the - * authentication reply. */ -#else - sta->flags |= WLAN_STA_AUTH; - wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); - sta->auth_alg = WLAN_AUTH_OPEN; - mlme_authenticate_indication(hapd, sta); -#endif - break; - case WLAN_AUTH_SHARED_KEY: - resp = auth_shared_key(hapd, sta, auth_transaction, challenge, - fc & WLAN_FC_ISWEP); - sta->auth_alg = WLAN_AUTH_SHARED_KEY; - mlme_authenticate_indication(hapd, sta); - if (sta->challenge && auth_transaction == 1) { - resp_ies[0] = WLAN_EID_CHALLENGE; - resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN; - os_memcpy(resp_ies + 2, sta->challenge, - WLAN_AUTH_CHALLENGE_LEN); - resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; - } - break; -#ifdef CONFIG_IEEE80211R - case WLAN_AUTH_FT: - sta->auth_alg = WLAN_AUTH_FT; - if (sta->wpa_sm == NULL) - sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr); - if (sta->wpa_sm == NULL) { - wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " - "state machine"); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid, - auth_transaction, mgmt->u.auth.variable, - len - IEEE80211_HDRLEN - - sizeof(mgmt->u.auth), - handle_auth_ft_finish, hapd); - /* handle_auth_ft_finish() callback will complete auth. */ - return; -#endif /* CONFIG_IEEE80211R */ - } - - fail: - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, - auth_transaction + 1, resp, resp_ies, resp_ies_len); -} - - -static void handle_assoc(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, size_t len, int reassoc) -{ - u16 capab_info, listen_interval; - u16 resp = WLAN_STATUS_SUCCESS; - u8 *pos, *wpa_ie; - size_t wpa_ie_len; - int send_deauth = 0, send_len, left, i; - struct sta_info *sta; - struct ieee802_11_elems elems; - u8 buf[sizeof(struct ieee80211_mgmt) + 512]; - struct ieee80211_mgmt *reply; - - if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : - sizeof(mgmt->u.assoc_req))) { - printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)" - "\n", reassoc, (unsigned long) len); - return; - } - - if (reassoc) { - capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); - listen_interval = le_to_host16( - mgmt->u.reassoc_req.listen_interval); - wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR - " capab_info=0x%02x listen_interval=%d current_ap=" - MACSTR, - MAC2STR(mgmt->sa), capab_info, listen_interval, - MAC2STR(mgmt->u.reassoc_req.current_ap)); - left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); - pos = mgmt->u.reassoc_req.variable; - } else { - capab_info = le_to_host16(mgmt->u.assoc_req.capab_info); - listen_interval = le_to_host16( - mgmt->u.assoc_req.listen_interval); - wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR - " capab_info=0x%02x listen_interval=%d", - MAC2STR(mgmt->sa), capab_info, listen_interval); - left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); - pos = mgmt->u.assoc_req.variable; - } - - sta = ap_get_sta(hapd, mgmt->sa); -#ifdef CONFIG_IEEE80211R - if (sta && sta->auth_alg == WLAN_AUTH_FT && - (sta->flags & WLAN_STA_AUTH) == 0) { - wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " - "prior to authentication since it is using " - "over-the-DS FT", MAC2STR(mgmt->sa)); - } else -#endif /* CONFIG_IEEE80211R */ - if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { - printf("STA " MACSTR " trying to associate before " - "authentication\n", MAC2STR(mgmt->sa)); - if (sta) { - printf(" sta: addr=" MACSTR " aid=%d flags=0x%04x\n", - MAC2STR(sta->addr), sta->aid, sta->flags); - } - send_deauth = 1; - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; - goto fail; - } - - if (listen_interval > hapd->conf->max_listen_interval) { - hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Too large Listen Interval (%d)", - listen_interval); - resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE; - goto fail; - } - - sta->capability = capab_info; - sta->listen_interval = listen_interval; - - /* followed by SSID and Supported rates; and HT capabilities if 802.11n - * is used */ - if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed || - !elems.ssid) { - printf("STA " MACSTR " sent invalid association request\n", - MAC2STR(sta->addr)); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - if (elems.ssid_len != hapd->conf->ssid.ssid_len || - os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) != 0) - { - char ssid_txt[33]; - ieee802_11_print_ssid(ssid_txt, elems.ssid, elems.ssid_len); - printf("Station " MACSTR " tried to associate with " - "unknown SSID '%s'\n", MAC2STR(sta->addr), ssid_txt); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - sta->flags &= ~WLAN_STA_WMM; - if (elems.wmm && hapd->conf->wmm_enabled) { - if (hostapd_eid_wmm_valid(hapd, elems.wmm, elems.wmm_len)) - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "invalid WMM element in association " - "request"); - else - sta->flags |= WLAN_STA_WMM; - } - - if (!elems.supp_rates) { - hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "No supported rates element in AssocReq"); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - if (elems.supp_rates_len > sizeof(sta->supported_rates)) { - hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Invalid supported rates element length %d", - elems.supp_rates_len); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); - os_memcpy(sta->supported_rates, elems.supp_rates, - elems.supp_rates_len); - sta->supported_rates_len = elems.supp_rates_len; - - if (elems.ext_supp_rates) { - if (elems.supp_rates_len + elems.ext_supp_rates_len > - sizeof(sta->supported_rates)) { - hostapd_logger(hapd, mgmt->sa, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "Invalid supported rates element length" - " %d+%d", elems.supp_rates_len, - elems.ext_supp_rates_len); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - os_memcpy(sta->supported_rates + elems.supp_rates_len, - elems.ext_supp_rates, elems.ext_supp_rates_len); - sta->supported_rates_len += elems.ext_supp_rates_len; - } - -#ifdef CONFIG_IEEE80211N - /* save HT capabilities in the sta object */ - os_memset(&sta->ht_capabilities, 0, sizeof(sta->ht_capabilities)); - if (elems.ht_capabilities && - elems.ht_capabilities_len >= - sizeof(struct ieee80211_ht_capability)) { - sta->flags |= WLAN_STA_HT; - sta->ht_capabilities.id = WLAN_EID_HT_CAP; - sta->ht_capabilities.length = - sizeof(struct ieee80211_ht_capability); - os_memcpy(&sta->ht_capabilities.data, - elems.ht_capabilities, - sizeof(struct ieee80211_ht_capability)); - } else - sta->flags &= ~WLAN_STA_HT; -#endif /* CONFIG_IEEE80211N */ - - if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { - wpa_ie = elems.rsn_ie; - wpa_ie_len = elems.rsn_ie_len; - } else if ((hapd->conf->wpa & WPA_PROTO_WPA) && - elems.wpa_ie) { - wpa_ie = elems.wpa_ie; - wpa_ie_len = elems.wpa_ie_len; - } else { - wpa_ie = NULL; - wpa_ie_len = 0; - } -#ifdef CONFIG_WPS - sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); - if (hapd->conf->wps_state && wpa_ie == NULL) { - if (elems.wps_ie) { - wpa_printf(MSG_DEBUG, "STA included WPS IE in " - "(Re)Association Request - assume WPS is " - "used"); - sta->flags |= WLAN_STA_WPS; - wpabuf_free(sta->wps_ie); - sta->wps_ie = wpabuf_alloc_copy(elems.wps_ie + 4, - elems.wps_ie_len - 4); - } else { - wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE " - "in (Re)Association Request - possible WPS " - "use"); - sta->flags |= WLAN_STA_MAYBE_WPS; - } - } else -#endif /* CONFIG_WPS */ - if (hapd->conf->wpa && wpa_ie == NULL) { - printf("STA " MACSTR ": No WPA/RSN IE in association " - "request\n", MAC2STR(sta->addr)); - resp = WLAN_STATUS_INVALID_IE; - goto fail; - } - - if (hapd->conf->wpa && wpa_ie) { - int res; - wpa_ie -= 2; - wpa_ie_len += 2; - if (sta->wpa_sm == NULL) - sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, - sta->addr); - if (sta->wpa_sm == NULL) { - printf("Failed to initialize WPA state machine\n"); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, - wpa_ie, wpa_ie_len, - elems.mdie, elems.mdie_len); - if (res == WPA_INVALID_GROUP) - resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_PAIRWISE) - resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_AKMP) - resp = WLAN_STATUS_AKMP_NOT_VALID; - else if (res == WPA_ALLOC_FAIL) - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; -#ifdef CONFIG_IEEE80211W - else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) - resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; - else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) - resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; -#endif /* CONFIG_IEEE80211W */ - else if (res == WPA_INVALID_MDIE) - resp = WLAN_STATUS_INVALID_MDIE; - else if (res != WPA_IE_OK) - resp = WLAN_STATUS_INVALID_IE; - if (resp != WLAN_STATUS_SUCCESS) - goto fail; -#ifdef CONFIG_IEEE80211W - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && - sta->sa_query_count > 0) - ap_check_sa_query_timeout(hapd, sta); - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && - (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { - /* - * STA has already been associated with MFP and SA - * Query timeout has not been reached. Reject the - * association attempt temporarily and start SA Query, - * if one is not pending. - */ - - if (sta->sa_query_count == 0) - ap_sta_start_sa_query(hapd, sta); - - resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; - goto fail; - } - - if (wpa_auth_uses_mfp(sta->wpa_sm)) - sta->flags |= WLAN_STA_MFP; - else - sta->flags &= ~WLAN_STA_MFP; -#endif /* CONFIG_IEEE80211W */ - -#ifdef CONFIG_IEEE80211R - if (sta->auth_alg == WLAN_AUTH_FT) { - if (!reassoc) { - wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " - "to use association (not " - "re-association) with FT auth_alg", - MAC2STR(sta->addr)); - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - - resp = wpa_ft_validate_reassoc(sta->wpa_sm, pos, left); - if (resp != WLAN_STATUS_SUCCESS) - goto fail; - } -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211N - if ((sta->flags & WLAN_STA_HT) && - wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { - wpa_printf(MSG_DEBUG, "HT: " MACSTR " tried to " - "use TKIP with HT association", - MAC2STR(sta->addr)); - resp = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; - goto fail; - } -#endif /* CONFIG_IEEE80211N */ - } else - wpa_auth_sta_no_wpa(sta->wpa_sm); - - if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) - sta->flags |= WLAN_STA_NONERP; - for (i = 0; i < sta->supported_rates_len; i++) { - if ((sta->supported_rates[i] & 0x7f) > 22) { - sta->flags &= ~WLAN_STA_NONERP; - break; - } - } - if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) { - sta->nonerp_set = 1; - hapd->iface->num_sta_non_erp++; - if (hapd->iface->num_sta_non_erp == 1) - ieee802_11_set_beacons(hapd->iface); - } - - if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) && - !sta->no_short_slot_time_set) { - sta->no_short_slot_time_set = 1; - hapd->iface->num_sta_no_short_slot_time++; - if (hapd->iface->current_mode->mode == - HOSTAPD_MODE_IEEE80211G && - hapd->iface->num_sta_no_short_slot_time == 1) - ieee802_11_set_beacons(hapd->iface); - } - - if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) - sta->flags |= WLAN_STA_SHORT_PREAMBLE; - else - sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; - - if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && - !sta->no_short_preamble_set) { - sta->no_short_preamble_set = 1; - hapd->iface->num_sta_no_short_preamble++; - if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G - && hapd->iface->num_sta_no_short_preamble == 1) - ieee802_11_set_beacons(hapd->iface); - } - -#ifdef CONFIG_IEEE80211N - if (sta->flags & WLAN_STA_HT) { - u16 ht_capab = le_to_host16( - sta->ht_capabilities.data.capabilities_info); - wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities " - "Info: 0x%04x", MAC2STR(sta->addr), ht_capab); - if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) { - if (!sta->no_ht_gf_set) { - sta->no_ht_gf_set = 1; - hapd->iface->num_sta_ht_no_gf++; - } - wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no " - "greenfield, num of non-gf stations %d", - __func__, MAC2STR(sta->addr), - hapd->iface->num_sta_ht_no_gf); - } - if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) { - if (!sta->ht_20mhz_set) { - sta->ht_20mhz_set = 1; - hapd->iface->num_sta_ht_20mhz++; - } - wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, " - "num of 20MHz HT STAs %d", - __func__, MAC2STR(sta->addr), - hapd->iface->num_sta_ht_20mhz); - } - } else { - if (!sta->no_ht_set) { - sta->no_ht_set = 1; - hapd->iface->num_sta_no_ht++; - } - if (hapd->iconf->ieee80211n) { - wpa_printf(MSG_DEBUG, "%s STA " MACSTR - " - no HT, num of non-HT stations %d", - __func__, MAC2STR(sta->addr), - hapd->iface->num_sta_no_ht); - } - } - - if (hostapd_ht_operation_update(hapd->iface) > 0) - ieee802_11_set_beacons(hapd->iface); -#endif /* CONFIG_IEEE80211N */ - - /* get a unique AID */ - if (sta->aid > 0) { - wpa_printf(MSG_DEBUG, " old AID %d", sta->aid); - } else { - for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++) - if (hapd->sta_aid[sta->aid - 1] == NULL) - break; - if (sta->aid > MAX_AID_TABLE_SIZE) { - sta->aid = 0; - resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; - wpa_printf(MSG_ERROR, " no room for more AIDs"); - goto fail; - } else { - hapd->sta_aid[sta->aid - 1] = sta; - wpa_printf(MSG_DEBUG, " new AID %d", sta->aid); - } - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "association OK (aid %d)", sta->aid); - /* Station will be marked associated, after it acknowledges AssocResp - */ - -#ifdef CONFIG_IEEE80211W - if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) { - wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out " - "SA Query procedure", reassoc ? "re" : ""); - /* TODO: Send a protected Disassociate frame to the STA using - * the old key and Reason Code "Previous Authentication no - * longer valid". Make sure this is only sent protected since - * unprotected frame would be received by the STA that is now - * trying to associate. - */ - } -#endif /* CONFIG_IEEE80211W */ - - if (reassoc) { - os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap, - ETH_ALEN); - } - - if (sta->last_assoc_req) - os_free(sta->last_assoc_req); - sta->last_assoc_req = os_malloc(len); - if (sta->last_assoc_req) - os_memcpy(sta->last_assoc_req, mgmt, len); - - /* Make sure that the previously registered inactivity timer will not - * remove the STA immediately. */ - sta->timeout_next = STA_NULLFUNC; - - fail: - os_memset(buf, 0, sizeof(buf)); - reply = (struct ieee80211_mgmt *) buf; - reply->frame_control = - IEEE80211_FC(WLAN_FC_TYPE_MGMT, - (send_deauth ? WLAN_FC_STYPE_DEAUTH : - (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : - WLAN_FC_STYPE_ASSOC_RESP))); - os_memcpy(reply->da, mgmt->sa, ETH_ALEN); - os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); - os_memcpy(reply->bssid, mgmt->bssid, ETH_ALEN); - - send_len = IEEE80211_HDRLEN; - if (send_deauth) { - send_len += sizeof(reply->u.deauth); - reply->u.deauth.reason_code = host_to_le16(resp); - } else { - u8 *p; - send_len += sizeof(reply->u.assoc_resp); - reply->u.assoc_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd, sta, 0)); - reply->u.assoc_resp.status_code = host_to_le16(resp); - reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) - | BIT(14) | BIT(15)); - /* Supported rates */ - p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); - /* Extended supported rates */ - p = hostapd_eid_ext_supp_rates(hapd, p); - if (sta->flags & WLAN_STA_WMM) - p = hostapd_eid_wmm(hapd, p); - - p = hostapd_eid_ht_capabilities_info(hapd, p); - p = hostapd_eid_ht_operation(hapd, p); - -#ifdef CONFIG_IEEE80211R - if (resp == WLAN_STATUS_SUCCESS) { - /* IEEE 802.11r: Mobility Domain Information, Fast BSS - * Transition Information, RSN */ - p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, - buf + sizeof(buf) - p, - sta->auth_alg); - } -#endif /* CONFIG_IEEE80211R */ - -#ifdef CONFIG_IEEE80211W - if (resp == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) - p = hostapd_eid_assoc_comeback_time(hapd, sta, p); -#endif /* CONFIG_IEEE80211W */ - - send_len += p - reply->u.assoc_resp.variable; - } - - if (hostapd_send_mgmt_frame(hapd, reply, send_len, 0) < 0) - perror("handle_assoc: send"); -} - - -static void handle_disassoc(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, size_t len) -{ - struct sta_info *sta; - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) { - printf("handle_disassoc - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d", - MAC2STR(mgmt->sa), - le_to_host16(mgmt->u.disassoc.reason_code)); - - sta = ap_get_sta(hapd, mgmt->sa); - if (sta == NULL) { - printf("Station " MACSTR " trying to disassociate, but it " - "is not associated.\n", MAC2STR(mgmt->sa)); - return; - } - - sta->flags &= ~WLAN_STA_ASSOC; - wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "disassociated"); - sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; - ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); - /* Stop Accounting and IEEE 802.1X sessions, but leave the STA - * authenticated. */ - accounting_sta_stop(hapd, sta); - ieee802_1x_free_station(sta); - hostapd_sta_remove(hapd, sta->addr); - - if (sta->timeout_next == STA_NULLFUNC || - sta->timeout_next == STA_DISASSOC) { - sta->timeout_next = STA_DEAUTH; - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, - hapd, sta); - } - - mlme_disassociate_indication( - hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); -} - - -static void handle_deauth(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, size_t len) -{ - struct sta_info *sta; - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) { - printf("handle_deauth - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - wpa_printf(MSG_DEBUG, "deauthentication: STA=" MACSTR - " reason_code=%d", - MAC2STR(mgmt->sa), - le_to_host16(mgmt->u.deauth.reason_code)); - - sta = ap_get_sta(hapd, mgmt->sa); - if (sta == NULL) { - printf("Station " MACSTR " trying to deauthenticate, but it " - "is not authenticated.\n", MAC2STR(mgmt->sa)); - return; - } - - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, "deauthenticated"); - mlme_deauthenticate_indication( - hapd, sta, le_to_host16(mgmt->u.deauth.reason_code)); - sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; - ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); - ap_free_sta(hapd, sta); -} - - -static void handle_beacon(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, size_t len, - struct hostapd_frame_info *fi) -{ - struct ieee802_11_elems elems; - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) { - printf("handle_beacon - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - (void) ieee802_11_parse_elems(mgmt->u.beacon.variable, - len - (IEEE80211_HDRLEN + - sizeof(mgmt->u.beacon)), &elems, - 0); - - ap_list_process_beacon(hapd->iface, mgmt, &elems, fi); -} - - -#ifdef CONFIG_IEEE80211W - -/* MLME-SAQuery.request */ -void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, - const u8 *addr, const u8 *trans_id) -{ - struct ieee80211_mgmt mgmt; - u8 *end; - - 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 (hostapd_send_mgmt_frame(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) - perror("ieee802_11_send_sa_query_req: send"); -} - - -static void hostapd_sa_query_action(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, size_t len) -{ - struct sta_info *sta; - u8 *end; - int i; - - end = mgmt->u.action.u.sa_query_resp.trans_id + - WLAN_SA_QUERY_TR_ID_LEN; - if (((u8 *) mgmt) + len < end) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " - "frame (len=%lu)", (unsigned long) len); - return; - } - - if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) { - wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " - "Action %d", mgmt->u.action.u.sa_query_resp.action); - return; - } - - 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, - 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 */ - } - - hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "handle_action - unknown action category %d or invalid " - "frame", - mgmt->u.action.category); - if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && - !(mgmt->sa[0] & 0x01)) { - /* - * IEEE 802.11-REVma/D9.0 - 7.3.1.11 - * Return the Action frame to the source without change - * except that MSB of the Category set to 1. - */ - wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " - "frame back to sender"); - os_memcpy(mgmt->da, mgmt->sa, ETH_ALEN); - os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); - os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); - mgmt->u.action.category |= 0x80; - - hostapd_send_mgmt_frame(hapd, mgmt, len, 0); - } -} - - -/** - * ieee802_11_mgmt - process incoming IEEE 802.11 management frames - * @hapd: hostapd BSS data structure (the BSS to which the management frame was - * sent to) - * @buf: management frame data (starting from IEEE 802.11 header) - * @len: length of frame data in octets - * @stype: management frame subtype from frame control field - * @fi: meta data about received frame (signal level, etc.) - * - * Process all incoming IEEE 802.11 management frames. This will be called for - * each frame received from the kernel driver through wlan#ap interface. In - * addition, it can be called to re-inserted pending frames (e.g., when using - * external RADIUS server as an MAC ACL). - */ -void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype, - struct hostapd_frame_info *fi) -{ - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; - int broadcast; - - if (stype == WLAN_FC_STYPE_BEACON) { - handle_beacon(hapd, mgmt, len, fi); - return; - } - - if (fi && fi->passive_scan) - return; - - broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && - mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && - mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; - - if (!broadcast && - os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { - printf("MGMT: BSSID=" MACSTR " not our address\n", - MAC2STR(mgmt->bssid)); - return; - } - - - if (stype == WLAN_FC_STYPE_PROBE_REQ) { - handle_probe_req(hapd, mgmt, len); - return; - } - - if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { - hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "MGMT: DA=" MACSTR " not our address", - MAC2STR(mgmt->da)); - return; - } - - switch (stype) { - case WLAN_FC_STYPE_AUTH: - wpa_printf(MSG_DEBUG, "mgmt::auth"); - handle_auth(hapd, mgmt, len); - break; - case WLAN_FC_STYPE_ASSOC_REQ: - wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); - handle_assoc(hapd, mgmt, len, 0); - break; - case WLAN_FC_STYPE_REASSOC_REQ: - wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); - handle_assoc(hapd, mgmt, len, 1); - break; - case WLAN_FC_STYPE_DISASSOC: - wpa_printf(MSG_DEBUG, "mgmt::disassoc"); - handle_disassoc(hapd, mgmt, len); - break; - case WLAN_FC_STYPE_DEAUTH: - wpa_printf(MSG_DEBUG, "mgmt::deauth"); - handle_deauth(hapd, mgmt, len); - break; - case WLAN_FC_STYPE_ACTION: - wpa_printf(MSG_DEBUG, "mgmt::action"); - handle_action(hapd, mgmt, len); - break; - default: - hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "unknown mgmt frame subtype %d", stype); - break; - } -} - - -static void handle_auth_cb(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, - size_t len, int ok) -{ - u16 auth_alg, auth_transaction, status_code; - struct sta_info *sta; - - if (!ok) { - hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_NOTICE, - "did not acknowledge authentication response"); - return; - } - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { - printf("handle_auth_cb - too short payload (len=%lu)\n", - (unsigned long) len); - return; - } - - auth_alg = le_to_host16(mgmt->u.auth.auth_alg); - auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); - status_code = le_to_host16(mgmt->u.auth.status_code); - - sta = ap_get_sta(hapd, mgmt->da); - if (!sta) { - printf("handle_auth_cb: STA " MACSTR " not found\n", - MAC2STR(mgmt->da)); - return; - } - - if (status_code == WLAN_STATUS_SUCCESS && - ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || - (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "authenticated"); - sta->flags |= WLAN_STA_AUTH; - } -} - - -#ifdef CONFIG_IEEE80211N -static void -hostapd_get_ht_capab(struct hostapd_data *hapd, - struct ht_cap_ie *ht_cap_ie, - struct ht_cap_ie *neg_ht_cap_ie) -{ - u16 cap; - - os_memcpy(neg_ht_cap_ie, ht_cap_ie, sizeof(struct ht_cap_ie)); - cap = le_to_host16(neg_ht_cap_ie->data.capabilities_info); - cap &= hapd->iconf->ht_capab; - cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED); - - /* FIXME: Rx STBC needs to be handled specially */ - cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK); - neg_ht_cap_ie->data.capabilities_info = host_to_le16(cap); -} -#endif /* CONFIG_IEEE80211N */ - - -static void handle_assoc_cb(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, - size_t len, int reassoc, int ok) -{ - u16 status; - struct sta_info *sta; - int new_assoc = 1; -#ifdef CONFIG_IEEE80211N - struct ht_cap_ie ht_cap; -#endif /* CONFIG_IEEE80211N */ - struct ht_cap_ie *ht_cap_ptr = NULL; - int set_flags, flags_and, flags_or; - - if (!ok) { - hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "did not acknowledge association response"); - return; - } - - if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : - sizeof(mgmt->u.assoc_resp))) { - printf("handle_assoc_cb(reassoc=%d) - too short payload " - "(len=%lu)\n", reassoc, (unsigned long) len); - return; - } - - if (reassoc) - status = le_to_host16(mgmt->u.reassoc_resp.status_code); - else - status = le_to_host16(mgmt->u.assoc_resp.status_code); - - sta = ap_get_sta(hapd, mgmt->da); - if (!sta) { - printf("handle_assoc_cb: STA " MACSTR " not found\n", - MAC2STR(mgmt->da)); - return; - } - - if (status != WLAN_STATUS_SUCCESS) - goto fail; - - /* Stop previous accounting session, if one is started, and allocate - * new session id for the new session. */ - accounting_sta_stop(hapd, sta); - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, - "associated (aid %d)", - sta->aid); - - if (sta->flags & WLAN_STA_ASSOC) - new_assoc = 0; - sta->flags |= WLAN_STA_ASSOC; - - if (reassoc) - mlme_reassociate_indication(hapd, sta); - else - mlme_associate_indication(hapd, sta); - -#ifdef CONFIG_IEEE80211N - if (sta->flags & WLAN_STA_HT) { - ht_cap_ptr = &ht_cap; - hostapd_get_ht_capab(hapd, &sta->ht_capabilities, ht_cap_ptr); - } -#endif /* CONFIG_IEEE80211N */ - -#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. - */ - hostapd_sta_remove(hapd, sta->addr); - - if (hostapd_sta_add(hapd->conf->iface, hapd, sta->addr, sta->aid, - sta->capability, sta->supported_rates, - sta->supported_rates_len, 0, sta->listen_interval, - ht_cap_ptr)) - { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_NOTICE, - "Could not add STA to kernel driver"); - } - - if (sta->eapol_sm == NULL) { - /* - * This STA does not use RADIUS server for EAP authentication, - * so bind it to the selected VLAN interface now, since the - * interface selection is not going to change anymore. - */ - ap_sta_bind_vlan(hapd, sta, 0); - } else if (sta->vlan_id) { - /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ - ap_sta_bind_vlan(hapd, sta, 0); - } - - set_flags = WLAN_STA_SHORT_PREAMBLE | WLAN_STA_WMM | WLAN_STA_MFP; - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && - sta->flags & WLAN_STA_AUTHORIZED) - set_flags |= WLAN_STA_AUTHORIZED; - flags_or = sta->flags & set_flags; - flags_and = sta->flags | ~set_flags; - hostapd_sta_set_flags(hapd, sta->addr, sta->flags, - flags_or, flags_and); - - if (sta->auth_alg == WLAN_AUTH_FT) - wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); - else - wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); - hostapd_new_assoc_sta(hapd, sta, !new_assoc); - - ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); - - fail: - /* Copy of the association request is not needed anymore */ - if (sta->last_assoc_req) { - os_free(sta->last_assoc_req); - sta->last_assoc_req = NULL; - } -} - - -/** - * ieee802_11_mgmt_cb - Process management frame TX status callback - * @hapd: hostapd BSS data structure (the BSS from which the management frame - * was sent from) - * @buf: management frame data (starting from IEEE 802.11 header) - * @len: length of frame data in octets - * @stype: management frame subtype from frame control field - * @ok: Whether the frame was ACK'ed - */ -void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, - u16 stype, int ok) -{ - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; - - switch (stype) { - case WLAN_FC_STYPE_AUTH: - wpa_printf(MSG_DEBUG, "mgmt::auth cb"); - handle_auth_cb(hapd, mgmt, len, ok); - break; - case WLAN_FC_STYPE_ASSOC_RESP: - wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb"); - handle_assoc_cb(hapd, mgmt, len, 0, ok); - break; - case WLAN_FC_STYPE_REASSOC_RESP: - wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb"); - handle_assoc_cb(hapd, mgmt, len, 1, ok); - break; - case WLAN_FC_STYPE_PROBE_RESP: - wpa_printf(MSG_DEBUG, "mgmt::proberesp cb"); - break; - case WLAN_FC_STYPE_DEAUTH: - /* ignore */ - break; - case WLAN_FC_STYPE_ACTION: - wpa_printf(MSG_DEBUG, "mgmt::action cb"); - break; - default: - printf("unknown mgmt cb frame subtype %d\n", stype); - break; - } -} - - -static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx, - void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - hapd->tkip_countermeasures = 0; - hostapd_set_countermeasures(hapd, 0); - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended"); -} - - -static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) -{ - struct sta_info *sta; - - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated"); - - wpa_auth_countermeasures_start(hapd->wpa_auth); - hapd->tkip_countermeasures = 1; - hostapd_set_countermeasures(hapd, 1); - wpa_gtk_rekey(hapd->wpa_auth); - eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); - eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, - hapd, NULL); - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - hostapd_sta_deauth(hapd, sta->addr, - WLAN_REASON_MICHAEL_MIC_FAILURE); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | - WLAN_STA_AUTHORIZED); - hostapd_sta_remove(hapd, sta->addr); - } -} - - -void ieee80211_michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, - int local) -{ - time_t now; - - if (addr && local) { - struct sta_info *sta = ap_get_sta(hapd, addr); - if (sta != NULL) { - wpa_auth_sta_local_mic_failure_report(sta->wpa_sm); - hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, - "Michael MIC failure detected in " - "received frame"); - mlme_michaelmicfailure_indication(hapd, addr); - } else { - wpa_printf(MSG_DEBUG, - "MLME-MICHAELMICFAILURE.indication " - "for not associated STA (" MACSTR - ") ignored", MAC2STR(addr)); - return; - } - } - - time(&now); - if (now > hapd->michael_mic_failure + 60) { - hapd->michael_mic_failures = 1; - } else { - hapd->michael_mic_failures++; - if (hapd->michael_mic_failures > 1) - ieee80211_tkip_countermeasures_start(hapd); - } - hapd->michael_mic_failure = now; -} - - -int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) -{ - /* TODO */ - return 0; -} - - -int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen) -{ - /* TODO */ - return 0; -} - -#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/hostapd/ieee802_11.h b/contrib/hostapd/hostapd/ieee802_11.h deleted file mode 100644 index ca8ef93c09..0000000000 --- a/contrib/hostapd/hostapd/ieee802_11.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2006, Jouni Malinen - * Copyright (c) 2007-2008, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef IEEE802_11_H -#define IEEE802_11_H - -#include "ieee802_11_defs.h" -#include "ieee802_11_common.h" - -struct hostapd_frame_info { - u32 phytype; - u32 channel; - u32 datarate; - u32 ssi_signal; - - unsigned int passive_scan:1; -}; - -struct hostapd_iface; -struct hostapd_data; -struct sta_info; - -void ieee802_11_send_deauth(struct hostapd_data *hapd, u8 *addr, u16 reason); -void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, - u16 stype, struct hostapd_frame_info *fi); -void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, - u16 stype, int ok); -void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len); -void ieee80211_michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, - int local); -int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); -int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen); -u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, - int probe); -u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); -u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); -u8 * hostapd_eid_ht_capabilities_info(struct hostapd_data *hapd, u8 *eid); -u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); -int hostapd_ht_operation_update(struct hostapd_iface *iface); -void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, - const u8 *addr, const u8 *trans_id); - -#endif /* IEEE802_11_H */ diff --git a/contrib/hostapd/hostapd/logwatch/README b/contrib/hostapd/hostapd/logwatch/README deleted file mode 100644 index 3cba511909..0000000000 --- a/contrib/hostapd/hostapd/logwatch/README +++ /dev/null @@ -1,9 +0,0 @@ -Logwatch is a utility for analyzing system logs and provide a human -readable summary. This directory has a configuration file and a log -analyzer script for parsing hostapd system log entries for logwatch. -These files can be installed by copying them to following locations: - -/etc/log.d/conf/services/hostapd.conf -/etc/log.d/scripts/services/hostapd - -More information about logwatch is available from http://www.logwatch.org/ diff --git a/contrib/hostapd/hostapd/main.c b/contrib/hostapd/hostapd/main.c new file mode 100644 index 0000000000..5a1b0a9e92 --- /dev/null +++ b/contrib/hostapd/hostapd/main.c @@ -0,0 +1,720 @@ +/* + * hostapd / main() + * Copyright (c) 2002-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#ifndef CONFIG_NATIVE_WINDOWS +#include +#include +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/random.h" +#include "crypto/tls.h" +#include "common/version.h" +#include "drivers/driver.h" +#include "eap_server/eap.h" +#include "eap_server/tncs.h" +#include "ap/hostapd.h" +#include "ap/ap_config.h" +#include "ap/ap_drv_ops.h" +#include "config_file.h" +#include "eap_register.h" +#include "ctrl_iface.h" + + +struct hapd_global { + void **drv_priv; + size_t drv_count; +}; + +static struct hapd_global global; + + +#ifndef CONFIG_NO_HOSTAPD_LOGGER +static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, + int level, const char *txt, size_t len) +{ + struct hostapd_data *hapd = ctx; + char *format, *module_str; + int maxlen; + int conf_syslog_level, conf_stdout_level; + unsigned int conf_syslog, conf_stdout; + + maxlen = len + 100; + format = os_malloc(maxlen); + if (!format) + return; + + if (hapd && hapd->conf) { + conf_syslog_level = hapd->conf->logger_syslog_level; + conf_stdout_level = hapd->conf->logger_stdout_level; + conf_syslog = hapd->conf->logger_syslog; + conf_stdout = hapd->conf->logger_stdout; + } else { + conf_syslog_level = conf_stdout_level = 0; + conf_syslog = conf_stdout = (unsigned int) -1; + } + + switch (module) { + case HOSTAPD_MODULE_IEEE80211: + module_str = "IEEE 802.11"; + break; + case HOSTAPD_MODULE_IEEE8021X: + module_str = "IEEE 802.1X"; + break; + case HOSTAPD_MODULE_RADIUS: + module_str = "RADIUS"; + break; + case HOSTAPD_MODULE_WPA: + module_str = "WPA"; + break; + case HOSTAPD_MODULE_DRIVER: + module_str = "DRIVER"; + break; + case HOSTAPD_MODULE_IAPP: + module_str = "IAPP"; + break; + case HOSTAPD_MODULE_MLME: + module_str = "MLME"; + break; + default: + module_str = NULL; + break; + } + + if (hapd && hapd->conf && addr) + os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s", + hapd->conf->iface, MAC2STR(addr), + module_str ? " " : "", module_str, txt); + else if (hapd && hapd->conf) + os_snprintf(format, maxlen, "%s:%s%s %s", + hapd->conf->iface, module_str ? " " : "", + module_str, txt); + else if (addr) + os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", + MAC2STR(addr), module_str ? " " : "", + module_str, txt); + else + os_snprintf(format, maxlen, "%s%s%s", + module_str, module_str ? ": " : "", txt); + + if ((conf_stdout & module) && level >= conf_stdout_level) { + wpa_debug_print_timestamp(); + wpa_printf(MSG_INFO, "%s", format); + } + +#ifndef CONFIG_NATIVE_WINDOWS + if ((conf_syslog & module) && level >= conf_syslog_level) { + int priority; + switch (level) { + case HOSTAPD_LEVEL_DEBUG_VERBOSE: + case HOSTAPD_LEVEL_DEBUG: + priority = LOG_DEBUG; + break; + case HOSTAPD_LEVEL_INFO: + priority = LOG_INFO; + break; + case HOSTAPD_LEVEL_NOTICE: + priority = LOG_NOTICE; + break; + case HOSTAPD_LEVEL_WARNING: + priority = LOG_WARNING; + break; + default: + priority = LOG_INFO; + break; + } + syslog(priority, "%s", format); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + os_free(format); +} +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ + + +/** + * hostapd_driver_init - Preparate driver interface + */ +static int hostapd_driver_init(struct hostapd_iface *iface) +{ + struct wpa_init_params params; + size_t i; + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_bss_config *conf = hapd->conf; + u8 *b = conf->bssid; + struct wpa_driver_capa capa; + + if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) { + wpa_printf(MSG_ERROR, "No hostapd driver wrapper available"); + return -1; + } + + /* Initialize the driver interface */ + if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5])) + b = NULL; + + os_memset(¶ms, 0, sizeof(params)); + for (i = 0; wpa_drivers[i]; i++) { + if (wpa_drivers[i] != hapd->driver) + continue; + + if (global.drv_priv[i] == NULL && + wpa_drivers[i]->global_init) { + 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); + return -1; + } + } + + params.global_priv = global.drv_priv[i]; + break; + } + params.bssid = b; + params.ifname = hapd->conf->iface; + params.ssid = hapd->conf->ssid.ssid; + params.ssid_len = hapd->conf->ssid.ssid_len; + params.test_socket = hapd->conf->test_socket; + params.use_pae_group_addr = hapd->conf->use_pae_group_addr; + + params.num_bridge = hapd->iface->num_bss; + params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *)); + if (params.bridge == NULL) + return -1; + for (i = 0; i < hapd->iface->num_bss; i++) { + struct hostapd_data *bss = hapd->iface->bss[i]; + if (bss->conf->bridge[0]) + params.bridge[i] = bss->conf->bridge; + } + + params.own_addr = hapd->own_addr; + + hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms); + os_free(params.bridge); + if (hapd->drv_priv == NULL) { + wpa_printf(MSG_ERROR, "%s driver initialization failed.", + hapd->driver->name); + hapd->driver = NULL; + return -1; + } + + if (hapd->driver->get_capa && + hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) { + iface->drv_flags = capa.flags; + iface->probe_resp_offloads = capa.probe_resp_offloads; + iface->extended_capa = capa.extended_capa; + iface->extended_capa_mask = capa.extended_capa_mask; + iface->extended_capa_len = capa.extended_capa_len; + iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; + } + + return 0; +} + + +/** + * hostapd_interface_init - Read configuration file and init BSS data + * + * This function is used to parse configuration file for a full interface (one + * or more BSSes sharing the same radio) and allocate memory for the BSS + * interfaces. No actiual driver operations are started. + */ +static struct hostapd_iface * +hostapd_interface_init(struct hapd_interfaces *interfaces, + const char *config_fname, int debug) +{ + struct hostapd_iface *iface; + int k; + + wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname); + iface = hostapd_init(interfaces, config_fname); + if (!iface) + return NULL; + iface->interfaces = interfaces; + + for (k = 0; k < debug; k++) { + if (iface->bss[0]->conf->logger_stdout_level > 0) + iface->bss[0]->conf->logger_stdout_level--; + } + + if (iface->conf->bss[0]->iface[0] == '\0' && + !hostapd_drv_none(iface->bss[0])) { + wpa_printf(MSG_ERROR, "Interface name not specified in %s", + config_fname); + hostapd_interface_deinit_free(iface); + return NULL; + } + + return iface; +} + + +/** + * handle_term - SIGINT and SIGTERM handler to terminate hostapd process + */ +static void handle_term(int sig, void *signal_ctx) +{ + wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig); + eloop_terminate(); +} + + +#ifndef CONFIG_NATIVE_WINDOWS + +static int handle_reload_iface(struct hostapd_iface *iface, void *ctx) +{ + if (hostapd_reload_config(iface) < 0) { + wpa_printf(MSG_WARNING, "Failed to read new configuration " + "file - continuing with old."); + } + return 0; +} + + +/** + * handle_reload - SIGHUP handler to reload configuration + */ +static void handle_reload(int sig, void *signal_ctx) +{ + struct hapd_interfaces *interfaces = signal_ctx; + wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration", + sig); + hostapd_for_each_interface(interfaces, handle_reload_iface, NULL); +} + + +static void handle_dump_state(int sig, void *signal_ctx) +{ + /* Not used anymore - ignore signal */ +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static int hostapd_global_init(struct hapd_interfaces *interfaces, + const char *entropy_file) +{ + int i; + + os_memset(&global, 0, sizeof(global)); + + hostapd_logger_register_cb(hostapd_logger_cb); + + if (eap_server_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; + } + + random_init(entropy_file); + +#ifndef CONFIG_NATIVE_WINDOWS + eloop_register_signal(SIGHUP, handle_reload, interfaces); + eloop_register_signal(SIGUSR1, handle_dump_state, interfaces); +#endif /* CONFIG_NATIVE_WINDOWS */ + eloop_register_signal_terminate(handle_term, interfaces); + +#ifndef CONFIG_NATIVE_WINDOWS + openlog("hostapd", 0, LOG_DAEMON); +#endif /* CONFIG_NATIVE_WINDOWS */ + + for (i = 0; wpa_drivers[i]; i++) + global.drv_count++; + if (global.drv_count == 0) { + wpa_printf(MSG_ERROR, "No drivers enabled"); + return -1; + } + global.drv_priv = os_calloc(global.drv_count, sizeof(void *)); + if (global.drv_priv == NULL) + return -1; + + return 0; +} + + +static void hostapd_global_deinit(const char *pid_file) +{ + int i; + + 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); + global.drv_priv = NULL; + +#ifdef EAP_SERVER_TNC + tncs_global_deinit(); +#endif /* EAP_SERVER_TNC */ + + random_deinit(); + + eloop_destroy(); + +#ifndef CONFIG_NATIVE_WINDOWS + closelog(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + eap_server_unregister_methods(); + + os_daemonize_terminate(pid_file); +} + + +static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize, + const char *pid_file) +{ +#ifdef EAP_SERVER_TNC + int tnc = 0; + size_t i, k; + + for (i = 0; !tnc && i < ifaces->count; i++) { + for (k = 0; k < ifaces->iface[i]->num_bss; k++) { + if (ifaces->iface[i]->bss[0]->conf->tnc) { + tnc++; + break; + } + } + } + + if (tnc && tncs_global_init() < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize TNCS"); + return -1; + } +#endif /* EAP_SERVER_TNC */ + + if (daemonize && os_daemonize(pid_file)) { + perror("daemon"); + return -1; + } + + eloop_run(); + + return 0; +} + + +static void show_version(void) +{ + fprintf(stderr, + "hostapd v" VERSION_STR "\n" + "User space daemon for IEEE 802.11 AP management,\n" + "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" + "Copyright (c) 2002-2014, Jouni Malinen " + "and contributors\n"); +} + + +static void usage(void) +{ + show_version(); + fprintf(stderr, + "\n" + "usage: hostapd [-hdBKtv] [-P ] [-e ] " + "\\\n" + " [-g ] [-G ] \\\n" + " \n" + "\n" + "options:\n" + " -h show this usage\n" + " -d show more debug messages (-dd for even more)\n" + " -B run daemon in the background\n" + " -e entropy file\n" + " -g global control interface path\n" + " -G group for control interfaces\n" + " -P PID file\n" + " -K include key data in debug messages\n" +#ifdef CONFIG_DEBUG_FILE + " -f log output to debug file instead of stdout\n" +#endif /* CONFIG_DEBUG_FILE */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + " -T = record to Linux tracing in addition to logging\n" + " (records all messages regardless of debug verbosity)\n" +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + " -t include timestamps in some debug messages\n" + " -v show hostapd version\n"); + + exit(1); +} + + +static const char * hostapd_msg_ifname_cb(void *ctx) +{ + struct hostapd_data *hapd = ctx; + if (hapd && hapd->iconf && hapd->iconf->bss && + hapd->iconf->num_bss > 0 && hapd->iconf->bss[0]) + return hapd->iconf->bss[0]->iface; + return NULL; +} + + +static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces, + const char *path) +{ + char *pos; + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = os_strdup(path); + if (interfaces->global_iface_path == NULL) + return -1; + pos = os_strrchr(interfaces->global_iface_path, '/'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "No '/' in the global control interface " + "file"); + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = NULL; + return -1; + } + + *pos = '\0'; + interfaces->global_iface_name = pos + 1; + + return 0; +} + + +static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces, + const char *group) +{ +#ifndef CONFIG_NATIVE_WINDOWS + struct group *grp; + grp = getgrnam(group); + if (grp == NULL) { + wpa_printf(MSG_ERROR, "Unknown group '%s'", group); + return -1; + } + interfaces->ctrl_iface_group = grp->gr_gid; +#endif /* CONFIG_NATIVE_WINDOWS */ + return 0; +} + + +int main(int argc, char *argv[]) +{ + struct hapd_interfaces interfaces; + int ret = 1; + size_t i, j; + int c, debug = 0, daemonize = 0; + char *pid_file = NULL; + const char *log_file = NULL; + const char *entropy_file = NULL; + char **bss_config = NULL, **tmp_bss; + size_t num_bss_configs = 0; +#ifdef CONFIG_DEBUG_LINUX_TRACING + int enable_trace_dbg = 0; +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + + if (os_program_init()) + return -1; + + os_memset(&interfaces, 0, sizeof(interfaces)); + interfaces.reload_config = hostapd_reload_config; + interfaces.config_read_cb = hostapd_config_read; + interfaces.for_each_interface = hostapd_for_each_interface; + interfaces.ctrl_iface_init = hostapd_ctrl_iface_init; + interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit; + interfaces.driver_init = hostapd_driver_init; + interfaces.global_iface_path = NULL; + interfaces.global_iface_name = NULL; + interfaces.global_ctrl_sock = -1; + + for (;;) { + c = getopt(argc, argv, "b:Bde:f:hKP:Ttvg:G:"); + if (c < 0) + break; + switch (c) { + case 'h': + usage(); + break; + case 'd': + debug++; + if (wpa_debug_level > 0) + wpa_debug_level--; + break; + case 'B': + daemonize++; + break; + case 'e': + entropy_file = optarg; + break; + case 'f': + log_file = optarg; + break; + case 'K': + wpa_debug_show_keys++; + break; + case 'P': + os_free(pid_file); + pid_file = os_rel2abs_path(optarg); + break; + case 't': + wpa_debug_timestamp++; + break; +#ifdef CONFIG_DEBUG_LINUX_TRACING + case 'T': + enable_trace_dbg = 1; + break; +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + case 'v': + show_version(); + exit(1); + break; + case 'g': + if (hostapd_get_global_ctrl_iface(&interfaces, optarg)) + return -1; + break; + case 'G': + if (hostapd_get_ctrl_iface_group(&interfaces, optarg)) + return -1; + break; + case 'b': + tmp_bss = os_realloc_array(bss_config, + num_bss_configs + 1, + sizeof(char *)); + if (tmp_bss == NULL) + goto out; + bss_config = tmp_bss; + bss_config[num_bss_configs++] = optarg; + break; + default: + usage(); + break; + } + } + + if (optind == argc && interfaces.global_iface_path == NULL && + num_bss_configs == 0) + usage(); + + wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb); + + if (log_file) + wpa_debug_open_file(log_file); +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (enable_trace_dbg) { + int tret = wpa_debug_open_linux_tracing(); + if (tret) { + wpa_printf(MSG_ERROR, "Failed to enable trace logging"); + return -1; + } + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + + interfaces.count = argc - optind; + if (interfaces.count || num_bss_configs) { + interfaces.iface = os_calloc(interfaces.count + num_bss_configs, + sizeof(struct hostapd_iface *)); + if (interfaces.iface == NULL) { + wpa_printf(MSG_ERROR, "malloc failed"); + return -1; + } + } + + if (hostapd_global_init(&interfaces, entropy_file)) { + wpa_printf(MSG_ERROR, "Failed to initilize global context"); + return -1; + } + + /* Allocate and parse configuration for full interface files */ + for (i = 0; i < interfaces.count; i++) { + interfaces.iface[i] = hostapd_interface_init(&interfaces, + argv[optind + i], + debug); + if (!interfaces.iface[i]) { + wpa_printf(MSG_ERROR, "Failed to initialize interface"); + goto out; + } + } + + /* Allocate and parse configuration for per-BSS files */ + for (i = 0; i < num_bss_configs; i++) { + struct hostapd_iface *iface; + char *fname; + + wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]); + fname = os_strchr(bss_config[i], ':'); + if (fname == NULL) { + wpa_printf(MSG_ERROR, + "Invalid BSS config identifier '%s'", + bss_config[i]); + goto out; + } + *fname++ = '\0'; + iface = hostapd_interface_init_bss(&interfaces, bss_config[i], + fname, debug); + if (iface == NULL) + goto out; + for (j = 0; j < interfaces.count; j++) { + if (interfaces.iface[j] == iface) + break; + } + if (j == interfaces.count) { + struct hostapd_iface **tmp; + tmp = os_realloc_array(interfaces.iface, + interfaces.count + 1, + sizeof(struct hostapd_iface *)); + if (tmp == NULL) { + hostapd_interface_deinit_free(iface); + goto out; + } + interfaces.iface = tmp; + interfaces.iface[interfaces.count++] = iface; + } + } + + /* + * Enable configured interfaces. Depending on channel configuration, + * this may complete full initialization before returning or use a + * callback mechanism to complete setup in case of operations like HT + * co-ex scans, ACS, or DFS are needed to determine channel parameters. + * In such case, the interface will be enabled from eloop context within + * hostapd_global_run(). + */ + interfaces.terminate_on_error = interfaces.count; + for (i = 0; i < interfaces.count; i++) { + if (hostapd_driver_init(interfaces.iface[i]) || + hostapd_setup_interface(interfaces.iface[i])) + goto out; + } + + hostapd_global_ctrl_iface_init(&interfaces); + + if (hostapd_global_run(&interfaces, daemonize, pid_file)) { + wpa_printf(MSG_ERROR, "Failed to start eloop"); + goto out; + } + + ret = 0; + + out: + hostapd_global_ctrl_iface_deinit(&interfaces); + /* Deinitialize all interfaces */ + for (i = 0; i < interfaces.count; i++) + hostapd_interface_deinit_free(interfaces.iface[i]); + os_free(interfaces.iface); + + hostapd_global_deinit(pid_file); + os_free(pid_file); + + if (log_file) + wpa_debug_close_file(); + wpa_debug_close_linux_tracing(); + + os_free(bss_config); + + os_program_deinit(); + + return ret; +} diff --git a/contrib/hostapd/hostapd/nt_password_hash.c b/contrib/hostapd/hostapd/nt_password_hash.c deleted file mode 100644 index 9df307d54a..0000000000 --- a/contrib/hostapd/hostapd/nt_password_hash.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * hostapd - Plaintext password to NtPasswordHash - * Copyright (c) 2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "ms_funcs.h" - - -int main(int argc, char *argv[]) -{ - unsigned char password_hash[16]; - size_t i; - char *password, buf[64], *pos; - - if (argc > 1) - password = argv[1]; - else { - if (fgets(buf, sizeof(buf), stdin) == NULL) { - printf("Failed to read password\n"); - return 1; - } - buf[sizeof(buf) - 1] = '\0'; - pos = buf; - while (*pos != '\0') { - if (*pos == '\r' || *pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - password = buf; - } - - nt_password_hash((u8 *) password, strlen(password), password_hash); - for (i = 0; i < sizeof(password_hash); i++) - printf("%02x", password_hash[i]); - printf("\n"); - - return 0; -} diff --git a/contrib/hostapd/hostapd/prism54.h b/contrib/hostapd/hostapd/prism54.h deleted file mode 100644 index cb0a9a19ba..0000000000 --- a/contrib/hostapd/hostapd/prism54.h +++ /dev/null @@ -1,177 +0,0 @@ -#ifndef PRISM54_H -#define PRISM54_H - -struct ieee802_3_hdr_s { - unsigned char da[6]; - unsigned char sa[6]; - unsigned short type; -} __attribute__ ((packed)); - -typedef struct ieee802_3_hdr_s ieee802_3_hdr; - -#define PIMOP_GET 0 -#define PIMOP_SET 1 -#define PIMOP_RESPONSE 2 -#define PIMOP_ERROR 3 -#define PIMOP_TRAP 4 - -struct pimdev_hdr_s { - int op; - unsigned long oid; -} __attribute__ ((packed)); - -typedef struct pimdev_hdr_s pimdev_hdr; - -#define DOT11_OID_ATTACHMENT 0x19000003 - -/* really need to check */ -#define DOT11_PKT_BEACON 0x80 -#define DOT11_PKT_ASSOC_RESP 0x10 -#define DOT11_PKT_REASSOC_RESP 0x30 -#define DOT11_PKT_PROBE_RESP 0x50 - -struct obj_attachment_hdr { - char type; - char reserved; - short id; - short size; -} __attribute__ ((packed)); - -struct obj_attachment { - char type; - char reserved; - short id; - short size; - char data[1]; -} __attribute__ ((packed)); - -#define DOT11_OID_MLMEAUTOLEVEL 0x19000001 -#define DOT11_MLME_AUTO 0 -#define DOT11_MLME_INTERMEDIATE 0x01000000 -#define DOT11_MLME_EXTENDED 0x02000000 - -#define DOT11_OID_DEAUTHENTICATE 0x18000000 -#define DOT11_OID_AUTHENTICATE 0x18000001 -#define DOT11_OID_DISASSOCIATE 0x18000002 -#define DOT11_OID_ASSOCIATE 0x18000003 -#define DOT11_OID_BEACON 0x18000005 -#define DOT11_OID_PROBE 0x18000006 -#define DOT11_OID_REASSOCIATE 0x1800000b - -struct obj_mlme { - char address[6]; - short id; - short state; - short code; -} __attribute__ ((packed)); - -#define DOT11_OID_DEAUTHENTICATEEX 0x18000007 -#define DOT11_OID_AUTHENTICATEEX 0x18000008 -#define DOT11_OID_DISASSOCIATEEX 0x18000009 -#define DOT11_OID_ASSOCIATEEX 0x1800000a -#define DOT11_OID_REASSOCIATEEX 0x1800000c - -struct obj_mlmeex { - char address[6]; - short id; - short state; - short code; - short size; - char data[1]; -} __attribute__ ((packed)); - -#define DOT11_OID_STAKEY 0x12000008 - -#define DOT11_PRIV_WEP 0 -#define DOT11_PRIV_TKIP 1 - -/* endian reversed to bigger endian */ -#define DOT11_STAKEY_OPTION_DEFAULTKEY 0x100 - -struct obj_stakey { - char address[6]; - char keyid; - char reserved; - short options; - char type; - char length; - char key[32]; -} __attribute__ ((packed)); - -#define DOT11_OID_DEFKEYID 0x12000003 -#define DOT11_OID_DEFKEY1 0x12000004 -#define DOT11_OID_DEFKEY2 0x12000005 -#define DOT11_OID_DEFKEY3 0x12000006 -#define DOT11_OID_DEFKEY4 0x12000007 - -struct obj_key { - char type; - char length; - char key[32]; -} __attribute__ ((packed)); - -#define DOT11_OID_STASC 0x1200000a - -struct obj_stasc { - char address[6]; - char keyid; - char tx_sc; - unsigned long sc_high; - unsigned short sc_low; -} __attribute__ ((packed)); - -#define DOT11_OID_CLIENTS 0x15000001 -#define DOT11_OID_CLIENTSASSOCIATED 0x15000002 -#define DOT11_OID_CLIENTST 0x15000003 -#define DOT11_OID_CLIENTEND 0x150007d9 -#define DOT11_OID_CLIENTFIND 0x150007db - -#define DOT11_NODE_UNKNOWN -#define DOT11_NODE_CLIENT -#define DOT11_NODE_AP - -/* endian reversed to bigger endian */ -#define DOT11_STATE_NONE 0 -#define DOT11_STATE_AUTHING 0x100 -#define DOT11_STATE_AUTH 0x200 -#define DOT11_STATE_ASSOCING 0x300 -#define DOT11_STATE_REASSOCING 0x400 -#define DOT11_STATE_ASSOC 0x500 -#define DOT11_STATE_WDS 0x600 - -struct obj_sta { - char address[6]; - char pad[2]; - char state; - char node; - short age; - char reserved1; - char rssi; - char rate; - char reserved2; -} __attribute__ ((packed)); - -#define DOT11_OID_SSID 0x10000002 -#define DOT11_OID_SSIDOVERRIDE 0x10000006 - -struct obj_ssid { - char length; - char octets[33]; -} __attribute__ ((packed)); - -#define DOT11_OID_EAPAUTHSTA 0x150007de -#define DOT11_OID_EAPUNAUTHSTA 0x150007df -/* not in 38801 datasheet??? */ -#define DOT11_OID_DOT1XENABLE 0x150007e0 -#define DOT11_OID_MICFAILURE 0x150007e1 -#define DOT11_OID_AUTHENABLE 0x12000000 -#define DOT11_OID_PRIVACYINVOKED 0x12000001 -#define DOT11_OID_EXUNENCRYPTED 0x12000002 - -#define DOT11_AUTH_OS 0x01000000 -#define DOT11_AUTH_SK 0x02000000 -#define DOT11_AUTH_BOTH 0x03000000 - -#define DOT11_BOOL_TRUE 0x01000000 - -#endif /* PRISM54_H */ diff --git a/contrib/hostapd/hostapd/priv_netlink.h b/contrib/hostapd/hostapd/priv_netlink.h deleted file mode 100644 index d1f6f663eb..0000000000 --- a/contrib/hostapd/hostapd/priv_netlink.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef PRIV_NETLINK_H -#define PRIV_NETLINK_H - -/* Private copy of needed Linux netlink/rtnetlink definitions. - * - * This should be replaced with user space header once one is available with C - * library, etc.. - */ - -#ifndef IFLA_IFNAME -#define IFLA_IFNAME 3 -#endif -#ifndef IFLA_WIRELESS -#define IFLA_WIRELESS 11 -#endif - -#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 NLMSG_ALIGNTO 4 -#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)) -#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr))) -#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0))) - -#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))) - - -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/contrib/hostapd/hostapd/radiotap.h b/contrib/hostapd/hostapd/radiotap.h deleted file mode 100644 index 508264c4cf..0000000000 --- a/contrib/hostapd/hostapd/radiotap.h +++ /dev/null @@ -1,242 +0,0 @@ -/* $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 - -/* 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/contrib/hostapd/hostapd/sta_info.h b/contrib/hostapd/hostapd/sta_info.h deleted file mode 100644 index e835970923..0000000000 --- a/contrib/hostapd/hostapd/sta_info.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * hostapd / Station table - * Copyright (c) 2002-2008, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef STA_INFO_H -#define STA_INFO_H - -int ap_for_each_sta(struct hostapd_data *hapd, - int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, - void *ctx), - void *ctx); -struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); -void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); -void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); -void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); -void hostapd_free_stas(struct hostapd_data *hapd); -void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); -void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, - u32 session_timeout); -void ap_sta_no_session_timeout(struct hostapd_data *hapd, - struct sta_info *sta); -struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); -void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, - u16 reason); -void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, - u16 reason); -int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, - int old_vlanid); -void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); -void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); -int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); - -#endif /* STA_INFO_H */ diff --git a/contrib/hostapd/hostapd/vlan_init.h b/contrib/hostapd/hostapd/vlan_init.h deleted file mode 100644 index cf55ac2462..0000000000 --- a/contrib/hostapd/hostapd/vlan_init.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * hostapd / VLAN initialization - * Copyright 2003, Instant802 Networks, Inc. - * Copyright 2005, Devicescape Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef VLAN_INIT_H -#define VLAN_INIT_H - -int vlan_init(struct hostapd_data *hapd); -void vlan_deinit(struct hostapd_data *hapd); -int vlan_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, - struct hostapd_bss_config *oldbss); -struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, - struct hostapd_vlan *vlan, - int vlan_id); -int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); -int vlan_setup_encryption_dyn(struct hostapd_data *hapd, - struct hostapd_ssid *mssid, - const char *dyn_vlan); - -#endif /* VLAN_INIT_H */ diff --git a/contrib/hostapd/hostapd/wme.h b/contrib/hostapd/hostapd/wme.h deleted file mode 100644 index e06b5bcab3..0000000000 --- a/contrib/hostapd/hostapd/wme.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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 - -/* - * 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 */ - -} __attribute__ ((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; -} __attribute__ ((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 */ - -} __attribute__ ((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; -} __attribute__ ((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 */ -}; - -struct ieee80211_mgmt; - -u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid); -int hostapd_eid_wmm_valid(struct hostapd_data *hapd, u8 *eid, size_t len); -int hostapd_wmm_sta_config(struct hostapd_data *hapd, struct sta_info *sta); -void hostapd_wmm_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, - size_t len); - -#endif /* WME_H */ diff --git a/contrib/hostapd/hostapd/wps_hostapd.c b/contrib/hostapd/hostapd/wps_hostapd.c deleted file mode 100644 index 818767ef46..0000000000 --- a/contrib/hostapd/hostapd/wps_hostapd.c +++ /dev/null @@ -1,1029 +0,0 @@ -/* - * hostapd / WPS integration - * Copyright (c) 2008, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "hostapd.h" -#include "driver.h" -#include "eloop.h" -#include "uuid.h" -#include "wpa_ctrl.h" -#include "ieee802_11_defs.h" -#include "sta_info.h" -#include "eapol_sm.h" -#include "wps/wps.h" -#include "wps/wps_defs.h" -#include "wps/wps_dev_attr.h" -#include "wps_hostapd.h" - - -#ifdef CONFIG_WPS_UPNP -#include "wps/wps_upnp.h" -static int hostapd_wps_upnp_init(struct hostapd_data *hapd, - struct wps_context *wps); -static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd); -#endif /* CONFIG_WPS_UPNP */ - - -static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, - size_t psk_len) -{ - struct hostapd_data *hapd = ctx; - struct hostapd_wpa_psk *p; - struct hostapd_ssid *ssid = &hapd->conf->ssid; - - wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " - MACSTR, MAC2STR(mac_addr)); - wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); - - if (psk_len != PMK_LEN) { - wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu", - (unsigned long) psk_len); - return -1; - } - - /* Add the new PSK to runtime PSK list */ - p = os_zalloc(sizeof(*p)); - if (p == NULL) - return -1; - os_memcpy(p->addr, mac_addr, ETH_ALEN); - os_memcpy(p->psk, psk, PMK_LEN); - - p->next = ssid->wpa_psk; - ssid->wpa_psk = p; - - if (ssid->wpa_psk_file) { - FILE *f; - char hex[PMK_LEN * 2 + 1]; - /* Add the new PSK to PSK list file */ - f = fopen(ssid->wpa_psk_file, "a"); - if (f == NULL) { - wpa_printf(MSG_DEBUG, "Failed to add the PSK to " - "'%s'", ssid->wpa_psk_file); - return -1; - } - - wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len); - fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex); - fclose(f); - } - - return 0; -} - - -static int hostapd_wps_set_ie_cb(void *ctx, const u8 *beacon_ie, - size_t beacon_ie_len, const u8 *probe_resp_ie, - size_t probe_resp_ie_len) -{ - struct hostapd_data *hapd = ctx; - - os_free(hapd->wps_beacon_ie); - if (beacon_ie_len == 0) { - hapd->wps_beacon_ie = NULL; - hapd->wps_beacon_ie_len = 0; - } else { - hapd->wps_beacon_ie = os_malloc(beacon_ie_len); - if (hapd->wps_beacon_ie == NULL) { - hapd->wps_beacon_ie_len = 0; - return -1; - } - os_memcpy(hapd->wps_beacon_ie, beacon_ie, beacon_ie_len); - hapd->wps_beacon_ie_len = beacon_ie_len; - } - hostapd_set_wps_beacon_ie(hapd, hapd->wps_beacon_ie, - hapd->wps_beacon_ie_len); - - os_free(hapd->wps_probe_resp_ie); - if (probe_resp_ie_len == 0) { - hapd->wps_probe_resp_ie = NULL; - hapd->wps_probe_resp_ie_len = 0; - } else { - hapd->wps_probe_resp_ie = os_malloc(probe_resp_ie_len); - if (hapd->wps_probe_resp_ie == NULL) { - hapd->wps_probe_resp_ie_len = 0; - return -1; - } - os_memcpy(hapd->wps_probe_resp_ie, probe_resp_ie, - probe_resp_ie_len); - hapd->wps_probe_resp_ie_len = probe_resp_ie_len; - } - hostapd_set_wps_probe_resp_ie(hapd, hapd->wps_probe_resp_ie, - hapd->wps_probe_resp_ie_len); - - return 0; -} - - -static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, - const struct wps_device_data *dev) -{ - struct hostapd_data *hapd = ctx; - char uuid[40], txt[400]; - int len; - if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) - return; - wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid); - len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED - "%s " MACSTR " [%s|%s|%s|%s|%s|%d-%08X-%d]", - uuid, MAC2STR(dev->mac_addr), dev->device_name, - dev->manufacturer, dev->model_name, - dev->model_number, dev->serial_number, - dev->categ, dev->oui, dev->sub_categ); - if (len > 0 && len < (int) sizeof(txt)) - wpa_msg(hapd, MSG_INFO, "%s", txt); - - if (hapd->conf->wps_pin_requests) { - FILE *f; - struct os_time t; - f = fopen(hapd->conf->wps_pin_requests, "a"); - if (f == NULL) - return; - os_get_time(&t); - fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s" - "\t%d-%08X-%d\n", - t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name, - dev->manufacturer, dev->model_name, dev->model_number, - dev->serial_number, - dev->categ, dev->oui, dev->sub_categ); - fclose(f); - } -} - - -static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, - const u8 *uuid_e) -{ - struct hostapd_data *hapd = ctx; - char uuid[40]; - if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) - return; - wpa_msg(hapd, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s", - MAC2STR(mac_addr), uuid); -} - - -static int str_starts(const char *str, const char *start) -{ - return os_strncmp(str, start, os_strlen(start)) == 0; -} - - -static void wps_reload_config(void *eloop_data, void *user_ctx) -{ - struct hostapd_iface *iface = eloop_data; - - wpa_printf(MSG_DEBUG, "WPS: Reload configuration data"); - if (hostapd_reload_config(iface) < 0) { - wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " - "configuration"); - } -} - - -static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) -{ - struct hostapd_data *hapd = ctx; - FILE *oconf, *nconf; - size_t len, i; - char *tmp_fname; - char buf[1024]; - int multi_bss; - int wpa; - - wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", - cred->cred_attr, cred->cred_attr_len); - - wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings"); - wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len); - wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x", - cred->auth_type); - wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type); - wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx); - wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", - cred->key, cred->key_len); - wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, - MAC2STR(cred->mac_addr)); - - if ((hapd->conf->wps_cred_processing == 1 || - hapd->conf->wps_cred_processing == 2) && cred->cred_attr) { - size_t blen = cred->cred_attr_len * 2 + 1; - char *_buf = os_malloc(blen); - if (_buf) { - wpa_snprintf_hex(_buf, blen, - cred->cred_attr, cred->cred_attr_len); - wpa_msg(hapd, MSG_INFO, "%s%s", - WPS_EVENT_NEW_AP_SETTINGS, _buf); - os_free(_buf); - } - } else - wpa_msg(hapd, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS); - - if (hapd->conf->wps_cred_processing == 1) - return 0; - - 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_pwd_auth_fail(struct hostapd_data *hapd, - struct wps_event_pwd_auth_fail *data) -{ - FILE *f; - - if (!data->enrollee) - return; - - /* - * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup - * if this happens multiple times. - */ - hapd->ap_pin_failures++; - if (hapd->ap_pin_failures < 4) - return; - - wpa_msg(hapd, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED); - hapd->wps->ap_setup_locked = 1; - - wps_registrar_update_ie(hapd->wps->registrar); - - if (hapd->conf->wps_cred_processing == 1) - return; - - f = fopen(hapd->iface->config_fname, "a"); - if (f == NULL) { - wpa_printf(MSG_WARNING, "WPS: Could not append to the current " - "configuration file"); - return; - } - - fprintf(f, "# WPS AP Setup Locked based on possible attack\n"); - fprintf(f, "ap_setup_locked=1\n"); - fclose(f); - - /* TODO: dualband AP may need to update multiple configuration files */ - - wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); -} - - -static void hostapd_wps_event_cb(void *ctx, enum wps_event event, - union wps_event_data *data) -{ - struct hostapd_data *hapd = ctx; - - if (event == WPS_EV_PWD_AUTH_FAIL) - hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); -} - - -static void hostapd_wps_clear_ies(struct hostapd_data *hapd) -{ - os_free(hapd->wps_beacon_ie); - hapd->wps_beacon_ie = NULL; - hapd->wps_beacon_ie_len = 0; - hostapd_set_wps_beacon_ie(hapd, NULL, 0); - - os_free(hapd->wps_probe_resp_ie); - hapd->wps_probe_resp_ie = NULL; - hapd->wps_probe_resp_ie_len = 0; - hostapd_set_wps_probe_resp_ie(hapd, NULL, 0); -} - - -int hostapd_init_wps(struct hostapd_data *hapd, - struct hostapd_bss_config *conf) -{ - struct wps_context *wps; - struct wps_registrar_config cfg; - - if (conf->wps_state == 0) { - hostapd_wps_clear_ies(hapd); - return 0; - } - - wps = os_zalloc(sizeof(*wps)); - if (wps == NULL) - return -1; - - wps->cred_cb = hostapd_wps_cred_cb; - wps->event_cb = hostapd_wps_event_cb; - wps->cb_ctx = hapd; - - os_memset(&cfg, 0, sizeof(cfg)); - wps->wps_state = hapd->conf->wps_state; - wps->ap_setup_locked = hapd->conf->ap_setup_locked; - if (is_nil_uuid(hapd->conf->uuid)) { - uuid_gen_mac_addr(hapd->own_addr, wps->uuid); - wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address", - wps->uuid, UUID_LEN); - } else - os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN); - wps->ssid_len = hapd->conf->ssid.ssid_len; - os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len); - wps->ap = 1; - os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN); - wps->dev.device_name = hapd->conf->device_name ? - os_strdup(hapd->conf->device_name) : NULL; - wps->dev.manufacturer = hapd->conf->manufacturer ? - os_strdup(hapd->conf->manufacturer) : NULL; - wps->dev.model_name = hapd->conf->model_name ? - os_strdup(hapd->conf->model_name) : NULL; - wps->dev.model_number = hapd->conf->model_number ? - os_strdup(hapd->conf->model_number) : NULL; - wps->dev.serial_number = hapd->conf->serial_number ? - os_strdup(hapd->conf->serial_number) : NULL; - if (hapd->conf->config_methods) { - char *m = hapd->conf->config_methods; - if (os_strstr(m, "label")) - wps->config_methods |= WPS_CONFIG_LABEL; - if (os_strstr(m, "display")) - wps->config_methods |= WPS_CONFIG_DISPLAY; - if (os_strstr(m, "push_button")) - wps->config_methods |= WPS_CONFIG_PUSHBUTTON; - if (os_strstr(m, "keypad")) - wps->config_methods |= WPS_CONFIG_KEYPAD; - } - if (hapd->conf->device_type) { - char *pos; - u8 oui[4]; - /* -- */ - wps->dev.categ = atoi(hapd->conf->device_type); - pos = os_strchr(hapd->conf->device_type, '-'); - if (pos == NULL) { - wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); - os_free(wps); - return -1; - } - pos++; - if (hexstr2bin(pos, oui, 4)) { - wpa_printf(MSG_ERROR, "WPS: Invalid device_type OUI"); - os_free(wps); - return -1; - } - wps->dev.oui = WPA_GET_BE32(oui); - pos = os_strchr(pos, '-'); - if (pos == NULL) { - wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); - os_free(wps); - return -1; - } - pos++; - wps->dev.sub_categ = atoi(pos); - } - wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); - wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? - WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ - - if (conf->wpa & WPA_PROTO_RSN) { - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) - wps->auth_types |= WPS_AUTH_WPA2PSK; - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) - wps->auth_types |= WPS_AUTH_WPA2; - - if (conf->rsn_pairwise & WPA_CIPHER_CCMP) - wps->encr_types |= WPS_ENCR_AES; - if (conf->rsn_pairwise & WPA_CIPHER_TKIP) - wps->encr_types |= WPS_ENCR_TKIP; - } - - if (conf->wpa & WPA_PROTO_WPA) { - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) - wps->auth_types |= WPS_AUTH_WPAPSK; - if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) - wps->auth_types |= WPS_AUTH_WPA; - - if (conf->wpa_pairwise & WPA_CIPHER_CCMP) - wps->encr_types |= WPS_ENCR_AES; - if (conf->wpa_pairwise & WPA_CIPHER_TKIP) - wps->encr_types |= WPS_ENCR_TKIP; - } - - if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { - wps->encr_types |= WPS_ENCR_NONE; - wps->auth_types |= WPS_AUTH_OPEN; - } else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) { - wps->encr_types |= WPS_ENCR_WEP; - if (conf->auth_algs & WPA_AUTH_ALG_OPEN) - wps->auth_types |= WPS_AUTH_OPEN; - if (conf->auth_algs & WPA_AUTH_ALG_SHARED) - wps->auth_types |= WPS_AUTH_SHARED; - } else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) { - wps->auth_types |= WPS_AUTH_OPEN; - if (conf->default_wep_key_len) - wps->encr_types |= WPS_ENCR_WEP; - else - wps->encr_types |= WPS_ENCR_NONE; - } - - if (conf->ssid.wpa_psk_file) { - /* Use per-device PSKs */ - } else if (conf->ssid.wpa_passphrase) { - wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase); - wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase); - } else if (conf->ssid.wpa_psk) { - wps->network_key = os_malloc(2 * PMK_LEN + 1); - if (wps->network_key == NULL) { - os_free(wps); - return -1; - } - wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1, - conf->ssid.wpa_psk->psk, PMK_LEN); - wps->network_key_len = 2 * PMK_LEN; - } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) { - wps->network_key = os_malloc(conf->ssid.wep.len[0]); - if (wps->network_key == NULL) { - os_free(wps); - return -1; - } - os_memcpy(wps->network_key, conf->ssid.wep.key[0], - conf->ssid.wep.len[0]); - wps->network_key_len = conf->ssid.wep.len[0]; - } - - if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) { - /* Override parameters to enable security by default */ - wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; - wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; - } - - wps->ap_settings = conf->ap_settings; - wps->ap_settings_len = conf->ap_settings_len; - - cfg.new_psk_cb = hostapd_wps_new_psk_cb; - cfg.set_ie_cb = hostapd_wps_set_ie_cb; - cfg.pin_needed_cb = hostapd_wps_pin_needed_cb; - cfg.reg_success_cb = hostapd_wps_reg_success_cb; - cfg.cb_ctx = hapd; - cfg.skip_cred_build = conf->skip_cred_build; - cfg.extra_cred = conf->extra_cred; - cfg.extra_cred_len = conf->extra_cred_len; - cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) && - conf->skip_cred_build; - 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 */ - - hapd->wps = wps; - - return 0; -} - - -void hostapd_deinit_wps(struct hostapd_data *hapd) -{ - if (hapd->wps == NULL) - return; -#ifdef CONFIG_WPS_UPNP - hostapd_wps_upnp_deinit(hapd); -#endif /* CONFIG_WPS_UPNP */ - wps_registrar_deinit(hapd->wps->registrar); - os_free(hapd->wps->network_key); - wps_device_data_free(&hapd->wps->dev); - wps_free_pending_msgs(hapd->wps->upnp_msgs); - os_free(hapd->wps); - hapd->wps = NULL; - hostapd_wps_clear_ies(hapd); -} - - -int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin, 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); -} - - -void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr, - const u8 *ie, size_t ie_len) -{ - struct wpabuf *wps_ie; - const u8 *end, *pos, *wps; - - if (hapd->wps == NULL) - return; - - pos = ie; - end = ie + ie_len; - wps = NULL; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - return; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - WPA_GET_BE32(&pos[2]) == WPS_DEV_OUI_WFA) { - wps = pos; - break; - } - pos += 2 + pos[1]; - } - - if (wps == NULL) - return; /* No WPS IE in Probe Request */ - - wps_ie = wpabuf_alloc(ie_len); - if (wps_ie == NULL) - return; - - /* There may be multiple WPS IEs in the message, so need to concatenate - * their WPS Data fields */ - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - WPA_GET_BE32(&pos[2]) == WPS_DEV_OUI_WFA) - wpabuf_put_data(wps_ie, pos + 6, pos[1] - 4); - pos += 2 + pos[1]; - } - - if (wpabuf_len(wps_ie) > 0) { - wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie); -#ifdef CONFIG_WPS_UPNP - /* FIX: what exactly should be included in the WLANEvent? - * WPS attributes? Full ProbeReq frame? */ - upnp_wps_device_send_wlan_event(hapd->wps_upnp, addr, - UPNP_WPS_WLANEVENT_TYPE_PROBE, - wps_ie); -#endif /* CONFIG_WPS_UPNP */ - } - - wpabuf_free(wps_ie); -} - - -#ifdef CONFIG_WPS_UPNP - -static struct wpabuf * -hostapd_rx_req_get_device_info(void *priv, struct upnp_wps_peer *peer) -{ - struct hostapd_data *hapd = priv; - struct wps_config cfg; - struct wps_data *wps; - enum wsc_op_code op_code; - struct wpabuf *m1; - - /* - * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS - * registration over UPnP with the AP acting as an Enrollee. It should - * be noted that this is frequently used just to get the device data, - * i.e., there may not be any intent to actually complete the - * registration. - */ - - if (peer->wps) - wps_deinit(peer->wps); - - os_memset(&cfg, 0, sizeof(cfg)); - cfg.wps = hapd->wps; - cfg.pin = (u8 *) hapd->conf->ap_pin; - cfg.pin_len = os_strlen(hapd->conf->ap_pin); - wps = wps_init(&cfg); - if (wps == NULL) - return NULL; - - m1 = wps_get_msg(wps, &op_code); - if (m1 == NULL) { - wps_deinit(wps); - return NULL; - } - - peer->wps = wps; - - return m1; -} - - -static struct wpabuf * -hostapd_rx_req_put_message(void *priv, struct upnp_wps_peer *peer, - const struct wpabuf *msg) -{ - enum wps_process_res res; - enum wsc_op_code op_code; - - /* PutMessage: msg = InMessage, return OutMessage */ - res = wps_process_msg(peer->wps, WSC_UPnP, msg); - if (res == WPS_FAILURE) - return NULL; - return wps_get_msg(peer->wps, &op_code); -} - - -static struct wpabuf * -hostapd_rx_req_get_ap_settings(void *priv, const struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); - return NULL; -} - - -static int hostapd_rx_req_set_ap_settings(void *priv, const struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); - return -1; -} - - -static int hostapd_rx_req_del_ap_settings(void *priv, const struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); - return -1; -} - - -static struct wpabuf * -hostapd_rx_req_get_sta_settings(void *priv, const struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); - return NULL; -} - - -static int hostapd_rx_req_set_sta_settings(void *priv, - const struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); - return -1; -} - - -static int hostapd_rx_req_del_sta_settings(void *priv, - const struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); - return -1; -} - - -static int hostapd_rx_req_put_wlan_response( - void *priv, enum upnp_wps_wlanevent_type ev_type, - const u8 *mac_addr, const struct wpabuf *msg, - enum wps_msg_type msg_type) -{ - struct hostapd_data *hapd = priv; - struct sta_info *sta; - struct upnp_pending_message *p; - - wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr=" - MACSTR, ev_type, MAC2STR(mac_addr)); - wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage", - wpabuf_head(msg), wpabuf_len(msg)); - if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) { - wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected " - "PutWLANResponse WLANEventType %d", ev_type); - return -1; - } - - /* - * EAP response to ongoing to WPS Registration. Send it to EAP-WSC - * server implementation for delivery to the peer. - */ - - sta = ap_get_sta(hapd, mac_addr); - if (!sta) { - /* - * Workaround - Intel wsccmd uses bogus NewWLANEventMAC: - * Pick STA that is in an ongoing WPS registration without - * checking the MAC address. - */ - wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based " - "on NewWLANEventMAC; try wildcard match"); - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS)) - break; - } - } - - if (!sta) { - wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found"); - return 0; - } - - p = os_zalloc(sizeof(*p)); - if (p == NULL) - return -1; - os_memcpy(p->addr, sta->addr, ETH_ALEN); - p->msg = wpabuf_dup(msg); - p->type = msg_type; - p->next = hapd->wps->upnp_msgs; - hapd->wps->upnp_msgs = p; - - return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap); -} - - -static int hostapd_rx_req_set_selected_registrar(void *priv, - const struct wpabuf *msg) -{ - struct hostapd_data *hapd = priv; - return wps_registrar_set_selected_registrar(hapd->wps->registrar, msg); -} - - -static int hostapd_rx_req_reboot_ap(void *priv, const struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); - return -1; -} - - -static int hostapd_rx_req_reset_ap(void *priv, const struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); - return -1; -} - - -static int hostapd_rx_req_reboot_sta(void *priv, const struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); - return -1; -} - - -static int hostapd_rx_req_reset_sta(void *priv, const struct wpabuf *msg) -{ - wpa_printf(MSG_DEBUG, "WPS UPnP: TODO %s", __func__); - return -1; -} - - -static int hostapd_wps_upnp_init(struct hostapd_data *hapd, - struct wps_context *wps) -{ - struct upnp_wps_device_ctx *ctx; - - if (!hapd->conf->upnp_iface) - return 0; - ctx = os_zalloc(sizeof(*ctx)); - if (ctx == NULL) - return -1; - - ctx->rx_req_get_device_info = hostapd_rx_req_get_device_info; - ctx->rx_req_put_message = hostapd_rx_req_put_message; - ctx->rx_req_get_ap_settings = hostapd_rx_req_get_ap_settings; - ctx->rx_req_set_ap_settings = hostapd_rx_req_set_ap_settings; - ctx->rx_req_del_ap_settings = hostapd_rx_req_del_ap_settings; - ctx->rx_req_get_sta_settings = hostapd_rx_req_get_sta_settings; - ctx->rx_req_set_sta_settings = hostapd_rx_req_set_sta_settings; - ctx->rx_req_del_sta_settings = hostapd_rx_req_del_sta_settings; - ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response; - ctx->rx_req_set_selected_registrar = - hostapd_rx_req_set_selected_registrar; - ctx->rx_req_reboot_ap = hostapd_rx_req_reboot_ap; - ctx->rx_req_reset_ap = hostapd_rx_req_reset_ap; - ctx->rx_req_reboot_sta = hostapd_rx_req_reboot_sta; - ctx->rx_req_reset_sta = hostapd_rx_req_reset_sta; - - hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd); - if (hapd->wps_upnp == NULL) { - os_free(ctx); - return -1; - } - wps->wps_upnp = hapd->wps_upnp; - - if (upnp_wps_device_start(hapd->wps_upnp, hapd->conf->upnp_iface)) { - upnp_wps_device_deinit(hapd->wps_upnp); - hapd->wps_upnp = NULL; - return -1; - } - - return 0; -} - - -static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd) -{ - upnp_wps_device_deinit(hapd->wps_upnp); -} - -#endif /* CONFIG_WPS_UPNP */ diff --git a/contrib/hostapd/hostapd/wps_hostapd.h b/contrib/hostapd/hostapd/wps_hostapd.h deleted file mode 100644 index e949bee87a..0000000000 --- a/contrib/hostapd/hostapd/wps_hostapd.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * hostapd / WPS integration - * Copyright (c) 2008, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef WPS_HOSTAPD_H -#define WPS_HOSTAPD_H - -#ifdef CONFIG_WPS - -int hostapd_init_wps(struct hostapd_data *hapd, - struct hostapd_bss_config *conf); -void hostapd_deinit_wps(struct hostapd_data *hapd); -int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin, int timeout); -int hostapd_wps_button_pushed(struct hostapd_data *hapd); -void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, const u8 *addr, - const u8 *ie, size_t ie_len); - -#else /* CONFIG_WPS */ - -static inline int hostapd_init_wps(struct hostapd_data *hapd, - struct hostapd_bss_config *conf) -{ - return 0; -} - -static inline void hostapd_deinit_wps(struct hostapd_data *hapd) -{ -} - -static inline void hostapd_wps_probe_req_rx(struct hostapd_data *hapd, - const u8 *addr, - const u8 *ie, size_t ie_len) -{ -} -#endif /* CONFIG_WPS */ - -#endif /* WPS_HOSTAPD_H */ diff --git a/contrib/hostapd/patches/openssl-0.9.8x-tls-extensions.patch b/contrib/hostapd/patches/openssl-0.9.8x-tls-extensions.patch new file mode 100644 index 0000000000..d1c0dbe6c3 --- /dev/null +++ b/contrib/hostapd/patches/openssl-0.9.8x-tls-extensions.patch @@ -0,0 +1,396 @@ +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 +(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300). + +OpenSSL 0.9.8x 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.8x.orig/ssl/s3_clnt.c openssl-0.9.8x/ssl/s3_clnt.c +--- openssl-0.9.8x.orig/ssl/s3_clnt.c 2011-12-26 21:38:28.000000000 +0200 ++++ openssl-0.9.8x/ssl/s3_clnt.c 2012-07-07 10:46:31.501140621 +0300 +@@ -757,6 +757,21 @@ 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) + { +@@ -2725,11 +2740,8 @@ 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.8x.orig/ssl/s3_srvr.c openssl-0.9.8x/ssl/s3_srvr.c +--- openssl-0.9.8x.orig/ssl/s3_srvr.c 2012-02-16 17:21:17.000000000 +0200 ++++ openssl-0.9.8x/ssl/s3_srvr.c 2012-07-07 10:46:31.501140621 +0300 +@@ -1009,6 +1009,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 +@@ -1147,16 +1200,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.8x.orig/ssl/ssl_err.c openssl-0.9.8x/ssl/ssl_err.c +--- openssl-0.9.8x.orig/ssl/ssl_err.c 2012-03-12 16:50:55.000000000 +0200 ++++ openssl-0.9.8x/ssl/ssl_err.c 2012-07-07 10:46:31.501140621 +0300 +@@ -264,6 +264,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_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"}, + {0,NULL} + }; + +diff -upr openssl-0.9.8x.orig/ssl/ssl.h openssl-0.9.8x/ssl/ssl.h +--- openssl-0.9.8x.orig/ssl/ssl.h 2012-03-12 16:50:55.000000000 +0200 ++++ openssl-0.9.8x/ssl/ssl.h 2012-07-07 10:46:31.501140621 +0300 +@@ -344,6 +344,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 +@@ -362,6 +363,9 @@ typedef struct ssl_cipher_st + + 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 + { +@@ -1050,6 +1054,18 @@ struct ssl_st + + /* 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 +@@ -1663,6 +1679,15 @@ void *SSL_COMP_get_compression_methods(v + 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. +@@ -1866,6 +1891,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_SESSION_TICKET_EXT 213 + + /* Reason codes. */ + #define SSL_R_APP_DATA_IN_HANDSHAKE 100 +diff -upr openssl-0.9.8x.orig/ssl/ssl_sess.c openssl-0.9.8x/ssl/ssl_sess.c +--- openssl-0.9.8x.orig/ssl/ssl_sess.c 2010-02-01 18:48:40.000000000 +0200 ++++ openssl-0.9.8x/ssl/ssl_sess.c 2012-07-07 10:46:31.501140621 +0300 +@@ -712,6 +712,61 @@ 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_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; +diff -upr openssl-0.9.8x.orig/ssl/t1_lib.c openssl-0.9.8x/ssl/t1_lib.c +--- openssl-0.9.8x.orig/ssl/t1_lib.c 2012-01-04 16:25:10.000000000 +0200 ++++ openssl-0.9.8x/ssl/t1_lib.c 2012-07-07 10:47:31.153140501 +0300 +@@ -106,6 +106,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 + ssl3_free(s); + } + +@@ -206,8 +212,23 @@ unsigned char *ssl_add_clienthello_tlsex + int ticklen; + if (!s->new_session && 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 + */ +@@ -221,6 +242,7 @@ unsigned char *ssl_add_clienthello_tlsex + ret += ticklen; + } + } ++ skip_ext: + + if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp && + s->version != DTLS1_VERSION) +@@ -486,6 +508,15 @@ int ssl_parse_clienthello_tlsext(SSL *s, + return 0; + renegotiate_seen = 1; + } ++ 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->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) + { +@@ -663,6 +694,12 @@ int ssl_parse_serverhello_tlsext(SSL *s, + } + 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)) + { +@@ -920,6 +957,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); + } +diff -upr openssl-0.9.8x.orig/ssl/tls1.h openssl-0.9.8x/ssl/tls1.h +--- openssl-0.9.8x.orig/ssl/tls1.h 2009-11-08 16:51:54.000000000 +0200 ++++ openssl-0.9.8x/ssl/tls1.h 2012-07-07 10:46:31.501140621 +0300 +@@ -401,6 +401,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 extension struct */ ++struct tls_session_ticket_ext_st ++ { ++ unsigned short length; ++ void *data; ++ }; ++ + #ifdef __cplusplus + } + #endif +diff -upr openssl-0.9.8x.orig/util/ssleay.num openssl-0.9.8x/util/ssleay.num +--- openssl-0.9.8x.orig/util/ssleay.num 2008-06-05 13:57:21.000000000 +0300 ++++ openssl-0.9.8x/util/ssleay.num 2012-07-07 10:46:31.505140623 +0300 +@@ -242,3 +242,5 @@ SSL_set_SSL_CTX + 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/contrib/hostapd/hostapd/accounting.c b/contrib/hostapd/src/ap/accounting.c similarity index 67% rename from contrib/hostapd/hostapd/accounting.c rename to contrib/hostapd/src/ap/accounting.c index ce71678af4..6290d3f397 100644 --- a/contrib/hostapd/hostapd/accounting.c +++ b/contrib/hostapd/src/ap/accounting.c @@ -1,26 +1,23 @@ /* * hostapd / RADIUS Accounting - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2009, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" -#include "hostapd.h" +#include "utils/common.h" +#include "utils/eloop.h" #include "radius/radius.h" #include "radius/radius_client.h" -#include "eloop.h" -#include "accounting.h" +#include "hostapd.h" #include "ieee802_1x.h" -#include "driver.h" +#include "ap_config.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "accounting.h" /* Default interval in seconds for polling TX/RX octets from the driver if @@ -28,8 +25,8 @@ * 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 void accounting_sta_interim(struct hostapd_data *hapd, + struct sta_info *sta); static struct radius_msg * accounting_msg(struct hostapd_data *hapd, @@ -41,11 +38,12 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, u8 *val; size_t len; int i; + struct wpabuf *b; 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"); + wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); return NULL; } @@ -56,7 +54,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, 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"); + wpa_printf(MSG_INFO, "Could not add Acct-Session-Id"); goto fail; } } else { @@ -65,20 +63,32 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, status_type)) { - printf("Could not add Acct-Status-Type\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); goto fail; } - if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, + if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr, + RADIUS_ATTR_ACCT_AUTHENTIC) && + !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"); + wpa_printf(MSG_INFO, "Could not add Acct-Authentic"); goto fail; } if (sta) { + /* Use 802.1X identity if available */ val = ieee802_1x_get_identity(sta->eapol_sm, &len); + + /* Use RADIUS ACL identity if 802.1X provides no identity */ + if (!val && sta->identity) { + val = (u8 *) sta->identity; + len = os_strlen(sta->identity); + } + + /* Use STA MAC if neither 802.1X nor RADIUS ACL provided + * identity */ if (!val) { os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(sta->addr)); @@ -88,75 +98,16 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, len)) { - printf("Could not add User-Name\n"); + wpa_printf(MSG_INFO, "Could not add User-Name"); 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"); + if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta, + msg) < 0) 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); @@ -165,17 +116,34 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, val, len)) { - printf("Could not add Class\n"); + wpa_printf(MSG_INFO, "Could not add Class"); goto fail; } } + + b = ieee802_1x_get_radius_cui(sta->eapol_sm); + if (b && + !radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + wpabuf_head(b), wpabuf_len(b))) { + wpa_printf(MSG_ERROR, "Could not add CUI"); + goto fail; + } + + if (!b && sta->radius_cui && + !radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + (u8 *) sta->radius_cui, + os_strlen(sta->radius_cui))) { + wpa_printf(MSG_ERROR, "Could not add CUI from ACL"); + goto fail; + } } return msg; fail: radius_msg_free(msg); - os_free(msg); return NULL; } @@ -184,7 +152,7 @@ static int accounting_sta_update_stats(struct hostapd_data *hapd, struct sta_info *sta, struct hostap_sta_driver_data *data) { - if (hostapd_read_sta_data(hapd, data, sta->addr)) + if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) return -1; if (sta->last_rx_bytes > data->rx_bytes) @@ -238,16 +206,15 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) 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); + os_get_reltime(&sta->acct_session_start); sta->last_rx_bytes = sta->last_tx_bytes = 0; sta->acct_input_gigawords = sta->acct_output_gigawords = 0; - hostapd_sta_clear_stats(hapd, sta->addr); + hostapd_drv_sta_clear_stats(hapd, sta->addr); if (!hapd->conf->radius->acct_server) return; @@ -260,8 +227,9 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) hapd, sta); msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); - if (msg) - radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr); + if (msg && + radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0) + radius_msg_free(msg); sta->acct_session_started = 1; } @@ -273,6 +241,8 @@ static void accounting_sta_report(struct hostapd_data *hapd, struct radius_msg *msg; int cause = sta->acct_terminate_cause; struct hostap_sta_driver_data data; + struct os_reltime now_r, diff; + struct os_time now; u32 gigawords; if (!hapd->conf->radius->acct_server) @@ -282,13 +252,16 @@ static void accounting_sta_report(struct hostapd_data *hapd, stop ? RADIUS_ACCT_STATUS_TYPE_STOP : RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); if (!msg) { - printf("Could not create RADIUS Accounting message\n"); + wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message"); return; } + os_get_reltime(&now_r); + os_get_time(&now); + os_reltime_sub(&now_r, &sta->acct_session_start, &diff); 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"); + diff.sec)) { + wpa_printf(MSG_INFO, "Could not add Acct-Session-Time"); goto fail; } @@ -296,19 +269,19 @@ static void accounting_sta_report(struct hostapd_data *hapd, if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_INPUT_PACKETS, data.rx_packets)) { - printf("Could not add Acct-Input-Packets\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets"); 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"); + wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets"); 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"); + wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); goto fail; } gigawords = sta->acct_input_gigawords; @@ -319,13 +292,13 @@ static void accounting_sta_report(struct hostapd_data *hapd, !radius_msg_add_attr_int32( msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, gigawords)) { - printf("Could not add Acct-Input-Gigawords\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords"); 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"); + wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); goto fail; } gigawords = sta->acct_output_gigawords; @@ -336,14 +309,14 @@ static void accounting_sta_report(struct hostapd_data *hapd, !radius_msg_add_attr_int32( msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, gigawords)) { - printf("Could not add Acct-Output-Gigawords\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); goto fail; } } if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, - time(NULL))) { - printf("Could not add Event-Timestamp\n"); + now.sec)) { + wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); goto fail; } @@ -353,18 +326,18 @@ static void accounting_sta_report(struct hostapd_data *hapd, if (stop && cause && !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, cause)) { - printf("Could not add Acct-Terminate-Cause\n"); + wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); goto fail; } - radius_client_send(hapd->radius, msg, - stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, - sta->addr); + if (radius_client_send(hapd->radius, msg, + stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, + sta->addr) < 0) + goto fail; return; fail: radius_msg_free(msg); - os_free(msg); } @@ -373,7 +346,8 @@ static void accounting_sta_report(struct hostapd_data *hapd, * @hapd: hostapd BSS data * @sta: The station */ -void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) +static void accounting_sta_interim(struct hostapd_data *hapd, + struct sta_info *sta) { if (sta->acct_session_started) accounting_sta_report(hapd, sta, 0); @@ -400,7 +374,7 @@ void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) } -static void accounting_sta_get_id(struct hostapd_data *hapd, +void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta) { sta->acct_session_id_lo = hapd->acct_session_id_lo++; @@ -425,14 +399,13 @@ accounting_receive(struct radius_msg *msg, struct radius_msg *req, const u8 *shared_secret, size_t shared_secret_len, void *data) { - if (msg->hdr->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { - printf("Unknown RADIUS message code\n"); + if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { + wpa_printf(MSG_INFO, "Unknown RADIUS message code"); 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"); + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped"); return RADIUS_RX_INVALID_AUTHENTICATOR; } @@ -458,13 +431,13 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) 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"); + wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); radius_msg_free(msg); - os_free(msg); return; } - radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); + if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) + radius_msg_free(msg); } @@ -475,9 +448,12 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) */ int accounting_init(struct hostapd_data *hapd) { + struct os_time now; + /* 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); + os_get_time(&now); + hapd->acct_session_id_hi = now.sec; if (radius_client_register(hapd->radius, RADIUS_ACCT, accounting_receive, hapd)) @@ -497,14 +473,3 @@ void accounting_deinit(struct hostapd_data *hapd) { accounting_report_state(hapd, 0); } - - -int accounting_reconfig(struct hostapd_data *hapd, - struct hostapd_config *oldconf) -{ - if (!hapd->radius_client_reconfigured) - return 0; - - accounting_deinit(hapd); - return accounting_init(hapd); -} diff --git a/contrib/hostapd/src/ap/accounting.h b/contrib/hostapd/src/ap/accounting.h new file mode 100644 index 0000000000..dcc54ee94b --- /dev/null +++ b/contrib/hostapd/src/ap/accounting.h @@ -0,0 +1,44 @@ +/* + * hostapd / RADIUS Accounting + * Copyright (c) 2002-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ACCOUNTING_H +#define ACCOUNTING_H + +#ifdef CONFIG_NO_ACCOUNTING +static inline void accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + +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_get_id(struct hostapd_data *hapd, struct sta_info *sta); +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/contrib/hostapd/src/ap/acs.c b/contrib/hostapd/src/ap/acs.c new file mode 100644 index 0000000000..f58b091e09 --- /dev/null +++ b/contrib/hostapd/src/ap/acs.c @@ -0,0 +1,802 @@ +/* + * ACS - Automatic Channel Selection module + * Copyright (c) 2011, Atheros Communications + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include + +#include "utils/common.h" +#include "utils/list.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "ap_config.h" +#include "hw_features.h" +#include "acs.h" + +/* + * Automatic Channel Selection + * =========================== + * + * More info at + * ------------ + * http://wireless.kernel.org/en/users/Documentation/acs + * + * How to use + * ---------- + * - make sure you have CONFIG_ACS=y in hostapd's .config + * - use channel=0 or channel=acs to enable ACS + * + * How does it work + * ---------------- + * 1. passive scans are used to collect survey data + * (it is assumed that scan trigger collection of survey data in driver) + * 2. interference factor is calculated for each channel + * 3. ideal channel is picked depending on channel width by using adjacent + * channel interference factors + * + * Known limitations + * ----------------- + * - Current implementation depends heavily on the amount of time willing to + * spend gathering survey data during hostapd startup. Short traffic bursts + * may be missed and a suboptimal channel may be picked. + * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS + * + * Todo / Ideas + * ------------ + * - implement other interference computation methods + * - BSS/RSSI based + * - spectral scan based + * (should be possibly to hook this up with current ACS scans) + * - add wpa_supplicant support (for P2P) + * - collect a histogram of interference over time allowing more educated + * guess about an ideal channel (perhaps CSA could be used to migrate AP to a + * new "better" channel while running) + * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs + * when choosing the ideal channel + * + * Survey interference factor implementation details + * ------------------------------------------------- + * Generic interference_factor in struct hostapd_channel_data is used. + * + * The survey interference factor is defined as the ratio of the + * observed busy time over the time we spent on the channel, + * this value is then amplified by the observed noise floor on + * the channel in comparison to the lowest noise floor observed + * on the entire band. + * + * This corresponds to: + * --- + * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf) + * --- + * + * The coefficient of 2 reflects the way power in "far-field" + * radiation decreases as the square of distance from the antenna [1]. + * What this does is it decreases the observed busy time ratio if the + * noise observed was low but increases it if the noise was high, + * proportionally to the way "far field" radiation changes over + * distance. + * + * If channel busy time is not available the fallback is to use channel RX time. + * + * Since noise floor is in dBm it is necessary to convert it into Watts so that + * combined channel interference (e.g., HT40, which uses two channels) can be + * calculated easily. + * --- + * (busy time - tx time) / (active time - tx time) * + * 2^(10^(chan_nf/10) + 10^(band_min_nf/10)) + * --- + * + * However to account for cases where busy/rx time is 0 (channel load is then + * 0%) channel noise floor signal power is combined into the equation so a + * channel with lower noise floor is preferred. The equation becomes: + * --- + * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) * + * 2^(10^(chan_nf/10) + 10^(band_min_nf/10)) + * --- + * + * All this "interference factor" is purely subjective and only time + * will tell how usable this is. By using the minimum noise floor we + * remove any possible issues due to card calibration. The computation + * of the interference factor then is dependent on what the card itself + * picks up as the minimum noise, not an actual real possible card + * noise value. + * + * Total interference computation details + * -------------------------------------- + * The above channel interference factor is calculated with no respect to + * target operational bandwidth. + * + * To find an ideal channel the above data is combined by taking into account + * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels + * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth + * on 5 GHz. + * + * Each valid and possible channel spec (i.e., channel + width) is taken and its + * interference factor is computed by summing up interferences of each channel + * it overlaps. The one with least total interference is picked up. + * + * Note: This implies base channel interference factor must be non-negative + * allowing easy summing up. + * + * Example ACS analysis printout + * ----------------------------- + * + * ACS: Trying survey-based ACS + * ACS: Survey analysis for channel 1 (2412 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13 + * ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12 + * ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11 + * ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5 + * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4 + * ACS: * interference factor average: 0.0557166 + * ACS: Survey analysis for channel 2 (2417 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3 + * ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4 + * ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6 + * ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24 + * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4 + * ACS: * interference factor average: 0.050832 + * ACS: Survey analysis for channel 3 (2422 MHz) + * ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 + * ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3 + * ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 + * ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 + * ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3 + * ACS: * interference factor average: 0.0148838 + * ACS: Survey analysis for channel 4 (2427 MHz) + * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9 + * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3 + * ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: * interference factor average: 0.0160801 + * ACS: Survey analysis for channel 5 (2432 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66 + * ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7 + * ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2 + * ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109 + * ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3 + * ACS: * interference factor average: 0.232244 + * ACS: Survey analysis for channel 6 (2437 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89 + * ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13 + * ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5 + * ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70 + * ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10 + * ACS: * interference factor average: 0.232298 + * ACS: Survey analysis for channel 7 (2442 MHz) + * ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71 + * ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62 + * ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 + * ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 + * ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12 + * ACS: * interference factor average: 0.195031 + * ACS: Survey analysis for channel 8 (2447 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8 + * ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8 + * ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6 + * ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21 + * ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27 + * ACS: * interference factor average: 0.0865885 + * ACS: Survey analysis for channel 9 (2452 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2 + * ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5 + * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1 + * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: * interference factor average: 0.00993022 + * ACS: Survey analysis for channel 10 (2457 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8 + * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: * interference factor average: 0.0136033 + * ACS: Survey analysis for channel 11 (2462 MHz) + * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0 + * ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0 + * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7 + * ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15 + * ACS: * interference factor average: 0.0271605 + * ACS: Survey analysis for channel 12 (2467 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10 + * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1 + * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0 + * ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1 + * ACS: * interference factor average: 0.0148992 + * ACS: Survey analysis for channel 13 (2472 MHz) + * ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12 + * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9 + * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0 + * ACS: * interference factor average: 0.0260179 + * ACS: Survey analysis for selected bandwidth 20MHz + * ACS: * channel 1: total interference = 0.121432 + * ACS: * channel 2: total interference = 0.137512 + * ACS: * channel 3: total interference = 0.369757 + * ACS: * channel 4: total interference = 0.546338 + * ACS: * channel 5: total interference = 0.690538 + * ACS: * channel 6: total interference = 0.762242 + * ACS: * channel 7: total interference = 0.756092 + * ACS: * channel 8: total interference = 0.537451 + * ACS: * channel 9: total interference = 0.332313 + * ACS: * channel 10: total interference = 0.152182 + * ACS: * channel 11: total interference = 0.0916111 + * ACS: * channel 12: total interference = 0.0816809 + * ACS: * channel 13: total interference = 0.0680776 + * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776 + * + * [1] http://en.wikipedia.org/wiki/Near_and_far_field + */ + + +static int acs_request_scan(struct hostapd_iface *iface); + + +static void acs_clean_chan_surveys(struct hostapd_channel_data *chan) +{ + struct freq_survey *survey, *tmp; + + if (dl_list_empty(&chan->survey_list)) + return; + + dl_list_for_each_safe(survey, tmp, &chan->survey_list, + struct freq_survey, list) { + dl_list_del(&survey->list); + os_free(survey); + } +} + + +static void acs_cleanup(struct hostapd_iface *iface) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED) + acs_clean_chan_surveys(chan); + + dl_list_init(&chan->survey_list); + chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED; + chan->min_nf = 0; + } + + iface->chans_surveyed = 0; + iface->acs_num_completed_scans = 0; +} + + +static void acs_fail(struct hostapd_iface *iface) +{ + wpa_printf(MSG_ERROR, "ACS: Failed to start"); + acs_cleanup(iface); +} + + +static long double +acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf) +{ + long double factor, busy, total; + + if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) + busy = survey->channel_time_busy; + else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX) + busy = survey->channel_time_rx; + else { + /* This shouldn't really happen as survey data is checked in + * acs_sanity_check() */ + wpa_printf(MSG_ERROR, "ACS: Survey data missing"); + return 0; + } + + total = survey->channel_time; + + if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) { + busy -= survey->channel_time_tx; + total -= survey->channel_time_tx; + } + + /* TODO: figure out the best multiplier for noise floor base */ + factor = pow(10, survey->nf / 5.0L) + + (busy / total) * + pow(2, pow(10, (long double) survey->nf / 10.0L) - + pow(10, (long double) min_nf / 10.0L)); + + return factor; +} + + +static void +acs_survey_chan_interference_factor(struct hostapd_iface *iface, + struct hostapd_channel_data *chan) +{ + struct freq_survey *survey; + unsigned int i = 0; + long double int_factor = 0; + + if (dl_list_empty(&chan->survey_list)) + return; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + return; + + chan->interference_factor = 0; + + dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) + { + int_factor = acs_survey_interference_factor(survey, + iface->lowest_nf); + chan->interference_factor += int_factor; + wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu", + ++i, chan->min_nf, int_factor, + survey->nf, (unsigned long) survey->channel_time, + (unsigned long) survey->channel_time_busy, + (unsigned long) survey->channel_time_rx); + } + + chan->interference_factor = chan->interference_factor / + dl_list_len(&chan->survey_list); +} + + +static int acs_usable_ht40_chan(struct hostapd_channel_data *chan) +{ + const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, + 157, 184, 192 }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(allowed); i++) + if (chan->chan == allowed[i]) + return 1; + + return 0; +} + + +static int acs_survey_is_sufficient(struct freq_survey *survey) +{ + if (!(survey->filled & SURVEY_HAS_NF)) { + wpa_printf(MSG_ERROR, "ACS: Survey is missing noise floor"); + return 0; + } + + if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) { + wpa_printf(MSG_ERROR, "ACS: Survey is missing channel time"); + return 0; + } + + if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) && + !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) { + wpa_printf(MSG_ERROR, "ACS: Survey is missing RX and busy time (at least one is required)"); + return 0; + } + + return 1; +} + + +static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan) +{ + struct freq_survey *survey; + + dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list) + { + if (!acs_survey_is_sufficient(survey)) { + wpa_printf(MSG_ERROR, "ACS: Channel %d has insufficient survey data", + chan->chan); + return 0; + } + } + + return 1; + +} + + +static int acs_surveys_are_sufficient(struct hostapd_iface *iface) +{ + int i; + struct hostapd_channel_data *chan; + int valid = 0; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + if (!acs_survey_list_is_sufficient(chan)) + continue; + + valid++; + } + + /* We need at least survey data for one channel */ + return !!valid; +} + + +static int acs_usable_chan(struct hostapd_channel_data *chan) +{ + if (dl_list_empty(&chan->survey_list)) + return 0; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + return 0; + if (!acs_survey_list_is_sufficient(chan)) + return 0; + return 1; +} + + +static void acs_survey_all_chans_intereference_factor( + struct hostapd_iface *iface) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (!acs_usable_chan(chan)) + continue; + + wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)", + chan->chan, chan->freq); + + acs_survey_chan_interference_factor(iface, chan); + + wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg", + chan->interference_factor); + } +} + + +static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface, + int freq) +{ + struct hostapd_channel_data *chan; + int i; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + if (chan->freq == freq) + return chan; + } + + return NULL; +} + + +/* + * At this point it's assumed chan->interface_factor has been computed. + * This function should be reusable regardless of interference computation + * option (survey, BSS, spectral, ...). chan->interference factor must be + * summable (i.e., must be always greater than zero). + */ +static struct hostapd_channel_data * +acs_find_ideal_chan(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL, + *rand_chan = NULL; + long double factor, ideal_factor = 0; + int i, j; + int n_chans = 1; + + /* TODO: HT40- support */ + + if (iface->conf->ieee80211n && + iface->conf->secondary_channel == -1) { + wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+"); + return NULL; + } + + if (iface->conf->ieee80211n && + iface->conf->secondary_channel) + n_chans = 2; + + if (iface->conf->ieee80211ac && + iface->conf->vht_oper_chwidth == 1) + n_chans = 4; + + /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */ + + wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz", + n_chans == 1 ? 20 : + n_chans == 2 ? 40 : + n_chans == 4 ? 80 : + -1); + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + + /* HT40 on 5 GHz has a limited set of primary channels as per + * 11n Annex J */ + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A && + iface->conf->ieee80211n && + iface->conf->secondary_channel && + !acs_usable_ht40_chan(chan)) { + wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40", + chan->chan); + continue; + } + + factor = 0; + if (acs_usable_chan(chan)) + factor = chan->interference_factor; + + for (j = 1; j < n_chans; j++) { + adj_chan = acs_find_chan(iface, chan->freq + (j * 20)); + if (!adj_chan) + break; + + if (acs_usable_chan(adj_chan)) + factor += adj_chan->interference_factor; + } + + if (j != n_chans) { + wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth", + chan->chan); + continue; + } + + /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent + * channel interference factor. */ + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B || + iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) { + for (j = 0; j < n_chans; j++) { + /* TODO: perhaps a multiplier should be used + * here? */ + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) - 5); + if (adj_chan && acs_usable_chan(adj_chan)) + factor += adj_chan->interference_factor; + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) - 10); + if (adj_chan && acs_usable_chan(adj_chan)) + factor += adj_chan->interference_factor; + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) + 5); + if (adj_chan && acs_usable_chan(adj_chan)) + factor += adj_chan->interference_factor; + + adj_chan = acs_find_chan(iface, chan->freq + + (j * 20) + 10); + if (adj_chan && acs_usable_chan(adj_chan)) + factor += adj_chan->interference_factor; + } + } + + wpa_printf(MSG_DEBUG, "ACS: * channel %d: total interference = %Lg", + chan->chan, factor); + + if (acs_usable_chan(chan) && + (!ideal_chan || factor < ideal_factor)) { + ideal_factor = factor; + ideal_chan = chan; + } + + /* This channel would at least be usable */ + if (!rand_chan) + rand_chan = chan; + } + + if (ideal_chan) { + wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg", + ideal_chan->chan, ideal_chan->freq, ideal_factor); + return ideal_chan; + } + + return rand_chan; +} + + +static void acs_adjust_vht_center_freq(struct hostapd_iface *iface) +{ + wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency"); + + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + iface->conf->vht_oper_centr_freq_seg0_idx = + iface->conf->channel + 2; + break; + case VHT_CHANWIDTH_80MHZ: + iface->conf->vht_oper_centr_freq_seg0_idx = + iface->conf->channel + 6; + break; + default: + /* TODO: How can this be calculated? Adjust + * acs_find_ideal_chan() */ + wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now"); + break; + } +} + + +static int acs_study_survey_based(struct hostapd_iface *iface) +{ + wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS"); + + if (!iface->chans_surveyed) { + wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data"); + return -1; + } + + if (!acs_surveys_are_sufficient(iface)) { + wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data"); + return -1; + } + + acs_survey_all_chans_intereference_factor(iface); + return 0; +} + + +static int acs_study_options(struct hostapd_iface *iface) +{ + int err; + + err = acs_study_survey_based(iface); + if (err == 0) + return 0; + + /* TODO: If no surveys are available/sufficient this is a good + * place to fallback to BSS-based ACS */ + + return -1; +} + + +static void acs_study(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *ideal_chan; + int err; + + err = acs_study_options(iface); + if (err < 0) { + wpa_printf(MSG_ERROR, "ACS: All study options have failed"); + goto fail; + } + + ideal_chan = acs_find_ideal_chan(iface); + if (!ideal_chan) { + wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel"); + err = -1; + goto fail; + } + + iface->conf->channel = ideal_chan->chan; + + if (iface->conf->ieee80211ac) + acs_adjust_vht_center_freq(iface); + + err = 0; +fail: + /* + * hostapd_setup_interface_complete() will return -1 on failure, + * 0 on success and 0 is HOSTAPD_CHAN_VALID :) + */ + if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) { + acs_cleanup(iface); + return; + } + + /* This can possibly happen if channel parameters (secondary + * channel, center frequencies) are misconfigured */ + wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file."); + acs_fail(iface); +} + + +static void acs_scan_complete(struct hostapd_iface *iface) +{ + int err; + + iface->scan_cb = NULL; + + wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)", + iface->conf->acs_num_scans); + + err = hostapd_drv_get_survey(iface->bss[0], 0); + if (err) { + wpa_printf(MSG_ERROR, "ACS: Failed to get survey data"); + acs_fail(iface); + } + + if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) { + err = acs_request_scan(iface); + if (err) { + wpa_printf(MSG_ERROR, "ACS: Failed to request scan"); + goto fail; + } + + return; + } + + acs_study(iface); + return; +fail: + hostapd_acs_completed(iface, 1); + acs_fail(iface); +} + + +static int acs_request_scan(struct hostapd_iface *iface) +{ + struct wpa_driver_scan_params params; + struct hostapd_channel_data *chan; + int i, *freq; + + os_memset(¶ms, 0, sizeof(params)); + params.freqs = os_calloc(iface->current_mode->num_channels + 1, + sizeof(params.freqs[0])); + if (params.freqs == NULL) + return -1; + + freq = params.freqs; + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + *freq++ = chan->freq; + } + *freq = 0; + + iface->scan_cb = acs_scan_complete; + + wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d", + iface->acs_num_completed_scans + 1, + iface->conf->acs_num_scans); + + if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { + wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan"); + acs_cleanup(iface); + return -1; + } + + os_free(params.freqs); + return 0; +} + + +enum hostapd_chan_status acs_init(struct hostapd_iface *iface) +{ + int err; + + wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit"); + + acs_cleanup(iface); + + err = acs_request_scan(iface); + if (err < 0) + return HOSTAPD_CHAN_INVALID; + + hostapd_set_state(iface, HAPD_IFACE_ACS); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED); + + return HOSTAPD_CHAN_ACS; +} diff --git a/contrib/hostapd/src/ap/acs.h b/contrib/hostapd/src/ap/acs.h new file mode 100644 index 0000000000..fc85259e85 --- /dev/null +++ b/contrib/hostapd/src/ap/acs.h @@ -0,0 +1,27 @@ +/* + * ACS - Automatic Channel Selection module + * Copyright (c) 2011, Atheros Communications + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ACS_H +#define ACS_H + +#ifdef CONFIG_ACS + +enum hostapd_chan_status acs_init(struct hostapd_iface *iface); + +#else /* CONFIG_ACS */ + +static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface) +{ + wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel"); + return HOSTAPD_CHAN_INVALID; +} + +#endif /* CONFIG_ACS */ + +#endif /* ACS_H */ diff --git a/contrib/hostapd/src/ap/ap_config.c b/contrib/hostapd/src/ap/ap_config.c new file mode 100644 index 0000000000..368b2020e8 --- /dev/null +++ b/contrib/hostapd/src/ap/ap_config.c @@ -0,0 +1,885 @@ +/* + * hostapd / Configuration helper functions + * Copyright (c) 2003-2014, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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; + + bss->pwd_group = 19; /* ECC: GF(p=256) */ + +#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 */ + + /* Set to -1 as defaults depends on HT in setup */ + bss->wmm_enabled = -1; + +#ifdef CONFIG_IEEE80211R + bss->ft_over_ds = 1; +#endif /* CONFIG_IEEE80211R */ + + bss->radius_das_time_window = 300; + + bss->sae_anti_clogging_threshold = 5; +} + + +struct hostapd_config * hostapd_config_defaults(void) +{ +#define ecw2cw(ecw) ((1 << (ecw)) - 1) + + struct hostapd_config *conf; + struct hostapd_bss_config *bss; + 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, 0 }; + const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ + { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 }; + const struct hostapd_tx_queue_params txq_bk = + { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 }; + const struct hostapd_tx_queue_params txq_be = + { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0}; + const struct hostapd_tx_queue_params txq_vi = + { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30}; + const struct hostapd_tx_queue_params txq_vo = + { 1, (ecw2cw(aCWmin) + 1) / 4 - 1, + (ecw2cw(aCWmin) + 1) / 2 - 1, 15}; + +#undef ecw2cw + + 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; + } + conf->bss = os_calloc(1, sizeof(struct hostapd_bss_config *)); + if (conf->bss == NULL) { + os_free(conf); + os_free(bss); + return NULL; + } + conf->bss[0] = bss; + + bss->radius = os_zalloc(sizeof(*bss->radius)); + if (bss->radius == NULL) { + os_free(conf->bss); + os_free(conf); + os_free(bss); + return NULL; + } + + hostapd_config_defaults_bss(bss); + + conf->num_bss = 1; + + conf->beacon_int = 100; + conf->rts_threshold = -1; /* use driver default: 2347 */ + conf->fragm_threshold = -1; /* user driver default: 2346 */ + conf->send_probe_response = 1; + + conf->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->tx_queue[0] = txq_vo; + conf->tx_queue[1] = txq_vi; + conf->tx_queue[2] = txq_be; + conf->tx_queue[3] = txq_bk; + + conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; + + conf->ap_table_max_size = 255; + conf->ap_table_expiration_time = 60; + +#ifdef CONFIG_TESTING_OPTIONS + conf->ignore_probe_probability = 0.0d; + conf->ignore_auth_probability = 0.0d; + conf->ignore_assoc_probability = 0.0d; + conf->ignore_reassoc_probability = 0.0d; + conf->corrupt_gtk_rekey_mic_probability = 0.0d; +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_ACS + conf->acs_num_scans = 5; +#endif /* CONFIG_ACS */ + + 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); +} + + +struct hostapd_radius_attr * +hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type) +{ + for (; attr; attr = attr->next) { + if (attr->type == type) + return attr; + } + return NULL; +} + + +static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr) +{ + struct hostapd_radius_attr *prev; + + while (attr) { + prev = attr; + attr = attr->next; + wpabuf_free(prev->val); + os_free(prev); + } +} + + +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; + } +} + + +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->eap_user_sqlite); + + os_free(conf->eap_req_id_text); + os_free(conf->accept_mac); + os_free(conf->deny_mac); + os_free(conf->nas_identifier); + if (conf->radius) { + 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); + } + hostapd_config_free_radius_attr(conf->radius_auth_req_attr); + hostapd_config_free_radius_attr(conf->radius_acct_req_attr); + 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->ocsp_stapling_response); + 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); + os_free(conf->radius_das_shared_secret); + hostapd_config_free_vlan(conf); + os_free(conf->time_zone); + +#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->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); + wpabuf_free(conf->wps_nfc_dh_pubkey); + wpabuf_free(conf->wps_nfc_dh_privkey); + wpabuf_free(conf->wps_nfc_dev_pw); +#endif /* CONFIG_WPS */ + + os_free(conf->roaming_consortium); + os_free(conf->venue_name); + os_free(conf->nai_realm_data); + os_free(conf->network_auth_type); + os_free(conf->anqp_3gpp_cell_net); + os_free(conf->domain_name); + +#ifdef CONFIG_RADIUS_TEST + os_free(conf->dump_msk_file); +#endif /* CONFIG_RADIUS_TEST */ + +#ifdef CONFIG_HS20 + os_free(conf->hs20_oper_friendly_name); + os_free(conf->hs20_wan_metrics); + os_free(conf->hs20_connection_capability); + os_free(conf->hs20_operating_class); +#endif /* CONFIG_HS20 */ + + wpabuf_free(conf->vendor_elements); + + os_free(conf->sae_groups); + + os_free(conf->server_id); + + os_free(conf); +} + + +/** + * 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; +} + + +int hostapd_vlan_id_valid(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 1; + v = v->next; + } + 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) + return v->ifname; + v = v->next; + } + return NULL; +} + + +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk) +{ + struct hostapd_wpa_psk *psk; + int next_ok = prev_psk == NULL; + + if (p2p_dev_addr) { + wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR + " p2p_dev_addr=" MACSTR " prev_psk=%p", + MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk); + if (!is_zero_ether_addr(p2p_dev_addr)) + addr = NULL; /* Use P2P Device Address for matching */ + } else { + wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR + " prev_psk=%p", + MAC2STR(addr), prev_psk); + } + + for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) { + if (next_ok && + (psk->group || + (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) || + (!addr && p2p_dev_addr && + os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) == + 0))) + return psk->psk; + + if (psk->psk == prev_psk) + next_ok = 1; + } + + return NULL; +} + + +static int hostapd_config_check_bss(struct hostapd_bss_config *bss, + struct hostapd_config *conf, + int full_config) +{ + if (full_config && bss->ieee802_1x && !bss->eap_server && + !bss->radius->auth_servers) { + wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no " + "EAP authenticator configured)."); + return -1; + } + + if (bss->wpa) { + int wep, i; + + wep = bss->default_wep_key_len > 0 || + bss->individual_wep_key_len > 0; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (bss->ssid.wep.keys_set) { + wep = 1; + break; + } + } + + if (wep) { + wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported"); + return -1; + } + } + + if (full_config && bss->wpa && + bss->wpa_psk_radius != PSK_RADIUS_IGNORED && + bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { + wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no " + "RADIUS checking (macaddr_acl=2) enabled."); + return -1; + } + + if (full_config && bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && + bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL && + bss->ssid.wpa_psk_file == NULL && + (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED || + bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) { + wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase " + "is not configured."); + return -1; + } + + if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) { + size_t i; + + for (i = 0; i < conf->num_bss; i++) { + if (conf->bss[i] != bss && + (hostapd_mac_comp(conf->bss[i]->bssid, + bss->bssid) == 0)) { + wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR + " on interface '%s' and '%s'.", + MAC2STR(bss->bssid), + conf->bss[i]->iface, bss->iface); + return -1; + } + } + } + +#ifdef CONFIG_IEEE80211R + if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) && + (bss->nas_identifier == NULL || + os_strlen(bss->nas_identifier) < 1 || + os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) { + wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires " + "nas_identifier to be configured as a 1..48 octet " + "string"); + return -1; + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211N + if (full_config && conf->ieee80211n && + conf->hw_mode == HOSTAPD_MODE_IEEE80211B) { + bss->disable_11n = 1; + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not " + "allowed, disabling HT capabilites"); + } + + if (full_config && conf->ieee80211n && + bss->ssid.security_policy == SECURITY_STATIC_WEP) { + bss->disable_11n = 1; + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not " + "allowed, disabling HT capabilities"); + } + + if (full_config && conf->ieee80211n && bss->wpa && + !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) + { + bss->disable_11n = 1; + wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 " + "requires CCMP/GCMP to be enabled, disabling HT " + "capabilities"); + } +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_WPS2 + if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) { + wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid " + "configuration forced WPS to be disabled"); + bss->wps_state = 0; + } + + if (full_config && bss->wps_state && + bss->ssid.wep.keys_set && bss->wpa == 0) { + wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be " + "disabled"); + bss->wps_state = 0; + } + + if (full_config && bss->wps_state && bss->wpa && + (!(bss->wpa & 2) || + !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) { + wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " + "WPA2/CCMP forced WPS to be disabled"); + bss->wps_state = 0; + } +#endif /* CONFIG_WPS2 */ + +#ifdef CONFIG_HS20 + if (full_config && bss->hs20 && + (!(bss->wpa & 2) || + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP_256)))) { + wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP " + "configuration is required for Hotspot 2.0 " + "functionality"); + return -1; + } +#endif /* CONFIG_HS20 */ + + return 0; +} + + +int hostapd_config_check(struct hostapd_config *conf, int full_config) +{ + size_t i; + + if (full_config && conf->ieee80211d && + (!conf->country[0] || !conf->country[1])) { + wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without " + "setting the country_code"); + return -1; + } + + if (full_config && conf->ieee80211h && !conf->ieee80211d) { + wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without " + "IEEE 802.11d enabled"); + return -1; + } + + for (i = 0; i < conf->num_bss; i++) { + if (hostapd_config_check_bss(conf->bss[i], conf, full_config)) + return -1; + } + + return 0; +} + + +void hostapd_set_security_params(struct hostapd_bss_config *bss) +{ + if (bss->individual_wep_key_len == 0) { + /* individual keys are not use; can use key idx0 for + * broadcast keys */ + bss->broadcast_key_idx_min = 0; + } + + if ((bss->wpa & 2) && bss->rsn_pairwise == 0) + bss->rsn_pairwise = bss->wpa_pairwise; + bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, + bss->rsn_pairwise); + + bss->radius->auth_server = bss->radius->auth_servers; + bss->radius->acct_server = bss->radius->acct_servers; + + if (bss->wpa && bss->ieee802_1x) { + bss->ssid.security_policy = SECURITY_WPA; + } else if (bss->wpa) { + bss->ssid.security_policy = SECURITY_WPA_PSK; + } else if (bss->ieee802_1x) { + int cipher = WPA_CIPHER_NONE; + bss->ssid.security_policy = SECURITY_IEEE_802_1X; + bss->ssid.wep.default_len = bss->default_wep_key_len; + if (bss->default_wep_key_len) + cipher = bss->default_wep_key_len >= 13 ? + WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40; + bss->wpa_group = cipher; + bss->wpa_pairwise = cipher; + bss->rsn_pairwise = cipher; + } else if (bss->ssid.wep.keys_set) { + int cipher = WPA_CIPHER_WEP40; + if (bss->ssid.wep.len[0] >= 13) + cipher = WPA_CIPHER_WEP104; + bss->ssid.security_policy = SECURITY_STATIC_WEP; + bss->wpa_group = cipher; + bss->wpa_pairwise = cipher; + bss->rsn_pairwise = cipher; + } else { + bss->ssid.security_policy = SECURITY_PLAINTEXT; + bss->wpa_group = WPA_CIPHER_NONE; + bss->wpa_pairwise = WPA_CIPHER_NONE; + bss->rsn_pairwise = WPA_CIPHER_NONE; + } +} diff --git a/contrib/hostapd/hostapd/config.h b/contrib/hostapd/src/ap/ap_config.h similarity index 54% rename from contrib/hostapd/hostapd/config.h rename to contrib/hostapd/src/ap/ap_config.h index ea530d45d1..b4860a08b5 100644 --- a/contrib/hostapd/hostapd/config.h +++ b/contrib/hostapd/src/ap/ap_config.h @@ -1,28 +1,22 @@ /* - * hostapd / Configuration file - * Copyright (c) 2003-2007, Jouni Malinen - * Copyright (c) 2007-2008, Intel Corporation + * hostapd / Configuration definitions and helpers functions + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#ifndef CONFIG_H -#define CONFIG_H +#ifndef HOSTAPD_CONFIG_H +#define HOSTAPD_CONFIG_H -#include "defs.h" +#include "common/defs.h" #include "ip_addr.h" -#include "wpa_common.h" +#include "common/wpa_common.h" +#include "common/ieee802_11_common.h" +#include "wps/wps.h" -#ifndef IFNAMSIZ -#define IFNAMSIZ 16 -#endif +#define MAX_STA_COUNT 2007 +#define MAX_VLAN_ID 4094 typedef u8 macaddr[ETH_ALEN]; @@ -55,9 +49,12 @@ typedef enum hostap_security_policy { } secpolicy; struct hostapd_ssid { - char ssid[HOSTAPD_MAX_SSID_LEN + 1]; + u8 ssid[HOSTAPD_MAX_SSID_LEN]; size_t ssid_len; - int ssid_set; + unsigned int ssid_set:1; + unsigned int utf8_ssid:1; + unsigned int wpa_passphrase_set:1; + unsigned int wpa_psk_set:1; char vlan[IFNAMSIZ + 1]; secpolicy security_policy; @@ -72,11 +69,13 @@ struct hostapd_ssid { #define DYNAMIC_VLAN_OPTIONAL 1 #define DYNAMIC_VLAN_REQUIRED 2 int dynamic_vlan; +#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0 +#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1 +#define DYNAMIC_VLAN_NAMING_END 2 + int vlan_naming; #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; }; @@ -98,14 +97,19 @@ struct hostapd_vlan { }; #define PMK_LEN 32 +struct hostapd_sta_wpa_psk_short { + struct hostapd_sta_wpa_psk_short *next; + u8 psk[PMK_LEN]; +}; + struct hostapd_wpa_psk { struct hostapd_wpa_psk *next; int group; u8 psk[PMK_LEN]; u8 addr[ETH_ALEN]; + u8 p2p_dev_addr[ETH_ALEN]; }; -#define EAP_USER_MAX_METHODS 8 struct hostapd_eap_user { struct hostapd_eap_user *next; u8 *identity; @@ -113,7 +117,7 @@ struct hostapd_eap_user { struct { int vendor; u32 method; - } methods[EAP_USER_MAX_METHODS]; + } methods[EAP_MAX_METHODS]; u8 *password; size_t password_len; int phase2; @@ -124,25 +128,52 @@ struct hostapd_eap_user { int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ }; +struct hostapd_radius_attr { + u8 type; + struct wpabuf *val; + struct hostapd_radius_attr *next; +}; + -#define NUM_TX_QUEUES 8 +#define NUM_TX_QUEUES 4 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; + +#define MAX_ROAMING_CONSORTIUM_LEN 15 + +struct hostapd_roaming_consortium { + u8 len; + u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; }; +struct hostapd_lang_string { + u8 lang[3]; + u8 name_len; + u8 name[252]; +}; + +#define MAX_NAI_REALMS 10 +#define MAX_NAI_REALMLEN 255 +#define MAX_NAI_EAP_METHODS 5 +#define MAX_NAI_AUTH_TYPES 4 +struct hostapd_nai_realm_data { + u8 encoding; + char realm_buf[MAX_NAI_REALMLEN + 1]; + char *realm[MAX_NAI_REALMS]; + u8 eap_method_count; + struct hostapd_nai_realm_eap { + u8 eap_method; + u8 num_auths; + u8 auth_id[MAX_NAI_AUTH_TYPES]; + u8 auth_val[MAX_NAI_AUTH_TYPES]; + } eap_method[MAX_NAI_EAP_METHODS]; +}; /** * struct hostapd_bss_config - Per-BSS configuration @@ -150,14 +181,14 @@ struct hostapd_wmm_ac_params { struct hostapd_bss_config { char iface[IFNAMSIZ + 1]; char bridge[IFNAMSIZ + 1]; + char vlan_bridge[IFNAMSIZ + 1]; + char wds_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; @@ -167,10 +198,21 @@ struct hostapd_bss_config { int eap_server; /* Use internal EAP server instead of external * RADIUS server */ struct hostapd_eap_user *eap_user; + char *eap_user_sqlite; char *eap_sim_db; struct hostapd_ip_addr own_ip_addr; char *nas_identifier; struct hostapd_radius_servers *radius; + int acct_interim_interval; + int radius_request_cui; + struct hostapd_radius_attr *radius_auth_req_attr; + struct hostapd_radius_attr *radius_acct_req_attr; + int radius_das_port; + unsigned int radius_das_time_window; + int radius_das_require_event_timestamp; + struct hostapd_ip_addr radius_das_client_addr; + u8 *radius_das_shared_secret; + size_t radius_das_shared_secret_len; struct hostapd_ssid ssid; @@ -198,6 +240,9 @@ struct hostapd_bss_config { int num_accept_mac; struct mac_acl_entry *deny_mac; int num_deny_mac; + int wds_sta; + int isolate; + int start_disabled; int auth_algs; /* bitfield of allowed IEEE 802.11 authentication * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ @@ -205,16 +250,17 @@ struct hostapd_bss_config { int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */ int wpa_key_mgmt; #ifdef CONFIG_IEEE80211W - enum { - NO_IEEE80211W = 0, - IEEE80211W_OPTIONAL = 1, - IEEE80211W_REQUIRED = 2 - } ieee80211w; + 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 */ + enum { + PSK_RADIUS_IGNORED = 0, + PSK_RADIUS_ACCEPTED = 1, + PSK_RADIUS_REQUIRED = 2 + } wpa_psk_radius; int wpa_pairwise; int wpa_group; int wpa_group_rekey; @@ -235,10 +281,13 @@ struct hostapd_bss_config { struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; + int ft_over_ds; #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; @@ -246,6 +295,7 @@ struct hostapd_bss_config { char *private_key; char *private_key_passwd; int check_crl; + char *ocsp_stapling_response; char *dh_file; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; @@ -256,6 +306,8 @@ struct hostapd_bss_config { int pac_key_refresh_time; int eap_sim_aka_result_ind; int tnc; + int fragment_size; + u16 pwd_group; char *radius_server_clients; int radius_server_auth_port; @@ -272,8 +324,9 @@ struct hostapd_bss_config { int ignore_broadcast_ssid; int wmm_enabled; + int wmm_uapsd; - struct hostapd_vlan *vlan, *vlan_tail; + struct hostapd_vlan *vlan; macaddr bssid; @@ -284,10 +337,12 @@ struct hostapd_bss_config { */ u16 max_listen_interval; + int disable_pmksa_caching; int okc; /* Opportunistic Key Caching */ int wps_state; #ifdef CONFIG_WPS + int wps_independent; int ap_setup_locked; u8 uuid[16]; char *wps_pin_requests; @@ -296,7 +351,7 @@ struct hostapd_bss_config { char *model_name; char *model_number; char *serial_number; - char *device_type; + u8 device_type[WPS_DEV_TYPE_LEN]; char *config_methods; u8 os_version[4]; char *ap_pin; @@ -304,6 +359,7 @@ struct hostapd_bss_config { u8 *extra_cred; size_t extra_cred_len; int wps_cred_processing; + int force_per_enrollee_psk; u8 *ap_settings; size_t ap_settings_len; char *upnp_iface; @@ -312,23 +368,124 @@ struct hostapd_bss_config { char *model_description; char *model_url; char *upc; + struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; + int wps_nfc_pw_from_config; + int wps_nfc_dev_pw_id; + struct wpabuf *wps_nfc_dh_pubkey; + struct wpabuf *wps_nfc_dh_privkey; + struct wpabuf *wps_nfc_dev_pw; #endif /* CONFIG_WPS */ + int pbc_in_m1; + char *server_id; + +#define P2P_ENABLED BIT(0) +#define P2P_GROUP_OWNER BIT(1) +#define P2P_GROUP_FORMATION BIT(2) +#define P2P_MANAGE BIT(3) +#define P2P_ALLOW_CROSS_CONNECTION BIT(4) + int p2p; +#ifdef CONFIG_P2P + u8 ip_addr_go[4]; + u8 ip_addr_mask[4]; + u8 ip_addr_start[4]; + u8 ip_addr_end[4]; +#endif /* CONFIG_P2P */ + + int disassoc_low_ack; + int skip_inactivity_poll; + +#define TDLS_PROHIBIT BIT(0) +#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1) + int tdls; + int disable_11n; + int disable_11ac; + + /* IEEE 802.11v */ + int time_advertisement; + char *time_zone; + int wnm_sleep_mode; + int bss_transition; + + /* IEEE 802.11u - Interworking */ + int interworking; + int access_network_type; + int internet; + int asra; + int esr; + int uesa; + int venue_info_set; + u8 venue_group; + u8 venue_type; + u8 hessid[ETH_ALEN]; + + /* IEEE 802.11u - Roaming Consortium list */ + unsigned int roaming_consortium_count; + struct hostapd_roaming_consortium *roaming_consortium; + + /* IEEE 802.11u - Venue Name duples */ + unsigned int venue_name_count; + struct hostapd_lang_string *venue_name; + + /* IEEE 802.11u - Network Authentication Type */ + u8 *network_auth_type; + size_t network_auth_type_len; + + /* IEEE 802.11u - IP Address Type Availability */ + u8 ipaddr_type_availability; + u8 ipaddr_type_configured; + + /* IEEE 802.11u - 3GPP Cellular Network */ + u8 *anqp_3gpp_cell_net; + size_t anqp_3gpp_cell_net_len; + + /* IEEE 802.11u - Domain Name */ + u8 *domain_name; + size_t domain_name_len; + + unsigned int nai_realm_count; + struct hostapd_nai_realm_data *nai_realm_data; + + u16 gas_comeback_delay; + int gas_frag_limit; + + u8 qos_map_set[16 + 2 * 21]; + unsigned int qos_map_set_len; + +#ifdef CONFIG_HS20 + int hs20; + int disable_dgaf; + unsigned int hs20_oper_friendly_name_count; + struct hostapd_lang_string *hs20_oper_friendly_name; + u8 *hs20_wan_metrics; + u8 *hs20_connection_capability; + size_t hs20_connection_capability_len; + u8 *hs20_operating_class; + u8 hs20_operating_class_len; +#endif /* CONFIG_HS20 */ + + u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ + +#ifdef CONFIG_RADIUS_TEST + char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ + + struct wpabuf *vendor_elements; + + unsigned int sae_anti_clogging_threshold; + int *sae_groups; + +#ifdef CONFIG_TESTING_OPTIONS + u8 bss_load_test[5]; + u8 bss_load_test_set; +#endif /* CONFIG_TESTING_OPTIONS */ }; -typedef enum { - HOSTAPD_MODE_IEEE80211B, - HOSTAPD_MODE_IEEE80211G, - HOSTAPD_MODE_IEEE80211A, - NUM_HOSTAPD_MODES -} hostapd_hw_mode; - - /** * struct hostapd_config - Per-radio interface configuration */ struct hostapd_config { - struct hostapd_bss_config *bss, *last_bss; + struct hostapd_bss_config **bss, *last_bss; size_t num_bss; u16 beacon_int; @@ -336,26 +493,17 @@ struct hostapd_config { int fragm_threshold; u8 send_probe_response; u8 channel; - hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ + 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 passive_scan_interval; /* seconds, 0 = disabled */ - int passive_scan_listen; /* usec */ - int passive_scan_mode; int ap_table_max_size; int ap_table_expiration_time; @@ -368,6 +516,8 @@ struct hostapd_config { int ieee80211d; + int ieee80211h; /* DFS */ + struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; /* @@ -379,24 +529,38 @@ struct hostapd_config { */ struct hostapd_wmm_ac_params wmm_ac_params[4]; - enum { - INTERNAL_BRIDGE_DO_NOT_CONTROL = -1, - INTERNAL_BRIDGE_DISABLED = 0, - INTERNAL_BRIDGE_ENABLED = 1 - } bridge_packets; - -#ifdef CONFIG_IEEE80211N int ht_op_mode_fixed; u16 ht_capab; -#endif /* CONFIG_IEEE80211N */ int ieee80211n; int secondary_channel; + int require_ht; + int obss_interval; + u32 vht_capab; + int ieee80211ac; + int require_vht; + u8 vht_oper_chwidth; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; + +#ifdef CONFIG_TESTING_OPTIONS + double ignore_probe_probability; + double ignore_auth_probability; + double ignore_assoc_probability; + double ignore_reassoc_probability; + double corrupt_gtk_rekey_mic_probability; +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_ACS + unsigned int acs_num_scans; +#endif /* CONFIG_ACS */ }; int hostapd_mac_comp(const void *a, const void *b); int hostapd_mac_comp_empty(const void *a); -struct hostapd_config * hostapd_config_read(const char *fname); +struct hostapd_config * hostapd_config_defaults(void); +void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); +void hostapd_config_free_bss(struct hostapd_bss_config *conf); 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); @@ -404,12 +568,15 @@ 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); + const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk); int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); +int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id); 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); +struct hostapd_radius_attr * +hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type); +int hostapd_config_check(struct hostapd_config *conf, int full_config); +void hostapd_set_security_params(struct hostapd_bss_config *bss); -#endif /* CONFIG_H */ +#endif /* HOSTAPD_CONFIG_H */ diff --git a/contrib/hostapd/src/ap/ap_drv_ops.c b/contrib/hostapd/src/ap/ap_drv_ops.c new file mode 100644 index 0000000000..893e6d9eee --- /dev/null +++ b/contrib/hostapd/src/ap/ap_drv_ops.c @@ -0,0 +1,775 @@ +/* + * hostapd - Driver operations + * Copyright (c) 2009-2010, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps.h" +#include "p2p/p2p.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "ap_config.h" +#include "p2p_hostapd.h" +#include "hs20.h" +#include "ap_drv_ops.h" + + +u32 hostapd_sta_flags_to_drv(u32 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; +} + + +int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf **beacon_ret, + struct wpabuf **proberesp_ret, + struct wpabuf **assocresp_ret) +{ + struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL; + u8 buf[200], *pos; + + *beacon_ret = *proberesp_ret = *assocresp_ret = NULL; + + pos = buf; + pos = hostapd_eid_time_adv(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + } + pos = hostapd_eid_time_zone(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } + + pos = buf; + pos = hostapd_eid_ext_capab(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&assocresp, pos - buf) != 0) + goto fail; + wpabuf_put_data(assocresp, buf, pos - buf); + } + pos = hostapd_eid_interworking(hapd, pos); + pos = hostapd_eid_adv_proto(hapd, pos); + pos = hostapd_eid_roaming_consortium(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } + + if (hapd->wps_beacon_ie) { + if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) < + 0) + goto fail; + wpabuf_put_buf(beacon, hapd->wps_beacon_ie); + } + + if (hapd->wps_probe_resp_ie) { + if (wpabuf_resize(&proberesp, + wpabuf_len(hapd->wps_probe_resp_ie)) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie); + } + +#ifdef CONFIG_P2P + if (hapd->p2p_beacon_ie) { + if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) < + 0) + goto fail; + wpabuf_put_buf(beacon, hapd->p2p_beacon_ie); + } + + if (hapd->p2p_probe_resp_ie) { + if (wpabuf_resize(&proberesp, + wpabuf_len(hapd->p2p_probe_resp_ie)) < 0) + goto fail; + wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie); + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) { + if (wpabuf_resize(&beacon, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(beacon, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(beacon, p - start); + } + + if (wpabuf_resize(&proberesp, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(proberesp, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(proberesp, p - start); + } + } +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_WPS2 + if (hapd->conf->wps_state) { + struct wpabuf *a = wps_build_assoc_resp_ie(); + if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) + wpabuf_put_buf(assocresp, a); + wpabuf_free(a); + } +#endif /* CONFIG_WPS2 */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) { + if (wpabuf_resize(&assocresp, 100) == 0) { + u8 *start, *p; + start = wpabuf_put(assocresp, 0); + p = hostapd_eid_p2p_manage(hapd, start); + wpabuf_put(assocresp, p - start); + } + } +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_WIFI_DISPLAY + if (hapd->p2p_group) { + struct wpabuf *a; + a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS); + if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0) + wpabuf_put_buf(assocresp, a); + wpabuf_free(a); + } +#endif /* CONFIG_WIFI_DISPLAY */ + +#ifdef CONFIG_HS20 + pos = buf; + pos = hostapd_eid_hs20_indication(hapd, pos); + if (pos != buf) { + if (wpabuf_resize(&beacon, pos - buf) != 0) + goto fail; + wpabuf_put_data(beacon, buf, pos - buf); + + if (wpabuf_resize(&proberesp, pos - buf) != 0) + goto fail; + wpabuf_put_data(proberesp, buf, pos - buf); + } +#endif /* CONFIG_HS20 */ + + if (hapd->conf->vendor_elements) { + size_t add = wpabuf_len(hapd->conf->vendor_elements); + if (wpabuf_resize(&beacon, add) == 0) + wpabuf_put_buf(beacon, hapd->conf->vendor_elements); + if (wpabuf_resize(&proberesp, add) == 0) + wpabuf_put_buf(proberesp, hapd->conf->vendor_elements); + } + + *beacon_ret = beacon; + *proberesp_ret = proberesp; + *assocresp_ret = assocresp; + + return 0; + +fail: + wpabuf_free(beacon); + wpabuf_free(proberesp); + wpabuf_free(assocresp); + return -1; +} + + +void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf *beacon, + struct wpabuf *proberesp, + struct wpabuf *assocresp) +{ + wpabuf_free(beacon); + wpabuf_free(proberesp); + wpabuf_free(assocresp); +} + + +int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) +{ + struct wpabuf *beacon, *proberesp, *assocresp; + int ret; + + if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) + return 0; + + if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < + 0) + return -1; + + ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp, + assocresp); + + hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); + + return ret; +} + + +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); +} + + +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); +} + + +int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, + int enabled) +{ + struct wpa_bss_params params; + os_memset(¶ms, 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; +#ifdef CONFIG_IEEE80211W + params.ieee80211w = hapd->conf->ieee80211w; +#endif /* CONFIG_IEEE80211W */ + } + return hostapd_set_ieee8021x(hapd, ¶ms); +} + + +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, hapd->own_addr, + NULL, NULL, force_ifname, if_addr, NULL, 0); +} + + +int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname) +{ + return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname); +} + + +int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds, + const u8 *addr, int aid, int val) +{ + const char *bridge = NULL; + + if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL) + return -1; + if (hapd->conf->wds_bridge[0]) + bridge = hapd->conf->wds_bridge; + else if (hapd->conf->bridge[0]) + bridge = hapd->conf->bridge; + return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val, + bridge, ifname_wds); +} + + +int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, + u16 auth_alg) +{ + if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL) + return 0; + return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg); +} + + +int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->sta_auth == NULL) + return 0; + return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr, + seq, status, ie, len); +} + + +int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL) + return 0; + return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr, + reassoc, status, ie, len); +} + + +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, + const struct ieee80211_vht_capabilities *vht_capab, + u32 flags, u8 qosinfo) +{ + struct hostapd_sta_add_params params; + + if (hapd->driver == NULL) + return 0; + if (hapd->driver->sta_add == NULL) + return 0; + + os_memset(¶ms, 0, sizeof(params)); + params.addr = addr; + params.aid = aid; + params.capability = capability; + params.supp_rates = supp_rates; + params.supp_rates_len = supp_rates_len; + params.listen_interval = listen_interval; + params.ht_capabilities = ht_capab; + params.vht_capabilities = vht_capab; + params.flags = hostapd_sta_flags_to_drv(flags); + params.qosinfo = qosinfo; + return hapd->driver->sta_add(hapd->drv_priv, ¶ms); +} + + +int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, + u8 *tspec_ie, size_t tspec_ielen) +{ + if (hapd->driver == NULL || hapd->driver->add_tspec == NULL) + return 0; + return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie, + tspec_ielen); +} + + +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, + const char *bridge, int use_existing) +{ + 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, + bridge, use_existing); +} + + +int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, + const char *ifname) +{ + if (hapd->driver == NULL || hapd->drv_priv == 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_params(struct hostapd_freq_params *data, int mode, + int freq, int channel, int ht_enabled, + int vht_enabled, int sec_channel_offset, + int vht_oper_chwidth, int center_segment0, + int center_segment1, u32 vht_caps) +{ + int tmp; + + os_memset(data, 0, sizeof(*data)); + data->mode = mode; + data->freq = freq; + data->channel = channel; + data->ht_enabled = ht_enabled; + data->vht_enabled = vht_enabled; + data->sec_channel_offset = sec_channel_offset; + data->center_freq1 = freq + sec_channel_offset * 10; + data->center_freq2 = 0; + data->bandwidth = sec_channel_offset ? 40 : 20; + + /* + * This validation code is probably misplaced, maybe it should be + * in src/ap/hw_features.c and check the hardware support as well. + */ + if (data->vht_enabled) switch (vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + if (center_segment1) + return -1; + if (5000 + center_segment0 * 5 != data->center_freq1 && + 2407 + center_segment0 * 5 != data->center_freq1) + return -1; + break; + case VHT_CHANWIDTH_80P80MHZ: + if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { + wpa_printf(MSG_ERROR, + "80+80 channel width is not supported!"); + return -1; + } + if (center_segment1 == center_segment0 + 4 || + center_segment1 == center_segment0 - 4) + return -1; + data->center_freq2 = 5000 + center_segment1 * 5; + /* fall through */ + case VHT_CHANWIDTH_80MHZ: + data->bandwidth = 80; + if (vht_oper_chwidth == 1 && center_segment1) + return -1; + if (vht_oper_chwidth == 3 && !center_segment1) + return -1; + if (!sec_channel_offset) + return -1; + /* primary 40 part must match the HT configuration */ + tmp = (30 + freq - 5000 - center_segment0 * 5)/20; + tmp /= 2; + if (data->center_freq1 != 5000 + + center_segment0 * 5 - 20 + 40 * tmp) + return -1; + data->center_freq1 = 5000 + center_segment0 * 5; + break; + case VHT_CHANWIDTH_160MHZ: + data->bandwidth = 160; + if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | + VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { + wpa_printf(MSG_ERROR, + "160MHZ channel width is not supported!"); + return -1; + } + if (center_segment1) + return -1; + if (!sec_channel_offset) + return -1; + /* primary 40 part must match the HT configuration */ + tmp = (70 + freq - 5000 - center_segment0 * 5)/20; + tmp /= 2; + if (data->center_freq1 != 5000 + + center_segment0 * 5 - 60 + 40 * tmp) + return -1; + data->center_freq1 = 5000 + center_segment0 * 5; + break; + } + + return 0; +} + + +int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1) +{ + struct hostapd_freq_params data; + + if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, + vht_enabled, sec_channel_offset, + vht_oper_chwidth, + center_segment0, center_segment1, + hapd->iface->current_mode->vht_capab)) + return -1; + + if (hapd->driver == NULL) + return 0; + if (hapd->driver->set_freq == NULL) + return 0; + 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_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_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); +} + + +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_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; +} + + +int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration) +{ + if (hapd->driver && hapd->driver->set_noa) + return hapd->driver->set_noa(hapd->drv_priv, count, start, + duration); + return -1; +} + + +int hostapd_drv_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); +} + + +int hostapd_drv_send_mlme(struct hostapd_data *hapd, + const void *msg, size_t len, int noack) +{ + if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) + return 0; + return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack); +} + + +int hostapd_drv_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); +} + + +int hostapd_drv_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); +} + + +int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper, + const u8 *peer, u8 *buf, u16 *buf_len) +{ + if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL) + return -1; + return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf, + buf_len); +} + + +int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *data, + size_t len) +{ + if (hapd->driver == NULL || hapd->driver->send_action == NULL) + return 0; + return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, + hapd->own_addr, hapd->own_addr, data, + len, 0); +} + + +int hostapd_start_dfs_cac(struct hostapd_iface *iface, int mode, int freq, + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_freq_params data; + int res; + + if (!hapd->driver || !hapd->driver->start_dfs_cac) + return 0; + + if (!iface->conf->ieee80211h) { + wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality " + "is not enabled"); + return -1; + } + + if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled, + vht_enabled, sec_channel_offset, + vht_oper_chwidth, center_segment0, + center_segment1, + iface->current_mode->vht_capab)) + return -1; + + res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data); + if (!res) + iface->cac_started = 1; + + return res; +} + + +int hostapd_drv_set_qos_map(struct hostapd_data *hapd, + const u8 *qos_map_set, u8 qos_map_set_len) +{ + if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL) + return 0; + return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, + qos_map_set_len); +} diff --git a/contrib/hostapd/src/ap/ap_drv_ops.h b/contrib/hostapd/src/ap/ap_drv_ops.h new file mode 100644 index 0000000000..15a4b26782 --- /dev/null +++ b/contrib/hostapd/src/ap/ap_drv_ops.h @@ -0,0 +1,283 @@ +/* + * hostapd - Driver operations + * Copyright (c) 2009-2014, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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; +struct ieee80211_ht_capabilities; +struct ieee80211_vht_capabilities; +struct hostapd_freq_params; + +u32 hostapd_sta_flags_to_drv(u32 flags); +int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, + struct wpabuf **beacon, + struct wpabuf **proberesp, + struct wpabuf **assocresp); +void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon, + struct wpabuf *proberesp, + struct wpabuf *assocresp); +int hostapd_set_ap_wps_ie(struct hostapd_data *hapd); +int hostapd_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta); +int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname, + int enabled); +int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname); +int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname); +int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds, + const u8 *addr, int aid, int val); +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, + const struct ieee80211_vht_capabilities *vht_capab, + u32 flags, u8 qosinfo); +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, + const char *bridge, int use_existing); +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 vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1); +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_country(struct hostapd_data *hapd, const char *country); +int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, + int cw_min, int cw_max, int burst_time); +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_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); +int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration); +int hostapd_drv_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 hostapd_drv_send_mlme(struct hostapd_data *hapd, + const void *msg, size_t len, int noack); +int hostapd_drv_sta_deauth(struct hostapd_data *hapd, + const u8 *addr, int reason); +int hostapd_drv_sta_disassoc(struct hostapd_data *hapd, + const u8 *addr, int reason); +int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, + unsigned int wait, const u8 *dst, const u8 *data, + size_t len); +int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, + u16 auth_alg); +int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len); +int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len); +int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, + u8 *tspec_ie, size_t tspec_ielen); +int hostapd_start_dfs_cac(struct hostapd_iface *iface, int mode, int freq, + int channel, int ht_enabled, int vht_enabled, + int sec_channel_offset, int vht_oper_chwidth, + int center_segment0, int center_segment1); +int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode, + int freq, int channel, int ht_enabled, + int vht_enabled, int sec_channel_offset, + int vht_oper_chwidth, int center_segment0, + int center_segment1, u32 vht_caps); + + +#include "drivers/driver.h" + +int hostapd_drv_wnm_oper(struct hostapd_data *hapd, + enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len); + +int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set, + u8 qos_map_set_len); + +static inline int hostapd_drv_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); +} + +static inline int hostapd_drv_set_sta_vlan(const char *ifname, + struct hostapd_data *hapd, + const u8 *addr, int vlan_id) +{ + if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) + return 0; + return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, + vlan_id); +} + +static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) + return 0; + return hapd->driver->get_inact_sec(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + return 0; + return hapd->driver->sta_remove(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd, + const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + u32 flags) +{ + 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, flags); +} + +static inline int hostapd_drv_read_sta_data( + struct hostapd_data *hapd, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) + return -1; + return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); +} + +static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) + return 0; + return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); +} + +static inline int hostapd_drv_set_acl(struct hostapd_data *hapd, + struct hostapd_acl_params *params) +{ + if (hapd->driver == NULL || hapd->driver->set_acl == NULL) + return 0; + return hapd->driver->set_acl(hapd->drv_priv, params); +} + +static inline int hostapd_drv_set_ap(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) +{ + if (hapd->driver == NULL || hapd->driver->set_ap == NULL) + return 0; + return hapd->driver->set_ap(hapd->drv_priv, params); +} + +static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd, + const u8 *mac, int accepted, + u32 session_timeout) +{ + if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) + return 0; + return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, + session_timeout); +} + +static inline int hostapd_drv_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 inline int hostapd_drv_set_authmode(struct hostapd_data *hapd, + int auth_algs) +{ + if (hapd->driver == NULL || hapd->driver->set_authmode == NULL) + return 0; + return hapd->driver->set_authmode(hapd->drv_priv, auth_algs); +} + +static inline void hostapd_drv_poll_client(struct hostapd_data *hapd, + const u8 *own_addr, const u8 *addr, + int qos) +{ + if (hapd->driver == NULL || hapd->driver->poll_client == NULL) + return; + hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos); +} + +static inline int hostapd_drv_get_survey(struct hostapd_data *hapd, + unsigned int freq) +{ + if (hapd->driver == NULL) + return -1; + if (!hapd->driver->get_survey) + return -1; + return hapd->driver->get_survey(hapd->drv_priv, freq); +} + +static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2) +{ + if (hapd->driver == NULL || hapd->driver->get_country == NULL) + return -1; + return hapd->driver->get_country(hapd->drv_priv, alpha2); +} + +static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->get_radio_name == NULL) + return NULL; + return hapd->driver->get_radio_name(hapd->drv_priv); +} + +static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings) +{ + if (hapd->driver == NULL || hapd->driver->switch_channel == NULL) + return -ENOTSUP; + + return hapd->driver->switch_channel(hapd->drv_priv, settings); +} + +static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf, + size_t buflen) +{ + if (hapd->driver == NULL || hapd->driver->status == NULL) + return -1; + return hapd->driver->status(hapd->drv_priv, buf, buflen); +} + +#endif /* AP_DRV_OPS */ diff --git a/contrib/hostapd/src/ap/ap_list.c b/contrib/hostapd/src/ap/ap_list.c new file mode 100644 index 0000000000..f9b154012a --- /dev/null +++ b/contrib/hostapd/src/ap/ap_list.c @@ -0,0 +1,315 @@ +/* + * hostapd / AP table + * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2006, Devicescape Software, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README 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 "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; +} + + +static 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_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); + + 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; +} + + +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); + + 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; + 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; + } + + merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX, + elems->supp_rates, elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); + + 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 (elems->ht_operation && elems->ht_operation_len >= 1) + ap->channel = elems->ht_operation[0]; + else if (fi) + ap->channel = fi->channel; + + if (elems->ht_capabilities) + ap->ht_support = 1; + else + ap->ht_support = 0; + + os_get_reltime(&ap->last_beacon); + + 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 + " (channel %d) - enable protection", + MAC2STR(ap->addr), ap->channel); + set_beacon++; + } + +#ifdef CONFIG_IEEE80211N + if (!iface->olbc_ht && !ap->ht_support && + (ap->channel == 0 || + ap->channel == iface->conf->channel || + ap->channel == iface->conf->channel + + iface->conf->secondary_channel * 4)) { + iface->olbc_ht = 1; + hostapd_ht_operation_update(iface); + wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR + " (channel %d) - enable protection", + MAC2STR(ap->addr), ap->channel); + set_beacon++; + } +#endif /* CONFIG_IEEE80211N */ + + if (set_beacon) + ieee802_11_update_beacons(iface); +} + + +static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + struct os_reltime now; + struct ap_info *ap; + int set_beacon = 0; + + eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); + + if (!iface->ap_list) + return; + + os_get_reltime(&now); + + while (iface->ap_list) { + ap = iface->ap_list->prev; + if (!os_reltime_expired(&now, &ap->last_beacon, + iface->conf->ap_table_expiration_time)) + 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_update_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/contrib/hostapd/src/ap/ap_list.h b/contrib/hostapd/src/ap/ap_list.h new file mode 100644 index 0000000000..93dc0eda88 --- /dev/null +++ b/contrib/hostapd/src/ap/ap_list.h @@ -0,0 +1,53 @@ +/* + * hostapd / AP table + * Copyright (c) 2002-2003, Jouni Malinen + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2006, Devicescape Software, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README 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. */ + 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 */ + u8 addr[6]; + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + int erp; /* ERP Info or -1 if ERP info element not present */ + + int channel; + + int ht_support; + + struct os_reltime last_beacon; +}; + +struct ieee802_11_elems; +struct hostapd_frame_info; + +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/contrib/hostapd/hostapd/mlme.c b/contrib/hostapd/src/ap/ap_mlme.c similarity index 92% rename from contrib/hostapd/hostapd/mlme.c rename to contrib/hostapd/src/ap/ap_mlme.c index d883931cf4..a9596947fa 100644 --- a/contrib/hostapd/hostapd/mlme.c +++ b/contrib/hostapd/src/ap/ap_mlme.c @@ -4,24 +4,21 @@ * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" -#include "hostapd.h" +#include "utils/common.h" +#include "common/ieee802_11_defs.h" #include "ieee802_11.h" -#include "wpa.h" -#include "mlme.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) { @@ -35,6 +32,7 @@ static const char * mlme_auth_alg_str(int alg) return "unknown"; } +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ /** diff --git a/contrib/hostapd/hostapd/mlme.h b/contrib/hostapd/src/ap/ap_mlme.h similarity index 73% rename from contrib/hostapd/hostapd/mlme.h rename to contrib/hostapd/src/ap/ap_mlme.h index c77a9390a8..e7fd69d61c 100644 --- a/contrib/hostapd/hostapd/mlme.h +++ b/contrib/hostapd/src/ap/ap_mlme.h @@ -4,14 +4,8 @@ * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MLME_H diff --git a/contrib/hostapd/src/ap/authsrv.c b/contrib/hostapd/src/ap/authsrv.c new file mode 100644 index 0000000000..8bb58a6f66 --- /dev/null +++ b/contrib/hostapd/src/ap/authsrv.c @@ -0,0 +1,214 @@ +/* + * Authentication server setup + * Copyright (c) 2002-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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; + + 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)); + for (i = 0; i < EAP_MAX_METHODS; 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 = hapd; + 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; + srv.pwd_group = conf->pwd_group; + srv.server_id = conf->server_id ? conf->server_id : "hostapd"; +#ifdef CONFIG_RADIUS_TEST + srv.dump_msk_file = conf->dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ + + 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->private_key || 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(¶ms, 0, sizeof(params)); + params.ca_cert = hapd->conf->ca_cert; + params.client_cert = hapd->conf->server_cert; + params.private_key = hapd->conf->private_key; + params.private_key_passwd = hapd->conf->private_key_passwd; + params.dh_file = hapd->conf->dh_file; + params.ocsp_stapling_response = + hapd->conf->ocsp_stapling_response; + + if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { + 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/contrib/hostapd/src/ap/authsrv.h b/contrib/hostapd/src/ap/authsrv.h new file mode 100644 index 0000000000..2f4ed3419c --- /dev/null +++ b/contrib/hostapd/src/ap/authsrv.h @@ -0,0 +1,15 @@ +/* + * Authentication server setup + * Copyright (c) 2002-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/ap/beacon.c b/contrib/hostapd/src/ap/beacon.c new file mode 100644 index 0000000000..5318ecb742 --- /dev/null +++ b/contrib/hostapd/src/ap/beacon.c @@ -0,0 +1,941 @@ +/* + * 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-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 "wps/wps_defs.h" +#include "p2p/p2p.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "wpa_auth.h" +#include "wmm.h" +#include "ap_config.h" +#include "sta_info.h" +#include "p2p_hostapd.h" +#include "ap_drv_ops.h" +#include "beacon.h" +#include "hs20.h" + + +#ifdef NEED_AP_MLME + +static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->bss_load_test_set) { + if (2 + 5 > len) + return eid; + *eid++ = WLAN_EID_BSS_LOAD; + *eid++ = 5; + os_memcpy(eid, hapd->conf->bss_load_test, 5); + eid += 5; + } +#endif /* CONFIG_TESTING_OPTIONS */ + return eid; +} + + +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; + + if (hapd->iface->olbc) + erp |= ERP_INFO_USE_PROTECTION; + if (hapd->iface->num_sta_non_erp > 0) { + erp |= ERP_INFO_NON_ERP_PRESENT | + ERP_INFO_USE_PROTECTION; + } + 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) +{ + 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; +} + + +static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid) +{ + u8 chan; + + if (!hapd->iface->cs_freq_params.freq) + return eid; + + if (ieee80211_freq_to_chan(hapd->iface->cs_freq_params.freq, &chan) == + NUM_HOSTAPD_MODES) + return eid; + + *eid++ = WLAN_EID_CHANNEL_SWITCH; + *eid++ = 3; + *eid++ = hapd->iface->cs_block_tx; + *eid++ = chan; + *eid++ = hapd->iface->cs_count; + + return eid; +} + + +static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid) +{ + u8 sec_ch; + + if (!hapd->iface->cs_freq_params.sec_channel_offset) + return eid; + + if (hapd->iface->cs_freq_params.sec_channel_offset == -1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW; + else if (hapd->iface->cs_freq_params.sec_channel_offset == 1) + sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE; + else + return eid; + + *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; + *eid++ = 1; + *eid++ = sec_ch; + + return eid; +} + + +static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos, + u8 *start, unsigned int *csa_counter_off) +{ + u8 *old_pos = pos; + + if (!csa_counter_off) + return pos; + + *csa_counter_off = 0; + pos = hostapd_eid_csa(hapd, pos); + + if (pos != old_pos) { + /* save an offset to the counter - should be last byte */ + *csa_counter_off = pos - start - 1; + pos = hostapd_eid_secondary_channel(hapd, pos); + } + + return pos; +} + + +static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, + struct sta_info *sta, + const struct ieee80211_mgmt *req, + int is_p2p, size_t *resp_len) +{ + struct ieee80211_mgmt *resp; + u8 *pos, *epos; + size_t buflen; + +#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 */ +#ifdef CONFIG_P2P + if (hapd->p2p_probe_resp_ie) + buflen += wpabuf_len(hapd->p2p_probe_resp_ie); +#endif /* CONFIG_P2P */ + if (hapd->conf->vendor_elements) + buflen += wpabuf_len(hapd->conf->vendor_elements); + resp = os_zalloc(buflen); + if (resp == NULL) + return NULL; + + epos = ((u8 *) resp) + MAX_PROBERESP_LEN; + + resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_PROBE_RESP); + if (req) + os_memcpy(resp->da, req->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++ = 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); + + 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); + + pos = hostapd_eid_bss_load(hapd, pos, epos - pos); + +#ifdef CONFIG_IEEE80211N + pos = hostapd_eid_ht_capabilities(hapd, pos); + pos = hostapd_eid_ht_operation(hapd, pos); +#endif /* CONFIG_IEEE80211N */ + + pos = hostapd_eid_ext_capab(hapd, pos); + + pos = hostapd_eid_time_adv(hapd, pos); + pos = hostapd_eid_time_zone(hapd, pos); + + pos = hostapd_eid_interworking(hapd, pos); + pos = hostapd_eid_adv_proto(hapd, pos); + pos = hostapd_eid_roaming_consortium(hapd, pos); + + pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, + &hapd->iface->cs_c_off_proberesp); +#ifdef CONFIG_IEEE80211AC + pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_operation(hapd, pos); +#endif /* CONFIG_IEEE80211AC */ + + /* 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 */ + +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p && + hapd->p2p_probe_resp_ie) { + os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie), + wpabuf_len(hapd->p2p_probe_resp_ie)); + pos += wpabuf_len(hapd->p2p_probe_resp_ie); + } +#endif /* CONFIG_P2P */ +#ifdef CONFIG_P2P_MANAGER + if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == + P2P_MANAGE) + pos = hostapd_eid_p2p_manage(hapd, pos); +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_HS20 + pos = hostapd_eid_hs20_indication(hapd, pos); +#endif /* CONFIG_HS20 */ + + if (hapd->conf->vendor_elements) { + os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); + pos += wpabuf_len(hapd->conf->vendor_elements); + } + + *resp_len = pos - (u8 *) resp; + return (u8 *) resp; +} + + +enum ssid_match_result { + NO_SSID_MATCH, + EXACT_SSID_MATCH, + WILDCARD_SSID_MATCH +}; + +static enum ssid_match_result ssid_match(struct hostapd_data *hapd, + const u8 *ssid, size_t ssid_len, + const u8 *ssid_list, + size_t ssid_list_len) +{ + const u8 *pos, *end; + int wildcard = 0; + + if (ssid_len == 0) + wildcard = 1; + if (ssid_len == hapd->conf->ssid.ssid_len && + os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0) + return EXACT_SSID_MATCH; + + if (ssid_list == NULL) + return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; + + pos = ssid_list; + end = ssid_list + ssid_list_len; + while (pos + 1 <= end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[1] == 0) + wildcard = 1; + if (pos[1] == hapd->conf->ssid.ssid_len && + os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0) + return EXACT_SSID_MATCH; + pos += 2 + pos[1]; + } + + return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH; +} + + +void handle_probe_req(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int ssi_signal) +{ + u8 *resp; + struct ieee802_11_elems elems; + const u8 *ie; + size_t ie_len; + struct sta_info *sta = NULL; + size_t i, resp_len; + int noack; + enum ssid_match_result res; + + ie = mgmt->u.probe_req.variable; + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + 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, mgmt->da, mgmt->bssid, + ie, ie_len, ssi_signal) > 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; + } + + 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; + } + +#ifdef CONFIG_P2P + if (hapd->p2p && elems.wps_ie) { + struct wpabuf *wps; + wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); + if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) { + wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " + "due to mismatch with Requested Device " + "Type"); + wpabuf_free(wps); + return; + } + wpabuf_free(wps); + } + + if (hapd->p2p && elems.p2p) { + struct wpabuf *p2p; + p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE); + if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) { + wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request " + "due to mismatch with Device ID"); + wpabuf_free(p2p); + return; + } + wpabuf_free(p2p); + } +#endif /* CONFIG_P2P */ + + if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 && + elems.ssid_list_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); + +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_GROUP_OWNER) && + elems.ssid_len == P2P_WILDCARD_SSID_LEN && + os_memcmp(elems.ssid, P2P_WILDCARD_SSID, + P2P_WILDCARD_SSID_LEN) == 0) { + /* Process P2P Wildcard SSID like Wildcard SSID */ + elems.ssid_len = 0; + } +#endif /* CONFIG_P2P */ + + res = ssid_match(hapd, elems.ssid, elems.ssid_len, + elems.ssid_list, elems.ssid_list_len); + if (res != NO_SSID_MATCH) { + if (sta) + sta->ssid_probe = &hapd->conf->ssid; + } else { + 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' (DA " MACSTR ")%s", + MAC2STR(mgmt->sa), ssid_txt, + MAC2STR(mgmt->da), + elems.ssid_list ? " (SSID list)" : ""); + } + return; + } + +#ifdef CONFIG_INTERWORKING + if (elems.interworking && elems.interworking_len >= 1) { + u8 ant = elems.interworking[0] & 0x0f; + if (ant != INTERWORKING_ANT_WILDCARD && + ant != hapd->conf->access_network_type) { + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR + " for mismatching ANT %u ignored", + MAC2STR(mgmt->sa), ant); + return; + } + } + + if (elems.interworking && + (elems.interworking_len == 7 || elems.interworking_len == 9)) { + const u8 *hessid; + if (elems.interworking_len == 7) + hessid = elems.interworking + 1; + else + hessid = elems.interworking + 1 + 2; + if (!is_broadcast_ether_addr(hessid) && + os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) { + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR + " for mismatching HESSID " MACSTR + " ignored", + MAC2STR(mgmt->sa), MAC2STR(hessid)); + return; + } + } +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_GROUP_OWNER) && + supp_rates_11b_only(&elems)) { + /* Indicates support for 11b rates only */ + wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from " + MACSTR " with only 802.11b rates", + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_P2P */ + + /* TODO: verify that supp_rates contains at least one matching rate + * with AP configuration */ + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iconf->ignore_probe_probability > 0.0d && + drand48() < hapd->iconf->ignore_probe_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring probe request from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, + &resp_len); + if (resp == NULL) + return; + + /* + * If this is a broadcast probe request, apply no ack policy to avoid + * excessive retries. + */ + noack = !!(res == WILDCARD_SSID_MATCH && + is_broadcast_ether_addr(mgmt->da)); + + if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0) + wpa_printf(MSG_INFO, "handle_probe_req: send failed"); + + os_free(resp); + + wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s " + "SSID", MAC2STR(mgmt->sa), + elems.ssid_len == 0 ? "broadcast" : "our"); +} + + +static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd, + size_t *resp_len) +{ + /* check probe response offloading caps and print warnings */ + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD)) + return NULL; + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->wps_probe_resp_ie && + (!(hapd->iface->probe_resp_offloads & + (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS | + WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2)))) + wpa_printf(MSG_WARNING, "Device is trying to offload WPS " + "Probe Response while not supporting this"); +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie && + !(hapd->iface->probe_resp_offloads & + WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P)) + wpa_printf(MSG_WARNING, "Device is trying to offload P2P " + "Probe Response while not supporting this"); +#endif /* CONFIG_P2P */ + + if (hapd->conf->interworking && + !(hapd->iface->probe_resp_offloads & + WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING)) + wpa_printf(MSG_WARNING, "Device is trying to offload " + "Interworking Probe Response while not supporting " + "this"); + + /* Generate a Probe Response template for the non-P2P case */ + return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len); +} + +#endif /* NEED_AP_MLME */ + + +int ieee802_11_build_ap_params(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params) +{ + struct ieee80211_mgmt *head = NULL; + u8 *tail = NULL; + size_t head_len = 0, tail_len = 0; + u8 *resp = NULL; + size_t resp_len = 0; +#ifdef NEED_AP_MLME + u16 capab_info; + u8 *pos, *tailpos; + +#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 */ +#ifdef CONFIG_P2P + if (hapd->p2p_beacon_ie) + tail_len += wpabuf_len(hapd->p2p_beacon_ie); +#endif /* CONFIG_P2P */ + if (hapd->conf->vendor_elements) + tail_len += wpabuf_len(hapd->conf->vendor_elements); + 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 -1; + } + + 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); + + tailpos = hostapd_eid_bss_load(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - tailpos); + +#ifdef CONFIG_IEEE80211N + tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_ht_operation(hapd, tailpos); +#endif /* CONFIG_IEEE80211N */ + + tailpos = hostapd_eid_ext_capab(hapd, tailpos); + + /* + * TODO: Time Advertisement element should only be included in some + * DTIM Beacon frames. + */ + tailpos = hostapd_eid_time_adv(hapd, tailpos); + + tailpos = hostapd_eid_interworking(hapd, tailpos); + tailpos = hostapd_eid_adv_proto(hapd, tailpos); + tailpos = hostapd_eid_roaming_consortium(hapd, tailpos); + tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, + &hapd->iface->cs_c_off_beacon); +#ifdef CONFIG_IEEE80211AC + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_vht_operation(hapd, tailpos); +#endif /* CONFIG_IEEE80211AC */ + + /* 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 */ + +#ifdef CONFIG_P2P + if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) { + os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie), + wpabuf_len(hapd->p2p_beacon_ie)); + tailpos += wpabuf_len(hapd->p2p_beacon_ie); + } +#endif /* CONFIG_P2P */ +#ifdef CONFIG_P2P_MANAGER + if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) == + P2P_MANAGE) + tailpos = hostapd_eid_p2p_manage(hapd, tailpos); +#endif /* CONFIG_P2P_MANAGER */ + +#ifdef CONFIG_HS20 + tailpos = hostapd_eid_hs20_indication(hapd, tailpos); +#endif /* CONFIG_HS20 */ + + if (hapd->conf->vendor_elements) { + os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), + wpabuf_len(hapd->conf->vendor_elements)); + tailpos += wpabuf_len(hapd->conf->vendor_elements); + } + + tail_len = tailpos > tail ? tailpos - tail : 0; + + resp = hostapd_probe_resp_offloads(hapd, &resp_len); +#endif /* NEED_AP_MLME */ + + os_memset(params, 0, sizeof(*params)); + params->head = (u8 *) head; + params->head_len = head_len; + params->tail = tail; + params->tail_len = tail_len; + params->proberesp = resp; + params->proberesp_len = resp_len; + params->dtim_period = hapd->conf->dtim_period; + params->beacon_int = hapd->iconf->beacon_int; + params->basic_rates = hapd->iface->basic_rates; + params->ssid = hapd->conf->ssid.ssid; + params->ssid_len = hapd->conf->ssid.ssid_len; + params->pairwise_ciphers = hapd->conf->rsn_pairwise ? + hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise; + params->group_cipher = hapd->conf->wpa_group; + params->key_mgmt_suites = hapd->conf->wpa_key_mgmt; + params->auth_algs = hapd->conf->auth_algs; + params->wpa_version = hapd->conf->wpa; + params->privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa || + (hapd->conf->ieee802_1x && + (hapd->conf->default_wep_key_len || + hapd->conf->individual_wep_key_len)); + switch (hapd->conf->ignore_broadcast_ssid) { + case 0: + params->hide_ssid = NO_SSID_HIDING; + break; + case 1: + params->hide_ssid = HIDDEN_SSID_ZERO_LEN; + break; + case 2: + params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS; + break; + } + params->isolate = hapd->conf->isolate; +#ifdef NEED_AP_MLME + params->cts_protect = !!(ieee802_11_erp_info(hapd) & + ERP_INFO_USE_PROTECTION); + params->preamble = hapd->iface->num_sta_no_short_preamble == 0 && + hapd->iconf->preamble == SHORT_PREAMBLE; + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + params->short_slot_time = + hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1; + else + params->short_slot_time = -1; + if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) + params->ht_opmode = -1; + else + params->ht_opmode = hapd->iface->ht_op_mode; +#endif /* NEED_AP_MLME */ + params->interworking = hapd->conf->interworking; + if (hapd->conf->interworking && + !is_zero_ether_addr(hapd->conf->hessid)) + params->hessid = hapd->conf->hessid; + params->access_network_type = hapd->conf->access_network_type; + params->ap_max_inactivity = hapd->conf->ap_max_inactivity; +#ifdef CONFIG_HS20 + params->disable_dgaf = hapd->conf->disable_dgaf; +#endif /* CONFIG_HS20 */ + return 0; +} + + +void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params) +{ + os_free(params->tail); + params->tail = NULL; + os_free(params->head); + params->head = NULL; + os_free(params->proberesp); + params->proberesp = NULL; +} + + +int ieee802_11_set_beacon(struct hostapd_data *hapd) +{ + struct wpa_driver_ap_params params; + struct wpabuf *beacon, *proberesp, *assocresp; + int res, ret = -1; + + if (hapd->iface->csa_in_progress) { + wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period"); + return -1; + } + + hapd->beacon_set_done = 1; + + if (ieee802_11_build_ap_params(hapd, ¶ms) < 0) + return -1; + + if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) < + 0) + goto fail; + + params.beacon_ies = beacon; + params.proberesp_ies = proberesp; + params.assocresp_ies = assocresp; + + res = hostapd_drv_set_ap(hapd, ¶ms); + hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); + if (res) + wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); + else + ret = 0; +fail: + ieee802_11_free_ap_params(¶ms); + return ret; +} + + +int ieee802_11_set_beacons(struct hostapd_iface *iface) +{ + size_t i; + int ret = 0; + + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->started && + ieee802_11_set_beacon(iface->bss[i]) < 0) + ret = -1; + } + + return ret; +} + + +/* only update beacons if started */ +int ieee802_11_update_beacons(struct hostapd_iface *iface) +{ + size_t i; + int ret = 0; + + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->beacon_set_done && iface->bss[i]->started && + ieee802_11_set_beacon(iface->bss[i]) < 0) + ret = -1; + } + + return ret; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/src/ap/beacon.h b/contrib/hostapd/src/ap/beacon.h new file mode 100644 index 0000000000..722159a750 --- /dev/null +++ b/contrib/hostapd/src/ap/beacon.h @@ -0,0 +1,25 @@ +/* + * 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 software may be distributed under the terms of the BSD license. + * See README 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, + int ssi_signal); +int ieee802_11_set_beacon(struct hostapd_data *hapd); +int ieee802_11_set_beacons(struct hostapd_iface *iface); +int ieee802_11_update_beacons(struct hostapd_iface *iface); +int ieee802_11_build_ap_params(struct hostapd_data *hapd, + struct wpa_driver_ap_params *params); +void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params); + +#endif /* BEACON_H */ diff --git a/contrib/hostapd/src/ap/ctrl_iface_ap.c b/contrib/hostapd/src/ap/ctrl_iface_ap.c new file mode 100644 index 0000000000..8c0cbab665 --- /dev/null +++ b/contrib/hostapd/src/ap/ctrl_iface_ap.c @@ -0,0 +1,510 @@ +/* + * Control interface for shared AP commands + * Copyright (c) 2004-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 "hostapd.h" +#include "ieee802_1x.h" +#include "wpa_auth.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "wps_hostapd.h" +#include "p2p_hostapd.h" +#include "ctrl_iface_ap.h" +#include "ap_drv_ops.h" + + +static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + struct hostap_sta_driver_data data; + int ret; + + if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) + return 0; + + ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" + "rx_bytes=%lu\ntx_bytes=%lu\n", + data.rx_packets, data.tx_packets, + data.rx_bytes, data.tx_bytes); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; +} + + +static int hostapd_get_sta_conn_time(struct sta_info *sta, + char *buf, size_t buflen) +{ + struct os_reltime age; + int ret; + + if (!sta->connected_time.sec) + return 0; + + os_reltime_age(&sta->connected_time, &age); + + ret = os_snprintf(buf, buflen, "connected_time=%u\n", + (unsigned int) age.sec); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; +} + + +static const char * timeout_next_str(int val) +{ + switch (val) { + case STA_NULLFUNC: + return "NULLFUNC POLL"; + case STA_DISASSOC: + return "DISASSOC"; + case STA_DEAUTH: + return "DEAUTH"; + case STA_REMOVE: + return "REMOVE"; + case STA_DISASSOC_FROM_CLI: + return "DISASSOC_FROM_CLI"; + } + + return "?"; +} + + +static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + int len, res, ret, i; + + if (!sta) + return 0; + + len = 0; + ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=", + MAC2STR(sta->addr)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len); + if (ret < 0) + return len; + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n" + "listen_interval=%d\nsupported_rates=", + sta->aid, sta->capability, sta->listen_interval); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + for (i = 0; i < sta->supported_rates_len; i++) { + ret = os_snprintf(buf + len, buflen - len, "%02x%s", + sta->supported_rates[i], + i + 1 < sta->supported_rates_len ? " " : ""); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + + ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n", + timeout_next_str(sta->timeout_next)); + 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; + res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + + len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len); + len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len); + + 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; + const char *pos; + struct sta_info *sta; + + if (hwaddr_aton(txtaddr, addr)) { + ret = os_snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) + return -1; + + pos = os_strchr(txtaddr, ' '); + if (pos) { + pos++; + +#ifdef HOSTAPD_DUMP_STATE + if (os_strcmp(pos, "eapol") == 0) { + if (sta->eapol_sm == NULL) + return -1; + return eapol_auth_dump_state(sta->eapol_sm, buf, + buflen); + } +#endif /* HOSTAPD_DUMP_STATE */ + + return -1; + } + + return hostapd_ctrl_iface_sta_mib(hapd, sta, 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; + } + + if (!sta->next) + return 0; + + return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); +} + + +#ifdef CONFIG_P2P_MANAGER +static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype, + u8 minor_reason_code, const u8 *addr) +{ + struct ieee80211_mgmt *mgmt; + int ret; + u8 *pos; + + if (hapd->driver->send_frame == NULL) + return -1; + + mgmt = os_zalloc(sizeof(*mgmt) + 100); + if (mgmt == NULL) + return -1; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR + " with minor reason code %u (stype=%u)", + MAC2STR(addr), minor_reason_code, stype); + + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype); + 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); + if (stype == WLAN_FC_STYPE_DEAUTH) { + mgmt->u.deauth.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + pos = (u8 *) (&mgmt->u.deauth.reason_code + 1); + } else { + mgmt->u.disassoc.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1); + } + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 4 + 3 + 1; + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = P2P_OUI_TYPE; + + *pos++ = P2P_ATTR_MINOR_REASON_CODE; + WPA_PUT_LE16(pos, 1); + pos += 2; + *pos++ = minor_reason_code; + + ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt, + pos - (u8 *) mgmt, 1); + os_free(mgmt); + + return ret < 0 ? -1 : 0; +} +#endif /* CONFIG_P2P_MANAGER */ + + +int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", + txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + pos = os_strstr(txtaddr, " test="); + if (pos) { + struct ieee80211_mgmt mgmt; + int encrypt; + if (hapd->driver->send_frame == NULL) + return -1; + pos += 6; + encrypt = atoi(pos); + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DEAUTH); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + mgmt.u.deauth.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, + IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), + encrypt) < 0) + return -1; + return 0; + } + +#ifdef CONFIG_P2P_MANAGER + pos = os_strstr(txtaddr, " p2p="); + if (pos) { + return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH, + atoi(pos + 5), addr); + } +#endif /* CONFIG_P2P_MANAGER */ + + pos = os_strstr(txtaddr, " reason="); + if (pos) + reason = atoi(pos + 8); + + hostapd_drv_sta_deauth(hapd, addr, reason); + sta = ap_get_sta(hapd, addr); + if (sta) + ap_sta_deauthenticate(hapd, sta, reason); + else if (addr[0] == 0xff) + hostapd_free_stas(hapd); + + return 0; +} + + +int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", + txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + pos = os_strstr(txtaddr, " test="); + if (pos) { + struct ieee80211_mgmt mgmt; + int encrypt; + if (hapd->driver->send_frame == NULL) + return -1; + pos += 6; + encrypt = atoi(pos); + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_DISASSOC); + 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.disassoc.reason_code = + host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt, + IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), + encrypt) < 0) + return -1; + return 0; + } + +#ifdef CONFIG_P2P_MANAGER + pos = os_strstr(txtaddr, " p2p="); + if (pos) { + return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC, + atoi(pos + 5), addr); + } +#endif /* CONFIG_P2P_MANAGER */ + + pos = os_strstr(txtaddr, " reason="); + if (pos) + reason = atoi(pos + 8); + + hostapd_drv_sta_disassoc(hapd, addr, reason); + sta = ap_get_sta(hapd, addr); + if (sta) + ap_sta_disassociate(hapd, sta, reason); + else if (addr[0] == 0xff) + hostapd_free_stas(hapd); + + return 0; +} + + +int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, + size_t buflen) +{ + struct hostapd_iface *iface = hapd->iface; + int len = 0, ret; + size_t i; + + ret = os_snprintf(buf + len, buflen - len, + "state=%s\n" + "phy=%s\n" + "freq=%d\n" + "num_sta_non_erp=%d\n" + "num_sta_no_short_slot_time=%d\n" + "num_sta_no_short_preamble=%d\n" + "olbc=%d\n" + "num_sta_ht_no_gf=%d\n" + "num_sta_no_ht=%d\n" + "num_sta_ht_20_mhz=%d\n" + "olbc_ht=%d\n" + "ht_op_mode=0x%x\n", + hostapd_state_text(iface->state), + iface->phy, + iface->freq, + iface->num_sta_non_erp, + iface->num_sta_no_short_slot_time, + iface->num_sta_no_short_preamble, + iface->olbc, + iface->num_sta_ht_no_gf, + iface->num_sta_no_ht, + iface->num_sta_ht_20mhz, + iface->olbc_ht, + iface->ht_op_mode); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + ret = os_snprintf(buf + len, buflen - len, + "channel=%u\n" + "secondary_channel=%d\n" + "ieee80211n=%d\n" + "ieee80211ac=%d\n" + "vht_oper_chwidth=%d\n" + "vht_oper_centr_freq_seg0_idx=%d\n" + "vht_oper_centr_freq_seg1_idx=%d\n", + iface->conf->channel, + iface->conf->secondary_channel, + iface->conf->ieee80211n, + iface->conf->ieee80211ac, + iface->conf->vht_oper_chwidth, + iface->conf->vht_oper_centr_freq_seg0_idx, + iface->conf->vht_oper_centr_freq_seg1_idx); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + ret = os_snprintf(buf + len, buflen - len, + "bss[%d]=%s\n" + "bssid[%d]=" MACSTR "\n" + "ssid[%d]=%s\n" + "num_sta[%d]=%d\n", + (int) i, bss->conf->iface, + (int) i, MAC2STR(bss->own_addr), + (int) i, + wpa_ssid_txt(bss->conf->ssid.ssid, + bss->conf->ssid.ssid_len), + (int) i, bss->num_sta); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + + return len; +} + + +int hostapd_parse_csa_settings(const char *pos, + struct csa_settings *settings) +{ + char *end; + + if (!settings) + return -1; + + os_memset(settings, 0, sizeof(*settings)); + settings->cs_count = strtol(pos, &end, 10); + if (pos == end) { + wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided"); + return -1; + } + + settings->freq_params.freq = atoi(end); + if (settings->freq_params.freq == 0) { + wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided"); + return -1; + } + +#define SET_CSA_SETTING(str) \ + do { \ + const char *pos2 = os_strstr(pos, " " #str "="); \ + if (pos2) { \ + pos2 += sizeof(" " #str "=") - 1; \ + settings->freq_params.str = atoi(pos2); \ + } \ + } while (0) + + SET_CSA_SETTING(center_freq1); + SET_CSA_SETTING(center_freq2); + SET_CSA_SETTING(bandwidth); + SET_CSA_SETTING(sec_channel_offset); + settings->freq_params.ht_enabled = !!os_strstr(pos, " ht"); + settings->freq_params.vht_enabled = !!os_strstr(pos, " vht"); + settings->block_tx = !!os_strstr(pos, " blocktx"); +#undef SET_CSA_SETTING + + return 0; +} diff --git a/contrib/hostapd/src/ap/ctrl_iface_ap.h b/contrib/hostapd/src/ap/ctrl_iface_ap.h new file mode 100644 index 0000000000..ee58b4c969 --- /dev/null +++ b/contrib/hostapd/src/ap/ctrl_iface_ap.h @@ -0,0 +1,28 @@ +/* + * Control interface for shared AP commands + * Copyright (c) 2004-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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); +int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd, + const char *txtaddr); +int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd, + const char *txtaddr); +int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, + size_t buflen); +int hostapd_parse_csa_settings(const char *pos, + struct csa_settings *settings); + + +#endif /* CTRL_IFACE_AP_H */ diff --git a/contrib/hostapd/src/ap/dfs.c b/contrib/hostapd/src/ap/dfs.c new file mode 100644 index 0000000000..e4c00f8a14 --- /dev/null +++ b/contrib/hostapd/src/ap/dfs.c @@ -0,0 +1,803 @@ +/* + * DFS - Dynamic Frequency Selection + * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "drivers/driver.h" +#include "dfs.h" + + +static int dfs_get_used_n_chans(struct hostapd_iface *iface) +{ + int n_chans = 1; + + if (iface->conf->ieee80211n && iface->conf->secondary_channel) + n_chans = 2; + + if (iface->conf->ieee80211ac) { + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + break; + case VHT_CHANWIDTH_80MHZ: + n_chans = 4; + break; + case VHT_CHANWIDTH_160MHZ: + n_chans = 8; + break; + default: + break; + } + } + + return n_chans; +} + + +static int dfs_channel_available(struct hostapd_channel_data *chan, + int skip_radar) +{ + /* + * When radar detection happens, CSA is performed. However, there's no + * time for CAC, so radar channels must be skipped when finding a new + * channel for CSA. + */ + if (skip_radar && chan->flag & HOSTAPD_CHAN_RADAR) + return 0; + + if (chan->flag & HOSTAPD_CHAN_DISABLED) + return 0; + if ((chan->flag & HOSTAPD_CHAN_RADAR) && + ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == + HOSTAPD_CHAN_DFS_UNAVAILABLE)) + return 0; + return 1; +} + + +static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) +{ + /* + * The tables contain first valid channel number based on channel width. + * We will also choose this first channel as the control one. + */ + int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, + 184, 192 }; + /* + * VHT80, valid channels based on center frequency: + * 42, 58, 106, 122, 138, 155 + */ + int allowed_80[] = { 36, 52, 100, 116, 132, 149 }; + int *allowed = allowed_40; + unsigned int i, allowed_no = 0; + + switch (n_chans) { + case 2: + allowed = allowed_40; + allowed_no = ARRAY_SIZE(allowed_40); + break; + case 4: + allowed = allowed_80; + allowed_no = ARRAY_SIZE(allowed_80); + break; + default: + wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans); + break; + } + + for (i = 0; i < allowed_no; i++) { + if (chan->chan == allowed[i]) + return 1; + } + + return 0; +} + + +static int dfs_chan_range_available(struct hostapd_hw_modes *mode, + int first_chan_idx, int num_chans, + int skip_radar) +{ + struct hostapd_channel_data *first_chan, *chan; + int i; + + if (first_chan_idx + num_chans >= mode->num_channels) + return 0; + + first_chan = &mode->channels[first_chan_idx]; + + for (i = 0; i < num_chans; i++) { + chan = &mode->channels[first_chan_idx + i]; + + if (first_chan->freq + i * 20 != chan->freq) + return 0; + + if (!dfs_channel_available(chan, skip_radar)) + return 0; + } + + return 1; +} + + +/* + * The function assumes HT40+ operation. + * Make sure to adjust the following variables after calling this: + * - hapd->secondary_channel + * - hapd->vht_oper_centr_freq_seg0_idx + * - hapd->vht_oper_centr_freq_seg1_idx + */ +static int dfs_find_channel(struct hostapd_iface *iface, + struct hostapd_channel_data **ret_chan, + int idx, int skip_radar) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int i, channel_idx = 0, n_chans; + + mode = iface->current_mode; + n_chans = dfs_get_used_n_chans(iface); + + wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + + /* Skip HT40/VHT incompatible channels */ + if (iface->conf->ieee80211n && + iface->conf->secondary_channel && + !dfs_is_chan_allowed(chan, n_chans)) + continue; + + /* Skip incompatible chandefs */ + if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) + continue; + + if (ret_chan && idx == channel_idx) { + wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan); + *ret_chan = chan; + return idx; + } + wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan); + channel_idx++; + } + return channel_idx; +} + + +static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface, + struct hostapd_channel_data *chan, + int secondary_channel, + u8 *vht_oper_centr_freq_seg0_idx, + u8 *vht_oper_centr_freq_seg1_idx) +{ + if (!iface->conf->ieee80211ac) + return; + + if (!chan) + return; + + *vht_oper_centr_freq_seg1_idx = 0; + + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + if (secondary_channel == 1) + *vht_oper_centr_freq_seg0_idx = chan->chan + 2; + else if (secondary_channel == -1) + *vht_oper_centr_freq_seg0_idx = chan->chan - 2; + else + *vht_oper_centr_freq_seg0_idx = chan->chan; + break; + case VHT_CHANWIDTH_80MHZ: + *vht_oper_centr_freq_seg0_idx = chan->chan + 6; + break; + case VHT_CHANWIDTH_160MHZ: + *vht_oper_centr_freq_seg0_idx = chan->chan + 14; + break; + default: + wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now"); + break; + } + + wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d", + *vht_oper_centr_freq_seg0_idx, + *vht_oper_centr_freq_seg1_idx); +} + + +/* Return start channel idx we will use for mode->channels[idx] */ +static int dfs_get_start_chan_idx(struct hostapd_iface *iface) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int channel_no = iface->conf->channel; + int res = -1, i; + + /* HT40- */ + if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1) + channel_no -= 4; + + /* VHT */ + if (iface->conf->ieee80211ac) { + switch (iface->conf->vht_oper_chwidth) { + case VHT_CHANWIDTH_USE_HT: + break; + case VHT_CHANWIDTH_80MHZ: + channel_no = + iface->conf->vht_oper_centr_freq_seg0_idx - 6; + break; + case VHT_CHANWIDTH_160MHZ: + channel_no = + iface->conf->vht_oper_centr_freq_seg0_idx - 14; + break; + default: + wpa_printf(MSG_INFO, + "DFS only VHT20/40/80/160 is supported now"); + channel_no = -1; + break; + } + } + + /* Get idx */ + mode = iface->current_mode; + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + if (chan->chan == channel_no) { + res = i; + break; + } + } + + if (res == -1) + wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1"); + + return res; +} + + +/* At least one channel have radar flag */ +static int dfs_check_chans_radar(struct hostapd_iface *iface, + int start_chan_idx, int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i, res = 0; + + mode = iface->current_mode; + + for (i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if (channel->flag & HOSTAPD_CHAN_RADAR) + res++; + } + + return res; +} + + +/* All channels available */ +static int dfs_check_chans_available(struct hostapd_iface *iface, + int start_chan_idx, int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i; + + mode = iface->current_mode; + + for(i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) != + HOSTAPD_CHAN_DFS_AVAILABLE) + break; + } + + return i == n_chans; +} + + +/* At least one channel unavailable */ +static int dfs_check_chans_unavailable(struct hostapd_iface *iface, + int start_chan_idx, + int n_chans) +{ + struct hostapd_channel_data *channel; + struct hostapd_hw_modes *mode; + int i, res = 0; + + mode = iface->current_mode; + + for(i = 0; i < n_chans; i++) { + channel = &mode->channels[start_chan_idx + i]; + if (channel->flag & HOSTAPD_CHAN_DISABLED) + res++; + if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) == + HOSTAPD_CHAN_DFS_UNAVAILABLE) + res++; + } + + return res; +} + + +static struct hostapd_channel_data * +dfs_get_valid_channel(struct hostapd_iface *iface, + int *secondary_channel, + u8 *vht_oper_centr_freq_seg0_idx, + u8 *vht_oper_centr_freq_seg1_idx, + int skip_radar) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan = NULL; + int num_available_chandefs; + int chan_idx; + u32 _rand; + + wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); + + if (iface->current_mode == NULL) + return NULL; + + mode = iface->current_mode; + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return NULL; + + /* Get the count first */ + num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar); + if (num_available_chandefs == 0) + return NULL; + + os_get_random((u8 *) &_rand, sizeof(_rand)); + chan_idx = _rand % num_available_chandefs; + dfs_find_channel(iface, &chan, chan_idx, skip_radar); + + /* dfs_find_channel() calculations assume HT40+ */ + if (iface->conf->secondary_channel) + *secondary_channel = 1; + else + *secondary_channel = 0; + + dfs_adjust_vht_center_freq(iface, chan, + *secondary_channel, + vht_oper_centr_freq_seg0_idx, + vht_oper_centr_freq_seg1_idx); + + return chan; +} + + +static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state) +{ + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan = NULL; + int i; + + mode = iface->current_mode; + if (mode == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->freq == freq) { + if (chan->flag & HOSTAPD_CHAN_RADAR) { + chan->flag &= ~HOSTAPD_CHAN_DFS_MASK; + chan->flag |= state; + return 1; /* Channel found */ + } + } + } + wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq); + return 0; +} + + +static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled, + int chan_offset, int chan_width, int cf1, + int cf2, u32 state) +{ + int n_chans = 1, i; + struct hostapd_hw_modes *mode; + int frequency = freq; + int ret = 0; + + mode = iface->current_mode; + if (mode == NULL) + return 0; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) { + wpa_printf(MSG_WARNING, "current_mode != IEEE80211A"); + return 0; + } + + /* Seems cf1 and chan_width is enough here */ + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + n_chans = 1; + if (frequency == 0) + frequency = cf1; + break; + case CHAN_WIDTH_40: + n_chans = 2; + frequency = cf1 - 10; + break; + case CHAN_WIDTH_80: + n_chans = 4; + frequency = cf1 - 30; + break; + case CHAN_WIDTH_160: + n_chans = 8; + frequency = cf1 - 70; + break; + default: + wpa_printf(MSG_INFO, "DFS chan_width %d not supported", + chan_width); + break; + } + + wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency, + n_chans); + for (i = 0; i < n_chans; i++) { + ret += set_dfs_state_freq(iface, frequency, state); + frequency = frequency + 20; + } + + return ret; +} + + +static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq, + int chan_width, int cf1, int cf2) +{ + int start_chan_idx; + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *chan; + int n_chans, i, j, frequency = freq, radar_n_chans = 1; + u8 radar_chan; + int res = 0; + + /* Our configuration */ + mode = iface->current_mode; + start_chan_idx = dfs_get_start_chan_idx(iface); + n_chans = dfs_get_used_n_chans(iface); + + /* Check we are on DFS channel(s) */ + if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans)) + return 0; + + /* Reported via radar event */ + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + radar_n_chans = 1; + if (frequency == 0) + frequency = cf1; + break; + case CHAN_WIDTH_40: + radar_n_chans = 2; + frequency = cf1 - 10; + break; + case CHAN_WIDTH_80: + radar_n_chans = 4; + frequency = cf1 - 30; + break; + case CHAN_WIDTH_160: + radar_n_chans = 8; + frequency = cf1 - 70; + break; + default: + wpa_printf(MSG_INFO, "DFS chan_width %d not supported", + chan_width); + break; + } + + ieee80211_freq_to_chan(frequency, &radar_chan); + + for (i = 0; i < n_chans; i++) { + chan = &mode->channels[start_chan_idx + i]; + if (!(chan->flag & HOSTAPD_CHAN_RADAR)) + continue; + for (j = 0; j < radar_n_chans; j++) { + wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d", + chan->chan, radar_chan + j * 4); + if (chan->chan == radar_chan + j * 4) + res++; + } + } + + wpa_printf(MSG_DEBUG, "overlapped: %d", res); + + return res; +} + + +/* + * Main DFS handler + * 1 - continue channel/ap setup + * 0 - channel/ap setup will be continued after CAC + * -1 - hit critical error + */ +int hostapd_handle_dfs(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *channel; + int res, n_chans, start_chan_idx; + int skip_radar = 0; + + iface->cac_started = 0; + + do { + /* Get start (first) channel for current configuration */ + start_chan_idx = dfs_get_start_chan_idx(iface); + if (start_chan_idx == -1) + return -1; + + /* Get number of used channels, depend on width */ + n_chans = dfs_get_used_n_chans(iface); + + /* Check if any of configured channels require DFS */ + res = dfs_check_chans_radar(iface, start_chan_idx, n_chans); + wpa_printf(MSG_DEBUG, + "DFS %d channels required radar detection", + res); + if (!res) + return 1; + + /* Check if all channels are DFS available */ + res = dfs_check_chans_available(iface, start_chan_idx, n_chans); + wpa_printf(MSG_DEBUG, + "DFS all channels available, (SKIP CAC): %s", + res ? "yes" : "no"); + if (res) + return 1; + + /* Check if any of configured channels is unavailable */ + res = dfs_check_chans_unavailable(iface, start_chan_idx, + n_chans); + wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", + res, res ? "yes": "no"); + if (res) { + int sec; + u8 cf1, cf2; + + channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, + skip_radar); + if (!channel) { + wpa_printf(MSG_ERROR, "could not get valid channel"); + return -1; + } + + iface->freq = channel->freq; + iface->conf->channel = channel->chan; + iface->conf->secondary_channel = sec; + iface->conf->vht_oper_centr_freq_seg0_idx = cf1; + iface->conf->vht_oper_centr_freq_seg1_idx = cf2; + } + } while (res); + + /* Finally start CAC */ + hostapd_set_state(iface, HAPD_IFACE_DFS); + wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START + "freq=%d chan=%d sec_chan=%d", + iface->freq, + iface->conf->channel, iface->conf->secondary_channel); + if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode, + iface->freq, + iface->conf->channel, + iface->conf->ieee80211n, + iface->conf->ieee80211ac, + iface->conf->secondary_channel, + iface->conf->vht_oper_chwidth, + iface->conf->vht_oper_centr_freq_seg0_idx, + iface->conf->vht_oper_centr_freq_seg1_idx)) { + wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed"); + return -1; + } + + return 0; +} + + +int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED + "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + + if (success) { + /* Complete iface/ap configuration */ + set_dfs_state(iface, freq, ht_enabled, chan_offset, + chan_width, cf1, cf2, + HOSTAPD_CHAN_DFS_AVAILABLE); + iface->cac_started = 0; + hostapd_setup_interface_complete(iface, 0); + } + + return 0; +} + + +static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *channel; + int secondary_channel; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; + int skip_radar = 0; + int err = 1; + + /* Radar detected during active CAC */ + iface->cac_started = 0; + channel = dfs_get_valid_channel(iface, &secondary_channel, + &vht_oper_centr_freq_seg0_idx, + &vht_oper_centr_freq_seg1_idx, + skip_radar); + + if (!channel) { + wpa_printf(MSG_ERROR, "No valid channel available"); + hostapd_setup_interface_complete(iface, err); + return err; + } + + wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", + channel->chan); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL + "freq=%d chan=%d sec_chan=%d", channel->freq, + channel->chan, secondary_channel); + + iface->freq = channel->freq; + iface->conf->channel = channel->chan; + iface->conf->secondary_channel = secondary_channel; + iface->conf->vht_oper_centr_freq_seg0_idx = + vht_oper_centr_freq_seg0_idx; + iface->conf->vht_oper_centr_freq_seg1_idx = + vht_oper_centr_freq_seg1_idx; + err = 0; + + hostapd_setup_interface_complete(iface, err); + return err; +} + + +static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *channel; + int secondary_channel; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; + int skip_radar = 1; + struct csa_settings csa_settings; + struct hostapd_data *hapd = iface->bss[0]; + int err = 1; + + wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)", + __func__, iface->cac_started ? "yes" : "no", + iface->csa_in_progress ? "yes" : "no"); + + /* Check if CSA in progress */ + if (iface->csa_in_progress) + return 0; + + /* Check if active CAC */ + if (iface->cac_started) + return hostapd_dfs_start_channel_switch_cac(iface); + + /* Perform channel switch/CSA */ + channel = dfs_get_valid_channel(iface, &secondary_channel, + &vht_oper_centr_freq_seg0_idx, + &vht_oper_centr_freq_seg1_idx, + skip_radar); + + if (!channel) { + /* FIXME: Wait for channel(s) to become available */ + hostapd_disable_iface(iface); + return err; + } + + wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", + channel->chan); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL + "freq=%d chan=%d sec_chan=%d", channel->freq, + channel->chan, secondary_channel); + + /* Setup CSA request */ + os_memset(&csa_settings, 0, sizeof(csa_settings)); + csa_settings.cs_count = 5; + csa_settings.block_tx = 1; + err = hostapd_set_freq_params(&csa_settings.freq_params, + iface->conf->hw_mode, + channel->freq, + channel->chan, + iface->conf->ieee80211n, + iface->conf->ieee80211ac, + secondary_channel, + iface->conf->vht_oper_chwidth, + vht_oper_centr_freq_seg0_idx, + vht_oper_centr_freq_seg1_idx, + iface->current_mode->vht_capab); + + if (err) { + wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params"); + hostapd_disable_iface(iface); + return err; + } + + err = hostapd_switch_channel(hapd, &csa_settings); + if (err) { + wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback", + err); + iface->freq = channel->freq; + iface->conf->channel = channel->chan; + iface->conf->secondary_channel = secondary_channel; + iface->conf->vht_oper_centr_freq_seg0_idx = + vht_oper_centr_freq_seg0_idx; + iface->conf->vht_oper_centr_freq_seg1_idx = + vht_oper_centr_freq_seg1_idx; + + hostapd_disable_iface(iface); + hostapd_enable_iface(iface); + return 0; + } + + /* Channel configuration will be updated once CSA completes and + * ch_switch_notify event is received */ + + wpa_printf(MSG_DEBUG, "DFS waiting channel switch event"); + return 0; +} + + +int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + int res; + + if (!iface->conf->ieee80211h) + return 0; + + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED + "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + + /* mark radar frequency as invalid */ + res = set_dfs_state(iface, freq, ht_enabled, chan_offset, + chan_width, cf1, cf2, + HOSTAPD_CHAN_DFS_UNAVAILABLE); + + /* Skip if reported radar event not overlapped our channels */ + res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2); + if (!res) + return 0; + + /* radar detected while operating, switch the channel. */ + res = hostapd_dfs_start_channel_switch(iface); + + return res; +} + + +int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED + "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + /* TODO add correct implementation here */ + set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, + cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); + return 0; +} diff --git a/contrib/hostapd/src/ap/dfs.h b/contrib/hostapd/src/ap/dfs.h new file mode 100644 index 0000000000..859ff79159 --- /dev/null +++ b/contrib/hostapd/src/ap/dfs.h @@ -0,0 +1,25 @@ +/* + * DFS - Dynamic Frequency Selection + * Copyright (c) 2002-2013, Jouni Malinen + * Copyright (c) 2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#ifndef DFS_H +#define DFS_H + +int hostapd_handle_dfs(struct hostapd_iface *iface); + +int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2); +int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, + int ht_enabled, + int chan_offset, int chan_width, + int cf1, int cf2); +int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq, + int ht_enabled, + int chan_offset, int chan_width, int cf1, int cf2); + +#endif /* DFS_H */ diff --git a/contrib/hostapd/src/ap/drv_callbacks.c b/contrib/hostapd/src/ap/drv_callbacks.c new file mode 100644 index 0000000000..9af964686a --- /dev/null +++ b/contrib/hostapd/src/ap/drv_callbacks.c @@ -0,0 +1,1038 @@ +/* + * hostapd / Callback functions for driver wrappers + * Copyright (c) 2002-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 "crypto/random.h" +#include "p2p/p2p.h" +#include "wps/wps.h" +#include "wnm_ap.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "accounting.h" +#include "tkip_countermeasures.h" +#include "ieee802_1x.h" +#include "wpa_auth.h" +#include "wps_hostapd.h" +#include "ap_drv_ops.h" +#include "ap_config.h" +#include "hw_features.h" +#include "dfs.h" + + +int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, + const u8 *req_ies, size_t req_ies_len, int reassoc) +{ + struct sta_info *sta; + int new_assoc, res; + struct ieee802_11_elems elems; + const u8 *ie; + size_t ielen; +#ifdef CONFIG_IEEE80211R + u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; + u8 *p = buf; +#endif /* CONFIG_IEEE80211R */ + u16 reason = WLAN_REASON_UNSPECIFIED; + u16 status = WLAN_STATUS_SUCCESS; + const u8 *p2p_dev_addr = NULL; + + 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; + } + random_add_randomness(addr, ETH_ALEN); + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "associated"); + + ieee802_11_parse_elems(req_ies, req_ies_len, &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) { + ap_sta_no_session_timeout(hapd, sta); + accounting_sta_stop(hapd, sta); + + /* + * Make sure that the previously registered inactivity timer + * will not remove the STA immediately. + */ + sta->timeout_next = STA_NULLFUNC; + } else { + sta = ap_sta_add(hapd, addr); + if (sta == NULL) { + hostapd_drv_sta_disassoc(hapd, addr, + WLAN_REASON_DISASSOC_AP_BUSY); + return -1; + } + } + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2); + +#ifdef CONFIG_P2P + if (elems.p2p) { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, + P2P_IE_VENDOR_TYPE); + if (sta->p2p_ie) + p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_INTERWORKING + if (elems.ext_capab && elems.ext_capab_len > 4) { + if (elems.ext_capab[4] & 0x01) + sta->qos_map_enabled = 1; + } +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_HS20 + wpabuf_free(sta->hs20_ie); + if (elems.hs20 && elems.hs20_len > 4) { + sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, + elems.hs20_len - 4); + } else + sta->hs20_ie = NULL; +#endif /* CONFIG_HS20 */ + + if (hapd->conf->wpa) { + if (ie == NULL || ielen == 0) { +#ifdef CONFIG_WPS + 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; + } +#endif /* CONFIG_WPS */ + + wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); + return -1; + } +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && + os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { + struct wpabuf *wps; + sta->flags |= WLAN_STA_WPS; + wps = ieee802_11_vendor_ie_concat(ie, ielen, + WPS_IE_VENDOR_TYPE); + if (wps) { + if (wps_is_20(wps)) { + wpa_printf(MSG_DEBUG, "WPS: STA " + "supports WPS 2.0"); + sta->flags |= WLAN_STA_WPS2; + } + wpabuf_free(wps); + } + goto skip_wpa_check; + } +#endif /* CONFIG_WPS */ + + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, + p2p_dev_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, + elems.mdie, elems.mdie_len); + if (res != WPA_IE_OK) { + wpa_printf(MSG_DEBUG, "WPA/RSN information element " + "rejected? (res %u)", res); + wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); + if (res == WPA_INVALID_GROUP) { + reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; + status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + } else if (res == WPA_INVALID_PAIRWISE) { + reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; + status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + } else if (res == WPA_INVALID_AKMP) { + reason = WLAN_REASON_AKMP_NOT_VALID; + status = WLAN_STATUS_AKMP_NOT_VALID; + } +#ifdef CONFIG_IEEE80211W + else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) { + reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) { + reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; + status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + } +#endif /* CONFIG_IEEE80211W */ + else { + reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + } + goto fail; + } +#ifdef CONFIG_IEEE80211W + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + sta->sa_query_count > 0) + ap_check_sa_query_timeout(hapd, sta); + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + (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); + +#ifdef CONFIG_IEEE80211R + status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; + + p = hostapd_eid_assoc_comeback_time(hapd, sta, p); + + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, + p - buf); +#endif /* CONFIG_IEEE80211R */ + return 0; + } + + 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) { + status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies, + req_ies_len); + if (status != WLAN_STATUS_SUCCESS) { + if (status == WLAN_STATUS_INVALID_PMKID) + reason = WLAN_REASON_INVALID_IE; + if (status == WLAN_STATUS_INVALID_MDIE) + reason = WLAN_REASON_INVALID_IE; + if (status == WLAN_STATUS_INVALID_FTIE) + reason = WLAN_REASON_INVALID_IE; + goto fail; + } + } +#endif /* CONFIG_IEEE80211R */ + } else if (hapd->conf->wps_state) { +#ifdef CONFIG_WPS + struct wpabuf *wps; + if (req_ies) + wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len, + WPS_IE_VENDOR_TYPE); + else + wps = NULL; +#ifdef CONFIG_WPS_STRICT + if (wps && wps_validate_assoc_req(wps) < 0) { + reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + wpabuf_free(wps); + goto fail; + } +#endif /* CONFIG_WPS_STRICT */ + if (wps) { + sta->flags |= WLAN_STA_WPS; + if (wps_is_20(wps)) { + wpa_printf(MSG_DEBUG, "WPS: STA supports " + "WPS 2.0"); + sta->flags |= WLAN_STA_WPS2; + } + } else + sta->flags |= WLAN_STA_MAYBE_WPS; + wpabuf_free(wps); +#endif /* CONFIG_WPS */ + } +#ifdef CONFIG_WPS +skip_wpa_check: +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_IEEE80211R + p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf), + sta->auth_alg, req_ies, req_ies_len); + + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); +#else /* CONFIG_IEEE80211R */ + /* Keep compiler silent about unused variables */ + if (status) { + } +#endif /* CONFIG_IEEE80211R */ + + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + + if (reassoc && (sta->auth_alg == WLAN_AUTH_FT)) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); + else + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + +#ifdef CONFIG_P2P + if (req_ies) { + p2p_group_notif_assoc(hapd->p2p_group, sta->addr, + req_ies, req_ies_len); + } +#endif /* CONFIG_P2P */ + + return 0; + +fail: +#ifdef CONFIG_IEEE80211R + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); +#endif /* CONFIG_IEEE80211R */ + hostapd_drv_sta_disassoc(hapd, sta->addr, reason); + ap_free_sta(hapd, sta); + return -1; +} + + +void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + 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 reporting a station mode event while hostapd + * was running, so better make sure we stop processing such an + * event here. + */ + wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event " + "with no address"); + return; + } + + 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; + } + + ap_sta_set_authorized(hapd, sta, 0); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + 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); +} + + +void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta = ap_get_sta(hapd, addr); + + if (!sta || !hapd->conf->disassoc_low_ack) + return; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disconnected due to excessive " + "missing ACKs"); + hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK); + if (sta) + ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); +} + + +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset, int width, int cf1, int cf2) +{ +#ifdef NEED_AP_MLME + int channel, chwidth, seg0_idx = 0, seg1_idx = 0; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "driver had channel switch: " + "freq=%d, ht=%d, offset=%d, width=%d, cf1=%d, cf2=%d", + freq, ht, offset, width, cf1, cf2); + + hapd->iface->freq = freq; + + channel = hostapd_hw_get_channel(hapd, freq); + if (!channel) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, "driver switched to " + "bad channel!"); + return; + } + + switch (width) { + case CHAN_WIDTH_80: + chwidth = VHT_CHANWIDTH_80MHZ; + break; + case CHAN_WIDTH_80P80: + chwidth = VHT_CHANWIDTH_80P80MHZ; + break; + case CHAN_WIDTH_160: + chwidth = VHT_CHANWIDTH_160MHZ; + break; + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + case CHAN_WIDTH_40: + default: + chwidth = VHT_CHANWIDTH_USE_HT; + break; + } + + switch (hapd->iface->current_mode->mode) { + case HOSTAPD_MODE_IEEE80211A: + if (cf1 > 5000) + seg0_idx = (cf1 - 5000) / 5; + if (cf2 > 5000) + seg1_idx = (cf2 - 5000) / 5; + break; + default: + seg0_idx = hostapd_hw_get_channel(hapd, cf1); + seg1_idx = hostapd_hw_get_channel(hapd, cf2); + break; + } + + hapd->iconf->channel = channel; + hapd->iconf->ieee80211n = ht; + hapd->iconf->secondary_channel = offset; + hapd->iconf->vht_oper_chwidth = chwidth; + hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx; + hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx; + + if (hapd->iface->csa_in_progress && + freq == hapd->iface->cs_freq_params.freq) { + hostapd_cleanup_cs_params(hapd); + + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d", + freq); + } +#endif /* NEED_AP_MLME */ +} + + +void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, + const u8 *addr, int reason_code) +{ + switch (reason_code) { + case MAX_CLIENT_REACHED: + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR, + MAC2STR(addr)); + break; + case BLOCKED_CLIENT: + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR, + MAC2STR(addr)); + break; + } +} + + +int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, + const u8 *bssid, const u8 *ie, size_t ie_len, + int ssi_signal) +{ + size_t i; + int ret = 0; + + if (sa == NULL || ie == NULL) + return -1; + + random_add_randomness(sa, ETH_ALEN); + for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) { + if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, + sa, da, bssid, ie, ie_len, + ssi_signal) > 0) { + ret = 1; + break; + } + } + return ret; +} + + +#ifdef HOSTAPD + +#ifdef CONFIG_IEEE80211R +static void hostapd_notify_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; + + 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; + + hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len); +} +#endif /* CONFIG_IEEE80211R */ + + +static void hostapd_notif_auth(struct hostapd_data *hapd, + struct auth_info *rx_auth) +{ + struct sta_info *sta; + u16 status = WLAN_STATUS_SUCCESS; + u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; + size_t resp_ies_len = 0; + + sta = ap_get_sta(hapd, rx_auth->peer); + if (!sta) { + sta = ap_sta_add(hapd, rx_auth->peer); + if (sta == NULL) { + status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + } + sta->flags &= ~WLAN_STA_PREAUTH; + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); +#ifdef CONFIG_IEEE80211R + if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) { + sta->auth_alg = WLAN_AUTH_FT; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, NULL); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " + "state machine"); + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid, + rx_auth->auth_transaction, rx_auth->ies, + rx_auth->ies_len, + hostapd_notify_auth_ft_finish, hapd); + return; + } +#endif /* CONFIG_IEEE80211R */ +fail: + hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1, + status, resp_ies, resp_ies_len); +} + + +static void hostapd_action_rx(struct hostapd_data *hapd, + struct rx_mgmt *drv_mgmt) +{ + struct ieee80211_mgmt *mgmt; + struct sta_info *sta; + size_t plen __maybe_unused; + u16 fc; + + if (drv_mgmt->frame_len < 24 + 1) + return; + + plen = drv_mgmt->frame_len - 24 - 1; + + mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) + return; /* handled by the driver */ + + wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", + mgmt->u.action.category, (int) plen); + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "%s: station not found", __func__); + return; + } +#ifdef CONFIG_IEEE80211R + if (mgmt->u.action.category == WLAN_ACTION_FT) { + const u8 *payload = drv_mgmt->frame + 24 + 1; + wpa_ft_action_rx(sta->wpa_sm, payload, plen); + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) { + ieee802_11_sa_query_action( + hapd, mgmt->sa, + mgmt->u.action.u.sa_query_resp.action, + mgmt->u.action.u.sa_query_resp.trans_id); + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WNM + if (mgmt->u.action.category == WLAN_ACTION_WNM) { + ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); + } +#endif /* CONFIG_WNM */ +} + + +#ifdef NEED_AP_MLME + +#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 *bssid, const u8 *addr, + int wds) +{ + hapd = get_hapd_bssid(hapd->iface, bssid); + if (hapd == NULL || hapd == HAPD_BROADCAST) + return; + + ieee802_11_rx_from_unknown(hapd, addr, wds); +} + + +static int 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; + int ret; + + hdr = (const struct ieee80211_hdr *) rx_mgmt->frame; + bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len); + if (bssid == NULL) + return 0; + + 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 0; + } + + 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; + ret = 0; + for (i = 0; i < iface->num_bss; i++) { + if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame, + rx_mgmt->frame_len, &fi) > 0) + ret = 1; + } + } else + ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, + &fi); + + random_add_randomness(&fi, sizeof(fi)); + + return ret; +} + + +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_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; + struct sta_info *sta; + size_t j; + + for (j = 0; j < iface->num_bss; j++) { + if ((sta = ap_get_sta(iface->bss[j], src))) { + if (sta->flags & WLAN_STA_ASSOC) { + hapd = iface->bss[j]; + break; + } + } + } + + ieee802_1x_receive(hapd, src, data, data_len); +} + + +static struct hostapd_channel_data * hostapd_get_mode_channel( + struct hostapd_iface *iface, unsigned int freq) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (!chan) + return NULL; + if ((unsigned int) chan->freq == freq) + return chan; + } + + return NULL; +} + + +static void hostapd_update_nf(struct hostapd_iface *iface, + struct hostapd_channel_data *chan, + struct freq_survey *survey) +{ + if (!iface->chans_surveyed) { + chan->min_nf = survey->nf; + iface->lowest_nf = survey->nf; + } else { + if (dl_list_empty(&chan->survey_list)) + chan->min_nf = survey->nf; + else if (survey->nf < chan->min_nf) + chan->min_nf = survey->nf; + if (survey->nf < iface->lowest_nf) + iface->lowest_nf = survey->nf; + } +} + + +static void hostapd_event_get_survey(struct hostapd_data *hapd, + struct survey_results *survey_results) +{ + struct hostapd_iface *iface = hapd->iface; + struct freq_survey *survey, *tmp; + struct hostapd_channel_data *chan; + + if (dl_list_empty(&survey_results->survey_list)) { + wpa_printf(MSG_DEBUG, "No survey data received"); + return; + } + + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, + struct freq_survey, list) { + chan = hostapd_get_mode_channel(iface, survey->freq); + if (!chan) + continue; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + + dl_list_del(&survey->list); + dl_list_add_tail(&chan->survey_list, &survey->list); + + hostapd_update_nf(iface, chan, survey); + + iface->chans_surveyed++; + } +} + + +#ifdef NEED_AP_MLME + +static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq); + hostapd_dfs_radar_detected(hapd->iface, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq); + hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq); + hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + +static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq); + hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + +#endif /* NEED_AP_MLME */ + + +void wpa_supplicant_event(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) +{ + struct hostapd_data *hapd = ctx; +#ifndef CONFIG_NO_STDOUT_DEBUG + int level = MSG_DEBUG; + + if (event == EVENT_RX_MGMT && data->rx_mgmt.frame && + data->rx_mgmt.frame_len >= 24) { + const struct ieee80211_hdr *hdr; + u16 fc; + hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame; + fc = le_to_host16(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) + level = MSG_EXCESSIVE; + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) + level = MSG_EXCESSIVE; + } + + wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received", + event_to_string(event), event); +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + 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, NULL); + 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_EAPOL_TX_STATUS: + hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst, + data->eapol_tx_status.data, + data->eapol_tx_status.data_len, + data->eapol_tx_status.ack); + break; + case EVENT_DRIVER_CLIENT_POLL_OK: + hostapd_client_poll_ok(hapd, data->client_poll.addr); + break; + case EVENT_RX_FROM_UNKNOWN: + hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid, + data->rx_from_unknown.addr, + data->rx_from_unknown.wds); + break; +#endif /* NEED_AP_MLME */ + case EVENT_RX_MGMT: +#ifdef NEED_AP_MLME + if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0) + break; +#endif /* NEED_AP_MLME */ + hostapd_action_rx(hapd, &data->rx_mgmt); + break; + case EVENT_RX_PROBE_REQ: + if (data->rx_probe_req.sa == NULL || + data->rx_probe_req.ie == NULL) + break; + hostapd_probe_req_rx(hapd, data->rx_probe_req.sa, + data->rx_probe_req.da, + data->rx_probe_req.bssid, + data->rx_probe_req.ie, + data->rx_probe_req.ie_len, + data->rx_probe_req.ssi_signal); + 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, + data->assoc_info.reassoc); + 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; + case EVENT_STATION_LOW_ACK: + if (!data) + break; + hostapd_event_sta_low_ack(hapd, data->low_ack.addr); + break; + case EVENT_AUTH: + hostapd_notif_auth(hapd, &data->auth); + break; + case EVENT_CH_SWITCH: + if (!data) + break; + hostapd_event_ch_switch(hapd, data->ch_switch.freq, + data->ch_switch.ht_enabled, + data->ch_switch.ch_offset, + data->ch_switch.ch_width, + data->ch_switch.cf1, + data->ch_switch.cf2); + break; + case EVENT_CONNECT_FAILED_REASON: + if (!data) + break; + hostapd_event_connect_failed_reason( + hapd, data->connect_failed_reason.addr, + data->connect_failed_reason.code); + break; + case EVENT_SURVEY: + hostapd_event_get_survey(hapd, &data->survey_results); + break; +#ifdef NEED_AP_MLME + case EVENT_DFS_RADAR_DETECTED: + if (!data) + break; + hostapd_event_dfs_radar_detected(hapd, &data->dfs_event); + break; + case EVENT_DFS_CAC_FINISHED: + if (!data) + break; + hostapd_event_dfs_cac_finished(hapd, &data->dfs_event); + break; + case EVENT_DFS_CAC_ABORTED: + if (!data) + break; + hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event); + break; + case EVENT_DFS_NOP_FINISHED: + if (!data) + break; + hostapd_event_dfs_nop_finished(hapd, &data->dfs_event); + break; + case EVENT_CHANNEL_LIST_CHANGED: + /* channel list changed (regulatory?), update channel list */ + /* TODO: check this. hostapd_get_hw_features() initializes + * too much stuff. */ + /* hostapd_get_hw_features(hapd->iface); */ + hostapd_channel_list_updated( + hapd->iface, data->channel_list_changed.initiator); + break; +#endif /* NEED_AP_MLME */ + default: + wpa_printf(MSG_DEBUG, "Unknown event %d", event); + break; + } +} + +#endif /* HOSTAPD */ diff --git a/contrib/hostapd/src/ap/eap_user_db.c b/contrib/hostapd/src/ap/eap_user_db.c new file mode 100644 index 0000000000..79d50e5169 --- /dev/null +++ b/contrib/hostapd/src/ap/eap_user_db.c @@ -0,0 +1,270 @@ +/* + * hostapd / EAP user database + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ + +#include "common.h" +#include "eap_common/eap_wsc_common.h" +#include "eap_server/eap_methods.h" +#include "eap_server/eap.h" +#include "ap_config.h" +#include "hostapd.h" + +#ifdef CONFIG_SQLITE + +static void set_user_methods(struct hostapd_eap_user *user, const char *methods) +{ + char *buf, *start; + int num_methods; + + buf = os_strdup(methods); + if (buf == NULL) + return; + + os_memset(&user->methods, 0, sizeof(user->methods)); + num_methods = 0; + start = buf; + while (*start) { + char *pos3 = os_strchr(start, ','); + if (pos3) + *pos3++ = '\0'; + user->methods[num_methods].method = + eap_server_get_type(start, + &user->methods[num_methods].vendor); + if (user->methods[num_methods].vendor == EAP_VENDOR_IETF && + user->methods[num_methods].method == EAP_TYPE_NONE) { + if (os_strcmp(start, "TTLS-PAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_PAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-CHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_CHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2; + goto skip_eap; + } + wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'", + start); + os_free(buf); + return; + } + + num_methods++; + if (num_methods >= EAP_MAX_METHODS) + break; + skip_eap: + if (pos3 == NULL) + break; + start = pos3; + } + + os_free(buf); +} + + +static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct hostapd_eap_user *user = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "password") == 0 && argv[i]) { + os_free(user->password); + user->password_len = os_strlen(argv[i]); + user->password = (u8 *) os_strdup(argv[i]); + user->next = (void *) 1; + } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) { + set_user_methods(user, argv[i]); + } + } + + return 0; +} + + +static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct hostapd_eap_user *user = ctx; + int i, id = -1, methods = -1; + size_t len; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "identity") == 0 && argv[i]) + id = i; + else if (os_strcmp(col[i], "methods") == 0 && argv[i]) + methods = i; + } + + if (id < 0 || methods < 0) + return 0; + + len = os_strlen(argv[id]); + if (len <= user->identity_len && + os_memcmp(argv[id], user->identity, len) == 0 && + (user->password == NULL || len > user->password_len)) { + os_free(user->password); + user->password_len = os_strlen(argv[id]); + user->password = (u8 *) os_strdup(argv[id]); + user->next = (void *) 1; + set_user_methods(user, argv[methods]); + } + + return 0; +} + + +static const struct hostapd_eap_user * +eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2) +{ + sqlite3 *db; + struct hostapd_eap_user *user = NULL; + char id_str[256], cmd[300]; + size_t i; + + if (identity_len >= sizeof(id_str)) + return NULL; + os_memcpy(id_str, identity, identity_len); + id_str[identity_len] = '\0'; + for (i = 0; i < identity_len; i++) { + if (id_str[i] >= 'a' && id_str[i] <= 'z') + continue; + if (id_str[i] >= 'A' && id_str[i] <= 'Z') + continue; + if (id_str[i] >= '0' && id_str[i] <= '9') + continue; + if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' || + id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' || + id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' || + id_str[i] == '=' || id_str[i] == ' ') + continue; + wpa_printf(MSG_INFO, "DB: Unsupported character in identity"); + return NULL; + } + + os_free(hapd->tmp_eap_user.identity); + os_free(hapd->tmp_eap_user.password); + os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user)); + hapd->tmp_eap_user.phase2 = phase2; + hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1); + if (hapd->tmp_eap_user.identity == NULL) + return NULL; + os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len); + + if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) { + wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s", + hapd->conf->eap_user_sqlite, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + os_snprintf(cmd, sizeof(cmd), + "SELECT password,methods FROM users WHERE " + "identity='%s' AND phase2=%d;", id_str, phase2); + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) != + SQLITE_OK) { + wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation"); + } else if (hapd->tmp_eap_user.next) + user = &hapd->tmp_eap_user; + + if (user == NULL && !phase2) { + os_snprintf(cmd, sizeof(cmd), + "SELECT identity,methods FROM wildcards;"); + wpa_printf(MSG_DEBUG, "DB: %s", cmd); + if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user, + NULL) != SQLITE_OK) { + wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL " + "operation"); + } else if (hapd->tmp_eap_user.next) { + user = &hapd->tmp_eap_user; + os_free(user->identity); + user->identity = user->password; + user->identity_len = user->password_len; + user->password = NULL; + user->password_len = 0; + } + } + + sqlite3_close(db); + + return user; +} + +#endif /* CONFIG_SQLITE */ + + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2) +{ + const struct hostapd_bss_config *conf = hapd->conf; + 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; + } + +#ifdef CONFIG_SQLITE + if (user == NULL && conf->eap_user_sqlite) { + return eap_user_sqlite_get(hapd, identity, identity_len, + phase2); + } +#endif /* CONFIG_SQLITE */ + + return user; +} diff --git a/contrib/hostapd/src/ap/gas_serv.c b/contrib/hostapd/src/ap/gas_serv.c new file mode 100644 index 0000000000..b5fb7dfbc2 --- /dev/null +++ b/contrib/hostapd/src/ap/gas_serv.c @@ -0,0 +1,1199 @@ +/* + * Generic advertisement service (GAS) server + * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "sta_info.h" +#include "gas_serv.h" + + +static void convert_to_protected_dual(struct wpabuf *msg) +{ + u8 *categ = wpabuf_mhead_u8(msg); + *categ = WLAN_ACTION_PROTECTED_DUAL; +} + + +static struct gas_dialog_info * +gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) +{ + struct sta_info *sta; + struct gas_dialog_info *dia = NULL; + int i, j; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + /* + * We need a STA entry to be able to maintain state for + * the GAS query. + */ + wpa_printf(MSG_DEBUG, "ANQP: Add a temporary STA entry for " + "GAS query"); + sta = ap_sta_add(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Failed to add STA " MACSTR + " for GAS query", MAC2STR(addr)); + return NULL; + } + sta->flags |= WLAN_STA_GAS; + /* + * The default inactivity is 300 seconds. We don't need + * it to be that long. + */ + ap_sta_session_timeout(hapd, sta, 5); + } else { + ap_sta_replenish_timeout(hapd, sta, 5); + } + + if (sta->gas_dialog == NULL) { + sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX * + sizeof(struct gas_dialog_info)); + if (sta->gas_dialog == NULL) + return NULL; + } + + for (i = sta->gas_dialog_next, j = 0; j < GAS_DIALOG_MAX; i++, j++) { + if (i == GAS_DIALOG_MAX) + i = 0; + if (sta->gas_dialog[i].valid) + continue; + dia = &sta->gas_dialog[i]; + dia->valid = 1; + dia->index = i; + dia->dialog_token = dialog_token; + sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i; + return dia; + } + + wpa_msg(hapd->msg_ctx, MSG_ERROR, "ANQP: Could not create dialog for " + MACSTR " dialog_token %u. Consider increasing " + "GAS_DIALOG_MAX.", MAC2STR(addr), dialog_token); + + return NULL; +} + + +struct gas_dialog_info * +gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, + u8 dialog_token) +{ + struct sta_info *sta; + int i; + + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "ANQP: could not find STA " MACSTR, + MAC2STR(addr)); + return NULL; + } + for (i = 0; sta->gas_dialog && i < GAS_DIALOG_MAX; i++) { + if (sta->gas_dialog[i].dialog_token != dialog_token || + !sta->gas_dialog[i].valid) + continue; + return &sta->gas_dialog[i]; + } + wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for " + MACSTR " dialog_token %u", MAC2STR(addr), dialog_token); + return NULL; +} + + +void gas_serv_dialog_clear(struct gas_dialog_info *dia) +{ + wpabuf_free(dia->sd_resp); + os_memset(dia, 0, sizeof(*dia)); +} + + +static void gas_serv_free_dialogs(struct hostapd_data *hapd, + const u8 *sta_addr) +{ + struct sta_info *sta; + int i; + + sta = ap_get_sta(hapd, sta_addr); + if (sta == NULL || sta->gas_dialog == NULL) + return; + + for (i = 0; i < GAS_DIALOG_MAX; i++) { + if (sta->gas_dialog[i].valid) + return; + } + + os_free(sta->gas_dialog); + sta->gas_dialog = NULL; +} + + +#ifdef CONFIG_HS20 +static void anqp_add_hs_capab_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + u8 *len; + + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, HS20_STYPE_CAPABILITY_LIST); + if (hapd->conf->hs20_oper_friendly_name) + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); + if (hapd->conf->hs20_wan_metrics) + wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); + if (hapd->conf->hs20_connection_capability) + wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); + if (hapd->conf->nai_realm_data) + wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY); + if (hapd->conf->hs20_operating_class) + wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); + gas_anqp_set_element_len(buf, len); +} +#endif /* CONFIG_HS20 */ + + +static void anqp_add_capab_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + u8 *len; + + len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST); + wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST); + if (hapd->conf->venue_name) + wpabuf_put_le16(buf, ANQP_VENUE_NAME); + if (hapd->conf->network_auth_type) + wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); + if (hapd->conf->roaming_consortium) + wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM); + if (hapd->conf->ipaddr_type_configured) + wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); + if (hapd->conf->nai_realm_data) + wpabuf_put_le16(buf, ANQP_NAI_REALM); + if (hapd->conf->anqp_3gpp_cell_net) + wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); + if (hapd->conf->domain_name) + wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); +#ifdef CONFIG_HS20 + anqp_add_hs_capab_list(hapd, buf); +#endif /* CONFIG_HS20 */ + gas_anqp_set_element_len(buf, len); +} + + +static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) +{ + if (hapd->conf->venue_name) { + u8 *len; + unsigned int i; + len = gas_anqp_add_element(buf, ANQP_VENUE_NAME); + wpabuf_put_u8(buf, hapd->conf->venue_group); + wpabuf_put_u8(buf, hapd->conf->venue_type); + for (i = 0; i < hapd->conf->venue_name_count; i++) { + struct hostapd_lang_string *vn; + vn = &hapd->conf->venue_name[i]; + wpabuf_put_u8(buf, 3 + vn->name_len); + wpabuf_put_data(buf, vn->lang, 3); + wpabuf_put_data(buf, vn->name, vn->name_len); + } + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_network_auth_type(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->network_auth_type) { + wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE); + wpabuf_put_le16(buf, hapd->conf->network_auth_type_len); + wpabuf_put_data(buf, hapd->conf->network_auth_type, + hapd->conf->network_auth_type_len); + } +} + + +static void anqp_add_roaming_consortium(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + unsigned int i; + u8 *len; + + len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM); + for (i = 0; i < hapd->conf->roaming_consortium_count; i++) { + struct hostapd_roaming_consortium *rc; + rc = &hapd->conf->roaming_consortium[i]; + wpabuf_put_u8(buf, rc->len); + wpabuf_put_data(buf, rc->oi, rc->len); + } + gas_anqp_set_element_len(buf, len); +} + + +static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->ipaddr_type_configured) { + wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, hapd->conf->ipaddr_type_availability); + } +} + + +static void anqp_add_nai_realm_eap(struct wpabuf *buf, + struct hostapd_nai_realm_data *realm) +{ + unsigned int i, j; + + wpabuf_put_u8(buf, realm->eap_method_count); + + for (i = 0; i < realm->eap_method_count; i++) { + struct hostapd_nai_realm_eap *eap = &realm->eap_method[i]; + wpabuf_put_u8(buf, 2 + (3 * eap->num_auths)); + wpabuf_put_u8(buf, eap->eap_method); + wpabuf_put_u8(buf, eap->num_auths); + for (j = 0; j < eap->num_auths; j++) { + wpabuf_put_u8(buf, eap->auth_id[j]); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, eap->auth_val[j]); + } + } +} + + +static void anqp_add_nai_realm_data(struct wpabuf *buf, + struct hostapd_nai_realm_data *realm, + unsigned int realm_idx) +{ + u8 *realm_data_len; + + wpa_printf(MSG_DEBUG, "realm=%s, len=%d", realm->realm[realm_idx], + (int) os_strlen(realm->realm[realm_idx])); + realm_data_len = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, realm->encoding); + wpabuf_put_u8(buf, os_strlen(realm->realm[realm_idx])); + wpabuf_put_str(buf, realm->realm[realm_idx]); + anqp_add_nai_realm_eap(buf, realm); + gas_anqp_set_element_len(buf, realm_data_len); +} + + +static int hs20_add_nai_home_realm_matches(struct hostapd_data *hapd, + struct wpabuf *buf, + const u8 *home_realm, + size_t home_realm_len) +{ + unsigned int i, j, k; + u8 num_realms, num_matching = 0, encoding, realm_len, *realm_list_len; + struct hostapd_nai_realm_data *realm; + const u8 *pos, *realm_name, *end; + struct { + unsigned int realm_data_idx; + unsigned int realm_idx; + } matches[10]; + + pos = home_realm; + end = pos + home_realm_len; + if (pos + 1 > end) { + wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + num_realms = *pos++; + + for (i = 0; i < num_realms && num_matching < 10; i++) { + if (pos + 2 > end) { + wpa_hexdump(MSG_DEBUG, + "Truncated NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + encoding = *pos++; + realm_len = *pos++; + if (pos + realm_len > end) { + wpa_hexdump(MSG_DEBUG, + "Truncated NAI Home Realm Query", + home_realm, home_realm_len); + return -1; + } + realm_name = pos; + for (j = 0; j < hapd->conf->nai_realm_count && + num_matching < 10; j++) { + const u8 *rpos, *rend; + realm = &hapd->conf->nai_realm_data[j]; + if (encoding != realm->encoding) + continue; + + rpos = realm_name; + while (rpos < realm_name + realm_len && + num_matching < 10) { + for (rend = rpos; + rend < realm_name + realm_len; rend++) { + if (*rend == ';') + break; + } + for (k = 0; k < MAX_NAI_REALMS && + realm->realm[k] && + num_matching < 10; k++) { + if ((int) os_strlen(realm->realm[k]) != + rend - rpos || + os_strncmp((char *) rpos, + realm->realm[k], + rend - rpos) != 0) + continue; + matches[num_matching].realm_data_idx = + j; + matches[num_matching].realm_idx = k; + num_matching++; + } + rpos = rend + 1; + } + } + pos += realm_len; + } + + realm_list_len = gas_anqp_add_element(buf, ANQP_NAI_REALM); + wpabuf_put_le16(buf, num_matching); + + /* + * There are two ways to format. 1. each realm in a NAI Realm Data unit + * 2. all realms that share the same EAP methods in a NAI Realm Data + * unit. The first format is likely to be bigger in size than the + * second, but may be easier to parse and process by the receiver. + */ + for (i = 0; i < num_matching; i++) { + wpa_printf(MSG_DEBUG, "realm_idx %d, realm_data_idx %d", + matches[i].realm_data_idx, matches[i].realm_idx); + realm = &hapd->conf->nai_realm_data[matches[i].realm_data_idx]; + anqp_add_nai_realm_data(buf, realm, matches[i].realm_idx); + } + gas_anqp_set_element_len(buf, realm_list_len); + return 0; +} + + +static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf, + const u8 *home_realm, size_t home_realm_len, + int nai_realm, int nai_home_realm) +{ + if (nai_realm && hapd->conf->nai_realm_data) { + u8 *len; + unsigned int i, j; + len = gas_anqp_add_element(buf, ANQP_NAI_REALM); + wpabuf_put_le16(buf, hapd->conf->nai_realm_count); + for (i = 0; i < hapd->conf->nai_realm_count; i++) { + u8 *realm_data_len, *realm_len; + struct hostapd_nai_realm_data *realm; + + realm = &hapd->conf->nai_realm_data[i]; + realm_data_len = wpabuf_put(buf, 2); + wpabuf_put_u8(buf, realm->encoding); + realm_len = wpabuf_put(buf, 1); + for (j = 0; realm->realm[j]; j++) { + if (j > 0) + wpabuf_put_u8(buf, ';'); + wpabuf_put_str(buf, realm->realm[j]); + } + *realm_len = (u8 *) wpabuf_put(buf, 0) - realm_len - 1; + anqp_add_nai_realm_eap(buf, realm); + gas_anqp_set_element_len(buf, realm_data_len); + } + gas_anqp_set_element_len(buf, len); + } else if (nai_home_realm && hapd->conf->nai_realm_data) { + hs20_add_nai_home_realm_matches(hapd, buf, home_realm, + home_realm_len); + } +} + + +static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->anqp_3gpp_cell_net) { + wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK); + wpabuf_put_le16(buf, + hapd->conf->anqp_3gpp_cell_net_len); + wpabuf_put_data(buf, hapd->conf->anqp_3gpp_cell_net, + hapd->conf->anqp_3gpp_cell_net_len); + } +} + + +static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) +{ + if (hapd->conf->domain_name) { + wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); + wpabuf_put_le16(buf, hapd->conf->domain_name_len); + wpabuf_put_data(buf, hapd->conf->domain_name, + hapd->conf->domain_name_len); + } +} + + +#ifdef CONFIG_HS20 + +static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_oper_friendly_name) { + u8 *len; + unsigned int i; + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_FRIENDLY_NAME); + wpabuf_put_u8(buf, 0); /* Reserved */ + for (i = 0; i < hapd->conf->hs20_oper_friendly_name_count; i++) + { + struct hostapd_lang_string *vn; + vn = &hapd->conf->hs20_oper_friendly_name[i]; + wpabuf_put_u8(buf, 3 + vn->name_len); + wpabuf_put_data(buf, vn->lang, 3); + wpabuf_put_data(buf, vn->name, vn->name_len); + } + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_wan_metrics(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_wan_metrics) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_WAN_METRICS); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_wan_metrics, 13); + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_connection_capability(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_connection_capability) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_CONNECTION_CAPABILITY); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_connection_capability, + hapd->conf->hs20_connection_capability_len); + gas_anqp_set_element_len(buf, len); + } +} + + +static void anqp_add_operating_class(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_operating_class) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_data(buf, hapd->conf->hs20_operating_class, + hapd->conf->hs20_operating_class_len); + gas_anqp_set_element_len(buf, len); + } +} + +#endif /* CONFIG_HS20 */ + + +static struct wpabuf * +gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, + unsigned int request, + struct gas_dialog_info *di, + const u8 *home_realm, size_t home_realm_len) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(1400); + if (buf == NULL) + return NULL; + + if (request & ANQP_REQ_CAPABILITY_LIST) + anqp_add_capab_list(hapd, buf); + if (request & ANQP_REQ_VENUE_NAME) + anqp_add_venue_name(hapd, buf); + if (request & ANQP_REQ_NETWORK_AUTH_TYPE) + anqp_add_network_auth_type(hapd, buf); + if (request & ANQP_REQ_ROAMING_CONSORTIUM) + anqp_add_roaming_consortium(hapd, buf); + if (request & ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY) + anqp_add_ip_addr_type_availability(hapd, buf); + if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM)) + anqp_add_nai_realm(hapd, buf, home_realm, home_realm_len, + request & ANQP_REQ_NAI_REALM, + request & ANQP_REQ_NAI_HOME_REALM); + if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK) + anqp_add_3gpp_cellular_network(hapd, buf); + if (request & ANQP_REQ_DOMAIN_NAME) + anqp_add_domain_name(hapd, buf); + +#ifdef CONFIG_HS20 + if (request & ANQP_REQ_HS_CAPABILITY_LIST) + anqp_add_hs_capab_list(hapd, buf); + if (request & ANQP_REQ_OPERATOR_FRIENDLY_NAME) + anqp_add_operator_friendly_name(hapd, buf); + if (request & ANQP_REQ_WAN_METRICS) + anqp_add_wan_metrics(hapd, buf); + if (request & ANQP_REQ_CONNECTION_CAPABILITY) + anqp_add_connection_capability(hapd, buf); + if (request & ANQP_REQ_OPERATING_CLASS) + anqp_add_operating_class(hapd, buf); +#endif /* CONFIG_HS20 */ + + return buf; +} + + +static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx) +{ + struct gas_dialog_info *dia = eloop_data; + + wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for " + "dialog token %d", dia->dialog_token); + + gas_serv_dialog_clear(dia); +} + + +struct anqp_query_info { + unsigned int request; + unsigned int remote_request; + const u8 *home_realm_query; + size_t home_realm_query_len; + u16 remote_delay; +}; + + +static void set_anqp_req(unsigned int bit, const char *name, int local, + unsigned int remote, u16 remote_delay, + struct anqp_query_info *qi) +{ + qi->request |= bit; + if (local) { + wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name); + } else if (bit & remote) { + wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name); + qi->remote_request |= bit; + if (remote_delay > qi->remote_delay) + qi->remote_delay = remote_delay; + } else { + wpa_printf(MSG_DEBUG, "ANQP: %s not available", name); + } +} + + +static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, + struct anqp_query_info *qi) +{ + switch (info_id) { + case ANQP_CAPABILITY_LIST: + set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0, + 0, qi); + break; + case ANQP_VENUE_NAME: + set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name", + hapd->conf->venue_name != NULL, 0, 0, qi); + break; + case ANQP_NETWORK_AUTH_TYPE: + set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type", + hapd->conf->network_auth_type != NULL, + 0, 0, qi); + break; + case ANQP_ROAMING_CONSORTIUM: + set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium", + hapd->conf->roaming_consortium != NULL, 0, 0, qi); + break; + case ANQP_IP_ADDR_TYPE_AVAILABILITY: + set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY, + "IP Addr Type Availability", + hapd->conf->ipaddr_type_configured, + 0, 0, qi); + break; + case ANQP_NAI_REALM: + set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm", + hapd->conf->nai_realm_data != NULL, + 0, 0, qi); + break; + case ANQP_3GPP_CELLULAR_NETWORK: + set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK, + "3GPP Cellular Network", + hapd->conf->anqp_3gpp_cell_net != NULL, + 0, 0, qi); + break; + case ANQP_DOMAIN_NAME: + set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name", + hapd->conf->domain_name != NULL, + 0, 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", + info_id); + break; + } +} + + +static void rx_anqp_query_list(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list", + (unsigned int) (end - pos) / 2); + + while (pos + 2 <= end) { + rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi); + pos += 2; + } +} + + +#ifdef CONFIG_HS20 + +static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, + struct anqp_query_info *qi) +{ + switch (subtype) { + case HS20_STYPE_CAPABILITY_LIST: + set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List", + 1, 0, 0, qi); + break; + case HS20_STYPE_OPERATOR_FRIENDLY_NAME: + set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME, + "Operator Friendly Name", + hapd->conf->hs20_oper_friendly_name != NULL, + 0, 0, qi); + break; + case HS20_STYPE_WAN_METRICS: + set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics", + hapd->conf->hs20_wan_metrics != NULL, + 0, 0, qi); + break; + case HS20_STYPE_CONNECTION_CAPABILITY: + set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY, + "Connection Capability", + hapd->conf->hs20_connection_capability != NULL, + 0, 0, qi); + break; + case HS20_STYPE_OPERATING_CLASS: + set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class", + hapd->conf->hs20_operating_class != NULL, + 0, 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", + subtype); + break; + } +} + + +static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + qi->request |= ANQP_REQ_NAI_HOME_REALM; + qi->home_realm_query = pos; + qi->home_realm_query_len = end - pos; + if (hapd->conf->nai_realm_data != NULL) { + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query " + "(local)"); + } else { + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 NAI Home Realm Query not " + "available"); + } +} + + +static void rx_anqp_vendor_specific(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u32 oui; + u8 subtype; + + if (pos + 4 > end) { + wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " + "Query element"); + return; + } + + oui = WPA_GET_BE24(pos); + pos += 3; + if (oui != OUI_WFA) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", + oui); + return; + } + + if (*pos != HS20_ANQP_OUI_TYPE) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", + *pos); + return; + } + pos++; + + if (pos + 1 >= end) + return; + + subtype = *pos++; + pos++; /* Reserved */ + switch (subtype) { + case HS20_STYPE_QUERY_LIST: + wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Query List"); + while (pos < end) { + rx_anqp_hs_query_list(hapd, *pos, qi); + pos++; + } + break; + case HS20_STYPE_NAI_HOME_REALM_QUERY: + rx_anqp_hs_nai_home_realm(hapd, pos, end, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype " + "%u", subtype); + break; + } +} + +#endif /* CONFIG_HS20 */ + + +static void gas_serv_req_local_processing(struct hostapd_data *hapd, + const u8 *sa, u8 dialog_token, + struct anqp_query_info *qi, int prot) +{ + struct wpabuf *buf, *tx_buf; + + buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL, + qi->home_realm_query, + qi->home_realm_query_len); + wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses", + buf); + if (!buf) + return; + + if (wpabuf_len(buf) > hapd->gas_frag_limit || + hapd->conf->gas_comeback_delay) { + struct gas_dialog_info *di; + u16 comeback_delay = 1; + + if (hapd->conf->gas_comeback_delay) { + /* Testing - allow overriding of the delay value */ + comeback_delay = hapd->conf->gas_comeback_delay; + } + + wpa_printf(MSG_DEBUG, "ANQP: Too long response to fit in " + "initial response - use GAS comeback"); + di = gas_dialog_create(hapd, sa, dialog_token); + if (!di) { + wpa_printf(MSG_INFO, "ANQP: Could not create dialog " + "for " MACSTR " (dialog token %u)", + MAC2STR(sa), dialog_token); + wpabuf_free(buf); + return; + } + di->prot = prot; + di->sd_resp = buf; + di->sd_resp_pos = 0; + tx_buf = gas_anqp_build_initial_resp_buf( + dialog_token, WLAN_STATUS_SUCCESS, comeback_delay, + NULL); + } else { + wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)"); + tx_buf = gas_anqp_build_initial_resp_buf( + dialog_token, WLAN_STATUS_SUCCESS, 0, buf); + wpabuf_free(buf); + } + if (!tx_buf) + return; + if (prot) + convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); +} + + +static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, + const u8 *sa, + const u8 *data, size_t len, int prot) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 slen; + struct anqp_query_info qi; + const u8 *adv_proto; + + if (len < 1 + 2) + return; + + os_memset(&qi, 0, sizeof(qi)); + + dialog_token = *pos++; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: GAS Initial Request from " MACSTR " (dialog token %u) ", + MAC2STR(sa), dialog_token); + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Unexpected IE in GAS Initial Request: %u", *pos); + return; + } + adv_proto = pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Invalid IE in GAS Initial Request"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + struct wpabuf *buf; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Unsupported GAS advertisement protocol id %u", + *pos); + if (sa[0] & 0x01) + return; /* Invalid source address - drop silently */ + buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED, + 0, 2 + slen + 2); + if (buf == NULL) + return; + wpabuf_put_data(buf, adv_proto, 2 + slen); + wpabuf_put_le16(buf, 0); /* Query Response Length */ + if (prot) + convert_to_protected_dual(buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + return; + } + + pos = next; + /* Query Request */ + if (pos + 2 > end) + return; + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end) + return; + end = pos + slen; + + /* ANQP Query Request */ + while (pos < end) { + u16 info_id, elen; + + if (pos + 4 > end) + return; + + info_id = WPA_GET_LE16(pos); + pos += 2; + elen = WPA_GET_LE16(pos); + pos += 2; + + if (pos + elen > end) { + wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request"); + return; + } + + switch (info_id) { + case ANQP_QUERY_LIST: + rx_anqp_query_list(hapd, pos, pos + elen, &qi); + break; +#ifdef CONFIG_HS20 + case ANQP_VENDOR_SPECIFIC: + rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); + break; +#endif /* CONFIG_HS20 */ + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " + "Request element %u", info_id); + break; + } + + pos += elen; + } + + gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot); +} + + +void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, + struct gas_dialog_info *dialog) +{ + struct wpabuf *buf, *tx_buf; + u8 dialog_token = dialog->dialog_token; + size_t frag_len; + + if (dialog->sd_resp == NULL) { + buf = gas_serv_build_gas_resp_payload(hapd, + dialog->all_requested, + dialog, NULL, 0); + wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", + buf); + if (!buf) + goto tx_gas_response_done; + dialog->sd_resp = buf; + dialog->sd_resp_pos = 0; + } + frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; + if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay || + hapd->conf->gas_comeback_delay) { + u16 comeback_delay_tus = dialog->comeback_delay + + GAS_SERV_COMEBACK_DELAY_FUDGE; + u32 comeback_delay_secs, comeback_delay_usecs; + + if (hapd->conf->gas_comeback_delay) { + /* Testing - allow overriding of the delay value */ + comeback_delay_tus = hapd->conf->gas_comeback_delay; + } + + wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit " + "%u) and comeback delay %u, " + "requesting comebacks", (unsigned int) frag_len, + (unsigned int) hapd->gas_frag_limit, + dialog->comeback_delay); + tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, + WLAN_STATUS_SUCCESS, + comeback_delay_tus, + NULL); + if (tx_buf) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "GAS: Tx GAS Initial Resp (comeback = 10TU)"); + if (dialog->prot) + convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, + dst, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + } + wpabuf_free(tx_buf); + + /* start a timer of 1.5 * comeback-delay */ + comeback_delay_tus = comeback_delay_tus + + (comeback_delay_tus / 2); + comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000; + comeback_delay_usecs = (comeback_delay_tus * 1024) - + (comeback_delay_secs * 1000000); + eloop_register_timeout(comeback_delay_secs, + comeback_delay_usecs, + gas_serv_clear_cached_ies, dialog, + NULL); + goto tx_gas_response_done; + } + + buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + + dialog->sd_resp_pos, frag_len); + if (buf == NULL) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation " + "failed"); + goto tx_gas_response_done; + } + tx_buf = gas_anqp_build_initial_resp_buf(dialog_token, + WLAN_STATUS_SUCCESS, 0, buf); + wpabuf_free(buf); + if (tx_buf == NULL) + goto tx_gas_response_done; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial " + "Response (frag_id %d frag_len %d)", + dialog->sd_frag_id, (int) frag_len); + dialog->sd_frag_id++; + + if (dialog->prot) + convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst, + wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); +tx_gas_response_done: + gas_serv_clear_cached_ies(dialog, NULL); +} + + +static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, + const u8 *sa, + const u8 *data, size_t len, int prot) +{ + struct gas_dialog_info *dialog; + struct wpabuf *buf, *tx_buf; + u8 dialog_token; + size_t frag_len; + int more = 0; + + wpa_hexdump(MSG_DEBUG, "GAS: RX GAS Comeback Request", data, len); + if (len < 1) + return; + dialog_token = *data; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Dialog Token: %u", + dialog_token); + + dialog = gas_serv_dialog_find(hapd, sa, dialog_token); + if (!dialog) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: No pending SD " + "response fragment for " MACSTR " dialog token %u", + MAC2STR(sa), dialog_token); + + if (sa[0] & 0x01) + return; /* Invalid source address - drop silently */ + tx_buf = gas_anqp_build_comeback_resp_buf( + dialog_token, WLAN_STATUS_NO_OUTSTANDING_GAS_REQ, 0, 0, + 0, NULL); + if (tx_buf == NULL) + return; + goto send_resp; + } + + if (dialog->sd_resp == NULL) { + wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x", + dialog->requested, dialog->received); + if ((dialog->requested & dialog->received) != + dialog->requested) { + wpa_printf(MSG_DEBUG, "GAS: Did not receive response " + "from remote processing"); + gas_serv_dialog_clear(dialog); + tx_buf = gas_anqp_build_comeback_resp_buf( + dialog_token, + WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0, + NULL); + if (tx_buf == NULL) + return; + goto send_resp; + } + + buf = gas_serv_build_gas_resp_payload(hapd, + dialog->all_requested, + dialog, NULL, 0); + wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses", + buf); + if (!buf) + goto rx_gas_comeback_req_done; + dialog->sd_resp = buf; + dialog->sd_resp_pos = 0; + } + frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; + if (frag_len > hapd->gas_frag_limit) { + frag_len = hapd->gas_frag_limit; + more = 1; + } + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", + (unsigned int) frag_len); + buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) + + dialog->sd_resp_pos, frag_len); + if (buf == NULL) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate " + "buffer"); + goto rx_gas_comeback_req_done; + } + tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, + WLAN_STATUS_SUCCESS, + dialog->sd_frag_id, + more, 0, buf); + wpabuf_free(buf); + if (tx_buf == NULL) + goto rx_gas_comeback_req_done; + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response " + "(frag_id %d more=%d frag_len=%d)", + dialog->sd_frag_id, more, (int) frag_len); + dialog->sd_frag_id++; + dialog->sd_resp_pos += frag_len; + + if (more) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: %d more bytes remain " + "to be sent", + (int) (wpabuf_len(dialog->sd_resp) - + dialog->sd_resp_pos)); + } else { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " + "SD response sent"); + gas_serv_dialog_clear(dialog); + gas_serv_free_dialogs(hapd, sa); + } + +send_resp: + if (prot) + convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); + return; + +rx_gas_comeback_req_done: + gas_serv_clear_cached_ies(dialog, NULL); +} + + +static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len, + int freq) +{ + struct hostapd_data *hapd = ctx; + const struct ieee80211_mgmt *mgmt; + size_t hdr_len; + const u8 *sa, *data; + int prot; + + mgmt = (const struct ieee80211_mgmt *) buf; + hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf; + if (hdr_len > len) + return; + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && + mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL) + return; + /* + * Note: Public Action and Protected Dual of Public Action frames share + * the same payload structure, so it is fine to use definitions of + * Public Action frames to process both. + */ + prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL; + sa = mgmt->sa; + len -= hdr_len; + data = &mgmt->u.action.u.public_action.action; + switch (data[0]) { + case WLAN_PA_GAS_INITIAL_REQ: + gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot); + break; + case WLAN_PA_GAS_COMEBACK_REQ: + gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot); + break; + } +} + + +int gas_serv_init(struct hostapd_data *hapd) +{ + hapd->public_action_cb2 = gas_serv_rx_public_action; + hapd->public_action_cb2_ctx = hapd; + hapd->gas_frag_limit = 1400; + if (hapd->conf->gas_frag_limit > 0) + hapd->gas_frag_limit = hapd->conf->gas_frag_limit; + return 0; +} + + +void gas_serv_deinit(struct hostapd_data *hapd) +{ +} diff --git a/contrib/hostapd/src/ap/gas_serv.h b/contrib/hostapd/src/ap/gas_serv.h new file mode 100644 index 0000000000..74739fef15 --- /dev/null +++ b/contrib/hostapd/src/ap/gas_serv.h @@ -0,0 +1,72 @@ +/* + * Generic advertisement service (GAS) server + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_SERV_H +#define GAS_SERV_H + +#define ANQP_REQ_CAPABILITY_LIST \ + (1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST)) +#define ANQP_REQ_VENUE_NAME \ + (1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_NETWORK_AUTH_TYPE \ + (1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST)) +#define ANQP_REQ_ROAMING_CONSORTIUM \ + (1 << (ANQP_ROAMING_CONSORTIUM - ANQP_QUERY_LIST)) +#define ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY \ + (1 << (ANQP_IP_ADDR_TYPE_AVAILABILITY - ANQP_QUERY_LIST)) +#define ANQP_REQ_NAI_REALM \ + (1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST)) +#define ANQP_REQ_3GPP_CELLULAR_NETWORK \ + (1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST)) +#define ANQP_REQ_DOMAIN_NAME \ + (1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST)) +#define ANQP_REQ_HS_CAPABILITY_LIST \ + (0x10000 << HS20_STYPE_CAPABILITY_LIST) +#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \ + (0x10000 << HS20_STYPE_OPERATOR_FRIENDLY_NAME) +#define ANQP_REQ_WAN_METRICS \ + (0x10000 << HS20_STYPE_WAN_METRICS) +#define ANQP_REQ_CONNECTION_CAPABILITY \ + (0x10000 << HS20_STYPE_CONNECTION_CAPABILITY) +#define ANQP_REQ_NAI_HOME_REALM \ + (0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY) +#define ANQP_REQ_OPERATING_CLASS \ + (0x10000 << HS20_STYPE_OPERATING_CLASS) + +/* To account for latencies between hostapd and external ANQP processor */ +#define GAS_SERV_COMEBACK_DELAY_FUDGE 10 +#define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */ + +struct gas_dialog_info { + u8 valid; + u8 index; + struct wpabuf *sd_resp; /* Fragmented response */ + u8 dialog_token; + size_t sd_resp_pos; /* Offset in sd_resp */ + u8 sd_frag_id; + u16 comeback_delay; + int prot; /* whether Protected Dual of Public Action frame is used */ + + unsigned int requested; + unsigned int received; + unsigned int all_requested; +}; + +struct hostapd_data; + +void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst, + struct gas_dialog_info *dialog); +struct gas_dialog_info * +gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr, + u8 dialog_token); +void gas_serv_dialog_clear(struct gas_dialog_info *dialog); + +int gas_serv_init(struct hostapd_data *hapd); +void gas_serv_deinit(struct hostapd_data *hapd); + +#endif /* GAS_SERV_H */ diff --git a/contrib/hostapd/src/ap/hostapd.c b/contrib/hostapd/src/ap/hostapd.c new file mode 100644 index 0000000000..f9edf3b3f8 --- /dev/null +++ b/contrib/hostapd/src/ap/hostapd.c @@ -0,0 +1,2255 @@ +/* + * hostapd / Initialization and configuration + * Copyright (c) 2002-2014, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 "radius/radius_client.h" +#include "radius/radius_das.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" +#include "p2p_hostapd.h" +#include "gas_serv.h" +#include "dfs.h" + + +static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); +static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); +static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); +static int setup_interface2(struct hostapd_iface *iface); +static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx); + + +int hostapd_for_each_interface(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx) +{ + size_t i; + int ret; + + for (i = 0; i < interfaces->count; i++) { + ret = cb(interfaces->iface[i], ctx); + if (ret) + return ret; + } + + return 0; +} + + +static void hostapd_reload_bss(struct hostapd_data *hapd) +{ + struct hostapd_ssid *ssid; + +#ifndef CONFIG_NO_RADIUS + radius_client_reconfig(hapd->radius, hapd->conf->radius); +#endif /* CONFIG_NO_RADIUS */ + + ssid = &hapd->conf->ssid; + if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next && + ssid->wpa_passphrase_set && ssid->wpa_passphrase) { + /* + * Force PSK to be derived again since SSID or passphrase may + * have changed. + */ + os_free(ssid->wpa_psk); + ssid->wpa_psk = NULL; + } + 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) + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1); + else + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); + + if (hapd->conf->wpa && hapd->wpa_auth == NULL) { + hostapd_setup_wpa(hapd); + if (hapd->wpa_auth) + wpa_init_keys(hapd->wpa_auth); + } 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, hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)) { + wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); + /* try to continue */ + } + wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); +} + + +static void hostapd_clear_old(struct hostapd_iface *iface) +{ + size_t j; + + /* + * 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], + WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_broadcast_wep_clear(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(iface->bss[j]->radius, 0); +#endif /* CONFIG_NO_RADIUS */ + } +} + + +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_fname == NULL) { + /* Only in-memory config in use - assume it has been updated */ + hostapd_clear_old(iface); + for (j = 0; j < iface->num_bss; j++) + hostapd_reload_bss(iface->bss[j]); + return 0; + } + + if (iface->interfaces == NULL || + iface->interfaces->config_read_cb == NULL) + return -1; + newconf = iface->interfaces->config_read_cb(iface->config_fname); + if (newconf == NULL) + return -1; + + hostapd_clear_old(iface); + + oldconf = hapd->iconf; + iface->conf = newconf; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + hapd->iconf = newconf; + hapd->conf = newconf->bss[j]; + hostapd_reload_bss(hapd); + } + + hostapd_config_free(oldconf); + + + 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 (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, + 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 (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, + NULL, i, 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 && + hostapd_drv_set_key(hapd->conf->iface, + hapd, WPA_ALG_WEP, broadcast_ether_addr, idx, + 1, NULL, 0, ssid->wep.key[idx], + ssid->wep.len[idx])) { + wpa_printf(MSG_WARNING, "Could not set WEP encryption."); + errors++; + } + + return errors; +} + + +static void hostapd_free_hapd_data(struct hostapd_data *hapd) +{ + if (!hapd->started) { + wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started", + __func__, hapd->conf->iface); + return; + } + hapd->started = 0; + + wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface); + 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; + radius_das_deinit(hapd->radius_das); + hapd->radius_das = 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; + +#ifdef CONFIG_P2P + wpabuf_free(hapd->p2p_beacon_ie); + hapd->p2p_beacon_ie = NULL; + wpabuf_free(hapd->p2p_probe_resp_ie); + hapd->p2p_probe_resp_ie = NULL; +#endif /* CONFIG_P2P */ + + wpabuf_free(hapd->time_adv); + +#ifdef CONFIG_INTERWORKING + gas_serv_deinit(hapd); +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_SQLITE + os_free(hapd->tmp_eap_user.identity); + os_free(hapd->tmp_eap_user.password); +#endif /* CONFIG_SQLITE */ +} + + +/** + * hostapd_cleanup - Per-BSS cleanup (deinitialization) + * @hapd: Pointer to BSS data + * + * This function is used to free all per-BSS data structures and resources. + * Most of the modules that are initialized in hostapd_setup_bss() are + * deinitialized here. + */ +static void hostapd_cleanup(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd, + hapd->conf->iface); + if (hapd->iface->interfaces && + hapd->iface->interfaces->ctrl_iface_deinit) + hapd->iface->interfaces->ctrl_iface_deinit(hapd); + hostapd_free_hapd_data(hapd); +} + + +static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) +{ + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, 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; + os_free(iface->basic_rates); + iface->basic_rates = NULL; + ap_list_deinit(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) +{ + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + + hostapd_cleanup_iface_partial(iface); + hostapd_config_free(iface->conf); + iface->conf = NULL; + + os_free(iface->config_fname); + os_free(iface->bss); + wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface); + os_free(iface); +} + + +static void hostapd_clear_wep(struct hostapd_data *hapd) +{ + if (hapd->drv_priv) { + hostapd_set_privacy(hapd, 0); + hostapd_broadcast_wep_clear(hapd); + } +} + + +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; + } + + /* + * When IEEE 802.1X is not enabled, the driver may need to know how to + * set authentication algorithms for static WEP. + */ + hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs); + + for (i = 0; i < 4; i++) { + if (hapd->conf->ssid.wep.key[i] && + hostapd_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, u16 reason) +{ + int ret = 0; + u8 addr[ETH_ALEN]; + + if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL) + return 0; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries"); + if (hostapd_flush(hapd)) { + wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to " + "kernel driver"); + ret = -1; + } + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations"); + os_memset(addr, 0xff, ETH_ALEN); + hostapd_drv_sta_deauth(hapd, addr, reason); + hostapd_free_stas(hapd); + + 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 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); + + 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; +} + + +#ifndef CONFIG_NO_RADIUS + +static int hostapd_das_nas_mismatch(struct hostapd_data *hapd, + struct radius_das_attrs *attr) +{ + /* TODO */ + return 0; +} + + +static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd, + struct radius_das_attrs *attr) +{ + struct sta_info *sta = NULL; + char buf[128]; + + if (attr->sta_addr) + sta = ap_get_sta(hapd, attr->sta_addr); + + if (sta == NULL && attr->acct_session_id && + attr->acct_session_id_len == 17) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + os_snprintf(buf, sizeof(buf), "%08X-%08X", + sta->acct_session_id_hi, + sta->acct_session_id_lo); + if (os_memcmp(attr->acct_session_id, buf, 17) == 0) + break; + } + } + + if (sta == NULL && attr->cui) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + struct wpabuf *cui; + cui = ieee802_1x_get_radius_cui(sta->eapol_sm); + if (cui && wpabuf_len(cui) == attr->cui_len && + os_memcmp(wpabuf_head(cui), attr->cui, + attr->cui_len) == 0) + break; + } + } + + if (sta == NULL && attr->user_name) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + u8 *identity; + size_t identity_len; + identity = ieee802_1x_get_identity(sta->eapol_sm, + &identity_len); + if (identity && + identity_len == attr->user_name_len && + os_memcmp(identity, attr->user_name, identity_len) + == 0) + break; + } + } + + return sta; +} + + +static enum radius_das_res +hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + if (hostapd_das_nas_mismatch(hapd, attr)) + return RADIUS_DAS_NAS_MISMATCH; + + sta = hostapd_das_find_sta(hapd, attr); + if (sta == NULL) + return RADIUS_DAS_SESSION_NOT_FOUND; + + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); + + return RADIUS_DAS_SUCCESS; +} + +#endif /* CONFIG_NO_RADIUS */ + + +/** + * hostapd_setup_bss - Per-BSS setup (initialization) + * @hapd: Pointer to BSS data + * @first: Whether this BSS is the first BSS of an interface; -1 = not first, + * but interface may exist + * + * 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]; + + wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)", + __func__, hapd, hapd->conf->iface, first); + + if (hapd->started) { + wpa_printf(MSG_ERROR, "%s: Interface %s was already started", + __func__, hapd->conf->iface); + return -1; + } + hapd->started = 1; + + if (!first || first == -1) { + 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, + hapd->conf->bridge[0] ? hapd->conf->bridge : + NULL, first == -1)) { + wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" + MACSTR ")", MAC2STR(hapd->own_addr)); + hapd->interface_added = 0; + return -1; + } + } + + if (conf->wmm_enabled < 0) + conf->wmm_enabled = hapd->iconf->ieee80211n; + + hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID); + 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); + } + + 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), + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); + } + + 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, 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; + } + + if (hapd->conf->radius_das_port) { + struct radius_das_conf das_conf; + os_memset(&das_conf, 0, sizeof(das_conf)); + das_conf.port = hapd->conf->radius_das_port; + das_conf.shared_secret = hapd->conf->radius_das_shared_secret; + das_conf.shared_secret_len = + hapd->conf->radius_das_shared_secret_len; + das_conf.client_addr = &hapd->conf->radius_das_client_addr; + das_conf.time_window = hapd->conf->radius_das_time_window; + das_conf.require_event_timestamp = + hapd->conf->radius_das_require_event_timestamp; + das_conf.ctx = hapd; + das_conf.disconnect = hostapd_das_disconnect; + hapd->radius_das = radius_das_init(&das_conf); + if (hapd->radius_das == NULL) { + wpa_printf(MSG_ERROR, "RADIUS DAS 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; + } + +#ifdef CONFIG_INTERWORKING + if (gas_serv_init(hapd)) { + wpa_printf(MSG_ERROR, "GAS server initialization failed"); + return -1; + } + + if (conf->qos_map_set_len && + hostapd_drv_set_qos_map(hapd, conf->qos_map_set, + conf->qos_map_set_len)) { + wpa_printf(MSG_ERROR, "Failed to initialize QoS Map"); + return -1; + } +#endif /* CONFIG_INTERWORKING */ + + if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { + wpa_printf(MSG_ERROR, "VLAN initialization failed."); + return -1; + } + + if (!hapd->conf->start_disabled && ieee802_11_set_beacon(hapd) < 0) + return -1; + + if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0) + return -1; + + if (hapd->driver && hapd->driver->set_operstate) + hapd->driver->set_operstate(hapd->drv_priv, 1); + + return 0; +} + + +static void hostapd_tx_queue_params(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + int i; + struct hostapd_tx_queue_params *p; + + for (i = 0; i < NUM_TX_QUEUES; i++) { + p = &iface->conf->tx_queue[i]; + + if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin, + p->cwmax, p->burst)) { + wpa_printf(MSG_DEBUG, "Failed to set TX queue " + "parameters for queue %d.", i); + /* Continue anyway */ + } + } +} + + +static int hostapd_set_acl_list(struct hostapd_data *hapd, + struct mac_acl_entry *mac_acl, + int n_entries, u8 accept_acl) +{ + struct hostapd_acl_params *acl_params; + int i, err; + + acl_params = os_zalloc(sizeof(*acl_params) + + (n_entries * sizeof(acl_params->mac_acl[0]))); + if (!acl_params) + return -ENOMEM; + + for (i = 0; i < n_entries; i++) + os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr, + ETH_ALEN); + + acl_params->acl_policy = accept_acl; + acl_params->num_mac_acl = n_entries; + + err = hostapd_drv_set_acl(hapd, acl_params); + + os_free(acl_params); + + return err; +} + + +static void hostapd_set_acl(struct hostapd_data *hapd) +{ + struct hostapd_config *conf = hapd->iconf; + int err; + u8 accept_acl; + + if (hapd->iface->drv_max_acl_mac_addrs == 0) + return; + if (!(conf->bss[0]->num_accept_mac || conf->bss[0]->num_deny_mac)) + return; + + if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) { + if (conf->bss[0]->num_accept_mac) { + accept_acl = 1; + err = hostapd_set_acl_list(hapd, + conf->bss[0]->accept_mac, + conf->bss[0]->num_accept_mac, + accept_acl); + if (err) { + wpa_printf(MSG_DEBUG, "Failed to set accept acl"); + return; + } + } else { + wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file"); + } + } else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) { + if (conf->bss[0]->num_deny_mac) { + accept_acl = 0; + err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac, + conf->bss[0]->num_deny_mac, + accept_acl); + if (err) { + wpa_printf(MSG_DEBUG, "Failed to set deny acl"); + return; + } + } else { + wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file"); + } + } +} + + +static int start_ctrl_iface_bss(struct hostapd_data *hapd) +{ + if (!hapd->iface->interfaces || + !hapd->iface->interfaces->ctrl_iface_init) + return 0; + + if (hapd->iface->interfaces->ctrl_iface_init(hapd)) { + wpa_printf(MSG_ERROR, + "Failed to setup control interface for %s", + hapd->conf->iface); + return -1; + } + + return 0; +} + + +static int start_ctrl_iface(struct hostapd_iface *iface) +{ + size_t i; + + if (!iface->interfaces || !iface->interfaces->ctrl_iface_init) + return 0; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *hapd = iface->bss[i]; + if (iface->interfaces->ctrl_iface_init(hapd)) { + wpa_printf(MSG_ERROR, + "Failed to setup control interface for %s", + hapd->conf->iface); + return -1; + } + } + + return 0; +} + + +static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + + if (!iface->wait_channel_update) { + wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it"); + return; + } + + /* + * It is possible that the existing channel list is acceptable, so try + * to proceed. + */ + wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway"); + setup_interface2(iface); +} + + +void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator) +{ + if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER) + return; + + wpa_printf(MSG_DEBUG, "Channel list updated - continue setup"); + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + setup_interface2(iface); +} + + +static int setup_interface(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + size_t i; + + if (!iface->phy[0]) { + const char *phy = hostapd_drv_get_radio_name(hapd); + if (phy) { + wpa_printf(MSG_DEBUG, "phy: %s", phy); + os_strlcpy(iface->phy, phy, sizeof(iface->phy)); + } + } + + /* + * 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; + + /* + * Initialize control interfaces early to allow external monitoring of + * channel setup operations that may take considerable amount of time + * especially for DFS cases. + */ + if (start_ctrl_iface(iface)) + return -1; + + if (hapd->iconf->country[0] && hapd->iconf->country[1]) { + char country[4], previous_country[4]; + + hostapd_set_state(iface, HAPD_IFACE_COUNTRY_UPDATE); + if (hostapd_get_country(hapd, previous_country) < 0) + previous_country[0] = '\0'; + + 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; + } + + wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s", + previous_country, country); + + if (os_strncmp(previous_country, country, 2) != 0) { + wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update"); + iface->wait_channel_update = 1; + eloop_register_timeout(5, 0, + channel_list_update_timeout, + iface, NULL); + return 0; + } + } + + return setup_interface2(iface); +} + + +static int setup_interface2(struct hostapd_iface *iface) +{ + iface->wait_channel_update = 0; + + 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; + } + if (ret == 1) { + wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)"); + return 0; + } + 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; + } + + if (iface->conf->ieee80211h) + wpa_printf(MSG_DEBUG, "DFS support is enabled"); + } + return hostapd_setup_interface_complete(iface, 0); +} + + +/** + * hostapd_setup_interface_complete - Complete interface setup + * + * This function is called when previous steps in the interface setup has been + * completed. This can also start operations, e.g., DFS, that will require + * additional processing before interface is ready to be enabled. Such + * operations will call this function from eloop callbacks when finished. + */ +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"); + hostapd_set_state(iface, HAPD_IFACE_DISABLED); + if (iface->interfaces && iface->interfaces->terminate_on_error) + eloop_terminate(); + return -1; + } + + wpa_printf(MSG_DEBUG, "Completing interface initialization"); + if (iface->conf->channel) { +#ifdef NEED_AP_MLME + int res; +#endif /* NEED_AP_MLME */ + + iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel); + wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d " + "Frequency: %d MHz", + hostapd_hw_mode_txt(iface->conf->hw_mode), + iface->conf->channel, iface->freq); + +#ifdef NEED_AP_MLME + /* Check DFS */ + res = hostapd_handle_dfs(iface); + if (res <= 0) + return res; +#endif /* NEED_AP_MLME */ + + if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, + hapd->iconf->channel, + hapd->iconf->ieee80211n, + hapd->iconf->ieee80211ac, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + hapd->iconf->vht_oper_centr_freq_seg0_idx, + hapd->iconf->vht_oper_centr_freq_seg1_idx)) { + wpa_printf(MSG_ERROR, "Could not set channel for " + "kernel driver"); + return -1; + } + } + + if (iface->current_mode) { + if (hostapd_prepare_rates(iface, iface->current_mode)) { + wpa_printf(MSG_ERROR, "Failed to prepare rates " + "table."); + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Failed to prepare rates table."); + 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; + } + hapd = iface->bss[0]; + + hostapd_tx_queue_params(iface); + + ap_list_init(iface); + + hostapd_set_acl(hapd); + + if (hostapd_driver_commit(hapd) < 0) { + wpa_printf(MSG_ERROR, "%s: Failed to commit driver " + "configuration", __func__); + return -1; + } + + /* + * WPS UPnP module can be initialized only when the "upnp_iface" is up. + * If "interface" and "upnp_iface" are the same (e.g., non-bridge + * mode), the interface is up only after driver_commit, so initialize + * WPS after driver_commit. + */ + for (j = 0; j < iface->num_bss; j++) { + if (hostapd_init_wps_complete(iface->bss[j])) + return -1; + } + + hostapd_set_state(iface, HAPD_IFACE_ENABLED); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); + if (hapd->setup_complete_cb) + hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); + + wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", + iface->bss[0]->conf->iface); + if (iface->interfaces && iface->interfaces->terminate_on_error > 0) + iface->interfaces->terminate_on_error--; + + 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. + * + * If interface setup requires more time, e.g., to perform HT co-ex scans, ACS, + * or DFS operations, this function returns 0 before such operations have been + * completed. The pending operations are registered into eloop and will be + * completed from eloop callbacks. Those callbacks end up calling + * hostapd_setup_interface_complete() once setup has been completed. + */ +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; + + hapd->new_assoc_sta_cb = hostapd_new_assoc_sta; + hapd->iconf = conf; + hapd->conf = bss; + hapd->iface = hapd_iface; + hapd->driver = hapd->iconf->driver; + hapd->ctrl_sock = -1; + + return hapd; +} + + +static void hostapd_bss_deinit(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__, + hapd->conf->iface); + hostapd_free_stas(hapd); + hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); + hostapd_clear_wep(hapd); + hostapd_cleanup(hapd); +} + + +void hostapd_interface_deinit(struct hostapd_iface *iface) +{ + int j; + + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + if (iface == NULL) + return; + + eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + iface->wait_channel_update = 0; + + for (j = iface->num_bss - 1; j >= 0; j--) + hostapd_bss_deinit(iface->bss[j]); +} + + +void hostapd_interface_free(struct hostapd_iface *iface) +{ + size_t j; + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + for (j = 0; j < iface->num_bss; j++) { + wpa_printf(MSG_DEBUG, "%s: free hapd %p", + __func__, iface->bss[j]); + os_free(iface->bss[j]); + } + hostapd_cleanup_iface(iface); +} + + +/** + * hostapd_init - Allocate and initialize per-interface data + * @config_file: Path to the configuration file + * Returns: Pointer to the allocated interface data or %NULL on failure + * + * This function is used to allocate main data structures for per-interface + * data. The allocated data buffer will be freed by calling + * hostapd_cleanup_iface(). + */ +struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, + const char *config_file) +{ + struct hostapd_iface *hapd_iface = NULL; + struct hostapd_config *conf = NULL; + struct hostapd_data *hapd; + size_t i; + + hapd_iface = os_zalloc(sizeof(*hapd_iface)); + if (hapd_iface == NULL) + goto fail; + + hapd_iface->config_fname = os_strdup(config_file); + if (hapd_iface->config_fname == NULL) + goto fail; + + conf = interfaces->config_read_cb(hapd_iface->config_fname); + if (conf == NULL) + goto fail; + hapd_iface->conf = conf; + + hapd_iface->num_bss = conf->num_bss; + hapd_iface->bss = os_calloc(conf->num_bss, + sizeof(struct hostapd_data *)); + if (hapd_iface->bss == NULL) + goto fail; + + for (i = 0; i < conf->num_bss; i++) { + hapd = hapd_iface->bss[i] = + hostapd_alloc_bss_data(hapd_iface, conf, + conf->bss[i]); + if (hapd == NULL) + goto fail; + hapd->msg_ctx = hapd; + } + + return hapd_iface; + +fail: + wpa_printf(MSG_ERROR, "Failed to set up interface with %s", + config_file); + if (conf) + hostapd_config_free(conf); + if (hapd_iface) { + os_free(hapd_iface->config_fname); + os_free(hapd_iface->bss); + wpa_printf(MSG_DEBUG, "%s: free iface %p", + __func__, hapd_iface); + os_free(hapd_iface); + } + return NULL; +} + + +static int ifname_in_use(struct hapd_interfaces *interfaces, const char *ifname) +{ + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_iface *iface = interfaces->iface[i]; + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + if (os_strcmp(ifname, hapd->conf->iface) == 0) + return 1; + } + } + + return 0; +} + + +/** + * hostapd_interface_init_bss - Read configuration file and init BSS data + * + * This function is used to parse configuration file for a BSS. This BSS is + * added to an existing interface sharing the same radio (if any) or a new + * interface is created if this is the first interface on a radio. This + * allocate memory for the BSS. No actual driver operations are started. + * + * This is similar to hostapd_interface_init(), but for a case where the + * configuration is used to add a single BSS instead of all BSSes for a radio. + */ +struct hostapd_iface * +hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, + const char *config_fname, int debug) +{ + struct hostapd_iface *new_iface = NULL, *iface = NULL; + struct hostapd_data *hapd; + int k; + size_t i, bss_idx; + + if (!phy || !*phy) + return NULL; + + for (i = 0; i < interfaces->count; i++) { + if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) { + iface = interfaces->iface[i]; + break; + } + } + + wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s", + config_fname, phy, iface ? "" : " --> new PHY"); + if (iface) { + struct hostapd_config *conf; + struct hostapd_bss_config **tmp_conf; + struct hostapd_data **tmp_bss; + struct hostapd_bss_config *bss; + const char *ifname; + + /* Add new BSS to existing iface */ + conf = interfaces->config_read_cb(config_fname); + if (conf == NULL) + return NULL; + if (conf->num_bss > 1) { + wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config"); + hostapd_config_free(conf); + return NULL; + } + + ifname = conf->bss[0]->iface; + if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) { + wpa_printf(MSG_ERROR, + "Interface name %s already in use", ifname); + hostapd_config_free(conf); + return NULL; + } + + tmp_conf = os_realloc_array( + iface->conf->bss, iface->conf->num_bss + 1, + sizeof(struct hostapd_bss_config *)); + tmp_bss = os_realloc_array(iface->bss, iface->num_bss + 1, + sizeof(struct hostapd_data *)); + if (tmp_bss) + iface->bss = tmp_bss; + if (tmp_conf) { + iface->conf->bss = tmp_conf; + iface->conf->last_bss = tmp_conf[0]; + } + if (tmp_bss == NULL || tmp_conf == NULL) { + hostapd_config_free(conf); + return NULL; + } + bss = iface->conf->bss[iface->conf->num_bss] = conf->bss[0]; + iface->conf->num_bss++; + + hapd = hostapd_alloc_bss_data(iface, iface->conf, bss); + if (hapd == NULL) { + iface->conf->num_bss--; + hostapd_config_free(conf); + return NULL; + } + iface->conf->last_bss = bss; + iface->bss[iface->num_bss] = hapd; + hapd->msg_ctx = hapd; + + bss_idx = iface->num_bss++; + conf->num_bss--; + conf->bss[0] = NULL; + hostapd_config_free(conf); + } else { + /* Add a new iface with the first BSS */ + new_iface = iface = hostapd_init(interfaces, config_fname); + if (!iface) + return NULL; + os_strlcpy(iface->phy, phy, sizeof(iface->phy)); + iface->interfaces = interfaces; + bss_idx = 0; + } + + for (k = 0; k < debug; k++) { + if (iface->bss[bss_idx]->conf->logger_stdout_level > 0) + iface->bss[bss_idx]->conf->logger_stdout_level--; + } + + if (iface->conf->bss[bss_idx]->iface[0] == '\0' && + !hostapd_drv_none(iface->bss[bss_idx])) { + wpa_printf(MSG_ERROR, "Interface name not specified in %s", + config_fname); + if (new_iface) + hostapd_interface_deinit_free(new_iface); + return NULL; + } + + return iface; +} + + +void hostapd_interface_deinit_free(struct hostapd_iface *iface) +{ + const struct wpa_driver_ops *driver; + void *drv_priv; + + wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); + if (iface == NULL) + return; + wpa_printf(MSG_DEBUG, "%s: num_bss=%u conf->num_bss=%u", + __func__, (unsigned int) iface->num_bss, + (unsigned int) iface->conf->num_bss); + driver = iface->bss[0]->driver; + drv_priv = iface->bss[0]->drv_priv; + hostapd_interface_deinit(iface); + wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", + __func__, driver, drv_priv); + if (driver && driver->hapd_deinit && drv_priv) + driver->hapd_deinit(drv_priv); + hostapd_interface_free(iface); +} + + +int hostapd_enable_iface(struct hostapd_iface *hapd_iface) +{ + if (hapd_iface->bss[0]->drv_priv != NULL) { + wpa_printf(MSG_ERROR, "Interface %s already enabled", + hapd_iface->conf->bss[0]->iface); + return -1; + } + + wpa_printf(MSG_DEBUG, "Enable interface %s", + hapd_iface->conf->bss[0]->iface); + + if (hostapd_config_check(hapd_iface->conf, 1) < 0) { + wpa_printf(MSG_INFO, "Invalid configuration - cannot enable"); + return -1; + } + + if (hapd_iface->interfaces == NULL || + hapd_iface->interfaces->driver_init == NULL || + hapd_iface->interfaces->driver_init(hapd_iface)) + return -1; + + if (hostapd_setup_interface(hapd_iface)) { + const struct wpa_driver_ops *driver; + void *drv_priv; + + driver = hapd_iface->bss[0]->driver; + drv_priv = hapd_iface->bss[0]->drv_priv; + wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", + __func__, driver, drv_priv); + if (driver && driver->hapd_deinit && drv_priv) { + driver->hapd_deinit(drv_priv); + hapd_iface->bss[0]->drv_priv = NULL; + } + return -1; + } + + return 0; +} + + +int hostapd_reload_iface(struct hostapd_iface *hapd_iface) +{ + size_t j; + + wpa_printf(MSG_DEBUG, "Reload interface %s", + hapd_iface->conf->bss[0]->iface); + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_set_security_params(hapd_iface->conf->bss[j]); + if (hostapd_config_check(hapd_iface->conf, 1) < 0) { + wpa_printf(MSG_ERROR, "Updated configuration is invalid"); + return -1; + } + hostapd_clear_old(hapd_iface); + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_reload_bss(hapd_iface->bss[j]); + + return 0; +} + + +int hostapd_disable_iface(struct hostapd_iface *hapd_iface) +{ + size_t j; + const struct wpa_driver_ops *driver; + void *drv_priv; + + if (hapd_iface == NULL) + return -1; + wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED); + driver = hapd_iface->bss[0]->driver; + drv_priv = hapd_iface->bss[0]->drv_priv; + + /* whatever hostapd_interface_deinit does */ + for (j = 0; j < hapd_iface->num_bss; j++) { + struct hostapd_data *hapd = hapd_iface->bss[j]; + hostapd_free_stas(hapd); + hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING); + hostapd_clear_wep(hapd); + hostapd_free_hapd_data(hapd); + } + + wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit", + __func__, driver, drv_priv); + if (driver && driver->hapd_deinit && drv_priv) { + driver->hapd_deinit(drv_priv); + hapd_iface->bss[0]->drv_priv = NULL; + } + + /* From hostapd_cleanup_iface: These were initialized in + * hostapd_setup_interface and hostapd_setup_interface_complete + */ + hostapd_cleanup_iface_partial(hapd_iface); + + wpa_printf(MSG_DEBUG, "Interface %s disabled", + hapd_iface->bss[0]->conf->iface); + hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED); + return 0; +} + + +static struct hostapd_iface * +hostapd_iface_alloc(struct hapd_interfaces *interfaces) +{ + struct hostapd_iface **iface, *hapd_iface; + + iface = os_realloc_array(interfaces->iface, interfaces->count + 1, + sizeof(struct hostapd_iface *)); + if (iface == NULL) + return NULL; + interfaces->iface = iface; + hapd_iface = interfaces->iface[interfaces->count] = + os_zalloc(sizeof(*hapd_iface)); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " + "the interface", __func__); + return NULL; + } + interfaces->count++; + hapd_iface->interfaces = interfaces; + + return hapd_iface; +} + + +static struct hostapd_config * +hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, + const char *ctrl_iface) +{ + struct hostapd_bss_config *bss; + struct hostapd_config *conf; + + /* Allocates memory for bss and conf */ + conf = hostapd_config_defaults(); + if (conf == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " + "configuration", __func__); + return NULL; + } + + conf->driver = wpa_drivers[0]; + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "No driver wrappers registered!"); + hostapd_config_free(conf); + return NULL; + } + + bss = conf->last_bss = conf->bss[0]; + + os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); + bss->ctrl_interface = os_strdup(ctrl_iface); + if (bss->ctrl_interface == NULL) { + hostapd_config_free(conf); + return NULL; + } + + /* Reading configuration file skipped, will be done in SET! + * From reading the configuration till the end has to be done in + * SET + */ + return conf; +} + + +static struct hostapd_iface * hostapd_data_alloc( + struct hapd_interfaces *interfaces, struct hostapd_config *conf) +{ + size_t i; + struct hostapd_iface *hapd_iface = + interfaces->iface[interfaces->count - 1]; + struct hostapd_data *hapd; + + hapd_iface->conf = conf; + hapd_iface->num_bss = conf->num_bss; + + hapd_iface->bss = os_zalloc(conf->num_bss * + sizeof(struct hostapd_data *)); + if (hapd_iface->bss == NULL) + return NULL; + + for (i = 0; i < conf->num_bss; i++) { + hapd = hapd_iface->bss[i] = + hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]); + if (hapd == NULL) + return NULL; + hapd->msg_ctx = hapd; + } + + hapd_iface->interfaces = interfaces; + + return hapd_iface; +} + + +int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) +{ + struct hostapd_config *conf = NULL; + struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL; + struct hostapd_data *hapd; + char *ptr; + size_t i, j; + const char *conf_file = NULL, *phy_name = NULL; + + if (os_strncmp(buf, "bss_config=", 11) == 0) { + char *pos; + phy_name = buf + 11; + pos = os_strchr(phy_name, ':'); + if (!pos) + return -1; + *pos++ = '\0'; + conf_file = pos; + if (!os_strlen(conf_file)) + return -1; + + hapd_iface = hostapd_interface_init_bss(interfaces, phy_name, + conf_file, 0); + if (!hapd_iface) + return -1; + for (j = 0; j < interfaces->count; j++) { + if (interfaces->iface[j] == hapd_iface) + break; + } + if (j == interfaces->count) { + struct hostapd_iface **tmp; + tmp = os_realloc_array(interfaces->iface, + interfaces->count + 1, + sizeof(struct hostapd_iface *)); + if (!tmp) { + hostapd_interface_deinit_free(hapd_iface); + return -1; + } + interfaces->iface = tmp; + interfaces->iface[interfaces->count++] = hapd_iface; + new_iface = hapd_iface; + } + + if (new_iface) { + if (interfaces->driver_init(hapd_iface) || + hostapd_setup_interface(hapd_iface)) { + interfaces->count--; + goto fail; + } + } else { + /* Assign new BSS with bss[0]'s driver info */ + hapd = hapd_iface->bss[hapd_iface->num_bss - 1]; + hapd->driver = hapd_iface->bss[0]->driver; + hapd->drv_priv = hapd_iface->bss[0]->drv_priv; + os_memcpy(hapd->own_addr, hapd_iface->bss[0]->own_addr, + ETH_ALEN); + + if (start_ctrl_iface_bss(hapd) < 0 || + (hapd_iface->state == HAPD_IFACE_ENABLED && + hostapd_setup_bss(hapd, -1))) { + hapd_iface->conf->num_bss--; + hapd_iface->num_bss--; + wpa_printf(MSG_DEBUG, "%s: free hapd %p %s", + __func__, hapd, hapd->conf->iface); + os_free(hapd); + return -1; + } + } + return 0; + } + + ptr = os_strchr(buf, ' '); + if (ptr == NULL) + return -1; + *ptr++ = '\0'; + + if (os_strncmp(ptr, "config=", 7) == 0) + conf_file = ptr + 7; + + for (i = 0; i < interfaces->count; i++) { + if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface, + buf)) { + wpa_printf(MSG_INFO, "Cannot add interface - it " + "already exists"); + return -1; + } + } + + hapd_iface = hostapd_iface_alloc(interfaces); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for interface", __func__); + goto fail; + } + + if (conf_file && interfaces->config_read_cb) { + conf = interfaces->config_read_cb(conf_file); + if (conf && conf->bss) + os_strlcpy(conf->bss[0]->iface, buf, + sizeof(conf->bss[0]->iface)); + } else + conf = hostapd_config_alloc(interfaces, buf, ptr); + if (conf == NULL || conf->bss == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for configuration", __func__); + goto fail; + } + + hapd_iface = hostapd_data_alloc(interfaces, conf); + if (hapd_iface == NULL) { + wpa_printf(MSG_ERROR, "%s: Failed to allocate memory " + "for hostapd", __func__); + goto fail; + } + + if (start_ctrl_iface(hapd_iface) < 0) + goto fail; + + wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0]->iface); + + return 0; + +fail: + if (conf) + hostapd_config_free(conf); + if (hapd_iface) { + if (hapd_iface->bss) { + for (i = 0; i < hapd_iface->num_bss; i++) { + hapd = hapd_iface->bss[i]; + if (hapd && hapd_iface->interfaces && + hapd_iface->interfaces->ctrl_iface_deinit) + hapd_iface->interfaces-> + ctrl_iface_deinit(hapd); + wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)", + __func__, hapd_iface->bss[i], + hapd_iface->bss[i]->conf->iface); + os_free(hapd_iface->bss[i]); + } + os_free(hapd_iface->bss); + } + wpa_printf(MSG_DEBUG, "%s: free iface %p", + __func__, hapd_iface); + os_free(hapd_iface); + } + return -1; +} + + +static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx) +{ + size_t i; + + wpa_printf(MSG_INFO, "Remove BSS '%s'", iface->conf->bss[idx]->iface); + + /* Remove hostapd_data only if it has already been initialized */ + if (idx < iface->num_bss) { + struct hostapd_data *hapd = iface->bss[idx]; + + hostapd_bss_deinit(hapd); + wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)", + __func__, hapd, hapd->conf->iface); + hostapd_config_free_bss(hapd->conf); + os_free(hapd); + + iface->num_bss--; + + for (i = idx; i < iface->num_bss; i++) + iface->bss[i] = iface->bss[i + 1]; + } else { + hostapd_config_free_bss(iface->conf->bss[idx]); + iface->conf->bss[idx] = NULL; + } + + iface->conf->num_bss--; + for (i = idx; i < iface->conf->num_bss; i++) + iface->conf->bss[i] = iface->conf->bss[i + 1]; + + return 0; +} + + +int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf) +{ + struct hostapd_iface *hapd_iface; + size_t i, j, k = 0; + + for (i = 0; i < interfaces->count; i++) { + hapd_iface = interfaces->iface[i]; + if (hapd_iface == NULL) + return -1; + if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) { + wpa_printf(MSG_INFO, "Remove interface '%s'", buf); + hostapd_interface_deinit_free(hapd_iface); + k = i; + while (k < (interfaces->count - 1)) { + interfaces->iface[k] = + interfaces->iface[k + 1]; + k++; + } + interfaces->count--; + return 0; + } + + for (j = 0; j < hapd_iface->conf->num_bss; j++) { + if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) + return hostapd_remove_bss(hapd_iface, j); + } + } + return -1; +} + + +/** + * 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) { + hostapd_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); + +#ifdef CONFIG_P2P + if (sta->p2p_ie == NULL && !sta->no_p2p_set) { + sta->no_p2p_set = 1; + hapd->num_sta_no_p2p++; + if (hapd->num_sta_no_p2p == 1) + hostapd_p2p_non_p2p_sta_connected(hapd); + } +#endif /* CONFIG_P2P */ + + /* 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) { + os_get_reltime(&sta->connected_time); + 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); + + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - ap_max_inactivity)", + __func__, MAC2STR(sta->addr), + hapd->conf->ap_max_inactivity); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, + ap_handle_timer, hapd, sta); + } +} + + +const char * hostapd_state_text(enum hostapd_iface_state s) +{ + switch (s) { + case HAPD_IFACE_UNINITIALIZED: + return "UNINITIALIZED"; + case HAPD_IFACE_DISABLED: + return "DISABLED"; + case HAPD_IFACE_COUNTRY_UPDATE: + return "COUNTRY_UPDATE"; + case HAPD_IFACE_ACS: + return "ACS"; + case HAPD_IFACE_HT_SCAN: + return "HT_SCAN"; + case HAPD_IFACE_DFS: + return "DFS"; + case HAPD_IFACE_ENABLED: + return "ENABLED"; + } + + return "UNKNOWN"; +} + + +void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s) +{ + wpa_printf(MSG_INFO, "%s: interface state %s->%s", + iface->conf->bss[0]->iface, hostapd_state_text(iface->state), + hostapd_state_text(s)); + iface->state = s; +} + + +#ifdef NEED_AP_MLME + +static void free_beacon_data(struct beacon_data *beacon) +{ + os_free(beacon->head); + beacon->head = NULL; + os_free(beacon->tail); + beacon->tail = NULL; + os_free(beacon->probe_resp); + beacon->probe_resp = NULL; + os_free(beacon->beacon_ies); + beacon->beacon_ies = NULL; + os_free(beacon->proberesp_ies); + beacon->proberesp_ies = NULL; + os_free(beacon->assocresp_ies); + beacon->assocresp_ies = NULL; +} + + +static int hostapd_build_beacon_data(struct hostapd_iface *iface, + struct beacon_data *beacon) +{ + struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra; + struct wpa_driver_ap_params params; + int ret; + struct hostapd_data *hapd = iface->bss[0]; + + os_memset(beacon, 0, sizeof(*beacon)); + ret = ieee802_11_build_ap_params(hapd, ¶ms); + if (ret < 0) + return ret; + + ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra, + &proberesp_extra, + &assocresp_extra); + if (ret) + goto free_ap_params; + + ret = -1; + beacon->head = os_malloc(params.head_len); + if (!beacon->head) + goto free_ap_extra_ies; + + os_memcpy(beacon->head, params.head, params.head_len); + beacon->head_len = params.head_len; + + beacon->tail = os_malloc(params.tail_len); + if (!beacon->tail) + goto free_beacon; + + os_memcpy(beacon->tail, params.tail, params.tail_len); + beacon->tail_len = params.tail_len; + + if (params.proberesp != NULL) { + beacon->probe_resp = os_malloc(params.proberesp_len); + if (!beacon->probe_resp) + goto free_beacon; + + os_memcpy(beacon->probe_resp, params.proberesp, + params.proberesp_len); + beacon->probe_resp_len = params.proberesp_len; + } + + /* copy the extra ies */ + if (beacon_extra) { + beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra)); + if (!beacon->beacon_ies) + goto free_beacon; + + os_memcpy(beacon->beacon_ies, + beacon_extra->buf, wpabuf_len(beacon_extra)); + beacon->beacon_ies_len = wpabuf_len(beacon_extra); + } + + if (proberesp_extra) { + beacon->proberesp_ies = + os_malloc(wpabuf_len(proberesp_extra)); + if (!beacon->proberesp_ies) + goto free_beacon; + + os_memcpy(beacon->proberesp_ies, proberesp_extra->buf, + wpabuf_len(proberesp_extra)); + beacon->proberesp_ies_len = wpabuf_len(proberesp_extra); + } + + if (assocresp_extra) { + beacon->assocresp_ies = + os_malloc(wpabuf_len(assocresp_extra)); + if (!beacon->assocresp_ies) + goto free_beacon; + + os_memcpy(beacon->assocresp_ies, assocresp_extra->buf, + wpabuf_len(assocresp_extra)); + beacon->assocresp_ies_len = wpabuf_len(assocresp_extra); + } + + ret = 0; +free_beacon: + /* if the function fails, the caller should not free beacon data */ + if (ret) + free_beacon_data(beacon); + +free_ap_extra_ies: + hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra, + assocresp_extra); +free_ap_params: + ieee802_11_free_ap_params(¶ms); + return ret; +} + + +/* + * TODO: This flow currently supports only changing frequency within the + * same hw_mode. Any other changes to MAC parameters or provided settings (even + * width) are not supported. + */ +static int hostapd_change_config_freq(struct hostapd_data *hapd, + struct hostapd_config *conf, + struct hostapd_freq_params *params, + struct hostapd_freq_params *old_params) +{ + int channel; + + if (!params->channel) { + /* check if the new channel is supported by hw */ + channel = hostapd_hw_get_channel(hapd, params->freq); + if (!channel) + return -1; + } else { + channel = params->channel; + } + + /* if a pointer to old_params is provided we save previous state */ + if (old_params) { + old_params->channel = conf->channel; + old_params->ht_enabled = conf->ieee80211n; + old_params->sec_channel_offset = conf->secondary_channel; + } + + conf->channel = channel; + conf->ieee80211n = params->ht_enabled; + conf->secondary_channel = params->sec_channel_offset; + + /* TODO: maybe call here hostapd_config_check here? */ + + return 0; +} + + +static int hostapd_fill_csa_settings(struct hostapd_iface *iface, + struct csa_settings *settings) +{ + struct hostapd_freq_params old_freq; + int ret; + + os_memset(&old_freq, 0, sizeof(old_freq)); + if (!iface || !iface->freq || iface->csa_in_progress) + return -1; + + ret = hostapd_change_config_freq(iface->bss[0], iface->conf, + &settings->freq_params, + &old_freq); + if (ret) + return ret; + + ret = hostapd_build_beacon_data(iface, &settings->beacon_after); + + /* change back the configuration */ + hostapd_change_config_freq(iface->bss[0], iface->conf, + &old_freq, NULL); + + if (ret) + return ret; + + /* set channel switch parameters for csa ie */ + iface->cs_freq_params = settings->freq_params; + iface->cs_count = settings->cs_count; + iface->cs_block_tx = settings->block_tx; + + ret = hostapd_build_beacon_data(iface, &settings->beacon_csa); + if (ret) { + free_beacon_data(&settings->beacon_after); + return ret; + } + + settings->counter_offset_beacon = iface->cs_c_off_beacon; + settings->counter_offset_presp = iface->cs_c_off_proberesp; + + return 0; +} + + +void hostapd_cleanup_cs_params(struct hostapd_data *hapd) +{ + os_memset(&hapd->iface->cs_freq_params, 0, + sizeof(hapd->iface->cs_freq_params)); + hapd->iface->cs_count = 0; + hapd->iface->cs_block_tx = 0; + hapd->iface->cs_c_off_beacon = 0; + hapd->iface->cs_c_off_proberesp = 0; + hapd->iface->csa_in_progress = 0; +} + + +int hostapd_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings) +{ + int ret; + ret = hostapd_fill_csa_settings(hapd->iface, settings); + if (ret) + return ret; + + ret = hostapd_drv_switch_channel(hapd, settings); + free_beacon_data(&settings->beacon_csa); + free_beacon_data(&settings->beacon_after); + + if (ret) { + /* if we failed, clean cs parameters */ + hostapd_cleanup_cs_params(hapd); + return ret; + } + + hapd->iface->csa_in_progress = 1; + return 0; +} + +#endif /* NEED_AP_MLME */ diff --git a/contrib/hostapd/src/ap/hostapd.h b/contrib/hostapd/src/ap/hostapd.h new file mode 100644 index 0000000000..489ab16527 --- /dev/null +++ b/contrib/hostapd/src/ap/hostapd.h @@ -0,0 +1,424 @@ +/* + * hostapd / Initialization and configuration + * Copyright (c) 2002-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HOSTAPD_H +#define HOSTAPD_H + +#include "common/defs.h" +#include "ap_config.h" +#include "drivers/driver.h" + +struct wpa_ctrl_dst; +struct radius_server_data; +struct upnp_wps_device_sm; +struct hostapd_data; +struct sta_info; +struct ieee80211_ht_capabilities; +struct full_dynamic_vlan; +enum wps_event; +union wps_event_data; + +struct hostapd_iface; +struct hostapd_dynamic_iface; + +struct hapd_interfaces { + int (*reload_config)(struct hostapd_iface *iface); + struct hostapd_config * (*config_read_cb)(const char *config_fname); + 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); + int (*driver_init)(struct hostapd_iface *iface); + + size_t count; + size_t count_dynamic; + int global_ctrl_sock; + char *global_iface_path; + char *global_iface_name; +#ifndef CONFIG_NATIVE_WINDOWS + gid_t ctrl_iface_group; +#endif /* CONFIG_NATIVE_WINDOWS */ + struct hostapd_iface **iface; + struct hostapd_dynamic_iface **dynamic_iface; + + size_t terminate_on_error; +}; + +enum hostapd_chan_status { + HOSTAPD_CHAN_VALID = 0, /* channel is ready */ + HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */ + HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */ +}; + +struct hostapd_probereq_cb { + int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, + const u8 *ie, size_t ie_len, int ssi_signal); + 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; + int ssi_signal; /* dBm */ +}; + +enum wps_status { + WPS_STATUS_SUCCESS = 1, + WPS_STATUS_FAILURE +}; + +enum pbc_status { + WPS_PBC_STATUS_DISABLE, + WPS_PBC_STATUS_ACTIVE, + WPS_PBC_STATUS_TIMEOUT, + WPS_PBC_STATUS_OVERLAP +}; + +struct wps_stat { + enum wps_status status; + enum wps_error_indication failure_reason; + enum pbc_status pbc_status; + u8 peer_addr[ETH_ALEN]; +}; + + +/** + * 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 */ + unsigned int started:1; + + 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; + + void (*new_assoc_sta_cb)(struct hostapd_data *hapd, + struct sta_info *sta, int reassoc); + + void *msg_ctx; /* ctx for wpa_msg() calls */ + void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */ + + struct radius_client_data *radius; + u32 acct_session_id_hi, acct_session_id_lo; + struct radius_das_data *radius_das; + + 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; + struct os_reltime 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; + + /* Time Advertisement */ + u8 time_update_counter; + struct wpabuf *time_adv; + +#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; + + int beacon_set_done; + struct wpabuf *wps_beacon_ie; + struct wpabuf *wps_probe_resp_ie; +#ifdef CONFIG_WPS + unsigned int ap_pin_failures; + unsigned int ap_pin_failures_consecutive; + struct upnp_wps_device_sm *wps_upnp; + unsigned int ap_pin_lockout_time; + + struct wps_stat wps_stats; +#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 (*public_action_cb2)(void *ctx, const u8 *buf, size_t len, + int freq); + void *public_action_cb2_ctx; + + int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len, + int freq); + void *vendor_action_cb_ctx; + + void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr, + const u8 *uuid_e); + void *wps_reg_success_cb_ctx; + + void (*wps_event_cb)(void *ctx, enum wps_event event, + union wps_event_data *data); + void *wps_event_cb_ctx; + + void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr, + int authorized, const u8 *p2p_dev_addr); + void *sta_authorized_cb_ctx; + + void (*setup_complete_cb)(void *ctx); + void *setup_complete_cb_ctx; + + void (*new_psk_cb)(void *ctx, const u8 *mac_addr, + const u8 *p2p_dev_addr, const u8 *psk, + size_t psk_len); + void *new_psk_cb_ctx; + +#ifdef CONFIG_P2P + struct p2p_data *p2p; + struct p2p_group *p2p_group; + struct wpabuf *p2p_beacon_ie; + struct wpabuf *p2p_probe_resp_ie; + + /* Number of non-P2P association stations */ + int num_sta_no_p2p; + + /* Periodic NoA (used only when no non-P2P clients in the group) */ + int noa_enabled; + int noa_start; + int noa_duration; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_INTERWORKING + size_t gas_frag_limit; +#endif /* CONFIG_INTERWORKING */ + +#ifdef CONFIG_SQLITE + struct hostapd_eap_user tmp_eap_user; +#endif /* CONFIG_SQLITE */ + +#ifdef CONFIG_SAE + /** Key used for generating SAE anti-clogging tokens */ + u8 sae_token_key[8]; + struct os_reltime last_sae_token_key_update; +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_TESTING_OPTIONS + int ext_mgmt_frame_handling; +#endif /* CONFIG_TESTING_OPTIONS */ +}; + + +/** + * struct hostapd_iface - hostapd per-interface data structure + */ +struct hostapd_iface { + struct hapd_interfaces *interfaces; + void *owner; + char *config_fname; + struct hostapd_config *conf; + char phy[16]; /* Name of the PHY (radio) */ + + enum hostapd_iface_state { + HAPD_IFACE_UNINITIALIZED, + HAPD_IFACE_DISABLED, + HAPD_IFACE_COUNTRY_UPDATE, + HAPD_IFACE_ACS, + HAPD_IFACE_HT_SCAN, + HAPD_IFACE_DFS, + HAPD_IFACE_ENABLED + } state; + + size_t num_bss; + struct hostapd_data **bss; + + unsigned int wait_channel_update:1; + unsigned int cac_started:1; + + 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]; + + unsigned int drv_flags; + + /* + * A bitmap of supported protocols for probe response offload. See + * struct wpa_driver_capa in driver.h + */ + unsigned int probe_resp_offloads; + + /* extended capabilities supported by the driver */ + const u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; + + unsigned int drv_max_acl_mac_addrs; + + 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 *basic_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; + + /* surveying helpers */ + + /* number of channels surveyed */ + unsigned int chans_surveyed; + + /* lowest observed noise floor in dBm */ + s8 lowest_nf; + + /* channel switch parameters */ + struct hostapd_freq_params cs_freq_params; + u8 cs_count; + int cs_block_tx; + unsigned int cs_c_off_beacon; + unsigned int cs_c_off_proberesp; + int csa_in_progress; + +#ifdef CONFIG_ACS + unsigned int acs_num_completed_scans; +#endif /* CONFIG_ACS */ + + void (*scan_cb)(struct hostapd_iface *iface); +}; + +/** + * struct hostapd_dynamic_iface - hostapd per dynamically allocated + * or added interface data structure + */ +struct hostapd_dynamic_iface { + char parent[IFNAMSIZ + 1]; + char iface[IFNAMSIZ + 1]; + unsigned int usage; +}; + +/* hostapd.c */ +int hostapd_for_each_interface(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); +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); +struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces, + const char *config_file); +struct hostapd_iface * +hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy, + const char *config_fname, int debug); +void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, + int reassoc); +void hostapd_interface_deinit_free(struct hostapd_iface *iface); +int hostapd_enable_iface(struct hostapd_iface *hapd_iface); +int hostapd_reload_iface(struct hostapd_iface *hapd_iface); +int hostapd_disable_iface(struct hostapd_iface *hapd_iface); +int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf); +int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf); +void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); +void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); +const char * hostapd_state_text(enum hostapd_iface_state s); +int hostapd_switch_channel(struct hostapd_data *hapd, + struct csa_settings *settings); +void hostapd_cleanup_cs_params(struct hostapd_data *hapd); + +/* utils.c */ +int hostapd_register_probereq_cb(struct hostapd_data *hapd, + int (*cb)(void *ctx, const u8 *sa, + const u8 *da, const u8 *bssid, + const u8 *ie, size_t ie_len, + int ssi_signal), + 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, int reassoc); +void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); +void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr); +void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, + const u8 *addr, int reason_code); +int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, + const u8 *bssid, const u8 *ie, size_t ie_len, + int ssi_signal); +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, + int offset, int width, int cf1, int cf2); + +const struct hostapd_eap_user * +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, + size_t identity_len, int phase2); + +#endif /* HOSTAPD_H */ diff --git a/contrib/hostapd/src/ap/hs20.c b/contrib/hostapd/src/ap/hs20.c new file mode 100644 index 0000000000..45d518bc1e --- /dev/null +++ b/contrib/hostapd/src/ap/hs20.c @@ -0,0 +1,31 @@ +/* + * Hotspot 2.0 AP ANQP processing + * Copyright (c) 2009, Atheros Communications, Inc. + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "ap_config.h" +#include "hs20.h" + + +u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid) +{ + if (!hapd->conf->hs20) + return eid; + *eid++ = WLAN_EID_VENDOR_SPECIFIC; + *eid++ = 5; + WPA_PUT_BE24(eid, OUI_WFA); + eid += 3; + *eid++ = HS20_INDICATION_OUI_TYPE; + /* Hotspot Configuration: DGAF Enabled */ + *eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00; + return eid; +} diff --git a/contrib/hostapd/src/ap/hs20.h b/contrib/hostapd/src/ap/hs20.h new file mode 100644 index 0000000000..98698ce2fd --- /dev/null +++ b/contrib/hostapd/src/ap/hs20.h @@ -0,0 +1,16 @@ +/* + * Hotspot 2.0 AP ANQP processing + * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HS20_H +#define HS20_H + +struct hostapd_data; + +u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid); + +#endif /* HS20_H */ diff --git a/contrib/hostapd/src/ap/hw_features.c b/contrib/hostapd/src/ap/hw_features.c new file mode 100644 index 0000000000..4e66379572 --- /dev/null +++ b/contrib/hostapd/src/ap/hw_features.c @@ -0,0 +1,1031 @@ +/* + * hostapd / Hardware feature query and different modes + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2008-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 "common/wpa_ctrl.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "acs.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); +} + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static char * dfs_info(struct hostapd_channel_data *chan) +{ + static char info[256]; + char *state; + + switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) { + case HOSTAPD_CHAN_DFS_UNKNOWN: + state = "unknown"; + break; + case HOSTAPD_CHAN_DFS_USABLE: + state = "usable"; + break; + case HOSTAPD_CHAN_DFS_UNAVAILABLE: + state = "unavailable"; + break; + case HOSTAPD_CHAN_DFS_AVAILABLE: + state = "available"; + break; + default: + return ""; + } + os_snprintf(info, sizeof(info), " (DFS state = %s)", state); + info[sizeof(info) - 1] = '\0'; + + return info; +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +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]; + int dfs_enabled = hapd->iconf->ieee80211h && + (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR); + + /* set flag for channels we can use in current regulatory + * domain */ + for (j = 0; j < feature->num_channels; j++) { + int dfs = 0; + + /* + * Disable all channels that are marked not to allow + * IBSS operation or active scanning. + * Use radar channels only if the driver supports DFS. + */ + if ((feature->channels[j].flag & + HOSTAPD_CHAN_RADAR) && dfs_enabled) { + dfs = 1; + } else 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%s", + feature->mode, + feature->channels[j].chan, + feature->channels[j].freq, + feature->channels[j].max_tx_power, + dfs ? dfs_info(&feature->channels[j]) : ""); + } + } + + return ret; +} + + +int hostapd_prepare_rates(struct hostapd_iface *iface, + 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 (iface->conf->basic_rates) + basic_rates = iface->conf->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; + case HOSTAPD_MODE_IEEE80211AD: + return 0; /* No basic rates for 11ad */ + default: + return -1; + } + + i = 0; + while (basic_rates[i] >= 0) + i++; + if (i) + i++; /* -1 termination */ + os_free(iface->basic_rates); + iface->basic_rates = os_malloc(i * sizeof(int)); + if (iface->basic_rates) + os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int)); + + os_free(iface->current_rates); + iface->num_rates = 0; + + iface->current_rates = + os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); + if (!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 (iface->conf->supported_rates && + !hostapd_rate_found(iface->conf->supported_rates, + mode->rates[i])) + continue; + + rate = &iface->current_rates[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", + iface->num_rates, rate->rate, rate->flags); + iface->num_rates++; + } + + if ((iface->num_rates == 0 || num_basic_rates == 0) && + (!iface->conf->ieee80211n || !iface->conf->require_ht)) { + wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " + "rate sets (%d,%d).", + 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 < ARRAY_SIZE(allowed); 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) { + int sec = oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + *sec_chan = *pri_chan + 4; + else if (sec == 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 ieee80211n_check_scan(struct hostapd_iface *iface) +{ + struct wpa_scan_results *scan_res; + int oper40; + int res; + + /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is + * allowed per IEEE Std 802.11-2012, 10.15.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; + } + + res = ieee80211n_allowed_ht40_channel_pair(iface); + hostapd_setup_interface_complete(iface, !res); +} + + +static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface, + struct wpa_driver_scan_params *params) +{ + /* Scan only the affected frequency range */ + int pri_freq, sec_freq; + int affected_start, affected_end; + int i, pos; + struct hostapd_hw_modes *mode; + + if (iface->current_mode == NULL) + return; + + 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); + + mode = iface->current_mode; + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); + if (params->freqs == NULL) + return; + pos = 0; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + if (chan->freq < affected_start || + chan->freq > affected_end) + continue; + params->freqs[pos++] = chan->freq; + } +} + + +static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface, + struct wpa_driver_scan_params *params) +{ + /* Scan only the affected frequency range */ + int pri_freq; + int affected_start, affected_end; + int i, pos; + struct hostapd_hw_modes *mode; + + if (iface->current_mode == NULL) + return; + + pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + if (iface->conf->secondary_channel > 0) { + affected_start = pri_freq - 10; + affected_end = pri_freq + 30; + } else { + affected_start = pri_freq - 30; + affected_end = pri_freq + 10; + } + wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", + affected_start, affected_end); + + mode = iface->current_mode; + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); + if (params->freqs == NULL) + return; + pos = 0; + + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + if (chan->freq < affected_start || + chan->freq > affected_end) + continue; + params->freqs[pos++] = chan->freq; + } +} + + +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 */ + + hostapd_set_state(iface, HAPD_IFACE_HT_SCAN); + wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " + "40 MHz channel"); + os_memset(¶ms, 0, sizeof(params)); + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + ieee80211n_scan_channels_2g4(iface, ¶ms); + else + ieee80211n_scan_channels_5g(iface, ¶ms); + if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { + wpa_printf(MSG_ERROR, "Failed to request a scan of " + "neighboring BSSes"); + os_free(params.freqs); + return -1; + } + os_free(params.freqs); + + 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 ((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; +} + + +#ifdef CONFIG_IEEE80211AC + +static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name) +{ + u32 req_cap = conf & cap; + + /* + * Make sure we support all requested capabilities. + * NOTE: We assume that 'cap' represents a capability mask, + * not a discrete value. + */ + if ((hw & req_cap) != req_cap) { + wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]", + name); + return 0; + } + return 1; +} + + +static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 cap, + const char *name) +{ + u32 hw_max = hw & cap; + u32 conf_val = conf & cap; + + if (conf_val > hw_max) { + int offset = find_first_bit(cap); + wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", + name, conf_val >> offset, hw_max >> offset); + return 0; + } + return 1; +} + + +static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) +{ + u32 hw = iface->current_mode->vht_capab; + u32 conf = iface->conf->vht_capab; + + wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x", + hw, conf); + +#define VHT_CAP_CHECK(cap) \ + do { \ + if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \ + return 0; \ + } while (0) + +#define VHT_CAP_CHECK_MAX(cap) \ + do { \ + if (!ieee80211ac_cap_check_max(hw, conf, cap, #cap)) \ + return 0; \ + } while (0) + + VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); + VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ); + VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ); + VHT_CAP_CHECK(VHT_CAP_RXLDPC); + VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); + VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); + VHT_CAP_CHECK(VHT_CAP_TXSTBC); + VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); + VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); + VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); + VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); + VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); + VHT_CAP_CHECK(VHT_CAP_HTC_VHT); + VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT); + VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); + VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); + VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); + VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); + +#undef VHT_CAP_CHECK +#undef VHT_CAP_CHECK_MAX + + return 1; +} +#endif /* CONFIG_IEEE80211AC */ + +#endif /* CONFIG_IEEE80211N */ + + +int hostapd_check_ht_capab(struct hostapd_iface *iface) +{ +#ifdef CONFIG_IEEE80211N + int ret; + if (!iface->conf->ieee80211n) + return 0; + if (!ieee80211n_supported_ht_capab(iface)) + return -1; +#ifdef CONFIG_IEEE80211AC + if (!ieee80211ac_supported_vht_capab(iface)) + return -1; +#endif /* CONFIG_IEEE80211AC */ + ret = ieee80211n_check_40mhz(iface); + if (ret) + return ret; + if (!ieee80211n_allowed_ht40_channel_pair(iface)) + return -1; +#endif /* CONFIG_IEEE80211N */ + + return 0; +} + + +static int hostapd_is_usable_chan(struct hostapd_iface *iface, + int channel, int primary) +{ + int i; + struct hostapd_channel_data *chan; + + for (i = 0; i < iface->current_mode->num_channels; i++) { + chan = &iface->current_mode->channels[i]; + if (chan->chan != channel) + continue; + + if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) + return 1; + + wpa_printf(MSG_DEBUG, + "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s%s", + primary ? "" : "Configured HT40 secondary ", + i, chan->chan, chan->flag, + chan->flag & HOSTAPD_CHAN_NO_IBSS ? " NO-IBSS" : "", + chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN ? + " PASSIVE-SCAN" : "", + chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); + } + + return 0; +} + + +static int hostapd_is_usable_chans(struct hostapd_iface *iface) +{ + if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) + return 0; + + if (!iface->conf->secondary_channel) + return 1; + + return hostapd_is_usable_chan(iface, iface->conf->channel + + iface->conf->secondary_channel * 4, 0); +} + + +static enum hostapd_chan_status +hostapd_check_chans(struct hostapd_iface *iface) +{ + if (iface->conf->channel) { + if (hostapd_is_usable_chans(iface)) + return HOSTAPD_CHAN_VALID; + else + return HOSTAPD_CHAN_INVALID; + } + + /* + * The user set channel=0 or channel=acs_survey + * which is used to trigger ACS. + */ + + switch (acs_init(iface)) { + case HOSTAPD_CHAN_ACS: + return HOSTAPD_CHAN_ACS; + case HOSTAPD_CHAN_VALID: + case HOSTAPD_CHAN_INVALID: + default: + return HOSTAPD_CHAN_INVALID; + } +} + + +static void hostapd_notify_bad_chans(struct hostapd_iface *iface) +{ + 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)); + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured channel"); +} + + +int hostapd_acs_completed(struct hostapd_iface *iface, int err) +{ + int ret = -1; + + if (err) + goto out; + + switch (hostapd_check_chans(iface)) { + case HOSTAPD_CHAN_VALID: + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, + ACS_EVENT_COMPLETED "freq=%d channel=%d", + hostapd_hw_get_freq(iface->bss[0], + iface->conf->channel), + iface->conf->channel); + break; + case HOSTAPD_CHAN_ACS: + wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available"); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); + hostapd_notify_bad_chans(iface); + goto out; + case HOSTAPD_CHAN_INVALID: + default: + wpa_printf(MSG_ERROR, "ACS picked unusable channels"); + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED); + hostapd_notify_bad_chans(iface); + goto out; + } + + ret = hostapd_check_ht_capab(iface); + if (ret < 0) + goto out; + if (ret == 1) { + wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback"); + return 0; + } + + ret = 0; +out: + return hostapd_setup_interface_complete(iface, ret); +} + + +/** + * hostapd_select_hw_mode - Select the hardware mode + * @iface: Pointer to interface data. + * Returns: 0 on success, < 0 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; + + 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) (hw_mode in hostapd.conf)", + (int) iface->conf->hw_mode); + return -2; + } + + switch (hostapd_check_chans(iface)) { + case HOSTAPD_CHAN_VALID: + return 0; + case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */ + return 1; + case HOSTAPD_CHAN_INVALID: + default: + hostapd_notify_bad_chans(iface); + return -3; + } + + 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"; + case HOSTAPD_MODE_IEEE80211AD: + return "IEEE 802.11ad"; + default: + return "UNKNOWN"; + } +} + + +int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) +{ + int i; + + if (!hapd->iface->current_mode) + return 0; + + for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { + struct hostapd_channel_data *ch = + &hapd->iface->current_mode->channels[i]; + if (ch->chan == chan) + return ch->freq; + } + + return 0; +} + + +int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) +{ + int i; + + if (!hapd->iface->current_mode) + return 0; + + for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { + struct hostapd_channel_data *ch = + &hapd->iface->current_mode->channels[i]; + if (ch->freq == freq) + return ch->chan; + } + + return 0; +} diff --git a/contrib/hostapd/src/ap/hw_features.h b/contrib/hostapd/src/ap/hw_features.h new file mode 100644 index 0000000000..783ae5e126 --- /dev/null +++ b/contrib/hostapd/src/ap/hw_features.h @@ -0,0 +1,66 @@ +/* + * hostapd / Hardware feature query and different modes + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2008-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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_acs_completed(struct hostapd_iface *iface, int err); +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); +int hostapd_prepare_rates(struct hostapd_iface *iface, + struct hostapd_hw_modes *mode); +#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 -100; +} + +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; +} + +static inline int hostapd_prepare_rates(struct hostapd_iface *iface, + struct hostapd_hw_modes *mode) +{ + return 0; +} + +#endif /* NEED_AP_MLME */ + +#endif /* HW_FEATURES_H */ diff --git a/contrib/hostapd/hostapd/iapp.c b/contrib/hostapd/src/ap/iapp.c similarity index 85% rename from contrib/hostapd/hostapd/iapp.c rename to contrib/hostapd/src/ap/iapp.c index 6d6dba8f7b..bad080f028 100644 --- a/contrib/hostapd/hostapd/iapp.c +++ b/contrib/hostapd/src/ap/iapp.c @@ -2,14 +2,8 @@ * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) * Copyright (c) 2002-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -37,7 +31,7 @@ * - IEEE 802.11 context transfer */ -#include "includes.h" +#include "utils/includes.h" #include #include #ifdef USE_KERNEL_HEADERS @@ -46,11 +40,14 @@ #include #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 "iapp.h" -#include "eloop.h" #include "sta_info.h" +#include "iapp.h" #define IAPP_MULTICAST "224.0.1.178" @@ -207,7 +204,7 @@ static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num) 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]"); + wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno)); } @@ -234,7 +231,7 @@ static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr) * FIX: what is correct RW with 802.11? */ if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0) - perror("send[L2 Update]"); + wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno)); } @@ -279,8 +276,8 @@ static void iapp_process_add_notify(struct iapp_data *iapp, struct sta_info *sta; if (len != sizeof(*add)) { - printf("Invalid IAPP-ADD packet length %d (expected %lu)\n", - len, (unsigned long) sizeof(*add)); + wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)", + len, (unsigned long) sizeof(*add)); return; } @@ -304,10 +301,7 @@ static void iapp_process_add_notify(struct iapp_data *iapp, hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_DEBUG, "Removing STA due to IAPP ADD-notify"); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); - eloop_cancel_timeout(ap_handle_timer, iapp->hapd, sta); - eloop_register_timeout(0, 0, ap_handle_timer, iapp->hapd, sta); - sta->timeout_next = STA_REMOVE; + ap_sta_disconnect(iapp->hapd, sta, NULL, 0); } @@ -332,7 +326,8 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); if (len < 0) { - perror("recvfrom"); + wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s", + strerror(errno)); return; } @@ -356,17 +351,18 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) 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); + wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d", + hdr->version); return; } if (hlen > len) { - printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len); + wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)", + hlen, len); return; } if (hlen < len) { - printf("Ignoring %d extra bytes from IAPP frame\n", - len - hlen); + wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame", + len - hlen); len = hlen; } @@ -382,7 +378,7 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) /* TODO: process */ break; default: - printf("Unknown IAPP command %d\n", hdr->command); + wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command); break; } } @@ -409,7 +405,8 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0); if (iapp->udp_sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } @@ -417,35 +414,38 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) 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)"); + wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } ifindex = ifr.ifr_ifindex; if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFADDR)"); + wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s", + strerror(errno)); 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); + wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)", + 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)"); + wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s", + strerror(errno)); 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); + wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)", + paddr->sin_family); iapp_deinit(iapp); return NULL; } @@ -456,7 +456,8 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) uaddr.sin_port = htons(IAPP_UDP_PORT); if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, sizeof(uaddr)) < 0) { - perror("bind[UDP]"); + wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } @@ -467,14 +468,16 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) mreq.imr_ifindex = 0; if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]"); + wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s", + strerror(errno)); 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]"); + wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); iapp_deinit(iapp); return NULL; } @@ -484,19 +487,20 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) addr.sll_ifindex = ifindex; if (bind(iapp->packet_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind[PACKET]"); + wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s", + strerror(errno)); 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"); + wpa_printf(MSG_INFO, "Could not register read socket for IAPP"); iapp_deinit(iapp); return NULL; } - printf("IEEE 802.11F (IAPP) using interface %s\n", iface); + wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface); /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually @@ -521,7 +525,8 @@ void iapp_deinit(struct iapp_data *iapp) mreq.imr_ifindex = 0; if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]"); + wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s", + strerror(errno)); } eloop_unregister_read_sock(iapp->udp_sock); @@ -533,21 +538,3 @@ void iapp_deinit(struct iapp_data *iapp) } os_free(iapp); } - -int iapp_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, - struct hostapd_bss_config *oldbss) -{ - if (hapd->conf->ieee802_11f != oldbss->ieee802_11f || - os_strcmp(hapd->conf->iapp_iface, oldbss->iapp_iface) != 0) { - iapp_deinit(hapd->iapp); - hapd->iapp = NULL; - - if (hapd->conf->ieee802_11f) { - hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface); - if (hapd->iapp == NULL) - return -1; - } - } - - return 0; -} diff --git a/contrib/hostapd-0.4.9/iapp.h b/contrib/hostapd/src/ap/iapp.h similarity index 72% rename from contrib/hostapd-0.4.9/iapp.h rename to contrib/hostapd/src/ap/iapp.h index d6e8f6570e..c22118342a 100644 --- a/contrib/hostapd-0.4.9/iapp.h +++ b/contrib/hostapd/src/ap/iapp.h @@ -1,3 +1,11 @@ +/* + * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) + * Copyright (c) 2002-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + #ifndef IAPP_H #define IAPP_H diff --git a/contrib/hostapd/src/ap/ieee802_11.c b/contrib/hostapd/src/ap/ieee802_11.c new file mode 100644 index 0000000000..dee3c7a38e --- /dev/null +++ b/contrib/hostapd/src/ap/ieee802_11.c @@ -0,0 +1,2266 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/crypto.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/wpa_ctrl.h" +#include "common/sae.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "p2p/p2p.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 "p2p_hostapd.h" +#include "ap_drv_ops.h" +#include "wnm_ap.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 (hapd->iconf->ieee80211n && hapd->iconf->require_ht) + num++; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) + num++; + 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++; + } + + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) { + count++; + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; + } + + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) { + count++; + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; + } + + 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 (hapd->iconf->ieee80211n && hapd->iconf->require_ht) + num++; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) + num++; + 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++; + } + + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) { + count++; + if (count > 8) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY; + } + + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) { + count++; + if (count > 8) + *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY; + } + + 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; +} + + +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]; + struct os_time now; + int r; + sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); + if (sta->challenge == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + os_get_time(&now); + r = os_random(); + os_memcpy(key, &now.sec, 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)"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + os_free(sta->challenge); + sta->challenge = NULL; + + return 0; +} + + +static void send_auth_reply(struct hostapd_data *hapd, + const u8 *dst, const u8 *bssid, + u16 auth_alg, u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len) +{ + struct ieee80211_mgmt *reply; + u8 *buf; + size_t rlen; + + rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; + buf = os_zalloc(rlen); + if (buf == NULL) + return; + + reply = (struct ieee80211_mgmt *) buf; + reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_AUTH); + os_memcpy(reply->da, dst, ETH_ALEN); + os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(reply->bssid, bssid, ETH_ALEN); + + reply->u.auth.auth_alg = host_to_le16(auth_alg); + reply->u.auth.auth_transaction = host_to_le16(auth_transaction); + reply->u.auth.status_code = host_to_le16(resp); + + if (ies && ies_len) + os_memcpy(reply->u.auth.variable, ies, ies_len); + + wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR + " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", + MAC2STR(dst), auth_alg, auth_transaction, + resp, (unsigned long) ies_len); + if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) + wpa_printf(MSG_INFO, "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 */ + + +#ifdef CONFIG_SAE + +static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct wpabuf *buf; + + if (hapd->conf->ssid.wpa_passphrase == NULL) { + wpa_printf(MSG_DEBUG, "SAE: No password available"); + return NULL; + } + + if (sae_prepare_commit(hapd->own_addr, sta->addr, + (u8 *) hapd->conf->ssid.wpa_passphrase, + os_strlen(hapd->conf->ssid.wpa_passphrase), + sta->sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); + return NULL; + } + + if (sae_process_commit(sta->sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); + return NULL; + } + + buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); + if (buf == NULL) + return NULL; + sae_write_commit(sta->sae, buf, NULL); + + return buf; +} + + +static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN); + if (buf == NULL) + return NULL; + + sae_write_confirm(sta->sae, buf); + + return buf; +} + + +static int use_sae_anti_clogging(struct hostapd_data *hapd) +{ + struct sta_info *sta; + unsigned int open = 0; + + if (hapd->conf->sae_anti_clogging_threshold == 0) + return 1; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!sta->sae) + continue; + if (sta->sae->state != SAE_COMMITTED && + sta->sae->state != SAE_CONFIRMED) + continue; + open++; + if (open >= hapd->conf->sae_anti_clogging_threshold) + return 1; + } + + return 0; +} + + +static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, + const u8 *token, size_t token_len) +{ + u8 mac[SHA256_MAC_LEN]; + + if (token_len != SHA256_MAC_LEN) + return -1; + if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), + addr, ETH_ALEN, mac) < 0 || + os_memcmp(token, mac, SHA256_MAC_LEN) != 0) + return -1; + + return 0; +} + + +static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, + const u8 *addr) +{ + struct wpabuf *buf; + u8 *token; + struct os_reltime now; + + os_get_reltime(&now); + if (!os_reltime_initialized(&hapd->last_sae_token_key_update) || + os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) { + if (random_get_bytes(hapd->sae_token_key, + sizeof(hapd->sae_token_key)) < 0) + return NULL; + wpa_hexdump(MSG_DEBUG, "SAE: Updated token key", + hapd->sae_token_key, sizeof(hapd->sae_token_key)); + hapd->last_sae_token_key_update = now; + } + + buf = wpabuf_alloc(SHA256_MAC_LEN); + if (buf == NULL) + return NULL; + + token = wpabuf_put(buf, SHA256_MAC_LEN); + hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), + addr, ETH_ALEN, token); + + return buf; +} + + +static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, size_t len, + u8 auth_transaction) +{ + u16 resp = WLAN_STATUS_SUCCESS; + struct wpabuf *data = NULL; + + if (!sta->sae) { + if (auth_transaction != 1) + return; + sta->sae = os_zalloc(sizeof(*sta->sae)); + if (sta->sae == NULL) + return; + sta->sae->state = SAE_NOTHING; + } + + if (auth_transaction == 1) { + const u8 *token = NULL; + size_t token_len = 0; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "start SAE authentication (RX commit)"); + resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, + ((const u8 *) mgmt) + len - + mgmt->u.auth.variable, &token, + &token_len, hapd->conf->sae_groups); + if (token && check_sae_token(hapd, sta->addr, token, token_len) + < 0) { + wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " + "incorrect token from " MACSTR, + MAC2STR(sta->addr)); + return; + } + + if (resp == WLAN_STATUS_SUCCESS) { + if (!token && use_sae_anti_clogging(hapd)) { + wpa_printf(MSG_DEBUG, "SAE: Request anti-" + "clogging token from " MACSTR, + MAC2STR(sta->addr)); + data = auth_build_token_req(hapd, sta->addr); + resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; + } else { + data = auth_process_sae_commit(hapd, sta); + if (data == NULL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + else + sta->sae->state = SAE_COMMITTED; + } + } + } else if (auth_transaction == 2) { + if (sta->sae->state != SAE_COMMITTED) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "SAE confirm before commit"); + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + } + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "SAE authentication (RX confirm)"); + if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, + ((u8 *) mgmt) + len - + mgmt->u.auth.variable) < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + } else { + resp = WLAN_STATUS_SUCCESS; + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_SAE; + mlme_authenticate_indication(hapd, sta); + + data = auth_build_sae_confirm(hapd, sta); + if (data == NULL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + else { + sta->sae->state = SAE_ACCEPTED; + sae_clear_temp_data(sta->sae); + } + } + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "unexpected SAE authentication transaction %u", + auth_transaction); + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + } + + sta->auth_alg = WLAN_AUTH_SAE; + + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, + data ? wpabuf_head(data) : (u8 *) "", + data ? wpabuf_len(data) : 0); + wpabuf_free(data); +} +#endif /* CONFIG_SAE */ + + +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; + struct hostapd_sta_wpa_psk_short *psk = NULL; + u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; + size_t resp_ies_len = 0; + char *identity = NULL; + char *radius_cui = NULL; + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", + (unsigned long) len); + return; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->iconf->ignore_auth_probability > 0.0d && + drand48() < hapd->iconf->ignore_auth_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring auth frame from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + 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 && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && + auth_alg == WLAN_AUTH_FT) || +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SAE + (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && + auth_alg == WLAN_AUTH_SAE) || +#endif /* CONFIG_SAE */ + ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && + auth_alg == WLAN_AUTH_SHARED_KEY))) { + wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", + auth_alg); + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } + + if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { + wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)", + auth_transaction); + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + goto fail; + } + + if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { + wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", + 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, + &psk, &identity, &radius_cui); + + if (res == HOSTAPD_ACL_REJECT) { + wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", + 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_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + if (vlan_id > 0) { + if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { + 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); + } + + hostapd_free_psk_list(sta->psk); + if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { + sta->psk = psk; + psk = NULL; + } else { + sta->psk = NULL; + } + + sta->identity = identity; + identity = NULL; + sta->radius_cui = radius_cui; + radius_cui = NULL; + + 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)"); + 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); + 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, NULL); + 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 */ +#ifdef CONFIG_SAE + case WLAN_AUTH_SAE: + handle_auth_sae(hapd, sta, mgmt, len, auth_transaction); + return; +#endif /* CONFIG_SAE */ + } + + fail: + os_free(identity); + os_free(radius_cui); + hostapd_free_psk_list(psk); + + 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; + sta->qosinfo = 0; + if (wmm_ie && hapd->conf->wmm_enabled) { + struct wmm_information_element *wmm; + + 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"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_WMM; + wmm = (struct wmm_information_element *) wmm_ie; + sta->qosinfo = wmm->qos_info; + } + 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 + 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; + } + + sta->supported_rates_len = merge_byte_arrays( + sta->supported_rates, sizeof(sta->supported_rates), + elems->supp_rates, elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); + + return WLAN_STATUS_SUCCESS; +} + + +static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ext_capab_ie, size_t ext_capab_ie_len) +{ +#ifdef CONFIG_INTERWORKING + /* check for QoS Map support */ + if (ext_capab_ie_len >= 5) { + if (ext_capab_ie[4] & 0x01) + sta->qos_map_enabled = 1; + } +#endif /* CONFIG_INTERWORKING */ + + 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; + const u8 *p2p_dev_addr = NULL; + + 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 = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_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(hapd, sta, elems.ht_capabilities, + elems.ht_capabilities_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && + !(sta->flags & WLAN_STA_HT)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "Station does not support " + "mandatory HT PHY - reject association"); + return WLAN_STATUS_ASSOC_DENIED_NO_HT; + } +#endif /* CONFIG_IEEE80211N */ + +#ifdef CONFIG_IEEE80211AC + resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities, + elems.vht_capabilities_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && + !(sta->flags & WLAN_STA_VHT)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "Station does not support " + "mandatory VHT PHY - reject association"); + return WLAN_STATUS_ASSOC_DENIED_NO_VHT; + } +#endif /* CONFIG_IEEE80211AC */ + +#ifdef CONFIG_P2P + if (elems.p2p) { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, + P2P_IE_VENDOR_TYPE); + if (sta->p2p_ie) + p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); + } else { + wpabuf_free(sta->p2p_ie); + sta->p2p_ie = NULL; + } +#endif /* CONFIG_P2P */ + + 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 | WLAN_STA_WPS2); + 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); + if (sta->wps_ie && wps_is_20(sta->wps_ie)) { + wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0"); + sta->flags |= WLAN_STA_WPS2; + } + wpa_ie = NULL; + wpa_ie_len = 0; + if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in " + "(Re)Association Request - reject"); + return WLAN_STATUS_INVALID_IE; + } + } 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, + p2p_dev_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_SAE + if (wpa_auth_uses_sae(sta->wpa_sm) && + sta->auth_alg != WLAN_AUTH_SAE && + !(sta->auth_alg == WLAN_AUTH_FT && + wpa_auth_uses_ft_sae(sta->wpa_sm))) { + wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use " + "SAE AKM after non-SAE auth_alg %u", + MAC2STR(sta->addr), sta->auth_alg); + return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_IEEE80211N + if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && + 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); + +#ifdef CONFIG_P2P + p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len); +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_HS20 + wpabuf_free(sta->hs20_ie); + if (elems.hs20 && elems.hs20_len > 4) { + sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4, + elems.hs20_len - 4); + } else + sta->hs20_ie = NULL; +#endif /* CONFIG_HS20 */ + + 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 (hostapd_drv_send_mlme(hapd, &reply, send_len, 0) < 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 */ + +#ifdef CONFIG_IEEE80211AC + p = hostapd_eid_vht_capabilities(hapd, p); + p = hostapd_eid_vht_operation(hapd, p); +#endif /* CONFIG_IEEE80211AC */ + + p = hostapd_eid_ext_capab(hapd, p); + p = hostapd_eid_bss_max_idle_period(hapd, p); + if (sta->qos_map_enabled) + p = hostapd_eid_qos_map_set(hapd, p); + + if (sta->flags & WLAN_STA_WMM) + p = hostapd_eid_wmm(hapd, p); + +#ifdef CONFIG_WPS + if ((sta->flags & WLAN_STA_WPS) || + ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) { + 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 */ + +#ifdef CONFIG_P2P + if (sta->p2p_ie) { + struct wpabuf *p2p_resp_ie; + enum p2p_status_code status; + switch (status_code) { + case WLAN_STATUS_SUCCESS: + status = P2P_SC_SUCCESS; + break; + case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA: + status = P2P_SC_FAIL_LIMIT_REACHED; + break; + default: + status = P2P_SC_FAIL_INVALID_PARAMS; + break; + } + p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status); + if (p2p_resp_ie) { + os_memcpy(p, wpabuf_head(p2p_resp_ie), + wpabuf_len(p2p_resp_ie)); + p += wpabuf_len(p2p_resp_ie); + wpabuf_free(p2p_resp_ie); + } + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_P2P_MANAGER + if (hapd->conf->p2p & P2P_MANAGE) + p = hostapd_eid_p2p_manage(hapd, p); +#endif /* CONFIG_P2P_MANAGER */ + + send_len += p - reply->u.assoc_resp.variable; + + if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 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))) { + wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)", + reassoc, (unsigned long) len); + return; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (reassoc) { + if (hapd->iconf->ignore_reassoc_probability > 0.0d && + drand48() < hapd->iconf->ignore_reassoc_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring reassoc request from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + } else { + if (hapd->iconf->ignore_assoc_probability > 0.0d && + drand48() < hapd->iconf->ignore_assoc_probability) { + wpa_printf(MSG_INFO, + "TESTING: ignoring assoc request from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + } +#endif /* CONFIG_TESTING_OPTIONS */ + + 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 + */ + sta->flags |= WLAN_STA_ASSOC_REQ_OK; + +#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)) { + wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)", + (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) { + wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated", + MAC2STR(mgmt->sa)); + return; + } + + ap_sta_set_authorized(hapd, sta, 0); + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated"); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + /* Stop Accounting and IEEE 802.1X sessions, but leave the STA + * authenticated. */ + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + hostapd_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)) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short " + "payload (len=%lu)", (unsigned long) len); + return; + } + + wpa_msg(hapd->msg_ctx, 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) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying " + "to deauthenticate, but it is not authenticated", + MAC2STR(mgmt->sa)); + return; + } + + ap_sta_set_authorized(hapd, sta, 0); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_ASSOC_REQ_OK); + 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)) { + wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)", + (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 + +static int hostapd_sa_query_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + const u8 *end; + + 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 0; + } + + ieee802_11_sa_query_action(hapd, mgmt->sa, + mgmt->u.action.u.sa_query_resp.action, + mgmt->u.action.u.sa_query_resp.trans_id); + return 1; +} + + +static int robust_action_frame(u8 category) +{ + return category != WLAN_ACTION_PUBLIC && + category != WLAN_ACTION_HT; +} +#endif /* CONFIG_IEEE80211W */ + + +static int handle_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct sta_info *sta; + sta = ap_get_sta(hapd, mgmt->sa); + + 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 0; + } + + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && + (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action " + "frame (category=%u) from unassociated STA " MACSTR, + MAC2STR(mgmt->sa), mgmt->u.action.category); + return 0; + } + +#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 0; + } +#endif /* CONFIG_IEEE80211W */ + + switch (mgmt->u.action.category) { +#ifdef CONFIG_IEEE80211R + case WLAN_ACTION_FT: + if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, + len - IEEE80211_HDRLEN)) + break; + return 1; +#endif /* CONFIG_IEEE80211R */ + case WLAN_ACTION_WMM: + hostapd_wmm_action(hapd, mgmt, len); + return 1; +#ifdef CONFIG_IEEE80211W + case WLAN_ACTION_SA_QUERY: + return hostapd_sa_query_action(hapd, mgmt, len); +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WNM + case WLAN_ACTION_WNM: + ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); + return 1; +#endif /* CONFIG_WNM */ + case WLAN_ACTION_PUBLIC: + case WLAN_ACTION_PROTECTED_DUAL: + if (hapd->public_action_cb) { + hapd->public_action_cb(hapd->public_action_cb_ctx, + (u8 *) mgmt, len, + hapd->iface->freq); + } + if (hapd->public_action_cb2) { + hapd->public_action_cb2(hapd->public_action_cb2_ctx, + (u8 *) mgmt, len, + hapd->iface->freq); + } + if (hapd->public_action_cb || hapd->public_action_cb2) + return 1; + break; + case WLAN_ACTION_VENDOR_SPECIFIC: + if (hapd->vendor_action_cb) { + if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx, + (u8 *) mgmt, len, + hapd->iface->freq) == 0) + return 1; + } + 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 0; + 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; + + if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) { + wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send " + "Action frame"); + } + os_free(resp); + } + + return 1; +} + + +/** + * 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). + */ +int 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; + int ret = 0; + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_mgmt_frame_handling) { + size_t hex_len = 2 * len + 1; + char *hex = os_malloc(hex_len); + if (hex) { + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-RX %s", hex); + os_free(hex); + } + return 1; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (len < 24) + return 0; + + 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 1; + } + + 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 && +#ifdef CONFIG_P2P + /* Invitation responses can be sent with the peer MAC as BSSID */ + !((hapd->conf->p2p & P2P_GROUP_OWNER) && + stype == WLAN_FC_STYPE_ACTION) && +#endif /* CONFIG_P2P */ + os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address", + MAC2STR(mgmt->bssid)); + return 0; + } + + + if (stype == WLAN_FC_STYPE_PROBE_REQ) { + handle_probe_req(hapd, mgmt, len, fi->ssi_signal); + return 1; + } + + 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 0; + } + + switch (stype) { + case WLAN_FC_STYPE_AUTH: + wpa_printf(MSG_DEBUG, "mgmt::auth"); + handle_auth(hapd, mgmt, len); + ret = 1; + break; + case WLAN_FC_STYPE_ASSOC_REQ: + wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); + handle_assoc(hapd, mgmt, len, 0); + ret = 1; + break; + case WLAN_FC_STYPE_REASSOC_REQ: + wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); + handle_assoc(hapd, mgmt, len, 1); + ret = 1; + break; + case WLAN_FC_STYPE_DISASSOC: + wpa_printf(MSG_DEBUG, "mgmt::disassoc"); + handle_disassoc(hapd, mgmt, len); + ret = 1; + break; + case WLAN_FC_STYPE_DEAUTH: + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth"); + handle_deauth(hapd, mgmt, len); + ret = 1; + break; + case WLAN_FC_STYPE_ACTION: + wpa_printf(MSG_DEBUG, "mgmt::action"); + ret = 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; + } + + return ret; +} + + +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)) { + wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)", + (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) { + wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", + 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 hostapd_set_wds_encryption(struct hostapd_data *hapd, + struct sta_info *sta, + char *ifname_wds) +{ + int i; + struct hostapd_ssid *ssid = sta->ssid; + + if (hapd->conf->ieee802_1x || hapd->conf->wpa) + return; + + for (i = 0; i < 4; i++) { + if (ssid->wep.key[i] && + hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i, + i == ssid->wep.idx, NULL, 0, + ssid->wep.key[i], ssid->wep.len[i])) { + wpa_printf(MSG_WARNING, + "Could not set WEP keys for WDS interface; %s", + ifname_wds); + break; + } + } +} + + +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; + struct ieee80211_vht_capabilities vht_cap; + + if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : + sizeof(mgmt->u.assoc_resp))) { + wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)", + reassoc, (unsigned long) len); + return; + } + + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found", + MAC2STR(mgmt->da)); + return; + } + + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "did not acknowledge association response"); + sta->flags &= ~WLAN_STA_ASSOC_REQ_OK; + return; + } + + if (reassoc) + status = le_to_host16(mgmt->u.reassoc_resp.status_code); + else + status = le_to_host16(mgmt->u.assoc_resp.status_code); + + 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; + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) || + sta->auth_alg == WLAN_AUTH_FT) { + /* + * Open, static WEP, or FT protocol; no separate authorization + * step. + */ + ap_sta_set_authorized(hapd, sta, 1); + } + + 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. + */ + hostapd_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 */ +#ifdef CONFIG_IEEE80211AC + if (sta->flags & WLAN_STA_VHT) + hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap); +#endif /* CONFIG_IEEE80211AC */ + + if (hostapd_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, + sta->flags & WLAN_STA_VHT ? &vht_cap : NULL, + sta->flags, sta->qosinfo)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "Could not add STA to kernel driver"); + + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_DISASSOC_AP_BUSY); + + goto fail; + } + + if (sta->flags & WLAN_STA_WDS) { + int ret; + char ifname_wds[IFNAMSIZ + 1]; + + ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, + sta->aid, 1); + if (!ret) + hostapd_set_wds_encryption(hapd, sta, ifname_wds); + } + + 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; + } + + hostapd_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; + } +} + + +static void handle_deauth_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + struct sta_info *sta; + if (mgmt->da[0] & 0x01) + return; + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); + return; + } + if (ok) + wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth", + MAC2STR(sta->addr)); + else + wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " + "deauth", MAC2STR(sta->addr)); + + ap_sta_deauth_cb(hapd, sta); +} + + +static void handle_disassoc_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + struct sta_info *sta; + if (mgmt->da[0] & 0x01) + return; + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); + return; + } + if (ok) + wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc", + MAC2STR(sta->addr)); + else + wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge " + "disassoc", MAC2STR(sta->addr)); + + ap_sta_disassoc_cb(hapd, sta); +} + + +/** + * 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; + +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_mgmt_frame_handling) { + wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d", + stype, ok); + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + 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_EXCESSIVE, "mgmt::proberesp cb"); + break; + case WLAN_FC_STYPE_DEAUTH: + wpa_printf(MSG_DEBUG, "mgmt::deauth cb"); + handle_deauth_cb(hapd, mgmt, len, ok); + break; + case WLAN_FC_STYPE_DISASSOC: + wpa_printf(MSG_DEBUG, "mgmt::disassoc cb"); + handle_disassoc_cb(hapd, mgmt, len, ok); + break; + case WLAN_FC_STYPE_ACTION: + wpa_printf(MSG_DEBUG, "mgmt::action cb"); + break; + default: + wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", 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 || !(sta->flags & WLAN_STA_ASSOC)) + 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 hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t len, int ack) +{ + struct sta_info *sta; + struct hostapd_iface *iface = hapd->iface; + + sta = ap_get_sta(hapd, dst); + 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, dst); + if (sta) + break; + } + } + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA " + MACSTR " that is not currently associated", + MAC2STR(dst)); + return; + } + + ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack); +} + + +void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr) +{ + 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)) + return; + + wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending " + "activity poll", MAC2STR(sta->addr)); + sta->flags &= ~WLAN_STA_PENDING_POLL; +} + + +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 (!hapd->conf->wds_sta) + return; + + if (wds && !(sta->flags & WLAN_STA_WDS)) { + int ret; + char ifname_wds[IFNAMSIZ + 1]; + + wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for " + "STA " MACSTR " (aid %u)", + MAC2STR(sta->addr), sta->aid); + sta->flags |= WLAN_STA_WDS; + ret = hostapd_set_wds_sta(hapd, ifname_wds, + sta->addr, sta->aid, 1); + if (!ret) + hostapd_set_wds_encryption(hapd, sta, + ifname_wds); + } + return; + } + + wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " + MACSTR, MAC2STR(src)); + if (src[0] & 0x01) { + /* Broadcast bit set in SA?! Ignore the frame silently. */ + return; + } + + if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) { + wpa_printf(MSG_DEBUG, "Association Response to the STA has " + "already been sent, but no TX status yet known - " + "ignore Class 3 frame issue with " MACSTR, + MAC2STR(src)); + return; + } + + if (sta && (sta->flags & WLAN_STA_AUTH)) + hostapd_drv_sta_disassoc( + hapd, src, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + else + hostapd_drv_sta_deauth( + hapd, src, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); +} + + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/src/ap/ieee802_11.h b/contrib/hostapd/src/ap/ieee802_11.h new file mode 100644 index 0000000000..5edeb71cb9 --- /dev/null +++ b/contrib/hostapd/src/ap/ieee802_11.h @@ -0,0 +1,85 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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; + +int 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_ext_capab(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid); +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); +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vht_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); +void hostapd_get_vht_capab(struct hostapd_data *hapd, + struct ieee80211_vht_capabilities *vht_cap, + struct ieee80211_vht_capabilities *neg_vht_cap); +u16 copy_sta_ht_capab(struct hostapd_data *hapd, 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); +u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_capab, size_t vht_capab_len); +void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, + const u8 *buf, size_t len, int ack); +void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t len, int ack); +void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, + int wds); +u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid); +void ieee802_11_sa_query_action(struct hostapd_data *hapd, + const u8 *sa, const u8 action_type, + const u8 *trans_id); +u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid); +int hostapd_update_time_adv(struct hostapd_data *hapd); +void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr); +u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid); + +#endif /* IEEE802_11_H */ diff --git a/contrib/hostapd/hostapd/ieee802_11_auth.c b/contrib/hostapd/src/ap/ieee802_11_auth.c similarity index 61% rename from contrib/hostapd/hostapd/ieee802_11_auth.c rename to contrib/hostapd/src/ap/ieee802_11_auth.c index 9aba1fe854..56c3ce0313 100644 --- a/contrib/hostapd/hostapd/ieee802_11_auth.c +++ b/contrib/hostapd/src/ap/ieee802_11_auth.c @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.11 authentication (ACL) - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * Access control list for IEEE 802.11 authentication can uses statically * configured ACL from configuration files or an external RADIUS server. @@ -17,34 +11,39 @@ * authentication frame processing. */ -#include "includes.h" - -#ifndef CONFIG_NATIVE_WINDOWS +#include "utils/includes.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/sha1.h" +#include "radius/radius.h" +#include "radius/radius_client.h" #include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" #include "ieee802_11.h" +#include "ieee802_1x.h" #include "ieee802_11_auth.h" -#include "radius/radius.h" -#include "radius/radius_client.h" -#include "eloop.h" -#include "driver.h" #define RADIUS_ACL_TIMEOUT 30 struct hostapd_cached_radius_acl { - time_t timestamp; + struct os_reltime 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_sta_wpa_psk_short *psk; + char *identity; + char *radius_cui; }; struct hostapd_acl_query_data { - time_t timestamp; + struct os_reltime timestamp; u8 radius_id; macaddr addr; u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ @@ -53,6 +52,16 @@ struct hostapd_acl_query_data { }; +#ifndef CONFIG_NO_RADIUS +static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e) +{ + os_free(e->identity); + os_free(e->radius_cui); + hostapd_free_psk_list(e->psk); + os_free(e); +} + + static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) { struct hostapd_cached_radius_acl *prev; @@ -60,42 +69,79 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) while (acl_cache) { prev = acl_cache; acl_cache = acl_cache->next; - os_free(prev); + hostapd_acl_cache_free_entry(prev); + } +} + + +static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk, + struct hostapd_sta_wpa_psk_short *src) +{ + struct hostapd_sta_wpa_psk_short **copy_to; + struct hostapd_sta_wpa_psk_short *copy_from; + + /* Copy PSK linked list */ + copy_to = psk; + copy_from = src; + while (copy_from && copy_to) { + *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); + if (*copy_to == NULL) + break; + os_memcpy(*copy_to, copy_from, + sizeof(struct hostapd_sta_wpa_psk_short)); + copy_from = copy_from->next; + copy_to = &((*copy_to)->next); } + if (copy_to) + *copy_to = NULL; } static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, u32 *session_timeout, - u32 *acct_interim_interval, int *vlan_id) + u32 *acct_interim_interval, int *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) { struct hostapd_cached_radius_acl *entry; - time_t now; + struct os_reltime now; - time(&now); - entry = hapd->acl_cache; + os_get_reltime(&now); - 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; - } + for (entry = hapd->acl_cache; entry; entry = entry->next) { + if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0) + continue; - entry = entry->next; + if (os_reltime_expired(&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; + copy_psk_list(psk, entry->psk); + if (identity) { + if (entry->identity) + *identity = os_strdup(entry->identity); + else + *identity = NULL; + } + if (radius_cui) { + if (entry->radius_cui) + *radius_cui = os_strdup(entry->radius_cui); + else + *radius_cui = NULL; + } + return entry->accepted; } return -1; } +#endif /* CONFIG_NO_RADIUS */ static void hostapd_acl_query_free(struct hostapd_acl_query_data *query) @@ -107,6 +153,7 @@ static void hostapd_acl_query_free(struct hostapd_acl_query_data *query) } +#ifndef CONFIG_NO_RADIUS static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, struct hostapd_acl_query_data *query) { @@ -135,37 +182,9 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, 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"); + if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, + NULL, msg) < 0) 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)); @@ -175,12 +194,6 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, 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))) { @@ -188,14 +201,15 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, goto fail; } - radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr); + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0) + goto fail; return 0; fail: radius_msg_free(msg); - os_free(msg); return -1; } +#endif /* CONFIG_NO_RADIUS */ /** @@ -207,11 +221,19 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, * @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 + * @psk: Linked list buffer for returning WPA PSK + * @identity: Buffer for returning identity (from RADIUS) + * @radius_cui: Buffer for returning CUI (from RADIUS) * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + * + * The caller is responsible for freeing the returned *identity and *radius_cui + * values with os_free(). */ 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) + u32 *acct_interim_interval, int *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) { if (session_timeout) *session_timeout = 0; @@ -219,6 +241,12 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, *acct_interim_interval = 0; if (vlan_id) *vlan_id = 0; + if (psk) + *psk = NULL; + if (identity) + *identity = NULL; + if (radius_cui) + *radius_cui = NULL; if (hostapd_maclist_found(hapd->conf->accept_mac, hapd->conf->num_accept_mac, addr, vlan_id)) @@ -234,12 +262,16 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, 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); + vlan_id, psk, + identity, radius_cui); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) return res; @@ -251,6 +283,14 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) { /* pending query in RADIUS retransmit queue; * do not generate a new one */ + if (identity) { + os_free(*identity); + *identity = NULL; + } + if (radius_cui) { + os_free(*radius_cui); + *radius_cui = NULL; + } return HOSTAPD_ACL_PENDING; } query = query->next; @@ -265,7 +305,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, wpa_printf(MSG_ERROR, "malloc for query data failed"); return HOSTAPD_ACL_REJECT; } - time(&query->timestamp); + os_get_reltime(&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 " @@ -289,13 +329,16 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, /* 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; } -static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) +#ifndef CONFIG_NO_RADIUS +static void hostapd_acl_expire_cache(struct hostapd_data *hapd, + struct os_reltime *now) { struct hostapd_cached_radius_acl *prev, *entry, *tmp; @@ -303,19 +346,18 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) entry = hapd->acl_cache; while (entry) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + if (os_reltime_expired(now, &entry->timestamp, + RADIUS_ACL_TIMEOUT)) { wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR " has expired.", MAC2STR(entry->addr)); if (prev) prev->next = entry->next; else hapd->acl_cache = entry->next; -#ifdef CONFIG_DRIVER_RADIUS_ACL - hostapd_set_radius_acl_expire(hapd, entry->addr); -#endif /* CONFIG_DRIVER_RADIUS_ACL */ + hostapd_drv_set_radius_acl_expire(hapd, entry->addr); tmp = entry; entry = entry->next; - os_free(tmp); + hostapd_acl_cache_free_entry(tmp); continue; } @@ -325,7 +367,8 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) } -static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) +static void hostapd_acl_expire_queries(struct hostapd_data *hapd, + struct os_reltime *now) { struct hostapd_acl_query_data *prev, *entry, *tmp; @@ -333,7 +376,8 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) entry = hapd->acl_queries; while (entry) { - if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + if (os_reltime_expired(now, &entry->timestamp, + RADIUS_ACL_TIMEOUT)) { wpa_printf(MSG_DEBUG, "ACL query for " MACSTR " has expired.", MAC2STR(entry->addr)); if (prev) @@ -361,16 +405,64 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) { struct hostapd_data *hapd = eloop_ctx; - time_t now; + struct os_reltime now; - time(&now); - hostapd_acl_expire_cache(hapd, now); - hostapd_acl_expire_queries(hapd, now); + os_get_reltime(&now); + hostapd_acl_expire_cache(hapd, &now); + hostapd_acl_expire_queries(hapd, &now); eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); } +static void decode_tunnel_passwords(struct hostapd_data *hapd, + const u8 *shared_secret, + size_t shared_secret_len, + struct radius_msg *msg, + struct radius_msg *req, + struct hostapd_cached_radius_acl *cache) +{ + int passphraselen; + char *passphrase, *strpassphrase; + size_t i; + struct hostapd_sta_wpa_psk_short *psk; + + /* + * Decode all tunnel passwords as PSK and save them into a linked list. + */ + for (i = 0; ; i++) { + passphrase = radius_msg_get_tunnel_password( + msg, &passphraselen, shared_secret, shared_secret_len, + req, i); + /* + * Passphrase is NULL iff there is no i-th Tunnel-Password + * attribute in msg. + */ + if (passphrase == NULL) + break; + /* + * passphrase does not contain the NULL termination. + * Add it here as pbkdf2_sha1() requires it. + */ + strpassphrase = os_zalloc(passphraselen + 1); + psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); + if (strpassphrase && psk) { + os_memcpy(strpassphrase, passphrase, passphraselen); + pbkdf2_sha1(strpassphrase, + hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len, 4096, + psk->psk, PMK_LEN); + psk->next = cache->psk; + cache->psk = psk; + psk = NULL; + } + os_free(strpassphrase); + os_free(psk); + os_free(passphrase); + } +} + + /** * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages * @msg: RADIUS response message @@ -389,11 +481,12 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, 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 == msg->hdr->identifier) + if (query->radius_id == hdr->identifier) break; prev = query; query = query->next; @@ -410,10 +503,10 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, return RADIUS_RX_INVALID_AUTHENTICATOR; } - if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && - msg->hdr->code != RADIUS_CODE_ACCESS_REJECT) { + 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", msg->hdr->code); + "query", hdr->code); return RADIUS_RX_UNKNOWN; } @@ -423,9 +516,12 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); goto done; } - time(&cache->timestamp); + os_get_reltime(&cache->timestamp); os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); - if (msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { + if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { + u8 *buf; + size_t len; + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, &cache->session_timeout) == 0) cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT; @@ -444,20 +540,42 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, } cache->vlan_id = radius_msg_get_vlanid(msg); + + decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, + msg, req, cache); + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, + &buf, &len, NULL) == 0) { + cache->identity = os_zalloc(len + 1); + if (cache->identity) + os_memcpy(cache->identity, buf, len); + } + if (radius_msg_get_attr_ptr( + msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) == 0) { + cache->radius_cui = os_zalloc(len + 1); + if (cache->radius_cui) + os_memcpy(cache->radius_cui, buf, len); + } + + if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && + !cache->psk) + cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; cache->next = hapd->acl_cache; hapd->acl_cache = cache; #ifdef CONFIG_DRIVER_RADIUS_ACL - hostapd_set_radius_acl_auth(hapd, query->addr, cache->accepted, - cache->session_timeout); + hostapd_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, - WLAN_FC_STYPE_AUTH, NULL); + ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL); +#endif /* NEED_AP_MLME */ #endif /* CONFIG_DRIVER_RADIUS_ACL */ done: @@ -470,6 +588,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, return RADIUS_RX_PROCESSED; } +#endif /* CONFIG_NO_RADIUS */ /** @@ -479,11 +598,13 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, */ 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; } @@ -497,9 +618,11 @@ 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) { @@ -510,14 +633,11 @@ void hostapd_acl_deinit(struct hostapd_data *hapd) } -int hostapd_acl_reconfig(struct hostapd_data *hapd, - struct hostapd_config *oldconf) +void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) { - if (!hapd->radius_client_reconfigured) - return 0; - - hostapd_acl_deinit(hapd); - return hostapd_acl_init(hapd); + while (psk) { + struct hostapd_sta_wpa_psk_short *prev = psk; + psk = psk->next; + os_free(prev); + } } - -#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/hostapd/ieee802_11_auth.h b/contrib/hostapd/src/ap/ieee802_11_auth.h similarity index 53% rename from contrib/hostapd/hostapd/ieee802_11_auth.h rename to contrib/hostapd/src/ap/ieee802_11_auth.h index 0eed825e29..2bc1065a22 100644 --- a/contrib/hostapd/hostapd/ieee802_11_auth.h +++ b/contrib/hostapd/src/ap/ieee802_11_auth.h @@ -2,14 +2,8 @@ * hostapd / IEEE 802.11 authentication (ACL) * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_11_AUTH_H @@ -24,10 +18,11 @@ enum { 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); + u32 *acct_interim_interval, int *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui); int hostapd_acl_init(struct hostapd_data *hapd); void hostapd_acl_deinit(struct hostapd_data *hapd); -int hostapd_acl_reconfig(struct hostapd_data *hapd, - struct hostapd_config *oldconf); +void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); #endif /* IEEE802_11_AUTH_H */ diff --git a/contrib/hostapd/src/ap/ieee802_11_ht.c b/contrib/hostapd/src/ap/ieee802_11_ht.c new file mode 100644 index 0000000000..31dc47edd1 --- /dev/null +++ b/contrib/hostapd/src/ap/ieee802_11_ht.c @@ -0,0 +1,281 @@ +/* + * hostapd / IEEE 802.11n HT + * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2007-2008, Intel Corporation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.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 || + hapd->conf->disable_11n) + 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); + + if (hapd->iconf->obss_interval) { + struct ieee80211_obss_scan_parameters *scan_params; + + *pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS; + *pos++ = sizeof(*scan_params); + + scan_params = (struct ieee80211_obss_scan_parameters *) pos; + os_memset(scan_params, 0, sizeof(*scan_params)); + scan_params->width_trigger_scan_interval = + host_to_le16(hapd->iconf->obss_interval); + + /* TODO: Fill in more parameters (supplicant ignores them) */ + + pos += sizeof(*scan_params); + } + + 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 || hapd->conf->disable_11n) + 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 +*/ +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++; + } + + new_op_mode = 0; + if (iface->num_sta_no_ht) + new_op_mode = OP_MODE_MIXED; + else if (iface->conf->secondary_channel && 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 hostapd_data *hapd, struct sta_info *sta, + const u8 *ht_capab, size_t ht_capab_len) +{ + /* Disable HT caps for STAs associated to no-HT BSSes. */ + if (!ht_capab || + ht_capab_len < sizeof(struct ieee80211_ht_capabilities) || + hapd->conf->disable_11n) { + 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); + + /* + * Mask out HT features we don't support, but don't overwrite + * non-symmetric features like STBC and SMPS. Just because + * we're not in dynamic SMPS mode the STA might still be. + */ + cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK | + HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK); + + /* + * 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/contrib/hostapd/src/ap/ieee802_11_shared.c b/contrib/hostapd/src/ap/ieee802_11_shared.c new file mode 100644 index 0000000000..eadaa4d187 --- /dev/null +++ b/contrib/hostapd/src/ap/ieee802_11_shared.c @@ -0,0 +1,494 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "ieee802_11.h" + + +#ifdef CONFIG_IEEE80211W + +u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid) +{ + u8 *pos = eid; + u32 timeout, tu; + struct os_reltime now, passed; + + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; + os_get_reltime(&now); + os_reltime_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; +} + + +/* 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 (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0) + wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed"); +} + + +static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd, + const u8 *sa, const u8 *trans_id) +{ + struct sta_info *sta; + struct ieee80211_mgmt resp; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " + MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + sta = ap_get_sta(hapd, 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(sa)); + return; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " + MACSTR, MAC2STR(sa)); + + os_memset(&resp, 0, sizeof(resp)); + resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(resp.da, 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, 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 (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0) + wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed"); +} + + +void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa, + const u8 action_type, const u8 *trans_id) +{ + struct sta_info *sta; + int i; + + if (action_type == WLAN_SA_QUERY_REQUEST) { + ieee802_11_send_sa_query_resp(hapd, sa, trans_id); + return; + } + + if (action_type != WLAN_SA_QUERY_RESPONSE) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " + "Action %d", action_type); + return; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " + MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + /* MLME-SAQuery.confirm */ + + sta = ap_get_sta(hapd, 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, + trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0) + break; + } + + if (i >= sta->sa_query_count) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " + "transaction identifier found"); + return; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Reply to pending SA Query received"); + ap_sta_stop_sa_query(hapd, sta); +} + +#endif /* CONFIG_IEEE80211W */ + + +static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) +{ + *pos = 0x00; + + switch (idx) { + case 0: /* Bits 0-7 */ + break; + case 1: /* Bits 8-15 */ + break; + case 2: /* Bits 16-23 */ + if (hapd->conf->wnm_sleep_mode) + *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ + if (hapd->conf->bss_transition) + *pos |= 0x08; /* Bit 19 - BSS Transition */ + break; + case 3: /* Bits 24-31 */ +#ifdef CONFIG_WNM + *pos |= 0x02; /* Bit 25 - SSID List */ +#endif /* CONFIG_WNM */ + if (hapd->conf->time_advertisement == 2) + *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ + if (hapd->conf->interworking) + *pos |= 0x80; /* Bit 31 - Interworking */ + break; + case 4: /* Bits 32-39 */ + if (hapd->conf->qos_map_set_len) + *pos |= 0x01; /* Bit 32 - QoS Map */ + if (hapd->conf->tdls & TDLS_PROHIBIT) + *pos |= 0x40; /* Bit 38 - TDLS Prohibited */ + if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) { + /* Bit 39 - TDLS Channel Switching Prohibited */ + *pos |= 0x80; + } + break; + case 5: /* Bits 40-47 */ + break; + case 6: /* Bits 48-55 */ + if (hapd->conf->ssid.utf8_ssid) + *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ + break; + } +} + + +u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 len = 0, i; + + if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH)) + len = 5; + if (len < 4 && hapd->conf->interworking) + len = 4; + if (len < 3 && hapd->conf->wnm_sleep_mode) + len = 3; + if (len < 7 && hapd->conf->ssid.utf8_ssid) + len = 7; +#ifdef CONFIG_WNM + if (len < 4) + len = 4; +#endif /* CONFIG_WNM */ + if (len < hapd->iface->extended_capa_len) + len = hapd->iface->extended_capa_len; + if (len == 0) + return eid; + + *pos++ = WLAN_EID_EXT_CAPAB; + *pos++ = len; + for (i = 0; i < len; i++, pos++) { + hostapd_ext_capab_byte(hapd, pos, i); + + if (i < hapd->iface->extended_capa_len) { + *pos &= ~hapd->iface->extended_capa_mask[i]; + *pos |= hapd->iface->extended_capa[i]; + } + } + + while (len > 0 && eid[1 + len] == 0) { + len--; + eid[1] = len; + } + if (len == 0) + return eid; + + return eid + 2 + len; +} + + +u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + u8 len = hapd->conf->qos_map_set_len; + + if (!len) + return eid; + + *pos++ = WLAN_EID_QOS_MAP_SET; + *pos++ = len; + os_memcpy(pos, hapd->conf->qos_map_set, len); + pos += len; + + return pos; +} + + +u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + u8 *len; + + if (!hapd->conf->interworking) + return eid; + + *pos++ = WLAN_EID_INTERWORKING; + len = pos++; + + *pos = hapd->conf->access_network_type; + if (hapd->conf->internet) + *pos |= INTERWORKING_ANO_INTERNET; + if (hapd->conf->asra) + *pos |= INTERWORKING_ANO_ASRA; + if (hapd->conf->esr) + *pos |= INTERWORKING_ANO_ESR; + if (hapd->conf->uesa) + *pos |= INTERWORKING_ANO_UESA; + pos++; + + if (hapd->conf->venue_info_set) { + *pos++ = hapd->conf->venue_group; + *pos++ = hapd->conf->venue_type; + } + + if (!is_zero_ether_addr(hapd->conf->hessid)) { + os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); + pos += ETH_ALEN; + } + + *len = pos - len - 1; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + + /* TODO: Separate configuration for ANQP? */ + if (!hapd->conf->interworking) + return eid; + + *pos++ = WLAN_EID_ADV_PROTO; + *pos++ = 2; + *pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */ + *pos++ = ACCESS_NETWORK_QUERY_PROTOCOL; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; +#ifdef CONFIG_INTERWORKING + u8 *len; + unsigned int i, count; + + if (!hapd->conf->interworking || + hapd->conf->roaming_consortium == NULL || + hapd->conf->roaming_consortium_count == 0) + return eid; + + *pos++ = WLAN_EID_ROAMING_CONSORTIUM; + len = pos++; + + /* Number of ANQP OIs (in addition to the max 3 listed here) */ + if (hapd->conf->roaming_consortium_count > 3 + 255) + *pos++ = 255; + else if (hapd->conf->roaming_consortium_count > 3) + *pos++ = hapd->conf->roaming_consortium_count - 3; + else + *pos++ = 0; + + /* OU #1 and #2 Lengths */ + *pos = hapd->conf->roaming_consortium[0].len; + if (hapd->conf->roaming_consortium_count > 1) + *pos |= hapd->conf->roaming_consortium[1].len << 4; + pos++; + + if (hapd->conf->roaming_consortium_count > 3) + count = 3; + else + count = hapd->conf->roaming_consortium_count; + + for (i = 0; i < count; i++) { + os_memcpy(pos, hapd->conf->roaming_consortium[i].oi, + hapd->conf->roaming_consortium[i].len); + pos += hapd->conf->roaming_consortium[i].len; + } + + *len = pos - len - 1; +#endif /* CONFIG_INTERWORKING */ + + return pos; +} + + +u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid) +{ + if (hapd->conf->time_advertisement != 2) + return eid; + + if (hapd->time_adv == NULL && + hostapd_update_time_adv(hapd) < 0) + return eid; + + if (hapd->time_adv == NULL) + return eid; + + os_memcpy(eid, wpabuf_head(hapd->time_adv), + wpabuf_len(hapd->time_adv)); + eid += wpabuf_len(hapd->time_adv); + + return eid; +} + + +u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid) +{ + size_t len; + + if (hapd->conf->time_advertisement != 2) + return eid; + + len = os_strlen(hapd->conf->time_zone); + + *eid++ = WLAN_EID_TIME_ZONE; + *eid++ = len; + os_memcpy(eid, hapd->conf->time_zone, len); + eid += len; + + return eid; +} + + +int hostapd_update_time_adv(struct hostapd_data *hapd) +{ + const int elen = 2 + 1 + 10 + 5 + 1; + struct os_time t; + struct os_tm tm; + u8 *pos; + + if (hapd->conf->time_advertisement != 2) + return 0; + + if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0) + return -1; + + if (!hapd->time_adv) { + hapd->time_adv = wpabuf_alloc(elen); + if (hapd->time_adv == NULL) + return -1; + pos = wpabuf_put(hapd->time_adv, elen); + } else + pos = wpabuf_mhead_u8(hapd->time_adv); + + *pos++ = WLAN_EID_TIME_ADVERTISEMENT; + *pos++ = 1 + 10 + 5 + 1; + + *pos++ = 2; /* UTC time at which the TSF timer is 0 */ + + /* Time Value at TSF 0 */ + /* FIX: need to calculate this based on the current TSF value */ + WPA_PUT_LE16(pos, tm.year); /* Year */ + pos += 2; + *pos++ = tm.month; /* Month */ + *pos++ = tm.day; /* Day of month */ + *pos++ = tm.hour; /* Hours */ + *pos++ = tm.min; /* Minutes */ + *pos++ = tm.sec; /* Seconds */ + WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */ + pos += 2; + *pos++ = 0; /* Reserved */ + + /* Time Error */ + /* TODO: fill in an estimate on the error */ + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + *pos++ = 0; + + *pos++ = hapd->time_update_counter++; + + return 0; +} + + +u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + +#ifdef CONFIG_WNM + if (hapd->conf->ap_max_inactivity > 0) { + unsigned int val; + *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; + *pos++ = 3; + val = hapd->conf->ap_max_inactivity; + if (val > 68000) + val = 68000; + val *= 1000; + val /= 1024; + if (val == 0) + val = 1; + if (val > 65535) + val = 65535; + WPA_PUT_LE16(pos, val); + pos += 2; + *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ + } +#endif /* CONFIG_WNM */ + + return pos; +} diff --git a/contrib/hostapd/src/ap/ieee802_11_vht.c b/contrib/hostapd/src/ap/ieee802_11_vht.c new file mode 100644 index 0000000000..f2ab182db3 --- /dev/null +++ b/contrib/hostapd/src/ap/ieee802_11_vht.c @@ -0,0 +1,171 @@ +/* + * hostapd / IEEE 802.11ac VHT + * Copyright (c) 2002-2009, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it 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 "hostapd.h" +#include "ap_config.h" +#include "sta_info.h" +#include "beacon.h" +#include "ieee802_11.h" + + +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_vht_capabilities *cap; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode || + hapd->conf->disable_11ac) + return eid; + + *pos++ = WLAN_EID_VHT_CAP; + *pos++ = sizeof(*cap); + + cap = (struct ieee80211_vht_capabilities *) pos; + os_memset(cap, 0, sizeof(*cap)); + cap->vht_capabilities_info = host_to_le32( + hapd->iface->conf->vht_capab); + + /* Supported MCS set comes from hw */ + os_memcpy(&cap->vht_supported_mcs_set, + hapd->iface->current_mode->vht_mcs_set, 8); + + pos += sizeof(*cap); + + return pos; +} + + +u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_vht_operation *oper; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac) + return eid; + + *pos++ = WLAN_EID_VHT_OPERATION; + *pos++ = sizeof(*oper); + + oper = (struct ieee80211_vht_operation *) pos; + os_memset(oper, 0, sizeof(*oper)); + + /* + * center freq = 5 GHz + (5 * index) + * So index 42 gives center freq 5.210 GHz + * which is channel 42 in 5G band + */ + oper->vht_op_info_chan_center_freq_seg0_idx = + hapd->iconf->vht_oper_centr_freq_seg0_idx; + oper->vht_op_info_chan_center_freq_seg1_idx = + hapd->iconf->vht_oper_centr_freq_seg1_idx; + + oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; + + /* VHT Basic MCS set comes from hw */ + /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ + oper->vht_basic_mcs_set = host_to_le16(0xfffc); + pos += sizeof(*oper); + + return pos; +} + + +u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_capab, size_t vht_capab_len) +{ + /* Disable VHT caps for STAs associated to no-VHT BSSes. */ + if (!vht_capab || + vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || + hapd->conf->disable_11ac) { + sta->flags &= ~WLAN_STA_VHT; + os_free(sta->vht_capabilities); + sta->vht_capabilities = NULL; + return WLAN_STATUS_SUCCESS; + } + + if (sta->vht_capabilities == NULL) { + sta->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (sta->vht_capabilities == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_VHT; + os_memcpy(sta->vht_capabilities, vht_capab, + sizeof(struct ieee80211_vht_capabilities)); + + return WLAN_STATUS_SUCCESS; +} + +void hostapd_get_vht_capab(struct hostapd_data *hapd, + struct ieee80211_vht_capabilities *vht_cap, + struct ieee80211_vht_capabilities *neg_vht_cap) +{ + u32 cap, own_cap, sym_caps; + + if (vht_cap == NULL) + return; + os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap)); + + cap = le_to_host32(neg_vht_cap->vht_capabilities_info); + own_cap = hapd->iconf->vht_capab; + + /* mask out symmetric VHT capabilities we don't support */ + sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160; + cap &= ~sym_caps | (own_cap & sym_caps); + + /* mask out beamformer/beamformee caps if not supported */ + if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE | + VHT_CAP_BEAMFORMEE_STS_MAX); + + if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE | + VHT_CAP_SOUNDING_DIMENSION_MAX); + + if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE)) + cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE; + + if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE)) + cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE; + + /* mask channel widths we don't support */ + switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) { + case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: + break; + case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: + if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) { + cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + } + break; + default: + cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK; + break; + } + + if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK)) + cap &= ~VHT_CAP_SHORT_GI_160; + + /* + * 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 (!(own_cap & VHT_CAP_RXSTBC_MASK)) + cap &= ~VHT_CAP_TXSTBC; + if (!(own_cap & VHT_CAP_TXSTBC)) + cap &= ~VHT_CAP_RXSTBC_MASK; + + neg_vht_cap->vht_capabilities_info = host_to_le32(cap); +} diff --git a/contrib/hostapd/hostapd/ieee802_1x.c b/contrib/hostapd/src/ap/ieee802_1x.c similarity index 70% rename from contrib/hostapd/hostapd/ieee802_1x.c rename to contrib/hostapd/src/ap/ieee802_1x.c index 7fd8028057..49b30e41c5 100644 --- a/contrib/hostapd/hostapd/ieee802_1x.c +++ b/contrib/hostapd/src/ap/ieee802_1x.c @@ -1,36 +1,36 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" -#include "hostapd.h" -#include "ieee802_1x.h" -#include "accounting.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/md5.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" #include "radius/radius.h" #include "radius/radius_client.h" -#include "eapol_sm.h" -#include "md5.h" -#include "rc4.h" -#include "eloop.h" -#include "sta_info.h" -#include "wpa.h" -#include "preauth.h" -#include "pmksa_cache.h" -#include "driver.h" -#include "hw_features.h" #include "eap_server/eap.h" -#include "ieee802_11_defs.h" +#include "eap_common/eap_wsc_common.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" +#include "p2p/p2p.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 "ap_drv_ops.h" +#include "wps_hostapd.h" +#include "ieee802_1x.h" static void ieee802_1x_finished(struct hostapd_data *hapd, @@ -67,7 +67,9 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); } else { - hostapd_send_eapol(hapd, sta->addr, buf, len, encrypt); + hostapd_drv_hapd_send_eapol( + hapd, sta->addr, buf, len, + encrypt, hostapd_sta_flags_to_drv(sta->flags)); } os_free(buf); @@ -83,26 +85,27 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, return; if (authorized) { - sta->flags |= WLAN_STA_AUTHORIZED; - res = hostapd_sta_set_flags(hapd, sta->addr, sta->flags, - WLAN_STA_AUTHORIZED, ~0); + ap_sta_set_authorized(hapd, sta, 1); + res = hostapd_set_authorized(hapd, sta, 1); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "authorizing port"); } else { - sta->flags &= ~WLAN_STA_AUTHORIZED; - res = hostapd_sta_set_flags(hapd, sta->addr, sta->flags, - 0, ~WLAN_STA_AUTHORIZED); + ap_sta_set_authorized(hapd, sta, 0); + res = hostapd_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); + wpa_printf(MSG_DEBUG, "Could not set station " MACSTR + " flags for kernel driver (errno=%d).", + MAC2STR(sta->addr), errno); } - if (authorized) + if (authorized) { + os_get_reltime(&sta->connected_time); accounting_sta_start(hapd, sta); + } } @@ -128,10 +131,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, 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_PUT_BE16(key->key_length, key_len); wpa_get_ntp_timestamp(key->replay_counter); - if (os_get_random(key->key_iv, sizeof(key->key_iv))) { + if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) { wpa_printf(MSG_ERROR, "Could not get random numbers"); os_free(buf); return; @@ -185,107 +188,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, } -static struct hostapd_wep_keys * -ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) -{ - struct hostapd_wep_keys *key; - - key = os_zalloc(sizeof(*key)); - if (key == NULL) - return NULL; - - key->default_len = hapd->conf->default_wep_key_len; - - if (key->idx >= hapd->conf->broadcast_key_idx_max || - key->idx < hapd->conf->broadcast_key_idx_min) - key->idx = hapd->conf->broadcast_key_idx_min; - else - key->idx++; - - if (!key->key[key->idx]) - key->key[key->idx] = os_malloc(key->default_len); - if (key->key[key->idx] == NULL || - os_get_random(key->key[key->idx], key->default_len)) { - printf("Could not generate random WEP key (dynamic VLAN).\n"); - os_free(key->key[key->idx]); - key->key[key->idx] = NULL; - os_free(key); - return NULL; - } - key->len[key->idx] = key->default_len; - - wpa_printf(MSG_DEBUG, "%s: Default WEP idx %d for dynamic VLAN\n", - ifname, key->idx); - wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)", - key->key[key->idx], key->len[key->idx]); - - if (hostapd_set_encryption(ifname, hapd, "WEP", NULL, key->idx, - key->key[key->idx], key->len[key->idx], 1)) - printf("Could not set dynamic VLAN WEP encryption key.\n"); - - hostapd_set_ieee8021x(ifname, hapd, 1); - - return key; -} - - -static struct hostapd_wep_keys * -ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid, - size_t vlan_id) -{ - const char *ifname; - - if (vlan_id == 0) - return &ssid->wep; - - if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys && - ssid->dyn_vlan_keys[vlan_id]) - return ssid->dyn_vlan_keys[vlan_id]; - - wpa_printf(MSG_DEBUG, "IEEE 802.1X: Creating new group " - "state machine for VLAN ID %lu", - (unsigned long) vlan_id); - - ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); - if (ifname == NULL) { - wpa_printf(MSG_DEBUG, "IEEE 802.1X: Unknown VLAN ID %lu - " - "cannot create group key state machine", - (unsigned long) vlan_id); - return NULL; - } - - if (ssid->dyn_vlan_keys == NULL) { - int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); - ssid->dyn_vlan_keys = os_zalloc(size); - if (ssid->dyn_vlan_keys == NULL) - return NULL; - ssid->max_dyn_vlan_keys = vlan_id; - } - - if (ssid->max_dyn_vlan_keys < vlan_id) { - struct hostapd_wep_keys **na; - int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); - na = os_realloc(ssid->dyn_vlan_keys, size); - if (na == NULL) - return NULL; - ssid->dyn_vlan_keys = na; - os_memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0, - (vlan_id - ssid->max_dyn_vlan_keys) * - sizeof(ssid->dyn_vlan_keys[0])); - ssid->max_dyn_vlan_keys = vlan_id; - } - - ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname); - - return ssid->dyn_vlan_keys[vlan_id]; -} - - void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) { - struct hostapd_wep_keys *key = NULL; + struct eapol_authenticator *eapol = hapd->eapol_auth; struct eapol_state_machine *sm = sta->eapol_sm; - int vlan_id; if (sm == NULL || !sm->eap_if->eapKeyData) return; @@ -293,19 +199,16 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR, MAC2STR(sta->addr)); - vlan_id = sta->vlan_id; - if (vlan_id < 0 || vlan_id > MAX_VLAN_ID) - vlan_id = 0; - - if (vlan_id) { - key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id); - if (key && key->key[key->idx]) - ieee802_1x_tx_key_one(hapd, sta, key->idx, 1, - key->key[key->idx], - key->len[key->idx]); - } else if (hapd->default_wep_key) { - ieee802_1x_tx_key_one(hapd, sta, hapd->default_wep_key_idx, 1, - hapd->default_wep_key, +#ifndef CONFIG_NO_VLAN + if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) { + wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported."); + return; + } +#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); } @@ -313,7 +216,8 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) u8 *ikey; ikey = os_malloc(hapd->conf->individual_wep_key_len); if (ikey == NULL || - os_get_random(ikey, hapd->conf->individual_wep_key_len)) { + random_get_bytes(ikey, hapd->conf->individual_wep_key_len)) + { wpa_printf(MSG_ERROR, "Could not generate random " "individual WEP key."); os_free(ikey); @@ -328,10 +232,9 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) /* TODO: set encryption in TX callback, i.e., only after STA * has ACKed EAPOL-Key frame */ - if (hostapd_set_encryption(hapd->conf->iface, hapd, "WEP", - sta->addr, 0, ikey, - hapd->conf->individual_wep_key_len, - 1)) { + if (hostapd_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."); } @@ -343,10 +246,9 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) const char *radius_mode_txt(struct hostapd_data *hapd) { - if (hapd->iface->current_mode == NULL) - return "802.11"; - - switch (hapd->iface->current_mode->mode) { + switch (hapd->iface->conf->hw_mode) { + case HOSTAPD_MODE_IEEE80211AD: + return "802.11ad"; case HOSTAPD_MODE_IEEE80211A: return "802.11a"; case HOSTAPD_MODE_IEEE80211G: @@ -371,6 +273,7 @@ int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta) } +#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) @@ -388,145 +291,210 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd, /* Save station identity for future RADIUS packets */ os_free(sm->identity); - sm->identity = os_malloc(identity_len + 1); + sm->identity = (u8 *) dup_binstr(identity, identity_len); 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) +static int add_common_radius_sta_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) { - struct radius_msg *msg; char buf[128]; - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_PORT) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port"); + return -1; + } - ieee802_1x_learn_identity(hapd, sm, eap, len); + 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))) { + wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id"); + return -1; + } - wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " - "packet"); + 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 (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_CONNECT_INFO) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Connect-Info"); + return -1; + } - 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; + if (sta->acct_session_id_hi || sta->acct_session_id_lo) { + 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))) { + wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); + return -1; + } } - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + return 0; +} - 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 && +int add_common_radius_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) +{ + char buf[128]; + struct hostapd_radius_attr *attr; + + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IP_ADDRESS) && + 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; + wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address"); + return -1; } #ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IPV6_ADDRESS) && + 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; + wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address"); + return -1; } #endif /* CONFIG_IPV6 */ - if (hapd->conf->nas_identifier && + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IDENTIFIER) && + 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; + wpa_printf(MSG_ERROR, "Could not add NAS-Identifier"); + return -1; } os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + MAC2STR(hapd->own_addr), + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); buf[sizeof(buf) - 1] = '\0'; - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_CALLED_STATION_ID) && + !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; + wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); + return -1; } - 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; + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_PORT_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type"); + return -1; } - /* 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 (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0) + return -1; + + for (attr = req_attr; attr; attr = attr->next) { + if (!radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val))) { + wpa_printf(MSG_ERROR, "Could not add RADIUS " + "attribute"); + return -1; + } } - 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; + return 0; +} + + +static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len) +{ + struct radius_msg *msg; + 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) { + wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); + return; } - 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'; + 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)) { + wpa_printf(MSG_INFO, "Could not add User-Name"); + goto fail; } - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Connect-Info\n"); + + if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta, + msg) < 0) + 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 (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_FRAMED_MTU) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { + wpa_printf(MSG_INFO, "Could not add Framed-MTU"); goto fail; } if (eap && !radius_msg_add_eap(msg, eap, len)) { - printf("Could not add EAP-Message\n"); + wpa_printf(MSG_INFO, "Could not add EAP-Message"); goto fail; } /* State attribute must be copied if and only if this packet is * Access-Request reply to the previous Access-Challenge */ - if (sm->last_recv_radius && sm->last_recv_radius->hdr->code == + 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"); + wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge"); goto fail; } if (res > 0) { @@ -534,35 +502,34 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, } } - radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr); + if (hapd->conf->radius_request_cui) { + const u8 *cui; + size_t cui_len; + /* Add previously learned CUI or nul CUI to request CUI */ + if (sm->radius_cui) { + cui = wpabuf_head(sm->radius_cui); + cui_len = wpabuf_len(sm->radius_cui); + } else { + cui = (const u8 *) "\0"; + cui_len = 1; + } + if (!radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + cui, cui_len)) { + wpa_printf(MSG_ERROR, "Could not add CUI"); + goto fail; + } + } + + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0) + goto fail; + return; fail: radius_msg_free(msg); - os_free(msg); -} - - -char *eap_type_text(u8 type) -{ - switch (type) { - case EAP_TYPE_IDENTITY: return "Identity"; - case EAP_TYPE_NOTIFICATION: return "Notification"; - case EAP_TYPE_NAK: return "Nak"; - case EAP_TYPE_MD5: return "MD5-Challenge"; - case EAP_TYPE_OTP: return "One-Time Password"; - case EAP_TYPE_GTC: return "Generic Token Card"; - case EAP_TYPE_TLS: return "TLS"; - case EAP_TYPE_TTLS: return "TTLS"; - case EAP_TYPE_PEAP: return "PEAP"; - case EAP_TYPE_SIM: return "SIM"; - case EAP_TYPE_FAST: return "FAST"; - case EAP_TYPE_SAKE: return "SAKE"; - case EAP_TYPE_PSK: return "PSK"; - case EAP_TYPE_PAX: return "PAX"; - default: return "Unknown"; - } } +#endif /* CONFIG_NO_RADIUS */ static void handle_eap_response(struct hostapd_data *hapd, @@ -577,7 +544,7 @@ static void handle_eap_response(struct hostapd_data *hapd, data = (u8 *) (eap + 1); if (len < sizeof(*eap) + 1) { - printf("handle_eap_response: too short response data\n"); + wpa_printf(MSG_INFO, "handle_eap_response: too short response data"); return; } @@ -587,7 +554,7 @@ static void handle_eap_response(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d " "id=%d len=%d) from STA: EAP Response-%s (%d)", eap->code, eap->identifier, be_to_host16(eap->length), - eap_type_text(type), type); + eap_server_get_name(0, type), type); sm->dot1xAuthEapolRespFramesRx++; @@ -605,7 +572,7 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, u16 eap_len; if (len < sizeof(*eap)) { - printf(" too short EAP packet\n"); + wpa_printf(MSG_INFO, " too short EAP packet"); return; } @@ -647,6 +614,23 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, } +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->p2p_ie, sta, + sta->identity, sta->radius_cui); +} + + /** * ieee802_1x_receive - Process the EAPOL frames from the Supplicant * @hapd: hostapd BSS data @@ -664,6 +648,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, struct ieee802_1x_eapol_key *key; u16 datalen; struct rsn_pmksa_cache_entry *pmksa; + int key_mgmt; if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->wps_state) @@ -672,13 +657,15 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR, (unsigned long) len, MAC2STR(sa)); sta = ap_get_sta(hapd, sa); - if (!sta) { - printf(" no station information available\n"); + if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) && + !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not " + "associated/Pre-authenticating STA"); return; } if (len < sizeof(*hdr)) { - printf(" too short IEEE 802.1X packet\n"); + wpa_printf(MSG_INFO, " too short IEEE 802.1X packet"); return; } @@ -688,7 +675,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, hdr->version, hdr->type, datalen); if (len - sizeof(*hdr) < datalen) { - printf(" frame too short for this IEEE 802.1X packet\n"); + wpa_printf(MSG_INFO, " frame too short for this IEEE 802.1X packet"); if (sta->eapol_sm) sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++; return; @@ -714,27 +701,44 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, 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))) + if (!hapd->conf->ieee802_1x && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " + "802.1X not enabled and WPS not used"); + return; + } + + key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); + if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " + "STA is using PSK"); return; + } if (!sta->eapol_sm) { - sta->eapol_sm = eapol_auth_alloc(hapd->eapol_auth, sta->addr, - sta->flags & WLAN_STA_PREAUTH, - sta); + 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; + if (!hapd->conf->ieee802_1x) { + u32 wflags = sta->flags & (WLAN_STA_WPS | + WLAN_STA_WPS2 | + WLAN_STA_MAYBE_WPS); + if (wflags == WLAN_STA_MAYBE_WPS || + wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) { + /* + * Delay EAPOL frame transmission until a + * possible WPS STA initiates the handshake + * with EAPOL-Start. Only allow the wait to be + * skipped if the STA is known to support WPS + * 2.0. + */ + wpa_printf(MSG_DEBUG, "WPS: Do not start " + "EAPOL until EAPOL-Start is " + "received"); + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } } #endif /* CONFIG_WPS */ @@ -768,6 +772,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, } sta->eapol_sm->eapolStart = TRUE; sta->eapol_sm->dot1xAuthEapolStartFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); break; @@ -780,11 +785,12 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, accounting_sta_stop(hapd, sta); sta->eapol_sm->eapolLogoff = TRUE; sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); break; case IEEE802_1X_TYPE_EAPOL_KEY: wpa_printf(MSG_DEBUG, " EAPOL-Key"); - if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + if (!ap_sta_is_authorized(sta)) { wpa_printf(MSG_DEBUG, " Dropped key data from " "unauthorized Supplicant"); break; @@ -819,6 +825,7 @@ 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; + int key_mgmt; #ifdef CONFIG_WPS if (hapd->conf->wps_state && hapd->conf->wpa && @@ -832,16 +839,32 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } #endif /* CONFIG_WPS */ - if ((!force_1x && !hapd->conf->ieee802_1x) || - wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) + if (!force_1x && !hapd->conf->ieee802_1x) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - " + "802.1X not enabled or forced for WPS"); + /* + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPS to PSK. + */ + ieee802_1x_free_station(sta); return; + } + + key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); + if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK"); + /* + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPA-EAP to PSK. + */ + ieee802_1x_free_station(sta); + return; + } if (sta->eapol_sm == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "start authentication"); - sta->eapol_sm = eapol_auth_alloc(hapd->eapol_auth, sta->addr, - sta->flags & WLAN_STA_PREAUTH, - sta); + sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); if (sta->eapol_sm == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -854,17 +877,40 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) #ifdef CONFIG_WPS sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; - if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) { + if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) { /* - * Delay EAPOL frame transmission until a possible WPS - * initiates the handshake with EAPOL-Start. + * Delay EAPOL frame transmission until a possible WPS STA + * initiates the handshake with EAPOL-Start. Only allow the + * wait to be skipped if the STA is known to support WPS 2.0. */ + wpa_printf(MSG_DEBUG, "WPS: Do not start EAPOL until " + "EAPOL-Start is received"); sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; } #endif /* CONFIG_WPS */ sta->eapol_sm->eap_if->portEnabled = TRUE; +#ifdef CONFIG_IEEE80211R + if (sta->auth_alg == WLAN_AUTH_FT) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "PMK from FT - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing FT information from R0KH. */ + 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; + sta->eapol_sm->authFail = FALSE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); + /* TODO: get vlan_id from R0KH using RRB message */ + return; + } +#endif /* CONFIG_IEEE80211R */ + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { int old_vlanid; @@ -879,6 +925,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); old_vlanid = sta->vlan_id; @@ -900,47 +947,6 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } -void ieee802_1x_free_radius_class(struct radius_class_data *class) -{ - size_t i; - if (class == NULL) - return; - for (i = 0; i < class->count; i++) - os_free(class->attr[i].data); - os_free(class->attr); - class->attr = NULL; - class->count = 0; -} - - -int ieee802_1x_copy_radius_class(struct radius_class_data *dst, - const struct radius_class_data *src) -{ - size_t i; - - if (src->attr == NULL) - return 0; - - dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data)); - if (dst->attr == NULL) - return -1; - - dst->count = 0; - - for (i = 0; i < src->count; i++) { - dst->attr[i].data = os_malloc(src->attr[i].len); - if (dst->attr[i].data == NULL) - break; - dst->count++; - os_memcpy(dst->attr[i].data, src->attr[i].data, - src->attr[i].len); - dst->attr[i].len = src->attr[i].len; - } - - return 0; -} - - void ieee802_1x_free_station(struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; @@ -950,23 +956,23 @@ void ieee802_1x_free_station(struct sta_info *sta) sta->eapol_sm = NULL; - if (sm->last_recv_radius) { - radius_msg_free(sm->last_recv_radius); - os_free(sm->last_recv_radius); - } +#ifndef CONFIG_NO_RADIUS + radius_msg_free(sm->last_recv_radius); + radius_free_class(&sm->radius_class); + wpabuf_free(sm->radius_cui); +#endif /* CONFIG_NO_RADIUS */ os_free(sm->identity); - ieee802_1x_free_radius_class(&sm->radius_class); 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; + struct wpabuf *eap; + const struct eap_hdr *hdr; int eap_type = -1; char buf[64]; struct radius_msg *msg; @@ -980,7 +986,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, msg = sm->last_recv_radius; - eap = radius_msg_get_eap(msg, &len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { /* RFC 3579, Chap. 2.6.3: * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message @@ -992,30 +998,32 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, return; } - if (len < sizeof(*hdr)) { + if (wpabuf_len(eap) < sizeof(*hdr)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "too short EAP packet " "received from authentication server"); - os_free(eap); + wpabuf_free(eap); sm->eap_if->aaaEapNoReq = TRUE; return; } - if (len > sizeof(*hdr)) - eap_type = eap[sizeof(*hdr)]; + if (wpabuf_len(eap) > sizeof(*hdr)) + eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)]; - hdr = (struct eap_hdr *) eap; + hdr = wpabuf_head(eap); switch (hdr->code) { case EAP_CODE_REQUEST: if (eap_type >= 0) sm->eap_type_authsrv = eap_type; os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", - eap_type >= 0 ? eap_type_text(eap_type) : "??", + eap_type >= 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_type_text(eap_type) : "??", + eap_type >= 0 ? eap_server_get_name(0, eap_type) : + "??", eap_type); break; case EAP_CODE_SUCCESS: @@ -1037,7 +1045,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, sm->eap_if->aaaEapReq = TRUE; wpabuf_free(sm->eap_if->aaaEapReqData); - sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len); + sm->eap_if->aaaEapReqData = eap; } @@ -1097,12 +1105,12 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, sm == NULL) return; - ieee802_1x_free_radius_class(&sm->radius_class); + 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)); + nclass = os_calloc(count, sizeof(struct radius_attr_data)); if (nclass == NULL) return; @@ -1153,13 +1161,10 @@ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, NULL) < 0) return; - identity = os_malloc(len + 1); + identity = (u8 *) dup_binstr(buf, len); 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'", @@ -1172,6 +1177,32 @@ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, } +/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */ +static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + struct wpabuf *cui; + u8 *buf; + size_t len; + + if (sm == NULL) + return; + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) < 0) + return; + + cui = wpabuf_alloc_copy(buf, len); + if (cui == NULL) + return; + + wpabuf_free(sm->radius_cui); + sm->radius_cui = cui; +} + + struct sta_id_search { u8 identifier; struct eapol_state_machine *sm; @@ -1225,8 +1256,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, 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, msg->hdr->identifier); + 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"); @@ -1236,7 +1268,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be * present when packet contains an EAP-Message attribute */ - if (msg->hdr->code == RADIUS_CODE_ACCESS_REJECT && + 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) { @@ -1245,15 +1277,14 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, "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"); + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped"); return RADIUS_RX_INVALID_AUTHENTICATOR; } - if (msg->hdr->code != RADIUS_CODE_ACCESS_ACCEPT && - msg->hdr->code != RADIUS_CODE_ACCESS_REJECT && - msg->hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { - printf("Unknown RADIUS message code\n"); + if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT && + hdr->code != RADIUS_CODE_ACCESS_REJECT && + hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { + wpa_printf(MSG_INFO, "Unknown RADIUS message code"); return RADIUS_RX_UNKNOWN; } @@ -1261,11 +1292,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR, MAC2STR(sta->addr)); - if (sm->last_recv_radius) { - radius_msg_free(sm->last_recv_radius); - os_free(sm->last_recv_radius); - } - + radius_msg_free(sm->last_recv_radius); sm->last_recv_radius = msg; session_timeout_set = @@ -1275,8 +1302,8 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, &termination_action)) termination_action = RADIUS_TERMINATION_ACTION_DEFAULT; - if (hapd->conf->radius->acct_interim_interval == 0 && - msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT && + 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) { @@ -1291,17 +1318,17 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, } - switch (msg->hdr->code) { + 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_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -1315,8 +1342,10 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, "ID in Access-Accept"); break; } +#endif /* CONFIG_NO_VLAN */ - ap_sta_bind_vlan(hapd, sta, old_vlanid); + if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0) + break; /* RFC 3580, Ch. 3.17 */ if (session_timeout_set && termination_action == @@ -1331,6 +1360,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, shared_secret_len); ieee802_1x_store_radius_class(hapd, sta, msg); ieee802_1x_update_sta_identity(hapd, sta, msg); + ieee802_1x_update_sta_cui(hapd, sta, msg); if (sm->eap_if->eapKeyAvailable && wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, session_timeout_set ? @@ -1373,6 +1403,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, return RADIUS_RX_QUEUED; } +#endif /* CONFIG_NO_RADIUS */ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) @@ -1384,11 +1415,10 @@ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "aborting authentication"); - if (sm->last_recv_radius) { - radius_msg_free(sm->last_recv_radius); - os_free(sm->last_recv_radius); - sm->last_recv_radius = NULL; - } +#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) { /* @@ -1396,75 +1426,36 @@ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) * request and we cannot continue EAP processing (EAP-Failure * could only be sent if the EAP peer actually replied). */ - sm->eap_if->portEnabled = FALSE; - hostapd_sta_deauth(hapd, sta->addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | - WLAN_STA_AUTHORIZED); - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta); - sta->timeout_next = STA_REMOVE; - } -} - - -#ifdef HOSTAPD_DUMP_STATE -static void fprint_char(FILE *f, char c) -{ - if (c >= 32 && c < 127) - fprintf(f, "%c", c); - else - fprintf(f, "<%02x>", c); -} - - -void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta) -{ - struct eapol_state_machine *sm = sta->eapol_sm; - if (sm == NULL) - return; + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR, + MAC2STR(sta->addr)); - fprintf(f, "%sIEEE 802.1X:\n", prefix); - - if (sm->identity) { - size_t i; - fprintf(f, "%sidentity=", prefix); - for (i = 0; i < sm->identity_len; i++) - fprint_char(f, sm->identity[i]); - fprintf(f, "\n"); + sm->eap_if->portEnabled = FALSE; + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); } - - fprintf(f, "%slast EAP type: Authentication Server: %d (%s) " - "Supplicant: %d (%s)\n", prefix, - sm->eap_type_authsrv, eap_type_text(sm->eap_type_authsrv), - sm->eap_type_supp, eap_type_text(sm->eap_type_supp)); - - fprintf(f, "%scached_packets=%s\n", prefix, - sm->last_recv_radius ? "[RX RADIUS]" : ""); - - eapol_auth_dump_state(f, prefix, sm); } -#endif /* HOSTAPD_DUMP_STATE */ static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) { + struct eapol_authenticator *eapol = hapd->eapol_auth; + if (hapd->conf->default_wep_key_len < 1) return 0; - os_free(hapd->default_wep_key); - hapd->default_wep_key = os_malloc(hapd->conf->default_wep_key_len); - if (hapd->default_wep_key == NULL || - os_get_random(hapd->default_wep_key, - hapd->conf->default_wep_key_len)) { - printf("Could not generate random WEP key.\n"); - os_free(hapd->default_wep_key); - hapd->default_wep_key = NULL; + os_free(eapol->default_wep_key); + eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len); + if (eapol->default_wep_key == NULL || + random_get_bytes(eapol->default_wep_key, + hapd->conf->default_wep_key_len)) { + wpa_printf(MSG_INFO, "Could not generate random WEP key"); + 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", - hapd->default_wep_key, + eapol->default_wep_key, hapd->conf->default_wep_key_len); return 0; @@ -1485,36 +1476,38 @@ static int ieee802_1x_sta_key_available(struct hostapd_data *hapd, 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 (hapd->default_wep_key_idx >= 3) - hapd->default_wep_key_idx = + if (eapol->default_wep_key_idx >= 3) + eapol->default_wep_key_idx = hapd->conf->individual_wep_key_len > 0 ? 1 : 0; else - hapd->default_wep_key_idx++; + eapol->default_wep_key_idx++; wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d", - hapd->default_wep_key_idx); + 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(hapd->default_wep_key); - hapd->default_wep_key = NULL; + 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 (hostapd_set_encryption(hapd->conf->iface, hapd, "WEP", NULL, - hapd->default_wep_key_idx, - hapd->default_wep_key, - hapd->conf->default_wep_key_len, 1)) { + if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, + broadcast_ether_addr, + 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(hapd->default_wep_key); - hapd->default_wep_key = NULL; + os_free(eapol->default_wep_key); + eapol->default_wep_key = NULL; return; } @@ -1530,6 +1523,30 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) 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); } @@ -1537,10 +1554,12 @@ static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type, 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 */ } @@ -1562,19 +1581,15 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, { struct hostapd_data *hapd = ctx; const struct hostapd_eap_user *eap_user; - int i, count; + int i; - eap_user = hostapd_get_eap_user(hapd->conf, identity, - identity_len, phase2); + eap_user = hostapd_get_eap_user(hapd, 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++) { + for (i = 0; i < EAP_MAX_METHODS; i++) { user->methods[i].vendor = eap_user->methods[i].vendor; user->methods[i].method = eap_user->methods[i].method; } @@ -1586,6 +1601,7 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, 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; @@ -1608,6 +1624,7 @@ static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr) 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; @@ -1626,6 +1643,7 @@ static void ieee802_1x_logger(void *ctx, const u8 *addr, hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s", txt); +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ } @@ -1654,6 +1672,22 @@ static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx) } +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; @@ -1661,12 +1695,13 @@ int ieee802_1x_init(struct hostapd_data *hapd) struct eapol_auth_cb cb; os_memset(&conf, 0, sizeof(conf)); - conf.hapd = hapd; + 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; @@ -1680,6 +1715,16 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind; conf.tnc = hapd->conf->tnc; conf.wps = hapd->wps; + conf.fragment_size = hapd->conf->fragment_size; + conf.pwd_group = hapd->conf->pwd_group; + conf.pbc_in_m1 = hapd->conf->pbc_in_m1; + if (hapd->conf->server_id) { + conf.server_id = (const u8 *) hapd->conf->server_id; + conf.server_id_len = os_strlen(hapd->conf->server_id); + } else { + conf.server_id = (const u8 *) "hostapd"; + conf.server_id_len = 7; + } os_memset(&cb, 0, sizeof(cb)); cb.eapol_send = ieee802_1x_eapol_send; @@ -1691,29 +1736,31 @@ int ieee802_1x_init(struct hostapd_data *hapd) 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) && - hostapd_set_ieee8021x(hapd->conf->iface, hapd, 1)) + hostapd_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) { - hostapd_set_privacy(hapd, 1); - for (i = 0; i < 4; i++) - hostapd_set_encryption(hapd->conf->iface, hapd, - "none", NULL, i, NULL, 0, 0); + hostapd_drv_set_key(hapd->conf->iface, hapd, + WPA_ALG_NONE, NULL, i, 0, NULL, 0, + NULL, 0); ieee802_1x_rekey(hapd, NULL); - if (hapd->default_wep_key == NULL) + if (hapd->eapol_auth->default_wep_key == NULL) return -1; } @@ -1727,35 +1774,24 @@ void ieee802_1x_deinit(struct hostapd_data *hapd) if (hapd->driver != NULL && (hapd->conf->ieee802_1x || hapd->conf->wpa)) - hostapd_set_ieee8021x(hapd->conf->iface, hapd, 0); + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); eapol_auth_deinit(hapd->eapol_auth); hapd->eapol_auth = NULL; } -int ieee802_1x_reconfig(struct hostapd_data *hapd, - struct hostapd_config *oldconf, - struct hostapd_bss_config *oldbss) -{ - ieee802_1x_deinit(hapd); - return ieee802_1x_init(hapd); -} - - int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len, int ack) + 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)) + if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2) return 0; hdr = (struct ieee80211_hdr *) buf; @@ -1767,21 +1803,44 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, return 0; pos += 2; - xhdr = (struct ieee802_1x_hdr *) pos; - pos += sizeof(*xhdr); + return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos, + ack); +} + + +int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *buf, int len, int ack) +{ + const struct ieee802_1x_hdr *xhdr = + (const struct ieee802_1x_hdr *) buf; + const u8 *pos = buf + sizeof(*xhdr); + struct ieee802_1x_eapol_key *key; + if (len < (int) sizeof(*xhdr)) + return 0; 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); + if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY) + return 0; + + if (pos + sizeof(struct wpa_eapol_key) <= buf + len) { + const struct wpa_eapol_key *wpa; + wpa = (const struct wpa_eapol_key *) pos; + if (wpa->type == EAPOL_KEY_TYPE_RSN || + wpa->type == EAPOL_KEY_TYPE_WPA) + wpa_auth_eapol_key_tx_status(hapd->wpa_auth, + sta->wpa_sm, 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 + * retransmitted in case of failure. Try to re-send 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) { + if (!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 " @@ -1825,8 +1884,17 @@ u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, } +struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm) +{ + if (sm == NULL) + return NULL; + return sm->radius_cui; +} + + const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len) { + *len = 0; if (sm == NULL) return NULL; @@ -1884,6 +1952,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, { int len = 0, ret; struct eapol_state_machine *sm = sta->eapol_sm; + struct os_reltime diff; if (sm == NULL) return 0; @@ -1998,6 +2067,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, len += ret; /* dot1xAuthSessionStatsTable */ + os_reltime_age(&sta->acct_session_start, &diff); ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ @@ -2012,13 +2082,23 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, (wpa_key_mgmt_wpa_ieee8021x( wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, - (unsigned int) (time(NULL) - - sta->acct_session_start), + (unsigned int) diff.sec, sm->identity); if (ret < 0 || (size_t) ret >= buflen - len) return len; len += ret; + ret = os_snprintf(buf + len, buflen - len, + "last_eap_type_as=%d (%s)\n" + "last_eap_type_sta=%d (%s)\n", + sm->eap_type_authsrv, + eap_server_get_name(0, sm->eap_type_authsrv), + sm->eap_type_supp, + eap_server_get_name(0, sm->eap_type_supp)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + return len; } @@ -2039,4 +2119,27 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "Added PMKSA cache entry (IEEE 802.1X)"); } + + if (!success) { + /* + * Many devices require deauthentication after WPS provisioning + * and some may not be be able to do that themselves, so + * disconnect the client here. In addition, this may also + * benefit IEEE 802.1X/EAPOL authentication cases, too since + * the EAPOL PAE state machine would remain in HELD state for + * considerable amount of time and some EAP methods, like + * EAP-FAST with anonymous provisioning, may require another + * EAPOL authentication to be started to complete connection. + */ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: 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_IEEE_802_1X_AUTH_FAILED); + hostapd_wps_eap_completed(hapd); + } } diff --git a/contrib/hostapd/hostapd/ieee802_1x.h b/contrib/hostapd/src/ap/ieee802_1x.h similarity index 50% rename from contrib/hostapd/hostapd/ieee802_1x.h rename to contrib/hostapd/src/ap/ieee802_1x.h index 94cff93563..e1df94057d 100644 --- a/contrib/hostapd/hostapd/ieee802_1x.h +++ b/contrib/hostapd/src/ap/ieee802_1x.h @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2007, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_1X_H @@ -20,30 +14,8 @@ struct sta_info; struct eapol_state_machine; struct hostapd_config; struct hostapd_bss_config; - -/* RFC 3580, 4. RC4 EAPOL-Key Frame */ - -struct ieee802_1x_eapol_key { - u8 type; - u16 key_length; - u8 replay_counter[8]; /* does not repeat within the life of the keying - * material used to encrypt the Key field; - * 64-bit NTP timestamp MAY be used here */ - u8 key_iv[16]; /* cryptographically random number */ - u8 key_index; /* key flag in the most significant bit: - * 0 = broadcast (default key), - * 1 = unicast (key mapping key); key index is in the - * 7 least significant bits */ - u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with - * MS-MPPE-Send-Key as the key */ - - /* followed by key: if packet body length = 44 + key length, then the - * key field (of key_length bytes) contains the key in encrypted form; - * if packet body length = 44, key field is absent and key_length - * represents the number of least significant octets from - * MS-MPPE-Send-Key attribute to be used as the keying material; - * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ -} __attribute__ ((packed)); +struct hostapd_radius_attr; +struct radius_msg; void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, @@ -58,14 +30,14 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); int ieee802_1x_init(struct hostapd_data *hapd); void ieee802_1x_deinit(struct hostapd_data *hapd); -int ieee802_1x_reconfig(struct hostapd_data *hapd, - struct hostapd_config *oldconf, - struct hostapd_bss_config *oldbss); int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len, int ack); + const u8 *buf, size_t len, int ack); +int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *data, int 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); +struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm); 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); @@ -78,13 +50,12 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, void hostapd_get_ntp_timestamp(u8 *buf); char *eap_type_text(u8 type); -struct radius_class_data; - -void ieee802_1x_free_radius_class(struct radius_class_data *class); -int ieee802_1x_copy_radius_class(struct radius_class_data *dst, - const struct radius_class_data *src); - const char *radius_mode_txt(struct hostapd_data *hapd); int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); +int add_common_radius_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg); + #endif /* IEEE802_1X_H */ diff --git a/contrib/hostapd/src/ap/p2p_hostapd.c b/contrib/hostapd/src/ap/p2p_hostapd.c new file mode 100644 index 0000000000..795d313b8c --- /dev/null +++ b/contrib/hostapd/src/ap/p2p_hostapd.c @@ -0,0 +1,114 @@ +/* + * hostapd / P2P integration + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "p2p/p2p.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "sta_info.h" +#include "p2p_hostapd.h" + + +#ifdef CONFIG_P2P + +int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen) +{ + if (sta->p2p_ie == NULL) + return 0; + + return p2p_ie_text(sta->p2p_ie, buf, buf + buflen); +} + + +int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration) +{ + wpa_printf(MSG_DEBUG, "P2P: Set NoA parameters: count=%u start=%d " + "duration=%d", count, start, duration); + + if (count == 0) { + hapd->noa_enabled = 0; + hapd->noa_start = 0; + hapd->noa_duration = 0; + } + + if (count != 255) { + wpa_printf(MSG_DEBUG, "P2P: Non-periodic NoA - set " + "NoA parameters"); + return hostapd_driver_set_noa(hapd, count, start, duration); + } + + hapd->noa_enabled = 1; + hapd->noa_start = start; + hapd->noa_duration = duration; + + if (hapd->num_sta_no_p2p == 0) { + wpa_printf(MSG_DEBUG, "P2P: No legacy STAs connected - update " + "periodic NoA parameters"); + return hostapd_driver_set_noa(hapd, count, start, duration); + } + + wpa_printf(MSG_DEBUG, "P2P: Legacy STA(s) connected - do not enable " + "periodic NoA"); + + return 0; +} + + +void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "P2P: First non-P2P device connected"); + + if (hapd->noa_enabled) { + wpa_printf(MSG_DEBUG, "P2P: Disable periodic NoA"); + hostapd_driver_set_noa(hapd, 0, 0, 0); + } +} + + +void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "P2P: Last non-P2P device disconnected"); + + if (hapd->noa_enabled) { + wpa_printf(MSG_DEBUG, "P2P: Enable periodic NoA"); + hostapd_driver_set_noa(hapd, 255, hapd->noa_start, + hapd->noa_duration); + } +} + +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_P2P_MANAGER +u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid) +{ + u8 bitmap; + *eid++ = WLAN_EID_VENDOR_SPECIFIC; + *eid++ = 4 + 3 + 1; + WPA_PUT_BE24(eid, OUI_WFA); + eid += 3; + *eid++ = P2P_OUI_TYPE; + + *eid++ = P2P_ATTR_MANAGEABILITY; + WPA_PUT_LE16(eid, 1); + eid += 2; + bitmap = P2P_MAN_DEVICE_MANAGEMENT; + if (hapd->conf->p2p & P2P_ALLOW_CROSS_CONNECTION) + bitmap |= P2P_MAN_CROSS_CONNECTION_PERMITTED; + bitmap |= P2P_MAN_COEXISTENCE_OPTIONAL; + *eid++ = bitmap; + + return eid; +} +#endif /* CONFIG_P2P_MANAGER */ diff --git a/contrib/hostapd/src/ap/p2p_hostapd.h b/contrib/hostapd/src/ap/p2p_hostapd.h new file mode 100644 index 0000000000..0e3921c614 --- /dev/null +++ b/contrib/hostapd/src/ap/p2p_hostapd.h @@ -0,0 +1,35 @@ +/* + * hostapd / P2P integration + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef P2P_HOSTAPD_H +#define P2P_HOSTAPD_H + +#ifdef CONFIG_P2P + +int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +int hostapd_p2p_set_noa(struct hostapd_data *hapd, u8 count, int start, + int duration); +void hostapd_p2p_non_p2p_sta_connected(struct hostapd_data *hapd); +void hostapd_p2p_non_p2p_sta_disconnected(struct hostapd_data *hapd); + + +#else /* CONFIG_P2P */ + +static inline int hostapd_p2p_get_mib_sta(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + return 0; +} + +#endif /* CONFIG_P2P */ + +u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid); + +#endif /* P2P_HOSTAPD_H */ diff --git a/contrib/hostapd/hostapd/peerkey.c b/contrib/hostapd/src/ap/peerkey_auth.c similarity index 94% rename from contrib/hostapd/hostapd/peerkey.c rename to contrib/hostapd/src/ap/peerkey_auth.c index 83f3ce513d..ba5c606442 100644 --- a/contrib/hostapd/hostapd/peerkey.c +++ b/contrib/hostapd/src/ap/peerkey_auth.c @@ -1,25 +1,19 @@ /* * hostapd - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2008, Jouni Malinen + * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" -#include "common.h" -#include "eloop.h" -#include "sha1.h" -#include "sha256.h" -#include "wpa.h" -#include "defs.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "wpa_auth.h" #include "wpa_auth_i.h" #include "wpa_auth_ie.h" @@ -295,7 +289,7 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth, return; } - if (os_get_random(smk, PMK_LEN)) { + if (random_get_bytes(smk, PMK_LEN)) { wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); return; } diff --git a/contrib/hostapd/hostapd/pmksa_cache.c b/contrib/hostapd/src/ap/pmksa_cache_auth.c similarity index 73% rename from contrib/hostapd/hostapd/pmksa_cache.c rename to contrib/hostapd/src/ap/pmksa_cache_auth.c index 5f54a34c11..4720b59c05 100644 --- a/contrib/hostapd/hostapd/pmksa_cache.c +++ b/contrib/hostapd/src/ap/pmksa_cache_auth.c @@ -1,29 +1,20 @@ /* * hostapd - PMKSA cache for IEEE 802.11i RSN - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2008, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" -#include "common.h" -#include "ap.h" -#include "config.h" -#include "common.h" -#include "eloop.h" -#include "sha1.h" -#include "sha256.h" -#include "ieee802_1x.h" -#include "eapol_sm.h" -#include "pmksa_cache.h" +#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; @@ -41,40 +32,6 @@ struct rsn_pmksa_cache { }; -/** - * rsn_pmkid - Calculate PMK identifier - * @pmk: Pairwise master key - * @pmk_len: Length of pmk in bytes - * @aa: Authenticator address - * @spa: Supplicant address - * @pmkid: Buffer for PMKID - * @use_sha256: Whether to use SHA256-based KDF - * - * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy - * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) - */ -void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, - u8 *pmkid, int use_sha256) -{ - char *title = "PMK Name"; - const u8 *addr[3]; - const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA256_MAC_LEN]; - - addr[0] = (u8 *) title; - addr[1] = aa; - addr[2] = spa; - -#ifdef CONFIG_IEEE80211W - if (use_sha256) - hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); - else -#endif /* CONFIG_IEEE80211W */ - hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); - os_memcpy(pmkid, hash, PMKID_LEN); -} - - static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); @@ -83,13 +40,16 @@ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) if (entry == NULL) return; os_free(entry->identity); - ieee802_1x_free_radius_class(&entry->radius_class); + wpabuf_free(entry->cui); +#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) +void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) { struct rsn_pmksa_cache_entry *pos, *prev; @@ -131,15 +91,13 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&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); + MACSTR, MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); } pmksa_cache_set_expiration(pmksa); @@ -149,12 +107,12 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) { int sec; - struct os_time now; + struct os_reltime now; eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); if (pmksa->pmksa == NULL) return; - os_get_time(&now); + os_get_reltime(&now); sec = pmksa->pmksa->expiration - now.sec; if (sec < 0) sec = 0; @@ -177,11 +135,15 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, } } - ieee802_1x_copy_radius_class(&entry->radius_class, - &eapol->radius_class); + if (eapol->radius_cui) + entry->cui = wpabuf_dup(eapol->radius_cui); + +#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 = eapol->sta->vlan_id; + entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id; } @@ -203,16 +165,22 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, eapol->identity, eapol->identity_len); } - ieee802_1x_free_radius_class(&eapol->radius_class); - ieee802_1x_copy_radius_class(&eapol->radius_class, - &entry->radius_class); + if (entry->cui) { + wpabuf_free(eapol->radius_cui); + eapol->radius_cui = wpabuf_dup(entry->cui); + } + +#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; - eapol->sta->vlan_id = entry->vlan_id; + ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; } @@ -241,6 +209,8 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; pmksa->pmksa_count++; + if (prev == NULL) + pmksa_cache_set_expiration(pmksa); 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); @@ -248,8 +218,8 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, /** - * pmksa_cache_add - Add a PMKSA cache entry - * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * 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 @@ -265,12 +235,13 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, * based on the PMK. */ struct rsn_pmksa_cache_entry * -pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, +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; + struct os_reltime now; if (pmk_len > PMK_LEN) return NULL; @@ -282,7 +253,7 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t 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); + os_get_reltime(&now); entry->expiration = now.sec; if (session_timeout > 0) entry->expiration += session_timeout; @@ -294,7 +265,7 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, /* Replace an old entry for the same STA (if found) with the new entry */ - pos = pmksa_cache_get(pmksa, spa, NULL); + pos = pmksa_cache_auth_get(pmksa, spa, NULL); if (pos) pmksa_cache_free_entry(pmksa, pos); @@ -337,8 +308,11 @@ pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, old_entry->identity_len); } } - ieee802_1x_copy_radius_class(&entry->radius_class, - &old_entry->radius_class); + if (old_entry->cui) + entry->cui = wpabuf_dup(old_entry->cui); +#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; @@ -350,10 +324,10 @@ pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, /** - * pmksa_cache_deinit - Free all entries in PMKSA cache - * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * pmksa_cache_auth_deinit - Free all entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() */ -void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa) { struct rsn_pmksa_cache_entry *entry, *prev; int i; @@ -375,14 +349,15 @@ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) /** - * pmksa_cache_get - Fetch a PMKSA cache entry - * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * 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_get(struct rsn_pmksa_cache *pmksa, - const u8 *spa, const u8 *pmkid) +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; @@ -404,7 +379,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, /** * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC - * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() * @aa: Authenticator address * @spa: Supplicant address * @pmkid: PMKID @@ -434,14 +409,14 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( /** - * pmksa_cache_init - Initialize PMKSA cache + * 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_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx), void *ctx) +pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx) { struct rsn_pmksa_cache *pmksa; diff --git a/contrib/hostapd/hostapd/pmksa_cache.h b/contrib/hostapd/src/ap/pmksa_cache_auth.h similarity index 52% rename from contrib/hostapd/hostapd/pmksa_cache.h rename to contrib/hostapd/src/ap/pmksa_cache_auth.h index 6ba2da6073..aa90024d7d 100644 --- a/contrib/hostapd/hostapd/pmksa_cache.h +++ b/contrib/hostapd/src/ap/pmksa_cache_auth.h @@ -1,20 +1,16 @@ /* * hostapd - PMKSA cache for IEEE 802.11i RSN - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2008, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PMKSA_CACHE_H #define PMKSA_CACHE_H +#include "radius/radius.h" + /** * struct rsn_pmksa_cache_entry - PMKSA cache entry */ @@ -29,6 +25,7 @@ struct rsn_pmksa_cache_entry { u8 *identity; size_t identity_len; + struct wpabuf *cui; struct radius_class_data radius_class; u8 eap_type_authsrv; int vlan_id; @@ -38,25 +35,27 @@ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache; struct rsn_pmksa_cache * -pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx), void *ctx); -void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); -struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, - const u8 *spa, const u8 *pmkid); +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_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); +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); -void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, - u8 *pmkid, int use_sha256); +void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); #endif /* PMKSA_CACHE_H */ diff --git a/contrib/hostapd/hostapd/preauth.c b/contrib/hostapd/src/ap/preauth_auth.c similarity index 92% rename from contrib/hostapd/hostapd/preauth.c rename to contrib/hostapd/src/ap/preauth_auth.c index 9ab41eda0b..3e0c8000d0 100644 --- a/contrib/hostapd/hostapd/preauth.c +++ b/contrib/hostapd/src/ap/preauth_auth.c @@ -2,29 +2,26 @@ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" #ifdef CONFIG_RSN_PREAUTH -#include "hostapd.h" +#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 "eloop.h" #include "sta_info.h" -#include "wpa_common.h" -#include "eapol_sm.h" -#include "wpa.h" -#include "preauth.h" +#include "wpa_auth.h" +#include "preauth_auth.h" #ifndef ETH_P_PREAUTH #define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ @@ -256,7 +253,7 @@ void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); - ethhdr->h_proto = htons(ETH_P_PREAUTH); + 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, diff --git a/contrib/hostapd/hostapd/preauth.h b/contrib/hostapd/src/ap/preauth_auth.h similarity index 78% rename from contrib/hostapd/hostapd/preauth.h rename to contrib/hostapd/src/ap/preauth_auth.h index 5348bee9bf..69fb3566e0 100644 --- a/contrib/hostapd/hostapd/preauth.h +++ b/contrib/hostapd/src/ap/preauth_auth.h @@ -2,14 +2,8 @@ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication * Copyright (c) 2004-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PREAUTH_H diff --git a/contrib/hostapd/hostapd/sta_info.c b/contrib/hostapd/src/ap/sta_info.c similarity index 51% rename from contrib/hostapd/hostapd/sta_info.c rename to contrib/hostapd/src/ap/sta_info.c index a139ba9bdf..24e764d66b 100644 --- a/contrib/hostapd/hostapd/sta_info.c +++ b/contrib/hostapd/src/ap/sta_info.c @@ -1,42 +1,46 @@ /* * hostapd / Station table - * Copyright (c) 2002-2008, Jouni Malinen - * Copyright (c) 2007-2008, Intel Corporation + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "common/sae.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "p2p/p2p.h" #include "hostapd.h" -#include "sta_info.h" -#include "eloop.h" #include "accounting.h" #include "ieee802_1x.h" #include "ieee802_11.h" -#include "radius/radius.h" -#include "wpa.h" -#include "preauth.h" -#include "radius/radius_client.h" -#include "driver.h" +#include "ieee802_11_auth.h" +#include "wpa_auth.h" +#include "preauth_auth.h" +#include "ap_config.h" #include "beacon.h" -#include "hw_features.h" -#include "mlme.h" +#include "ap_mlme.h" #include "vlan_init.h" +#include "p2p_hostapd.h" +#include "ap_drv_ops.h" +#include "gas_serv.h" +#include "sta_info.h" -static int ap_sta_in_other_bss(struct hostapd_data *hapd, - struct sta_info *sta, u32 flags); +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); +static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx); +static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx); #ifdef CONFIG_IEEE80211W static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); #endif /* CONFIG_IEEE80211W */ +static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta); int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, @@ -65,6 +69,30 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) } +#ifdef CONFIG_P2P +struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + const u8 *p2p_dev_addr; + + if (sta->p2p_ie == NULL) + continue; + + p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie); + if (p2p_dev_addr == NULL) + continue; + + if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0) + return sta; + } + + return NULL; +} +#endif /* CONFIG_P2P */ + + static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta) { struct sta_info *tmp; @@ -120,15 +148,21 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) accounting_sta_stop(hapd, sta); - if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC) && - !(sta->flags & WLAN_STA_PREAUTH)) - hostapd_sta_remove(hapd, sta->addr); + /* just in case */ + ap_sta_set_authorized(hapd, sta, 0); + + if (sta->flags & WLAN_STA_WDS) + hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); + + if (!(sta->flags & WLAN_STA_PREAUTH)) + hostapd_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] = NULL; + hapd->sta_aid[(sta->aid - 1) / 32] &= + ~BIT((sta->aid - 1) % 32); hapd->num_sta--; if (sta->nonerp_set) { @@ -154,7 +188,6 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) set_beacon++; } -#ifdef CONFIG_IEEE80211N if (sta->no_ht_gf_set) { sta->no_ht_gf_set = 0; hapd->iface->num_sta_ht_no_gf--; @@ -170,20 +203,37 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) hapd->iface->num_sta_ht_20mhz--; } +#ifdef CONFIG_P2P + if (sta->no_p2p_set) { + sta->no_p2p_set = 0; + hapd->num_sta_no_p2p--; + if (hapd->num_sta_no_p2p == 0) + hostapd_p2p_non_p2p_sta_disconnected(hapd); + } +#endif /* CONFIG_P2P */ + +#if defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N) if (hostapd_ht_operation_update(hapd->iface) > 0) set_beacon++; -#endif /* CONFIG_IEEE80211N */ +#endif /* NEED_AP_MLME && CONFIG_IEEE80211N */ if (set_beacon) ieee802_11_set_beacons(hapd->iface); + wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR, + __func__, MAC2STR(sta->addr)); eloop_cancel_timeout(ap_handle_timer, hapd, sta); eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); ieee802_1x_free_station(sta); wpa_auth_sta_deinit(sta->wpa_sm); rsn_preauth_free_station(hapd, sta); - radius_client_flush_auth(hapd->radius, sta->addr); +#ifndef CONFIG_NO_RADIUS + if (hapd->radius) + radius_client_flush_auth(hapd->radius, sta->addr); +#endif /* CONFIG_NO_RADIUS */ os_free(sta->last_assoc_req); os_free(sta->challenge); @@ -193,7 +243,33 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_P2P + p2p_group_notif_disassoc(hapd->p2p_group, sta->addr); +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_INTERWORKING + if (sta->gas_dialog) { + int i; + for (i = 0; i < GAS_DIALOG_MAX; i++) + gas_serv_dialog_clear(&sta->gas_dialog[i]); + os_free(sta->gas_dialog); + } +#endif /* CONFIG_INTERWORKING */ + wpabuf_free(sta->wps_ie); + wpabuf_free(sta->p2p_ie); + wpabuf_free(sta->hs20_ie); + + os_free(sta->ht_capabilities); + os_free(sta->vht_capabilities); + hostapd_free_psk_list(sta->psk); + os_free(sta->identity); + os_free(sta->radius_cui); + +#ifdef CONFIG_SAE + sae_clear_data(sta->sae); + os_free(sta->sae); +#endif /* CONFIG_SAE */ os_free(sta); } @@ -232,7 +308,11 @@ 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; + int reason; + wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d", + __func__, MAC2STR(sta->addr), sta->flags, + sta->timeout_next); if (sta->timeout_next == STA_REMOVE) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " @@ -245,27 +325,51 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) (sta->timeout_next == STA_NULLFUNC || sta->timeout_next == STA_DISASSOC)) { int inactive_sec; - wpa_printf(MSG_DEBUG, "Checking STA " MACSTR " inactivity:", - MAC2STR(sta->addr)); - inactive_sec = hostapd_get_inact_sec(hapd, sta->addr); + /* + * Add random value to timeout so that we don't end up bouncing + * all stations at the same time if we have lots of associated + * stations that are idle (but keep re-associating). + */ + int fuzz = os_random() % 20; + inactive_sec = hostapd_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)); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Check inactivity: Could not " + "get station info from kernel driver for " + MACSTR, MAC2STR(sta->addr)); + /* + * The driver may not support this functionality. + * Anyway, try again after the next inactivity timeout, + * but do not disconnect the station now. + */ + next_time = hapd->conf->ap_max_inactivity + fuzz; } 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"); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Station " MACSTR " has been active %is ago", + MAC2STR(sta->addr), inactive_sec); sta->timeout_next = STA_NULLFUNC; - next_time = hapd->conf->ap_max_inactivity - + next_time = hapd->conf->ap_max_inactivity + fuzz - inactive_sec; + } else { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Station " MACSTR " has been " + "inactive too long: %d sec, max allowed: %d", + MAC2STR(sta->addr), inactive_sec, + hapd->conf->ap_max_inactivity); + + if (hapd->conf->skip_inactivity_poll) + sta->timeout_next = STA_DISASSOC; } } 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"); + !(sta->flags & WLAN_STA_PENDING_POLL) && + !hapd->conf->skip_inactivity_poll) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR + " has ACKed data poll", MAC2STR(sta->addr)); /* data nullfunc frame poll did not produce TX errors; assume * station ACKed it */ sta->timeout_next = STA_NULLFUNC; @@ -273,6 +377,9 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) } if (next_time) { + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%lu seconds)", + __func__, MAC2STR(sta->addr), next_time); eloop_register_timeout(next_time, 0, ap_handle_timer, hapd, sta); return; @@ -280,64 +387,43 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) if (sta->timeout_next == STA_NULLFUNC && (sta->flags & WLAN_STA_ASSOC)) { - /* send data frame to poll STA and check whether this frame - * is ACKed */ - struct ieee80211_hdr hdr; - - wpa_printf(MSG_DEBUG, " Polling STA with data frame"); + wpa_printf(MSG_DEBUG, " Polling STA"); sta->flags |= WLAN_STA_PENDING_POLL; - -#ifndef CONFIG_NATIVE_WINDOWS - os_memset(&hdr, 0, sizeof(hdr)); - if (hapd->driver && - os_strcmp(hapd->driver->name, "hostap") == 0) { - /* - * WLAN_FC_STYPE_NULLFUNC would be more appropriate, - * but it is apparently not retried so TX Exc events - * are not received for it. - */ - hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_DATA); - } else { - hdr.frame_control = - IEEE80211_FC(WLAN_FC_TYPE_DATA, - WLAN_FC_STYPE_NULLFUNC); - } - - hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); - os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN); - os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, - ETH_ALEN); - os_memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN); - - if (hostapd_send_mgmt_frame(hapd, &hdr, sizeof(hdr), 0) < 0) - perror("ap_handle_timer: send"); -#endif /* CONFIG_NATIVE_WINDOWS */ + hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr, + sta->flags & WLAN_STA_WMM); } 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)); + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "Timeout, sending %s info to STA " MACSTR, + deauth ? "deauthentication" : "disassociation", + MAC2STR(sta->addr)); if (deauth) { - hostapd_sta_deauth(hapd, sta->addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); - } else { - hostapd_sta_disassoc( + hostapd_drv_sta_deauth( hapd, sta->addr, - WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + WLAN_REASON_PREV_AUTH_NOT_VALID); + } else { + reason = (sta->timeout_next == STA_DISASSOC) ? + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY : + WLAN_REASON_PREV_AUTH_NOT_VALID; + + hostapd_drv_sta_disassoc(hapd, sta->addr, reason); } } switch (sta->timeout_next) { case STA_NULLFUNC: sta->timeout_next = STA_DISASSOC; + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)", + __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY); eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer, hapd, sta); break; case STA_DISASSOC: + case STA_DISASSOC_FROM_CLI: + ap_sta_set_authorized(hapd, sta, 0); sta->flags &= ~WLAN_STA_ASSOC; ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); if (!sta->acct_terminate_cause) @@ -348,17 +434,22 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated due to " "inactivity"); + reason = (sta->timeout_next == STA_DISASSOC) ? + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY : + WLAN_REASON_PREV_AUTH_NOT_VALID; sta->timeout_next = STA_DEAUTH; + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)", + __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY); eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, hapd, sta); - mlme_disassociate_indication( - hapd, sta, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + mlme_disassociate_indication(hapd, sta, reason); break; case STA_DEAUTH: case STA_REMOVE: hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " - "inactivity"); + "inactivity (timer DEAUTH/REMOVE)"); if (!sta->acct_terminate_cause) sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; @@ -377,8 +468,14 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) struct sta_info *sta = timeout_ctx; u8 addr[ETH_ALEN]; - if (!(sta->flags & WLAN_STA_AUTH)) + if (!(sta->flags & WLAN_STA_AUTH)) { + if (sta->flags & WLAN_STA_GAS) { + wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA " + "entry " MACSTR, MAC2STR(sta->addr)); + ap_free_sta(hapd, sta); + } return; + } mlme_deauthenticate_indication(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID); @@ -389,7 +486,19 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; os_memcpy(addr, sta->addr, ETH_ALEN); ap_free_sta(hapd, sta); - hostapd_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); +} + + +void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout) +{ + if (eloop_replenish_timeout(session_timeout, 0, + ap_handle_session_timer, hapd, sta) == 1) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "setting session timeout " + "to %d seconds", session_timeout); + } } @@ -432,17 +541,26 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) wpa_printf(MSG_ERROR, "malloc failed"); return NULL; } - sta->acct_interim_interval = hapd->conf->radius->acct_interim_interval; + sta->acct_interim_interval = hapd->conf->acct_interim_interval; + accounting_sta_get_id(hapd, sta); + + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { + wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout " + "for " MACSTR " (%d seconds - ap_max_inactivity)", + __func__, MAC2STR(addr), + hapd->conf->ap_max_inactivity); + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, + ap_handle_timer, hapd, sta); + } /* 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; } @@ -454,7 +572,7 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", MAC2STR(sta->addr)); - if (hostapd_sta_remove(hapd, sta->addr) && + if (hostapd_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)); @@ -464,8 +582,8 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) } -static int ap_sta_in_other_bss(struct hostapd_data *hapd, - struct sta_info *sta, u32 flags) +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; @@ -480,11 +598,22 @@ static int ap_sta_in_other_bss(struct hostapd_data *hapd, if (bss == hapd || bss == NULL) continue; sta2 = ap_get_sta(bss, sta->addr); - if (sta2 && ((sta2->flags & flags) == flags)) - return 1; + if (!sta2) + continue; + + ap_sta_disconnect(bss, sta2, sta2->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); } +} - return 0; + +static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + ap_sta_remove(hapd, sta); + mlme_disassociate_indication(hapd, sta, sta->disassoc_reason); } @@ -493,17 +622,36 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, { wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); - sta->flags &= ~WLAN_STA_ASSOC; - if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC)) - ap_sta_remove(hapd, sta); + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_DEAUTH; + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - " + "AP_MAX_INACTIVITY_AFTER_DISASSOC)", + __func__, MAC2STR(sta->addr), + AP_MAX_INACTIVITY_AFTER_DISASSOC); 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); + sta->disassoc_reason = reason; + sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, + ap_sta_disassoc_cb_timeout, hapd, sta); +} + + +static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + ap_sta_remove(hapd, sta); + mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason); } @@ -513,24 +661,52 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC)) - ap_sta_remove(hapd, sta); + ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_REMOVE; + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - " + "AP_MAX_INACTIVITY_AFTER_DEAUTH)", + __func__, MAC2STR(sta->addr), + AP_MAX_INACTIVITY_AFTER_DEAUTH); 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); + sta->deauth_reason = reason; + sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, + ap_sta_deauth_cb_timeout, hapd, sta); } +#ifdef CONFIG_WPS +int ap_sta_wps_cancel(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + if (sta && (sta->flags & WLAN_STA_WPS)) { + ap_sta_deauthenticate(hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR, + __func__, MAC2STR(sta->addr)); + return 1; + } + + return 0; +} +#endif /* CONFIG_WPS */ + + 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 @@ -553,15 +729,19 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) sta->vlan_id = 0; else if (sta->vlan_id > 0) { + struct hostapd_vlan *wildcard_vlan = NULL; vlan = hapd->conf->vlan; while (vlan) { - if (vlan->vlan_id == sta->vlan_id || - vlan->vlan_id == VLAN_ID_WILDCARD) { - iface = vlan->ifname; + if (vlan->vlan_id == sta->vlan_id) break; - } + if (vlan->vlan_id == VLAN_ID_WILDCARD) + wildcard_vlan = vlan; vlan = vlan->next; } + if (!vlan) + vlan = wildcard_vlan; + if (vlan) + iface = vlan->ifname; } if (sta->vlan_id > 0 && vlan == NULL) { @@ -626,7 +806,16 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0) wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA"); - return hostapd_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id); + ret = hostapd_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 */ } @@ -635,9 +824,9 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, 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); + struct os_reltime now, passed; + os_get_reltime(&now); + os_reltime_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, @@ -667,13 +856,14 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) 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); + nbuf = os_realloc_array(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); + os_get_reltime(&sta->sa_query_start); } trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; sta->sa_query_trans_id = nbuf; @@ -709,3 +899,159 @@ void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta) } #endif /* CONFIG_IEEE80211W */ + + +void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, + int authorized) +{ + const u8 *dev_addr = NULL; + char buf[100]; +#ifdef CONFIG_P2P + u8 addr[ETH_ALEN]; + u8 ip_addr_buf[4]; +#endif /* CONFIG_P2P */ + + if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED)) + return; + +#ifdef CONFIG_P2P + if (hapd->p2p_group == NULL) { + if (sta->p2p_ie != NULL && + p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0) + dev_addr = addr; + } else + dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr); +#endif /* CONFIG_P2P */ + + if (dev_addr) + os_snprintf(buf, sizeof(buf), MACSTR " p2p_dev_addr=" MACSTR, + MAC2STR(sta->addr), MAC2STR(dev_addr)); + else + os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr)); + + if (authorized) { + char ip_addr[100]; + ip_addr[0] = '\0'; +#ifdef CONFIG_P2P + if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) { + os_snprintf(ip_addr, sizeof(ip_addr), + " ip_addr=%u.%u.%u.%u", + ip_addr_buf[0], ip_addr_buf[1], + ip_addr_buf[2], ip_addr_buf[3]); + } +#endif /* CONFIG_P2P */ + + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s", + buf, ip_addr); + + if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx) + wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_CONNECTED "%s%s", + buf, ip_addr); + + sta->flags |= WLAN_STA_AUTHORIZED; + } else { + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); + + if (hapd->msg_ctx_parent && + hapd->msg_ctx_parent != hapd->msg_ctx) + wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, + AP_STA_DISCONNECTED "%s", buf); + + sta->flags &= ~WLAN_STA_AUTHORIZED; + } + + if (hapd->sta_authorized_cb) + hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx, + sta->addr, authorized, dev_addr); +} + + +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) + hostapd_drv_sta_deauth(hapd, addr, reason); + + if (sta == NULL) + return; + ap_sta_set_authorized(hapd, sta, 0); + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " + "for " MACSTR " (%d seconds - " + "AP_MAX_INACTIVITY_AFTER_DEAUTH)", + __func__, MAC2STR(sta->addr), + AP_MAX_INACTIVITY_AFTER_DEAUTH); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, + ap_handle_timer, hapd, sta); + sta->timeout_next = STA_REMOVE; + + sta->deauth_reason = reason; + sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0, + ap_sta_deauth_cb_timeout, hapd, sta); +} + + +void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (!(sta->flags & WLAN_STA_PENDING_DEAUTH_CB)) { + wpa_printf(MSG_DEBUG, "Ignore deauth cb for test frame"); + return; + } + sta->flags &= ~WLAN_STA_PENDING_DEAUTH_CB; + eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); + ap_sta_deauth_cb_timeout(hapd, sta); +} + + +void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (!(sta->flags & WLAN_STA_PENDING_DISASSOC_CB)) { + wpa_printf(MSG_DEBUG, "Ignore disassoc cb for test frame"); + return; + } + sta->flags &= ~WLAN_STA_PENDING_DISASSOC_CB; + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + ap_sta_disassoc_cb_timeout(hapd, sta); +} + + +int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) +{ + int res; + + buf[0] = '\0'; + res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), + (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), + (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), + (flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" : + ""), + (flags & WLAN_STA_SHORT_PREAMBLE ? + "[SHORT_PREAMBLE]" : ""), + (flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""), + (flags & WLAN_STA_WMM ? "[WMM]" : ""), + (flags & WLAN_STA_MFP ? "[MFP]" : ""), + (flags & WLAN_STA_WPS ? "[WPS]" : ""), + (flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""), + (flags & WLAN_STA_WDS ? "[WDS]" : ""), + (flags & WLAN_STA_NONERP ? "[NonERP]" : ""), + (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""), + (flags & WLAN_STA_GAS ? "[GAS]" : ""), + (flags & WLAN_STA_VHT ? "[VHT]" : ""), + (flags & WLAN_STA_WNM_SLEEP_MODE ? + "[WNM_SLEEP_MODE]" : "")); + + return res; +} diff --git a/contrib/hostapd/src/ap/sta_info.h b/contrib/hostapd/src/ap/sta_info.h new file mode 100644 index 0000000000..9b77e06091 --- /dev/null +++ b/contrib/hostapd/src/ap/sta_info.h @@ -0,0 +1,198 @@ +/* + * hostapd / Station table + * Copyright (c) 2002-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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_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_ASSOC_REQ_OK BIT(15) +#define WLAN_STA_WPS2 BIT(16) +#define WLAN_STA_GAS BIT(17) +#define WLAN_STA_VHT BIT(18) +#define WLAN_STA_WNM_SLEEP_MODE BIT(19) +#define WLAN_STA_PENDING_DISASSOC_CB BIT(29) +#define WLAN_STA_PENDING_DEAUTH_CB BIT(30) +#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; + u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ + + 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; + unsigned int no_p2p_set:1; + unsigned int qos_map_enabled:1; + + u16 auth_alg; + u8 previous_ap[6]; + + enum { + STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE, + STA_DISASSOC_FROM_CLI + } timeout_next; + + u16 deauth_reason; + u16 disassoc_reason; + + /* 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; + struct os_reltime 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; + /* PSKs from RADIUS authentication server */ + struct hostapd_sta_wpa_psk_short *psk; + + char *identity; /* User-Name from RADIUS */ + char *radius_cui; /* Chargeable-User-Identity from RADIUS */ + + struct ieee80211_ht_capabilities *ht_capabilities; + struct ieee80211_vht_capabilities *vht_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_reltime sa_query_start; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_INTERWORKING +#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ + struct gas_dialog_info *gas_dialog; + u8 gas_dialog_next; +#endif /* CONFIG_INTERWORKING */ + + struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ + struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */ + struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */ + + struct os_reltime connected_time; + +#ifdef CONFIG_SAE + struct sae_data *sae; +#endif /* CONFIG_SAE */ +}; + + +/* 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. Similarly, 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); +struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr); +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 hostapd_free_stas(struct hostapd_data *hapd); +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); +void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout); +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); +#ifdef CONFIG_WPS +int ap_sta_wps_cancel(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx); +#endif /* CONFIG_WPS */ +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); + +void ap_sta_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +static inline int ap_sta_is_authorized(struct sta_info *sta) +{ + return sta->flags & WLAN_STA_AUTHORIZED; +} + +void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta); + +int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen); + +#endif /* STA_INFO_H */ diff --git a/contrib/hostapd/src/ap/tkip_countermeasures.c b/contrib/hostapd/src/ap/tkip_countermeasures.c new file mode 100644 index 0000000000..4725e2b3e8 --- /dev/null +++ b/contrib/hostapd/src/ap/tkip_countermeasures.c @@ -0,0 +1,105 @@ +/* + * hostapd / TKIP countermeasures + * Copyright (c) 2002-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 "hostapd.h" +#include "sta_info.h" +#include "ap_mlme.h" +#include "wpa_auth.h" +#include "ap_drv_ops.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; + hostapd_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; + hostapd_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); + while ((sta = hapd->sta_list)) { + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET; + if (sta->flags & WLAN_STA_AUTH) { + mlme_deauthenticate_indication( + hapd, sta, + WLAN_REASON_MICHAEL_MIC_FAILURE); + } + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_MICHAEL_MIC_FAILURE); + ap_free_sta(hapd, sta); + } +} + + +void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); +} + + +int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) +{ + struct os_reltime now; + int ret = 0; + + 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 ret; + } + } + + os_get_reltime(&now); + if (os_reltime_expired(&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); + ret = 1; + } + } + hapd->michael_mic_failure = now; + + return ret; +} diff --git a/contrib/hostapd/src/ap/tkip_countermeasures.h b/contrib/hostapd/src/ap/tkip_countermeasures.h new file mode 100644 index 0000000000..d3eaed3cf9 --- /dev/null +++ b/contrib/hostapd/src/ap/tkip_countermeasures.h @@ -0,0 +1,15 @@ +/* + * hostapd / TKIP countermeasures + * Copyright (c) 2002-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TKIP_COUNTERMEASURES_H +#define TKIP_COUNTERMEASURES_H + +int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local); +void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd); + +#endif /* TKIP_COUNTERMEASURES_H */ diff --git a/contrib/hostapd/src/ap/utils.c b/contrib/hostapd/src/ap/utils.c new file mode 100644 index 0000000000..931968c84b --- /dev/null +++ b/contrib/hostapd/src/ap/utils.c @@ -0,0 +1,85 @@ +/* + * AP mode helper functions + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 *da, const u8 *bssid, + const u8 *ie, size_t ie_len, + int ssi_signal), + void *ctx) +{ + struct hostapd_probereq_cb *n; + + n = os_realloc_array(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->interfaces && + hapd->iface->interfaces->for_each_interface) + hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, prune_associations, &data); +} diff --git a/contrib/hostapd/hostapd/vlan_init.c b/contrib/hostapd/src/ap/vlan_init.c similarity index 51% rename from contrib/hostapd/hostapd/vlan_init.c rename to contrib/hostapd/src/ap/vlan_init.c index 87c61e2c01..509e557cee 100644 --- a/contrib/hostapd/hostapd/vlan_init.c +++ b/contrib/hostapd/src/ap/vlan_init.c @@ -2,22 +2,20 @@ * hostapd / VLAN initialization * Copyright 2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" +#include "utils/common.h" #include "hostapd.h" -#include "driver.h" +#include "ap_config.h" +#include "ap_drv_ops.h" #include "vlan_init.h" +#include "vlan_util.h" #ifdef CONFIG_FULL_DYNAMIC_VLAN @@ -28,8 +26,8 @@ #include #include -#include "priv_netlink.h" -#include "eloop.h" +#include "drivers/priv_netlink.h" +#include "utils/eloop.h" struct full_dynamic_vlan { @@ -43,7 +41,8 @@ static int ifconfig_helper(const char *if_name, int up) struct ifreq ifr; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("socket[AF_INET,SOCK_STREAM]"); + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); return -1; } @@ -51,7 +50,9 @@ static int ifconfig_helper(const char *if_name, int up) os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCGIFFLAGS]"); + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " + "for interface %s: %s", + __func__, if_name, strerror(errno)); close(fd); return -1; } @@ -62,7 +63,9 @@ static int ifconfig_helper(const char *if_name, int up) ifr.ifr_flags &= ~IFF_UP; if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCSIFFLAGS]"); + 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; } @@ -74,12 +77,14 @@ static int ifconfig_helper(const char *if_name, int up) 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); } @@ -104,16 +109,19 @@ static int br_delif(const char *br_name, const char *if_name) 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) { - perror("socket[AF_INET,SOCK_STREAM]"); + 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) { - printf("Failure determining interface index for '%s'\n", - if_name); + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); close(fd); return -1; } @@ -126,7 +134,9 @@ static int br_delif(const char *br_name, const char *if_name) if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { /* No error if interface already removed. */ - perror("ioctl[SIOCDEVPRIVATE,BRCTL_DEL_IF]"); + 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; } @@ -150,16 +160,19 @@ static int br_addif(const char *br_name, const char *if_name) 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) { - perror("socket[AF_INET,SOCK_STREAM]"); + 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) { - printf("Failure determining interface index for '%s'\n", - if_name); + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); close(fd); return -1; } @@ -177,7 +190,9 @@ static int br_addif(const char *br_name, const char *if_name) return 1; } - perror("ioctl[SIOCDEVPRIVATE,BRCTL_ADD_IF]"); + 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; } @@ -192,8 +207,10 @@ 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) { - perror("socket[AF_INET,SOCK_STREAM]"); + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); return -1; } @@ -202,7 +219,8 @@ static int br_delbr(const char *br_name) if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { /* No error if bridge already removed. */ - perror("ioctl[BRCTL_DEL_BRIDGE]"); + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " + "%s: %s", __func__, br_name, strerror(errno)); close(fd); return -1; } @@ -222,10 +240,13 @@ static int br_delbr(const char *br_name) static int br_addbr(const char *br_name) { int fd; - unsigned long arg[2]; + 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) { - perror("socket[AF_INET,SOCK_STREAM]"); + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); return -1; } @@ -238,12 +259,29 @@ static int br_addbr(const char *br_name) close(fd); return 1; } else { - perror("ioctl[BRCTL_ADD_BRIDGE]"); + 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; } @@ -259,7 +297,8 @@ static int br_getnumports(const char *br_name) struct ifreq ifr; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("socket[AF_INET,SOCK_STREAM]"); + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); return -1; } @@ -273,7 +312,9 @@ static int br_getnumports(const char *br_name) ifr.ifr_data = (__caddr_t) arg; if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { - perror("ioctl[SIOCDEVPRIVATE,BRCTL_GET_PORT_LIST]"); + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " + "failed for %s: %s", + __func__, br_name, strerror(errno)); close(fd); return -1; } @@ -289,18 +330,23 @@ static int br_getnumports(const char *br_name) } -static int vlan_rem(const char *if_name) +#ifndef CONFIG_VLAN_NETLINK + +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)) { - fprintf(stderr, "Interface name to long.\n"); + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); return -1; } if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("socket[AF_INET,SOCK_STREAM]"); + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); return -1; } @@ -310,7 +356,8 @@ static int vlan_rem(const char *if_name) if_request.cmd = DEL_VLAN_CMD; if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - perror("ioctl[SIOCSIFVLAN,DEL_VLAN_CMD]"); + wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " + "%s", __func__, if_name, strerror(errno)); close(fd); return -1; } @@ -328,20 +375,24 @@ static int vlan_rem(const char *if_name) returns 1 if the interface already exists returns 0 otherwise */ -static int vlan_add(const char *if_name, int vid) +int vlan_add(const char *if_name, int vid, const char *vlan_if_name) { 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)) { - fprintf(stderr, "Interface name to long.\n"); + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); return -1; } if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("socket[AF_INET,SOCK_STREAM]"); + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); return -1; } @@ -363,6 +414,9 @@ static int vlan_add(const char *if_name, int vid) 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; } } @@ -376,7 +430,9 @@ static int vlan_add(const char *if_name, int vid) if_request.cmd = ADD_VLAN_CMD; if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - perror("ioctl[SIOCSIFVLAN,ADD_VLAN_CMD]"); + wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: " + "%s", + __func__, if_request.device1, strerror(errno)); close(fd); return -1; } @@ -391,8 +447,11 @@ 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) { - perror("socket[AF_INET,SOCK_STREAM]"); + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); return -1; } @@ -401,7 +460,9 @@ static int vlan_set_name_type(unsigned int name_type) if_request.u.name_type = name_type; if_request.cmd = SET_VLAN_NAME_TYPE_CMD; if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { - perror("ioctl[SIOCSIFVLAN,SET_VLAN_NAME_TYPE_CMD]"); + 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; } @@ -410,6 +471,125 @@ static int vlan_set_name_type(unsigned int name_type) return 0; } +#endif /* CONFIG_VLAN_NETLINK */ + + +/** + * Increase the usage counter for given parent/ifname combination. + * If create is set, then this iface is added to the global list. + * Returns + * -1 on error + * 0 if iface is not in list + * 1 if iface is in list (was there or has been added) + */ +static int hapd_get_dynamic_iface(const char *parent, const char *ifname, + int create, struct hostapd_data *hapd) +{ + size_t i; + struct hostapd_dynamic_iface *j = NULL, **tmp; + struct hapd_interfaces *hapd_global = hapd->iface->interfaces; + + if (!parent) + parent = ""; + + for (i = 0; i < hapd_global->count_dynamic; i++) { + j = hapd_global->dynamic_iface[i]; + if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 && + os_strncmp(j->parent, parent, sizeof(j->parent)) == 0) + break; + } + if (i < hapd_global->count_dynamic) { + j->usage++; + return 1; + } + + /* new entry required */ + if (!create) + return 0; + + j = os_zalloc(sizeof(*j)); + if (!j) + return -1; + os_strlcpy(j->iface, ifname, sizeof(j->iface)); + os_strlcpy(j->parent, parent, sizeof(j->parent)); + + tmp = os_realloc_array(hapd_global->dynamic_iface, i + 1, + sizeof(*hapd_global->dynamic_iface)); + if (!tmp) { + wpa_printf(MSG_ERROR, "VLAN: Failed to allocate memory in %s", + __func__); + return -1; + } + hapd_global->count_dynamic++; + hapd_global->dynamic_iface = tmp; + hapd_global->dynamic_iface[i] = j; + + return 1; +} + + +/** + * Decrease the usage counter for given ifname. + * Returns + * -1 on error or if iface was not found + * 0 if iface was found and is still present + * 1 if iface was removed from global list + */ +static int hapd_put_dynamic_iface(const char *parent, const char *ifname, + struct hostapd_data *hapd) +{ + size_t i; + struct hostapd_dynamic_iface *j = NULL, **tmp; + struct hapd_interfaces *hapd_glob = hapd->iface->interfaces; + + if (!parent) + parent = ""; + + for (i = 0; i < hapd_glob->count_dynamic; i++) { + j = hapd_glob->dynamic_iface[i]; + if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 && + os_strncmp(j->parent, parent, sizeof(j->parent)) == 0) + break; + } + + if (i == hapd_glob->count_dynamic) { + /* + * Interface not in global list. This can happen if alloc in + * _get_ failed. + */ + return -1; + } + + if (j->usage > 0) { + j->usage--; + return 0; + } + + os_free(j); + for (; i < hapd_glob->count_dynamic - 1; i++) + hapd_glob->dynamic_iface[i] = hapd_glob->dynamic_iface[i + 1]; + hapd_glob->dynamic_iface[hapd_glob->count_dynamic - 1] = NULL; + hapd_glob->count_dynamic--; + + if (hapd_glob->count_dynamic == 0) { + os_free(hapd_glob->dynamic_iface); + hapd_glob->dynamic_iface = NULL; + return 1; + } + + tmp = os_realloc_array(hapd_glob->dynamic_iface, + hapd_glob->count_dynamic, + sizeof(*hapd_glob->dynamic_iface)); + if (!tmp) { + wpa_printf(MSG_ERROR, "VLAN: Failed to release memory in %s", + __func__); + return -1; + } + hapd_glob->dynamic_iface = tmp; + + return 1; +} + static void vlan_newlink(char *ifname, struct hostapd_data *hapd) { @@ -417,33 +597,65 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd) char br_name[IFNAMSIZ]; struct hostapd_vlan *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int vlan_naming = hapd->conf->ssid.vlan_naming; + int ret; + + 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 (hapd->conf->vlan_bridge[0]) { + os_snprintf(br_name, sizeof(br_name), "%s%d", + hapd->conf->vlan_bridge, + vlan->vlan_id); + } else if (tagged_interface) { + os_snprintf(br_name, sizeof(br_name), + "br%s.%d", tagged_interface, + vlan->vlan_id); + } else { + os_snprintf(br_name, sizeof(br_name), + "brvlan%d", vlan->vlan_id); + } - if (!br_addbr(br_name)) + ret = br_addbr(br_name); + if (hapd_get_dynamic_iface(NULL, br_name, ret == 0, + hapd)) vlan->clean |= DVLAN_CLEAN_BR; ifconfig_up(br_name); if (tagged_interface) { - - if (!vlan_add(tagged_interface, vlan->vlan_id)) + if (vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE) + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "%s.%d", tagged_interface, + vlan->vlan_id); + else + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); + + ifconfig_up(tagged_interface); + ret = vlan_add(tagged_interface, vlan->vlan_id, + vlan_ifname); + if (hapd_get_dynamic_iface(NULL, vlan_ifname, + ret == 0, hapd)) vlan->clean |= DVLAN_CLEAN_VLAN; - os_snprintf(vlan_ifname, sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); - - if (!br_addif(br_name, vlan_ifname)) + ret = br_addif(br_name, vlan_ifname); + if (hapd_get_dynamic_iface(br_name, + vlan_ifname, + ret == 0, hapd)) vlan->clean |= DVLAN_CLEAN_VLAN_PORT; ifconfig_up(vlan_ifname); } - if (!br_addif(br_name, ifname)) + ret = br_addif(br_name, ifname); + if (hapd_get_dynamic_iface(br_name, ifname, ret == 0, + hapd)) vlan->clean |= DVLAN_CLEAN_WLAN_PORT; ifconfig_up(ifname); @@ -461,28 +673,59 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd) char br_name[IFNAMSIZ]; struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; - int numports; + int vlan_naming = hapd->conf->ssid.vlan_naming; + + 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 (hapd->conf->vlan_bridge[0]) { + os_snprintf(br_name, sizeof(br_name), "%s%d", + hapd->conf->vlan_bridge, + vlan->vlan_id); + } else if (tagged_interface) { + os_snprintf(br_name, sizeof(br_name), + "br%s.%d", tagged_interface, + vlan->vlan_id); + } else { + os_snprintf(br_name, sizeof(br_name), + "brvlan%d", vlan->vlan_id); + } - if (tagged_interface) { - os_snprintf(vlan_ifname, sizeof(vlan_ifname), - "vlan%d", vlan->vlan_id); + if ((vlan->clean & DVLAN_CLEAN_WLAN_PORT) && + hapd_put_dynamic_iface(br_name, vlan->ifname, hapd)) + br_delif(br_name, vlan->ifname); - numports = br_getnumports(br_name); - if (numports == 1) { + if (tagged_interface) { + if (vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE) + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "%s.%d", tagged_interface, + vlan->vlan_id); + else + os_snprintf(vlan_ifname, + sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); + if ((vlan->clean & DVLAN_CLEAN_VLAN_PORT) && + hapd_put_dynamic_iface(br_name, vlan_ifname, + hapd)) br_delif(br_name, vlan_ifname); + ifconfig_down(vlan_ifname); + if ((vlan->clean & DVLAN_CLEAN_VLAN) && + hapd_put_dynamic_iface(NULL, vlan_ifname, + hapd)) vlan_rem(vlan_ifname); + } - ifconfig_down(br_name); - br_delbr(br_name); - } + if ((vlan->clean & DVLAN_CLEAN_BR) && + hapd_put_dynamic_iface(NULL, br_name, hapd) && + br_getnumports(br_name) == 0) { + ifconfig_down(br_name); + br_delbr(br_name); } if (vlan == first) { @@ -561,7 +804,8 @@ static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) (struct sockaddr *) &from, &fromlen); if (left < 0) { if (errno != EINTR && errno != EAGAIN) - perror("recvfrom(netlink)"); + wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", + __func__, strerror(errno)); return; } @@ -572,8 +816,9 @@ static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) len = h->nlmsg_len; plen = len - sizeof(*h); if (len > left || plen < 0) { - printf("Malformed netlink message: " - "len=%d left=%d plen=%d", len, left, plen); + wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " + "message: len=%d left=%d plen=%d", + len, left, plen); break; } @@ -592,8 +837,8 @@ static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) } if (left > 0) { - printf("%d extra bytes in the end of netlink message", - left); + wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " + "netlink message", __func__, left); } } @@ -608,11 +853,18 @@ full_dynamic_vlan_init(struct hostapd_data *hapd) if (priv == NULL) return NULL; - vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD); +#ifndef CONFIG_VLAN_NETLINK + vlan_set_name_type(hapd->conf->ssid.vlan_naming == + DYNAMIC_VLAN_NAMING_WITH_DEVICE ? + VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : + VLAN_NAME_TYPE_PLUS_VID_NO_PAD); +#endif /* CONFIG_VLAN_NETLINK */ priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (priv->s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," + "NETLINK_ROUTE) failed: %s", + __func__, strerror(errno)); os_free(priv); return NULL; } @@ -621,7 +873,8 @@ full_dynamic_vlan_init(struct hostapd_data *hapd) local.nl_family = AF_NETLINK; local.nl_groups = RTMGRP_LINK; if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); + wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", + __func__, strerror(errno)); close(priv->s); os_free(priv); return NULL; @@ -661,12 +914,12 @@ int vlan_setup_encryption_dyn(struct hostapd_data *hapd, * functions for setting up dynamic broadcast keys. */ for (i = 0; i < 4; i++) { if (mssid->wep.key[i] && - hostapd_set_encryption(dyn_vlan, hapd, "WEP", NULL, - i, mssid->wep.key[i], - mssid->wep.len[i], - i == mssid->wep.idx)) { - printf("VLAN: Could not set WEP encryption for " - "dynamic VLAN.\n"); + hostapd_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; } } @@ -679,14 +932,19 @@ static int vlan_dynamic_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan) { while (vlan) { - if (vlan->vlan_id != VLAN_ID_WILDCARD && - hostapd_if_add(hapd, HOSTAPD_IF_VLAN, vlan->ifname, NULL)) - { - if (errno != EEXIST) { - printf("Could not add VLAN iface: %s: %s\n", - vlan->ifname, strerror(errno)); - return -1; + if (vlan->vlan_id != VLAN_ID_WILDCARD) { + if (hostapd_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; @@ -705,10 +963,10 @@ static void vlan_dynamic_remove(struct hostapd_data *hapd, next = vlan->next; if (vlan->vlan_id != VLAN_ID_WILDCARD && - hostapd_if_remove(hapd, HOSTAPD_IF_VLAN, vlan->ifname, - NULL)) { - printf("Could not remove VLAN iface: %s: %s\n", - vlan->ifname, strerror(errno)); + hostapd_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) @@ -722,13 +980,31 @@ static void vlan_dynamic_remove(struct hostapd_data *hapd, int vlan_init(struct hostapd_data *hapd) { - if (vlan_dynamic_add(hapd, hapd->conf->vlan)) - return -1; - #ifdef CONFIG_FULL_DYNAMIC_VLAN hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ + if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED && + !hapd->conf->vlan) { + /* dynamic vlans enabled but no (or empty) vlan_file given */ + struct hostapd_vlan *vlan; + vlan = os_zalloc(sizeof(*vlan)); + if (vlan == NULL) { + wpa_printf(MSG_ERROR, "Out of memory while assigning " + "VLAN interfaces"); + return -1; + } + + vlan->vlan_id = VLAN_ID_WILDCARD; + os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", + hapd->conf->iface); + vlan->next = hapd->conf->vlan; + hapd->conf->vlan = vlan; + } + + if (vlan_dynamic_add(hapd, hapd->conf->vlan)) + return -1; + return 0; } @@ -739,21 +1015,11 @@ void vlan_deinit(struct hostapd_data *hapd) #ifdef CONFIG_FULL_DYNAMIC_VLAN full_dynamic_vlan_deinit(hapd->full_dynamic_vlan); + hapd->full_dynamic_vlan = NULL; #endif /* CONFIG_FULL_DYNAMIC_VLAN */ } -int vlan_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, - struct hostapd_bss_config *oldbss) -{ - vlan_dynamic_remove(hapd, oldbss->vlan); - if (vlan_dynamic_add(hapd, hapd->conf->vlan)) - return -1; - - return 0; -} - - struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan, int vlan_id) @@ -765,6 +1031,8 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, 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; @@ -788,7 +1056,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, pos); os_free(ifname); - if (hostapd_if_add(hapd, HOSTAPD_IF_VLAN, n->ifname, NULL)) { + if (hostapd_vlan_if_add(hapd, n->ifname)) { os_free(n); return NULL; } @@ -796,6 +1064,10 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, 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; } @@ -807,6 +1079,8 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) 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) { @@ -820,7 +1094,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) return 1; if (vlan->dynamic_vlan == 0) - hostapd_if_remove(hapd, HOSTAPD_IF_VLAN, vlan->ifname, NULL); + hostapd_vlan_if_remove(hapd, vlan->ifname); return 0; } diff --git a/contrib/hostapd/src/ap/vlan_init.h b/contrib/hostapd/src/ap/vlan_init.h new file mode 100644 index 0000000000..781eaac441 --- /dev/null +++ b/contrib/hostapd/src/ap/vlan_init.h @@ -0,0 +1,53 @@ +/* + * hostapd / VLAN initialization + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/ap/vlan_util.c b/contrib/hostapd/src/ap/vlan_util.c new file mode 100644 index 0000000000..cc54051b1e --- /dev/null +++ b/contrib/hostapd/src/ap/vlan_util.c @@ -0,0 +1,177 @@ +/* + * hostapd / VLAN netlink api + * Copyright (c) 2012, Michael Braun + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "vlan_util.h" + +/* + * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and + * tagged interface 'if_name'. + * + * returns -1 on error + * returns 1 if the interface already exists + * returns 0 otherwise +*/ +int vlan_add(const char *if_name, int vid, const char *vlan_if_name) +{ + int ret = -1; + struct nl_sock *handle = NULL; + struct nl_cache *cache = NULL; + struct rtnl_link *rlink = NULL; + int if_idx = 0; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, " + "vlan_if_name=%s)", if_name, vid, vlan_if_name); + + if ((os_strlen(if_name) + 1) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + vlan_if_name); + return -1; + } + + handle = nl_socket_alloc(); + if (!handle) { + wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket"); + goto vlan_add_error; + } + + if (nl_connect(handle, NETLINK_ROUTE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + goto vlan_add_error; + } + + if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + cache = NULL; + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + goto vlan_add_error; + } + + if (!(if_idx = rtnl_link_name2i(cache, if_name))) { + /* link does not exist */ + wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist", + if_name); + goto vlan_add_error; + } + + if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) { + /* link does exist */ + rtnl_link_put(rlink); + rlink = NULL; + wpa_printf(MSG_ERROR, "VLAN: interface %s already exists", + vlan_if_name); + ret = 1; + goto vlan_add_error; + } + + rlink = rtnl_link_alloc(); + if (!rlink) { + wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link"); + goto vlan_add_error; + } + + if (rtnl_link_set_type(rlink, "vlan") < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link type"); + goto vlan_add_error; + } + + rtnl_link_set_link(rlink, if_idx); + rtnl_link_set_name(rlink, vlan_if_name); + + if (rtnl_link_vlan_set_id(rlink, vid) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id"); + goto vlan_add_error; + } + + if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for " + "vlan %d on %s (%d)", + vlan_if_name, vid, if_name, if_idx); + goto vlan_add_error; + } + + ret = 0; + +vlan_add_error: + if (rlink) + rtnl_link_put(rlink); + if (cache) + nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + return ret; +} + + +int vlan_rem(const char *if_name) +{ + int ret = -1; + struct nl_sock *handle = NULL; + struct nl_cache *cache = NULL; + struct rtnl_link *rlink = NULL; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name); + + handle = nl_socket_alloc(); + if (!handle) { + wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket"); + goto vlan_rem_error; + } + + if (nl_connect(handle, NETLINK_ROUTE) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink"); + goto vlan_rem_error; + } + + if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) { + cache = NULL; + wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache"); + goto vlan_rem_error; + } + + if (!(rlink = rtnl_link_get_by_name(cache, if_name))) { + /* link does not exist */ + wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists", + if_name); + goto vlan_rem_error; + } + + if (rtnl_link_delete(handle, rlink) < 0) { + wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s", + if_name); + goto vlan_rem_error; + } + + ret = 0; + +vlan_rem_error: + if (rlink) + rtnl_link_put(rlink); + if (cache) + nl_cache_free(cache); + if (handle) + nl_socket_free(handle); + return ret; +} diff --git a/contrib/hostapd/src/ap/vlan_util.h b/contrib/hostapd/src/ap/vlan_util.h new file mode 100644 index 0000000000..bef5a16f6c --- /dev/null +++ b/contrib/hostapd/src/ap/vlan_util.h @@ -0,0 +1,15 @@ +/* + * hostapd / VLAN netlink api + * Copyright (c) 2012, Michael Braun + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef VLAN_UTIL_H +#define VLAN_UTIL_H + +int vlan_add(const char *if_name, int vid, const char *vlan_if_name); +int vlan_rem(const char *if_name); + +#endif /* VLAN_UTIL_H */ diff --git a/contrib/hostapd/hostapd/wme.c b/contrib/hostapd/src/ap/wmm.c similarity index 80% rename from contrib/hostapd/hostapd/wme.c rename to contrib/hostapd/src/ap/wmm.c index f2bbbd9cc2..6d4177c2a8 100644 --- a/contrib/hostapd/hostapd/wme.c +++ b/contrib/hostapd/src/ap/wmm.c @@ -2,24 +2,23 @@ * hostapd / WMM (Wi-Fi Multimedia) * Copyright 2002-2003, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#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 "wme.h" #include "sta_info.h" -#include "driver.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "wmm.h" /* TODO: maintain separate sequence and fragment numbers for each AC @@ -67,6 +66,12 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) wmm->version = WMM_VERSION; wmm->qos_info = hapd->parameter_set_count & 0xf; + if (hapd->conf->wmm_uapsd && + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) + wmm->qos_info |= 0x80; + + wmm->reserved = 0; + /* fill in a parameter set record for each AC */ for (e = 0; e < 4; e++) { struct wmm_ac_parameter *ac = &wmm->ac[e]; @@ -87,10 +92,12 @@ u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) } -/* 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, u8 *eid, size_t len) +/* + * This function is called when a station sends an association request with + * WMM info element. The function returns 1 on success or 0 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; @@ -99,7 +106,7 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, u8 *eid, size_t len) if (len < sizeof(struct wmm_information_element)) { wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)", (unsigned long) len); - return -1; + return 0; } wmm = (struct wmm_information_element *) eid; @@ -110,27 +117,10 @@ int hostapd_eid_wmm_valid(struct hostapd_data *hapd, u8 *eid, size_t len) 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; } - return 0; -} - - -/* This function is called when a station sends an ACK frame for an AssocResp - * frame (status=success) and the matching AssocReq contained a WMM element. - */ -int hostapd_wmm_sta_config(struct hostapd_data *hapd, struct sta_info *sta) -{ - /* update kernel STA data for WMM related items (WLAN_STA_WPA flag) */ - if (sta->flags & WLAN_STA_WMM) - hostapd_sta_set_flags(hapd, sta->addr, sta->flags, - WLAN_STA_WMM, ~0); - else - hostapd_sta_set_flags(hapd, sta->addr, sta->flags, - 0, ~WLAN_STA_WMM); - - return 0; + return 1; } @@ -160,30 +150,17 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, os_memcpy(t, tspec, sizeof(struct wmm_tspec_element)); len = ((u8 *) (t + 1)) - buf; - if (hostapd_send_mgmt_frame(hapd, m, len, 0) < 0) - perror("wmm_send_action: send"); + if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0) + wpa_printf(MSG_INFO, "wmm_send_action: send failed"); } -static void wmm_addts_req(struct hostapd_data *hapd, - struct ieee80211_mgmt *mgmt, - struct wmm_tspec_element *tspec, size_t len) +int wmm_process_tspec(struct wmm_tspec_element *tspec) { - u8 *end = ((u8 *) mgmt) + len; int medium_time, pps, duration; int up, psb, dir, tid; u16 val, surplus; - if ((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)); - up = (tspec->ts_info[1] >> 3) & 0x07; psb = (tspec->ts_info[1] >> 2) & 0x01; dir = (tspec->ts_info[0] >> 5) & 0x03; @@ -204,7 +181,7 @@ static void wmm_addts_req(struct hostapd_data *hapd, val = le_to_host16(tspec->nominal_msdu_size); if (val == 0) { wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)"); - goto invalid; + 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; @@ -213,7 +190,7 @@ static void wmm_addts_req(struct hostapd_data *hapd, if (le_to_host32(tspec->minimum_phy_rate) < 1000000) { wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate"); - goto invalid; + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 / @@ -226,7 +203,7 @@ static void wmm_addts_req(struct hostapd_data *hapd, if (surplus <= 0x2000) { wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not " "greater than unity"); - goto invalid; + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } medium_time = surplus * pps * duration / 0x2000; @@ -241,35 +218,47 @@ static void wmm_addts_req(struct hostapd_data *hapd, if (medium_time > 750000) { wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over " "75%% of available bandwidth"); - wmm_send_action(hapd, mgmt->sa, tspec, - WMM_ACTION_CODE_ADDTS_RESP, - mgmt->u.action.u.wmm_action.dialog_token, - WMM_ADDTS_STATUS_REFUSED); - return; + 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, - WMM_ADDTS_STATUS_ADMISSION_ACCEPTED); - return; - -invalid: - wmm_send_action(hapd, mgmt->sa, tspec, - WMM_ACTION_CODE_ADDTS_RESP, - mgmt->u.action.u.wmm_action.dialog_token, - WMM_ADDTS_STATUS_INVALID_PARAMETERS); + mgmt->u.action.u.wmm_action.dialog_token, res); } -void hostapd_wmm_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, - size_t len) +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; - u8 *pos = ((u8 *) mgmt) + 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); diff --git a/contrib/hostapd/src/ap/wmm.h b/contrib/hostapd/src/ap/wmm.h new file mode 100644 index 0000000000..b70b863603 --- /dev/null +++ b/contrib/hostapd/src/ap/wmm.h @@ -0,0 +1,23 @@ +/* + * hostapd / WMM (Wi-Fi Multimedia) + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/ap/wnm_ap.c b/contrib/hostapd/src/ap/wnm_ap.c new file mode 100644 index 0000000000..8e5bdcb068 --- /dev/null +++ b/contrib/hostapd/src/ap/wnm_ap.c @@ -0,0 +1,508 @@ +/* + * hostapd - WNM + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "ap/hostapd.h" +#include "ap/sta_info.h" +#include "ap/ap_config.h" +#include "ap/ap_drv_ops.h" +#include "ap/wpa_auth.h" +#include "wnm_ap.h" + +#define MAX_TFS_IE_LEN 1024 + + +/* get the TFS IE from driver */ +static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr, + u8 *buf, u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); + + return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); +} + + +/* set the TFS IE to driver */ +static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr, + u8 *buf, u16 *buf_len, enum wnm_oper oper) +{ + wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); + + return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len); +} + + +/* MLME-SLEEPMODE.response */ +static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, + const u8 *addr, u8 dialog_token, + u8 action_type, u16 intval) +{ + struct ieee80211_mgmt *mgmt; + int res; + size_t len; + size_t gtk_elem_len = 0; + size_t igtk_elem_len = 0; + struct wnm_sleep_element wnmsleep_ie; + u8 *wnmtfs_ie; + u8 wnmsleep_ie_len; + u16 wnmtfs_ie_len; + u8 *pos; + struct sta_info *sta; + enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ? + WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "%s: station not found", __func__); + return -EINVAL; + } + + /* WNM-Sleep Mode IE */ + os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element)); + wnmsleep_ie_len = sizeof(struct wnm_sleep_element); + wnmsleep_ie.eid = WLAN_EID_WNMSLEEP; + wnmsleep_ie.len = wnmsleep_ie_len - 2; + wnmsleep_ie.action_type = action_type; + wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT; + wnmsleep_ie.intval = host_to_le16(intval); + + /* TFS IE(s) */ + wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); + if (wnmtfs_ie == NULL) + return -1; + if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len, + tfs_oper)) { + wnmtfs_ie_len = 0; + os_free(wnmtfs_ie); + wnmtfs_ie = NULL; + } + +#define MAX_GTK_SUBELEM_LEN 45 +#define MAX_IGTK_SUBELEM_LEN 26 + mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + + MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN); + if (mgmt == NULL) { + wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " + "WNM-Sleep Response action frame"); + return -1; + } + 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->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP; + mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token; + pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable; + /* add key data if MFP is enabled */ + if (!wpa_auth_uses_mfp(sta->wpa_sm) || + action_type != WNM_SLEEP_MODE_EXIT) { + mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; + } else { + gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos); + pos += gtk_elem_len; + wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d", + (int) gtk_elem_len); +#ifdef CONFIG_IEEE80211W + res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); + if (res < 0) { + os_free(wnmtfs_ie); + os_free(mgmt); + return -1; + } + igtk_elem_len = res; + pos += igtk_elem_len; + wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", + (int) igtk_elem_len); +#endif /* CONFIG_IEEE80211W */ + + WPA_PUT_LE16((u8 *) + &mgmt->u.action.u.wnm_sleep_resp.keydata_len, + gtk_elem_len + igtk_elem_len); + } + os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len); + /* copy TFS IE here */ + pos += wnmsleep_ie_len; + if (wnmtfs_ie) + os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len); + + len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len + + igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len; + + /* In driver, response frame should be forced to sent when STA is in + * PS mode */ + res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, + mgmt->da, &mgmt->u.action.category, len); + + if (!res) { + wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response " + "frame"); + + /* when entering wnmsleep + * 1. pause the node in driver + * 2. mark the node so that AP won't update GTK/IGTK during + * WNM Sleep + */ + if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT && + wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) { + sta->flags |= WLAN_STA_WNM_SLEEP_MODE; + hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM, + addr, NULL, NULL); + wpa_set_wnmsleep(sta->wpa_sm, 1); + } + /* when exiting wnmsleep + * 1. unmark the node + * 2. start GTK/IGTK update if MFP is not used + * 3. unpause the node in driver + */ + if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT || + wnmsleep_ie.status == + WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) && + wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) { + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + wpa_set_wnmsleep(sta->wpa_sm, 0); + hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, + addr, NULL, NULL); + if (!wpa_auth_uses_mfp(sta->wpa_sm)) + wpa_wnmsleep_rekey_gtk(sta->wpa_sm); + } + } else + wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame"); + +#undef MAX_GTK_SUBELEM_LEN +#undef MAX_IGTK_SUBELEM_LEN + os_free(wnmtfs_ie); + os_free(mgmt); + return res; +} + + +static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, int len) +{ + /* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */ + const u8 *pos = frm; + u8 dialog_token; + struct wnm_sleep_element *wnmsleep_ie = NULL; + /* multiple TFS Req IE (assuming consecutive) */ + u8 *tfsreq_ie_start = NULL; + u8 *tfsreq_ie_end = NULL; + u16 tfsreq_ie_len = 0; + + dialog_token = *pos++; + while (pos + 1 < frm + len) { + u8 ie_len = pos[1]; + if (pos + 2 + ie_len > frm + len) + break; + if (*pos == WLAN_EID_WNMSLEEP) + wnmsleep_ie = (struct wnm_sleep_element *) pos; + else if (*pos == WLAN_EID_TFS_REQ) { + if (!tfsreq_ie_start) + tfsreq_ie_start = (u8 *) pos; + tfsreq_ie_end = (u8 *) pos; + } else + wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized", + *pos); + pos += ie_len + 2; + } + + if (!wnmsleep_ie) { + wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); + return; + } + + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER && + tfsreq_ie_start && tfsreq_ie_end && + tfsreq_ie_end - tfsreq_ie_start >= 0) { + tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) - + tfsreq_ie_start; + wpa_printf(MSG_DEBUG, "TFS Req IE(s) found"); + /* pass the TFS Req IE(s) to driver for processing */ + if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, + &tfsreq_ie_len, + WNM_SLEEP_TFS_REQ_IE_SET)) + wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE"); + } + + ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token, + wnmsleep_ie->action_type, + le_to_host16(wnmsleep_ie->intval)); + + if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { + /* clear the tfs after sending the resp frame */ + ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start, + &tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL); + } +} + + +static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, + const u8 *addr, + u8 dialog_token, + const char *url) +{ + struct ieee80211_mgmt *mgmt; + size_t url_len, len; + u8 *pos; + int res; + + if (url) + url_len = os_strlen(url); + else + url_len = 0; + + mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0)); + if (mgmt == NULL) + return -1; + 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->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token; + mgmt->u.action.u.bss_tm_req.req_mode = 0; + mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); + mgmt->u.action.u.bss_tm_req.validity_interval = 1; + pos = mgmt->u.action.u.bss_tm_req.variable; + if (url) { + *pos++ += url_len; + os_memcpy(pos, url, url_len); + pos += url_len; + } + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " + MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " + "validity_interval=%u", + MAC2STR(addr), dialog_token, + mgmt->u.action.u.bss_tm_req.req_mode, + le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer), + mgmt->u.action.u.bss_tm_req.validity_interval); + + len = pos - &mgmt->u.action.category; + res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, + mgmt->da, &mgmt->u.action.category, len); + os_free(mgmt); + return res; +} + + +static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, + size_t len) +{ + u8 dialog_token, reason; + const u8 *pos, *end; + + if (len < 2) { + wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from " + MACSTR, MAC2STR(addr)); + return; + } + + pos = frm; + end = pos + len; + dialog_token = *pos++; + reason = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from " + MACSTR " dialog_token=%u reason=%u", + MAC2STR(addr), dialog_token, reason); + + wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", + pos, end - pos); + + ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL); +} + + +static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, + const u8 *addr, const u8 *frm, + size_t len) +{ + u8 dialog_token, status_code, bss_termination_delay; + const u8 *pos, *end; + + if (len < 3) { + wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from " + MACSTR, MAC2STR(addr)); + return; + } + + pos = frm; + end = pos + len; + dialog_token = *pos++; + status_code = *pos++; + bss_termination_delay = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from " + MACSTR " dialog_token=%u status_code=%u " + "bss_termination_delay=%u", MAC2STR(addr), dialog_token, + status_code, bss_termination_delay); + + if (status_code == WNM_BSS_TM_ACCEPT) { + if (end - pos < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field"); + return; + } + wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR, + MAC2STR(pos)); + pos += ETH_ALEN; + } + + wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", + pos, end - pos); +} + + +int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + u8 action; + const u8 *payload; + size_t plen; + + if (len < IEEE80211_HDRLEN + 2) + return -1; + + payload = &mgmt->u.action.category; + payload++; + action = *payload++; + plen = (((const u8 *) mgmt) + len) - payload; + + switch (action) { + case WNM_BSS_TRANS_MGMT_QUERY: + ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload, + plen); + return 0; + case WNM_BSS_TRANS_MGMT_RESP: + ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload, + plen); + return 0; + case WNM_SLEEP_MODE_REQ: + ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen); + return 0; + } + + wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, + action, MAC2STR(mgmt->sa)); + return -1; +} + + +int wnm_send_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, int disassoc_timer) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + + os_memset(buf, 0, sizeof(buf)); + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, sta->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_WNM; + mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = 1; + mgmt->u.action.u.bss_tm_req.req_mode = + WNM_BSS_TM_REQ_DISASSOC_IMMINENT; + mgmt->u.action.u.bss_tm_req.disassoc_timer = + host_to_le16(disassoc_timer); + mgmt->u.action.u.bss_tm_req.validity_interval = 0; + + pos = mgmt->u.action.u.bss_tm_req.variable; + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to " + MACSTR, disassoc_timer, MAC2STR(sta->addr)); + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " + "Management Request frame"); + return -1; + } + + return 0; +} + + +int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, const char *url, + int disassoc_timer) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + size_t url_len; + + os_memset(buf, 0, sizeof(buf)); + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, sta->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_WNM; + mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = 1; + mgmt->u.action.u.bss_tm_req.req_mode = + WNM_BSS_TM_REQ_DISASSOC_IMMINENT | + WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; + mgmt->u.action.u.bss_tm_req.disassoc_timer = + host_to_le16(disassoc_timer); + mgmt->u.action.u.bss_tm_req.validity_interval = 0x01; + + pos = mgmt->u.action.u.bss_tm_req.variable; + + /* Session Information URL */ + url_len = os_strlen(url); + if (url_len > 255) + return -1; + *pos++ = url_len; + os_memcpy(pos, url, url_len); + pos += url_len; + + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, "Failed to send BSS Transition " + "Management Request frame"); + return -1; + } + + /* send disassociation frame after time-out */ + if (disassoc_timer) { + int timeout, beacon_int; + + /* + * Prevent STA from reconnecting using cached PMKSA to force + * full authentication with the authentication server (which may + * decide to reject the connection), + */ + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + + beacon_int = hapd->iconf->beacon_int; + if (beacon_int < 1) + beacon_int = 100; /* best guess */ + /* Calculate timeout in ms based on beacon_int in TU */ + timeout = disassoc_timer * beacon_int * 128 / 125; + wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR + " set to %d ms", MAC2STR(sta->addr), timeout); + + sta->timeout_next = STA_DISASSOC_FROM_CLI; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(timeout / 1000, + timeout % 1000 * 1000, + ap_handle_timer, hapd, sta); + } + + return 0; +} diff --git a/contrib/hostapd/src/ap/wnm_ap.h b/contrib/hostapd/src/ap/wnm_ap.h new file mode 100644 index 0000000000..eeaf5eca3a --- /dev/null +++ b/contrib/hostapd/src/ap/wnm_ap.h @@ -0,0 +1,22 @@ +/* + * IEEE 802.11v WNM related functions and structures + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WNM_AP_H +#define WNM_AP_H + +struct sta_info; + +int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len); +int wnm_send_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, int disassoc_timer); +int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, + struct sta_info *sta, const char *url, + int disassoc_timer); + +#endif /* WNM_AP_H */ diff --git a/contrib/hostapd/hostapd/wpa.c b/contrib/hostapd/src/ap/wpa_auth.c similarity index 64% rename from contrib/hostapd/hostapd/wpa.c rename to contrib/hostapd/src/ap/wpa_auth.c index 19b11d59d3..707a63f0cf 100644 --- a/contrib/hostapd/hostapd/wpa.c +++ b/contrib/hostapd/src/ap/wpa_auth.c @@ -1,34 +1,28 @@ /* - * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2009, Jouni Malinen + * IEEE 802.11 RSN / WPA Authenticator + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" - -#ifndef CONFIG_NATIVE_WINDOWS - -#include "common.h" -#include "config.h" -#include "eapol_sm.h" -#include "wpa.h" -#include "sha1.h" -#include "sha256.h" -#include "rc4.h" -#include "aes_wrap.h" -#include "crypto.h" -#include "eloop.h" +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/state_machine.h" +#include "utils/bitfield.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 "crypto/random.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "ap_config.h" #include "ieee802_11.h" -#include "pmksa_cache.h" -#include "state_machine.h" +#include "wpa_auth.h" +#include "pmksa_cache_auth.h" #include "wpa_auth_i.h" #include "wpa_auth_ie.h" @@ -38,7 +32,7 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); -static void wpa_sm_step(struct wpa_state_machine *sm); +static int wpa_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, @@ -46,11 +40,14 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, 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 int wpa_group_config_group_keys(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 */ +static const u32 eapol_key_timeout_first_group = 500; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; @@ -58,11 +55,12 @@ static const int dot11RSNAConfigPMKReauthThreshold = 70; static const int dot11RSNAConfigSATimeout = 60; -static inline void wpa_auth_mic_failure_report( +static inline int 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); + return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); + return 0; } @@ -85,11 +83,14 @@ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, - const u8 *addr, const u8 *prev_psk) + const u8 *addr, + const u8 *p2p_dev_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); + return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr, + prev_psk); } @@ -104,7 +105,7 @@ static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth, static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, int vlan_id, - const char *alg, const u8 *addr, int idx, + enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { if (wpa_auth->cb.set_key == NULL) @@ -123,15 +124,6 @@ static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, } -static inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth, - const u8 *addr, int idx, u8 *seq) -{ - if (wpa_auth->cb.get_seqnum_igtk == NULL) - return -1; - return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq); -} - - static inline int wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *data, size_t data_len, int encrypt) @@ -202,6 +194,7 @@ static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, { if (wpa_auth->cb.disconnect == NULL) return; + wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr)); wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } @@ -226,11 +219,13 @@ 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)) { + if (random_get_bytes(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"); + wpa_hexdump_key(MSG_DEBUG, "GMK", + wpa_auth->group->GMK, WPA_GMK_LEN); } if (wpa_auth->conf.wpa_gmk_rekey) { @@ -288,31 +283,42 @@ static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, } -static void wpa_group_set_key_len(struct wpa_group *group, int cipher) +static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - 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; - } + u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)]; + u8 rkey[32]; + unsigned long ptr; + + if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN); + + /* + * 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); + ptr = (unsigned long) group; + os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr)); + if (random_get_bytes(rkey, sizeof(rkey)) < 0) + return -1; + + if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + group->Counter, WPA_NONCE_LEN) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "Key Counter", + group->Counter, WPA_NONCE_LEN); + + return 0; } static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, - int vlan_id) + int vlan_id, int delay_init) { struct wpa_group *group; - u8 buf[ETH_ALEN + 8 + sizeof(group)]; - u8 rkey[32]; group = os_zalloc(sizeof(struct wpa_group)); if (group == NULL) @@ -320,30 +326,37 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, group->GTKAuthenticator = TRUE; group->vlan_id = vlan_id; + group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); - wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + if (random_pool_ready() != 1) { + wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " + "for secure operations - update keys later when " + "the first station connects"); + } - /* Counter = PRF-256(Random number, "Init Counter", - * Local MAC Address || Time) + /* + * Set initial GMK/Counter value here. The actual values that will be + * used in negotiations will be set once the first station tries to + * connect. This allows more time for collecting additional randomness + * on embedded devices. */ - 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)) { + if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) { 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); + if (delay_init) { + wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start " + "until Beacon frames have been configured"); + /* Initialization is completed in wpa_init_keys(). */ + } else { + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + } return group; } @@ -375,14 +388,15 @@ struct wpa_authenticator * wpa_init(const u8 *addr, return NULL; } - wpa_auth->group = wpa_group_init(wpa_auth, 0); + wpa_auth->group = wpa_group_init(wpa_auth, 0, 1); if (wpa_auth->group == NULL) { os_free(wpa_auth->wpa_ie); os_free(wpa_auth); return NULL; } - wpa_auth->pmksa = pmksa_cache_init(wpa_auth_pmksa_free_cb, wpa_auth); + 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); @@ -395,7 +409,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr, if (wpa_auth->ft_pmk_cache == NULL) { wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); os_free(wpa_auth->wpa_ie); - pmksa_cache_deinit(wpa_auth->pmksa); + pmksa_cache_auth_deinit(wpa_auth->pmksa); os_free(wpa_auth); return NULL; } @@ -411,10 +425,36 @@ struct wpa_authenticator * wpa_init(const u8 *addr, wpa_rekey_gtk, wpa_auth, NULL); } +#ifdef CONFIG_P2P + if (WPA_GET_BE32(conf->ip_addr_start)) { + int count = WPA_GET_BE32(conf->ip_addr_end) - + WPA_GET_BE32(conf->ip_addr_start) + 1; + if (count > 1000) + count = 1000; + if (count > 0) + wpa_auth->ip_pool = bitfield_alloc(count); + } +#endif /* CONFIG_P2P */ + return wpa_auth; } +int wpa_init_keys(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group = wpa_auth->group; + + wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial " + "keys"); + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return -1; + return 0; +} + + /** * wpa_deinit - Deinitialize WPA authenticator * @wpa_auth: Pointer to WPA authenticator data from wpa_init() @@ -431,13 +471,18 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth) wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); #endif /* CONFIG_PEERKEY */ - pmksa_cache_deinit(wpa_auth->pmksa); + 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 */ +#ifdef CONFIG_P2P + bitfield_free(wpa_auth->ip_pool); +#endif /* CONFIG_P2P */ + + os_free(wpa_auth->wpa_ie); group = wpa_auth->group; @@ -474,7 +519,7 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth, * configuration. */ group = wpa_auth->group; - wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group); group->GInit = TRUE; wpa_group_sm_step(wpa_auth, group); group->GInit = FALSE; @@ -485,14 +530,20 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_state_machine * -wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr) +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *p2p_dev_addr) { struct wpa_state_machine *sm; + if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return NULL; + sm = os_zalloc(sizeof(struct wpa_state_machine)); if (sm == NULL) return NULL; os_memcpy(sm->addr, addr, ETH_ALEN); + if (p2p_dev_addr) + os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); sm->wpa_auth = wpa_auth; sm->group = wpa_auth->group; @@ -501,26 +552,25 @@ wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr) } -void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm) +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; + 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; + return 0; } #endif /* CONFIG_IEEE80211R */ if (sm->started) { os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); sm->ReAuthenticationRequest = TRUE; - wpa_sm_step(sm); - return; + return wpa_sm_step(sm); } wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, @@ -528,10 +578,11 @@ void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, sm->started = 1; sm->Init = TRUE; - wpa_sm_step(sm); + if (wpa_sm_step(sm) == 1) + return 1; /* should not really happen */ sm->Init = FALSE; sm->AuthenticationRequest = TRUE; - wpa_sm_step(sm); + return wpa_sm_step(sm); } @@ -549,6 +600,26 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) static void wpa_free_sta_sm(struct wpa_state_machine *sm) { +#ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr)) { + u32 start; + wpa_printf(MSG_DEBUG, "P2P: Free assigned IP " + "address %u.%u.%u.%u from " MACSTR, + sm->ip_addr[0], sm->ip_addr[1], + sm->ip_addr[2], sm->ip_addr[3], + MAC2STR(sm->addr)); + start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start); + bitfield_clear(sm->wpa_auth->ip_pool, + WPA_GET_BE32(sm->ip_addr) - start); + } +#endif /* CONFIG_P2P */ + if (sm->GUpdateStationKeys) { + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + } +#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); @@ -570,6 +641,7 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm) } eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); if (sm->in_step_loop) { @@ -593,14 +665,14 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm) } -static int wpa_replay_counter_valid(struct wpa_state_machine *sm, +static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr, const u8 *replay_counter) { int i; for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { - if (!sm->key_replay[i].valid) + if (!ctr[i].valid) break; - if (os_memcmp(replay_counter, sm->key_replay[i].counter, + if (os_memcmp(replay_counter, ctr[i].counter, WPA_REPLAY_COUNTER_LEN) == 0) return 1; } @@ -608,6 +680,103 @@ static int wpa_replay_counter_valid(struct wpa_state_machine *sm, } +static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr, + const u8 *replay_counter) +{ + int i; + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (ctr[i].valid && + (replay_counter == NULL || + os_memcmp(replay_counter, ctr[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0)) + ctr[i].valid = FALSE; + } +} + + +#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 */ + + +static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int group) +{ + /* Supplicant reported a Michael MIC error */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure (group=%d))", + group); + + if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "ignore Michael MIC failure report since " + "group cipher is not TKIP"); + } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "ignore Michael MIC failure report since " + "pairwise cipher is not TKIP"); + } else { + if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0) + return 1; /* STA entry was removed */ + 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); + return 0; +} + + void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) @@ -619,6 +788,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, 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; @@ -630,6 +802,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, 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); + wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR + " key_info=0x%x type=%u key_data_length=%u", + MAC2STR(sm->addr), key_info, key->type, 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)", @@ -640,7 +815,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } if (sm->wpa == WPA_VERSION_WPA2) { - if (key->type != EAPOL_KEY_TYPE_RSN) { + if (key->type == EAPOL_KEY_TYPE_WPA) { + /* + * Some deployed station implementations seem to send + * msg 4/4 with incorrect type value in WPA2 mode. + */ + wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key " + "with unexpected WPA type in RSN mode"); + } else if (key->type != EAPOL_KEY_TYPE_RSN) { wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " "unexpected type %d in RSN mode", key->type); @@ -655,6 +837,11 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } } + wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce, + WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ @@ -688,7 +875,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, 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 (sm->pairwise == WPA_CIPHER_CCMP || + sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, @@ -704,7 +892,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use HMAC-SHA1-AES " - "with CCMP"); + "with CCMP/GCMP"); return; } } @@ -722,11 +910,44 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } if (!(key_info & WPA_KEY_INFO_REQUEST) && - !wpa_replay_counter_valid(sm, key->replay_counter)) { + !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { int i; - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key %s with unexpected " - "replay counter", msgtxt); + + if (msg == PAIRWISE_2 && + wpa_replay_counter_valid(sm->prev_key_replay, + key->replay_counter) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && + os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) + { + /* + * Some supplicant implementations (e.g., Windows XP + * WZC) update SNonce for each EAPOL-Key 2/4. This + * breaks the workaround on accepting any of the + * pending requests, so allow the SNonce to be updated + * even if we have already sent out EAPOL-Key 3/4. + */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Process SNonce update from STA " + "based on retransmitted EAPOL-Key " + "1/4"); + sm->update_snonce = 1; + wpa_replay_counter_mark_invalid(sm->prev_key_replay, + key->replay_counter); + goto continue_processing; + } + + if (msg == PAIRWISE_2 && + wpa_replay_counter_valid(sm->prev_key_replay, + key->replay_counter) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "ignore retransmitted EAPOL-Key %s - " + "SNonce did not change", msgtxt); + } else { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "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; @@ -739,19 +960,57 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } +continue_processing: switch (msg) { case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && - sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { + sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING && + (!sm->update_snonce || + sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) { 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; } + random_add_randomness(key->key_nonce, WPA_NONCE_LEN); + if (sm->group->reject_4way_hs_for_entropy) { + /* + * The system did not have enough entropy to generate + * strong random numbers. Reject the first 4-way + * handshake(s) and collect some entropy based on the + * information from it. Once enough entropy is + * available, the next atempt will trigger GMK/Key + * Counter update and the station will be allowed to + * continue. + */ + wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to " + "collect more entropy for random number " + "generation"); + random_mark_pool_ready(); + wpa_sta_disconnect(wpa_auth, sm->addr); + 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 || - sm->wpa_ie_len != key_data_length || - os_memcmp(sm->wpa_ie, key + 1, key_data_length) != 0) { + 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"); @@ -760,11 +1019,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, sm->wpa_ie, sm->wpa_ie_len); } wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", - (u8 *) (key + 1), key_data_length); + 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 */ +#ifdef CONFIG_P2P + if (kde.ip_addr_req && kde.ip_addr_req[0] && + wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { + int idx; + wpa_printf(MSG_DEBUG, "P2P: IP address requested in " + "EAPOL-Key exchange"); + idx = bitfield_get_first_zero(wpa_auth->ip_pool); + if (idx >= 0) { + u32 start = WPA_GET_BE32(wpa_auth->conf. + ip_addr_start); + bitfield_set(wpa_auth->ip_pool, idx); + WPA_PUT_BE32(sm->ip_addr, start + idx); + wpa_printf(MSG_DEBUG, "P2P: Assigned IP " + "address %u.%u.%u.%u to " MACSTR, + sm->ip_addr[0], sm->ip_addr[1], + sm->ip_addr[2], sm->ip_addr[3], + MAC2STR(sm->addr)); + } + } +#endif /* CONFIG_P2P */ break; case PAIRWISE_4: if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || @@ -828,7 +1113,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } sm->MICVerified = FALSE; - if (sm->PTK_valid) { + if (sm->PTK_valid && !sm->update_snonce) { 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"); @@ -836,6 +1121,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + sm->pending_1_of_4_timeout = 0; } if (key_info & WPA_KEY_INFO_REQUEST) { @@ -861,17 +1147,10 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, #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); + if (wpa_receive_error_report( + wpa_auth, sm, + !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) + return; /* STA entry was removed */ } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key Request for new " @@ -889,19 +1168,34 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, 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; + /* Do not allow the same key replay counter to be reused. */ + wpa_replay_counter_mark_invalid(sm->key_replay, + key->replay_counter); + + if (msg == PAIRWISE_2) { + /* + * Maintain a copy of the pending EAPOL-Key frames in + * case the EAPOL-Key frame was retransmitted. This is + * needed to allow EAPOL-Key msg 2/4 reply to another + * pending msg 1/4 to update the SNonce to work around + * unexpected supplicant behavior. + */ + os_memcpy(sm->prev_key_replay, sm->key_replay, + sizeof(sm->key_replay)); + } else { + os_memset(sm->prev_key_replay, 0, + sizeof(sm->prev_key_replay)); + } + + /* + * Make sure old valid counters are not accepted anymore and + * do not get copied again. + */ + wpa_replay_counter_mark_invalid(sm->key_replay, NULL); } #ifdef CONFIG_PEERKEY @@ -918,6 +1212,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, os_memcpy(sm->last_rx_eapol_key, data, data_len); sm->last_rx_eapol_key_len = data_len; + sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE); sm->EAPOLKeyReceived = TRUE; sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); @@ -926,25 +1221,37 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } -static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce, - u8 *gtk, size_t gtk_len) +static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, + const u8 *gnonce, u8 *gtk, size_t gtk_len) { - u8 data[ETH_ALEN + WPA_NONCE_LEN]; + u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16]; + u8 *pos; + int ret = 0; - /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ + /* GTK = PRF-X(GMK, "Group key expansion", + * AA || GNonce || Time || random data) + * The example described in the IEEE 802.11 standard uses only AA and + * GNonce as inputs here. Add some more entropy since this derivation + * is done only at the Authenticator and as such, does not need to be + * exactly same. + */ os_memcpy(data, addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); + pos = data + ETH_ALEN + WPA_NONCE_LEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + if (random_get_bytes(pos, 16) < 0) + ret = -1; #ifdef CONFIG_IEEE80211W - sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion", - data, sizeof(data), gtk, gtk_len); + sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len); #else /* CONFIG_IEEE80211W */ - sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", - data, sizeof(data), gtk, gtk_len); + if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len) + < 0) + ret = -1; #endif /* CONFIG_IEEE80211W */ - wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN); - wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len); + return ret; } @@ -953,6 +1260,7 @@ 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; + sm->pending_1_of_4_timeout = 0; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); sm->TimeoutEvt = TRUE; wpa_sm_step(sm); @@ -980,12 +1288,12 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, 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) + else if (sm->pairwise != WPA_CIPHER_TKIP) 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; + 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 " @@ -1027,20 +1335,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, 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; - } + WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); if (key_info & WPA_KEY_INFO_SMK_MESSAGE) WPA_PUT_BE16(key->key_length, 0); @@ -1113,6 +1408,16 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, } wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, key->key_mic); +#ifdef CONFIG_TESTING_OPTIONS + if (!pairwise && + wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0d && + drand48() < + wpa_auth->conf.corrupt_gtk_rekey_mic_probability) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "Corrupting group EAPOL-Key Key MIC"); + key->key_mic[0]++; + } +#endif /* CONFIG_TESTING_OPTIONS */ } wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, @@ -1140,10 +1445,15 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, keyidx, encr, 0); ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; - if (ctr == 1) - timeout_ms = eapol_key_timeout_first; + if (ctr == 1 && wpa_auth->conf.tx_status) + timeout_ms = pairwise ? eapol_key_timeout_first : + eapol_key_timeout_first_group; else timeout_ms = eapol_key_timeout_subseq; + if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) + sm->pending_1_of_4_timeout = 1; + wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " + "counter %d)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } @@ -1178,18 +1488,18 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); - wpa_auth_set_key(sm->wpa_auth, 0, "none", sm->addr, 0, (u8 *) "", 0); + wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); sm->pairwise_set = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } -void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) +int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) { int remove_ptk = 1; if (sm == NULL) - return; + return -1; wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "event %d notification", event); @@ -1204,6 +1514,24 @@ void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) 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 @@ -1217,9 +1545,13 @@ void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) 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; + return 0; #else /* CONFIG_IEEE80211R */ break; #endif /* CONFIG_IEEE80211R */ @@ -1242,23 +1574,7 @@ void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) wpa_remove_ptk(sm); } - wpa_sm_step(sm); -} - - -static const char * wpa_alg_txt(int alg) -{ - switch (alg) { - case WPA_CIPHER_CCMP: - return "CCMP"; - case WPA_CIPHER_TKIP: - return "TKIP"; - case WPA_CIPHER_WEP104: - case WPA_CIPHER_WEP40: - return "WEP"; - default: - return ""; - } + return wpa_sm_step(sm); } @@ -1319,12 +1635,59 @@ SM_STATE(WPA_PTK, AUTHENTICATION) } +static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + if (group->first_sta_seen) + return; + /* + * System has run bit further than at the time hostapd was started + * potentially very early during boot up. This provides better chances + * of collecting more randomness on embedded systems. Re-initialize the + * GMK and Counter here to improve their strength if there was not + * enough entropy available immediately after system startup. + */ + wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first " + "station"); + if (random_pool_ready() != 1) { + wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool " + "to proceed - reject first 4-way handshake"); + group->reject_4way_hs_for_entropy = TRUE; + } else { + group->first_sta_seen = TRUE; + group->reject_4way_hs_for_entropy = FALSE; + } + + wpa_group_init_gmk_and_counter(wpa_auth, group); + wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); +} + + 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); + + wpa_group_ensure_init(sm->wpa_auth, sm->group); sm->ReAuthenticationRequest = FALSE; + + /* + * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat + * ambiguous. The Authenticator state machine uses a counter that is + * incremented by one for each 4-way handshake. However, the security + * analysis of 4-way handshake points out that unpredictable nonces + * help in preventing precomputation attacks. Instead of the state + * machine definition, use an unpredictable nonce value here to provide + * stronger protection against potential precomputation attacks. + */ + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_ERROR, "WPA: Failed to get random data for " + "ANonce."); + sm->Disconnect = TRUE; + return; + } + wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce, + WPA_NONCE_LEN); /* 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 @@ -1376,7 +1739,7 @@ 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); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); if (psk) { os_memcpy(sm->PMK, psk, PMK_LEN); #ifdef CONFIG_IEEE80211R @@ -1439,7 +1802,7 @@ SM_STATE(WPA_PTK, PTKSTART) 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; + size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 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); @@ -1462,13 +1825,15 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; + sm->update_snonce = 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); + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, + sm->p2p_dev_addr, pmk); if (pmk == NULL) break; } else @@ -1492,6 +1857,28 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) 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 */ + + sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { @@ -1531,16 +1918,27 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) { struct wpa_igtk_kde igtk; struct wpa_group *gsm = sm->group; + u8 rsc[WPA_KEY_RSC_LEN]; if (!sm->mgmt_frame_prot) return pos; igtk.keyid[0] = gsm->GN_igtk; igtk.keyid[1] = 0; - if (wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) - < 0) + if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0) os_memset(igtk.pn, 0, sizeof(igtk.pn)); + else + os_memcpy(igtk.pn, rsc, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random IGTK to each STA to prevent use of + * IGTK in the BSS. + */ + if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0) + return pos; + } pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, (const u8 *) &igtk, sizeof(igtk), NULL, 0); @@ -1565,7 +1963,7 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { - u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; size_t gtk_len, kde_len; struct wpa_group *gsm = sm->group; u8 *wpa_ie; @@ -1581,10 +1979,12 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) return; } - /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN]) + /* 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 && @@ -1601,6 +2001,15 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gtk_len) < 0) + return; + gtk = dummy_gtk; + } keyidx = gsm->GN; _rsc = rsc; encr = 1; @@ -1611,11 +2020,35 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) gtk_len = 0; keyidx = 0; _rsc = NULL; + if (sm->rx_eapol_key_secure) { + /* + * It looks like Windows 7 supplicant tries to use + * Secure bit in msg 2/4 after having reported Michael + * MIC failure and it then rejects the 4-way handshake + * if msg 3/4 does not set Secure bit. Work around this + * by setting the Secure bit here even in the case of + * WPA if the supplicant used it first. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "STA used Secure bit in WPA msg 2/4 - " + "set Secure for 3/4 as workaround"); + secure = 1; + } } 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 */ +#ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr) > 0) + kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4; +#endif /* CONFIG_P2P */ kde = os_malloc(kde_len); if (kde == NULL) return; @@ -1623,6 +2056,18 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) 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; @@ -1632,6 +2077,50 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) } 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 */ +#ifdef CONFIG_P2P + if (WPA_GET_BE32(sm->ip_addr) > 0) { + u8 addr[3 * 4]; + os_memcpy(addr, sm->ip_addr, 4); + os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4); + os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4); + pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC, + addr, sizeof(addr), NULL, 0); + } +#endif /* CONFIG_P2P */ + 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 | @@ -1646,15 +2135,8 @@ SM_STATE(WPA_PTK, PTKINITDONE) SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = FALSE; if (sm->Pair) { - char *alg; - int klen; - if (sm->pairwise == WPA_CIPHER_TKIP) { - alg = "TKIP"; - klen = 32; - } else { - alg = "CCMP"; - klen = 16; - } + enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise); + int klen = wpa_cipher_key_len(sm->pairwise); 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); @@ -1709,8 +2191,11 @@ SM_STEP(WPA_PTK) if (sm->Init) SM_ENTER(WPA_PTK, INITIALIZE); else if (sm->Disconnect - /* || FIX: dot11RSNAConfigSALifetime timeout */) + /* || FIX: dot11RSNAConfigSALifetime timeout */) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "WPA_PTK: sm->Disconnect"); SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->DeauthenticationRequest) SM_ENTER(WPA_PTK, DISCONNECTED); else if (sm->AuthenticationRequest) @@ -1746,11 +2231,14 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "INITPMK - keyAvailable = false"); SM_ENTER(WPA_PTK, DISCONNECT); } break; case WPA_PTK_INITPSK: - if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, + NULL)) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, @@ -1766,6 +2254,9 @@ SM_STEP(WPA_PTK) else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKSTART: Retry limit %d reached", + dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); @@ -1783,12 +2274,18 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); break; case WPA_PTK_PTKINITNEGOTIATING: - if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && - sm->EAPOLKeyPairwise && sm->MICVerified) + if (sm->update_snonce) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKINITNEGOTIATING: Retry limit %d " + "reached", + dot11RSNAConfigPairwiseUpdateCount); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); @@ -1817,6 +2314,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) struct wpa_group *gsm = sm->group; u8 *kde, *pos, hdr[2]; size_t kde_len; + u8 *gtk, dummy_gtk[32]; SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); @@ -1837,6 +2335,16 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/2 msg of Group Key Handshake"); + gtk = gsm->GTK[gsm->GN - 1]; + if (sm->wpa_auth->conf.disable_gtk) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0) + return; + gtk = dummy_gtk; + } if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ieee80211w_kde_len(sm); @@ -1848,10 +2356,10 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) 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); + gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); } else { - kde = gsm->GTK[gsm->GN - 1]; + kde = gtk; pos = kde + gsm->GTK_len; } @@ -1927,20 +2435,24 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, { 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); + if (wpa_gmk_to_gtk(group->GMK, "Group key expansion", + wpa_auth->addr, group->GNonce, + group->GTK[group->GN - 1], group->GTK_len) < 0) + ret = -1; + wpa_hexdump_key(MSG_DEBUG, "GTK", + group->GTK[group->GN - 1], group->GTK_len); #ifdef CONFIG_IEEE80211W - if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { - if (os_get_random(group->IGTK[group->GN_igtk - 4], - WPA_IGTK_LEN) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to get new random " - "IGTK"); + if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { + os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_LEN); + if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion", + wpa_auth->addr, group->GNonce, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) ret = -1; - } wpa_hexdump_key(MSG_DEBUG, "IGTK", group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN); } @@ -1973,28 +2485,119 @@ static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { + if (ctx != NULL && ctx != sm->group) + return 0; + 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"); + sm->GUpdateStationKeys = FALSE; 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. + * This should not really happen, so add a debug log entry. + * Since we clear the GKeyDoneStations before the loop, the + * station needs to be counted here anyway. */ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "GUpdateStationKeys already set - do not " - "increment GKeyDoneStations"); - } else { - sm->group->GKeyDoneStations++; - sm->GUpdateStationKeys = TRUE; + "GUpdateStationKeys was already set when " + "marking station for GTK rekeying"); } + + /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */ + if (sm->is_wnmsleep) + return 0; + + sm->group->GKeyDoneStations++; + sm->GUpdateStationKeys = TRUE; + wpa_sm_step(sm); return 0; } +#ifdef CONFIG_WNM +/* update GTK when exiting WNM-Sleep Mode */ +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) +{ + if (sm == NULL || sm->is_wnmsleep) + return; + + wpa_group_update_sta(sm, NULL); +} + + +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) +{ + if (sm) + sm->is_wnmsleep = !!flag; +} + + +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_group *gsm = sm->group; + u8 *start = pos; + + /* + * GTK subelement: + * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | + * Key[5..32] + */ + *pos++ = WNM_SLEEP_SUBELEM_GTK; + *pos++ = 11 + gsm->GTK_len; + /* Key ID in B0-B1 of Key Info */ + WPA_PUT_LE16(pos, gsm->GN & 0x03); + pos += 2; + *pos++ = gsm->GTK_len; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0) + return 0; + pos += 8; + os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos += gsm->GTK_len; + + wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN); + wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit", + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + + return pos - start; +} + + +#ifdef CONFIG_IEEE80211W +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_group *gsm = sm->group; + u8 *start = pos; + + /* + * IGTK subelement: + * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16] + */ + *pos++ = WNM_SLEEP_SUBELEM_IGTK; + *pos++ = 2 + 6 + WPA_IGTK_LEN; + WPA_PUT_LE16(pos, gsm->GN_igtk); + pos += 2; + if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0) + return 0; + pos += 6; + + os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + pos += WPA_IGTK_LEN; + + wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit", + gsm->GN_igtk); + wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit", + gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + + return pos - start; +} +#endif /* CONFIG_IEEE80211W */ +#endif /* CONFIG_WNM */ + + static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, struct wpa_group *group) { @@ -2018,32 +2621,79 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, * 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); + if (group->GKeyDoneStations) { + wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected " + "GKeyDoneStations=%d when starting new GTK rekey", + group->GKeyDoneStations); + group->GKeyDoneStations = 0; + } + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); 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) +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int ret = 0; + + if (wpa_auth_set_key(wpa_auth, group->vlan_id, + wpa_cipher_to_alg(wpa_auth->conf.wpa_group), + broadcast_ether_addr, group->GN, + group->GTK[group->GN - 1], group->GTK_len) < 0) + ret = -1; + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION && + wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, + broadcast_ether_addr, group->GN_igtk, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx) +{ + if (sm->group == ctx) { + wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR + " for discconnection due to fatal failure", + MAC2STR(sm->addr)); + sm->Disconnect = TRUE; + } + + return 0; +} + + +static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE"); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_FATAL_FAILURE; + wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group); +} + + +static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); group->changed = TRUE; group->wpa_group_state = WPA_GROUP_SETKEYSDONE; - wpa_auth_set_key(wpa_auth, group->vlan_id, - wpa_alg_txt(wpa_auth->conf.wpa_group), - NULL, group->GN, group->GTK[group->GN - 1], - group->GTK_len); -#ifdef CONFIG_IEEE80211W - if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { - wpa_auth_set_key(wpa_auth, group->vlan_id, "IGTK", - NULL, group->GN_igtk, - group->IGTK[group->GN_igtk - 4], - WPA_IGTK_LEN); + if (wpa_group_config_group_keys(wpa_auth, group) < 0) { + wpa_group_fatal_failure(wpa_auth, group); + return -1; } -#endif /* CONFIG_IEEE80211W */ + + return 0; } @@ -2052,6 +2702,8 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, { if (group->GInit) { wpa_group_gtk_init(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) { + /* Do not allow group operations */ } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && group->GTKAuthenticator) { wpa_group_setkeysdone(wpa_auth, group); @@ -2067,17 +2719,17 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, } -static void wpa_sm_step(struct wpa_state_machine *sm) +static int wpa_sm_step(struct wpa_state_machine *sm) { if (sm == NULL) - return; + 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; + return 0; } sm->in_step_loop = 1; @@ -2102,7 +2754,9 @@ static void wpa_sm_step(struct wpa_state_machine *sm) 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; } @@ -2141,6 +2795,7 @@ void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) group->GN_igtk = tmp; #endif /* CONFIG_IEEE80211W */ wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); } } @@ -2151,23 +2806,6 @@ static const char * wpa_bool_txt(int bool) } -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 @@ -2176,19 +2814,21 @@ 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]; +#ifdef CONFIG_RSN_PREAUTH + const int preauth = 1; +#else /* CONFIG_RSN_PREAUTH */ + const int preauth = 0; +#endif /* CONFIG_RSN_PREAUTH */ 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 */ + "dot11RSNAPreauthenticationImplemented=%s\n" "dot11RSNAEnabled=%s\n" "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(preauth), 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) @@ -2228,7 +2868,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) !!wpa_auth->conf.wpa_strict_rekey, dot11RSNAConfigGroupUpdateCount, dot11RSNAConfigPairwiseUpdateCount, - wpa_cipher_bits(wpa_auth->conf.wpa_group), + wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, dot11RSNAConfigPMKLifetime, dot11RSNAConfigPMKReauthThreshold, dot11RSNAConfigSATimeout, @@ -2271,29 +2911,10 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) /* 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 + pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ? + WPA_PROTO_RSN : WPA_PROTO_WPA, + sm->pairwise); + if (pairwise == 0) return 0; ret = os_snprintf( @@ -2304,7 +2925,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" /* TODO: dot11RSNAStatsTKIPICVErrors */ "dot11RSNAStatsTKIPLocalMICFailures=%u\n" - "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoteMICFailures=%u\n" /* TODO: dot11RSNAStatsCCMPReplays */ /* TODO: dot11RSNAStatsCCMPDecryptErrors */ /* TODO: dot11RSNAStatsTKIPReplays */, @@ -2401,12 +3022,13 @@ 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) { - if (sm == NULL || sm->wpa != WPA_VERSION_WPA2) + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || + sm->wpa_auth->conf.disable_pmksa_caching) return -1; - if (pmksa_cache_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, - sm->wpa_auth->addr, sm->addr, session_timeout, - eapol, sm->wpa_key_mgmt)) + 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; @@ -2421,15 +3043,31 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, if (wpa_auth == NULL) return -1; - if (pmksa_cache_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, - sta_addr, session_timeout, eapol, - WPA_KEY_MGMT_IEEE8021X)) + 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; } +void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr) +{ + struct rsn_pmksa_cache_entry *pmksa; + + if (wpa_auth == NULL || wpa_auth->pmksa == NULL) + return; + pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); + if (pmksa) { + wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for " + MACSTR " based on request", MAC2STR(sta_addr)); + pmksa_cache_free_entry(wpa_auth->pmksa, pmksa); + } +} + + static struct wpa_group * wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) { @@ -2440,7 +3078,7 @@ wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", vlan_id); - group = wpa_group_init(wpa_auth, vlan_id); + group = wpa_group_init(wpa_auth, vlan_id, 0); if (group == NULL) return NULL; @@ -2474,6 +3112,9 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) if (sm->group == group) return 0; + if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) + return -1; + wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); @@ -2481,4 +3122,59 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) return 0; } -#endif /* CONFIG_NATIVE_WINDOWS */ + +void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int ack) +{ + if (wpa_auth == NULL || sm == NULL) + return; + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR + " ack=%d", MAC2STR(sm->addr), ack); + if (sm->pending_1_of_4_timeout && ack) { + /* + * Some deployed supplicant implementations update their SNonce + * for each EAPOL-Key 2/4 message even within the same 4-way + * handshake and then fail to use the first SNonce when + * deriving the PTK. This results in unsuccessful 4-way + * handshake whenever the relatively short initial timeout is + * reached and EAPOL-Key 1/4 is retransmitted. Try to work + * around this by increasing the timeout now that we know that + * the station has received the frame. + */ + int timeout_ms = eapol_key_timeout_subseq; + wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 " + "timeout by %u ms because of acknowledged frame", + timeout_ms); + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + eloop_register_timeout(timeout_ms / 1000, + (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, wpa_auth, sm); + } +} + + +int wpa_auth_uses_sae(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return wpa_key_mgmt_sae(sm->wpa_key_mgmt); +} + + +int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE; +} + + +#ifdef CONFIG_P2P +int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr) +{ + if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0) + return -1; + os_memcpy(addr, sm->ip_addr, 4); + return 0; +} +#endif /* CONFIG_P2P */ diff --git a/contrib/hostapd/hostapd/wpa.h b/contrib/hostapd/src/ap/wpa_auth.h similarity index 79% rename from contrib/hostapd/hostapd/wpa.h rename to contrib/hostapd/src/ap/wpa_auth.h index 7d9b3d3106..bc3dec45a0 100644 --- a/contrib/hostapd/hostapd/wpa.h +++ b/contrib/hostapd/src/ap/wpa_auth.h @@ -2,21 +2,16 @@ * hostapd - IEEE 802.11i-2004 / WPA Authenticator * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_H #define WPA_AUTH_H -#include "eapol_common.h" -#include "wpa_common.h" +#include "common/defs.h" +#include "common/eapol_common.h" +#include "common/wpa_common.h" #ifdef _MSC_VER #pragma pack(push, 1) @@ -44,13 +39,9 @@ struct ft_rrb_frame { #define FT_PACKET_R0KH_R1KH_RESP 201 #define FT_PACKET_R0KH_R1KH_PUSH 202 -#ifndef ETH_P_RRB -#define ETH_P_RRB 0x890D -#endif /* ETH_P_RRB */ - #define FT_R0KH_R1KH_PULL_DATA_LEN 44 #define FT_R0KH_R1KH_RESP_DATA_LEN 76 -#define FT_R0KH_R1KH_PUSH_DATA_LEN 80 +#define FT_R0KH_R1KH_PUSH_DATA_LEN 88 struct ft_r0kh_r1kh_pull_frame { u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ @@ -77,14 +68,15 @@ struct ft_r0kh_r1kh_resp_frame { u8 s1kh_id[ETH_ALEN]; /* copied from pull */ u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; - u8 pad[4]; /* 8-octet boundary for AES key wrap */ + 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 (80) */ + le16 data_length; /* little endian length of data (88) */ u8 ap_address[ETH_ALEN]; /* Encrypted with AES key-wrap */ @@ -95,6 +87,8 @@ struct ft_r0kh_r1kh_push_frame { 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; @@ -142,13 +136,12 @@ struct wpa_auth_config { int eapol_version; int peerkey; int wmm_enabled; + int wmm_uapsd; + int disable_pmksa_caching; int okc; + int tx_status; #ifdef CONFIG_IEEE80211W - enum { - WPA_NO_IEEE80211W = 0, - WPA_IEEE80211W_OPTIONAL = 1, - WPA_IEEE80211W_REQUIRED = 2 - } ieee80211w; + enum mfp_options ieee80211w; #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R #define SSID_LEN 32 @@ -163,7 +156,19 @@ struct wpa_auth_config { struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; + int ft_over_ds; #endif /* CONFIG_IEEE80211R */ + int disable_gtk; + int ap_mlme; +#ifdef CONFIG_TESTING_OPTIONS + double corrupt_gtk_rekey_mic_probability; +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_P2P + u8 ip_addr_go[4]; + u8 ip_addr_mask[4]; + u8 ip_addr_start[4]; + u8 ip_addr_end[4]; +#endif /* CONFIG_P2P */ }; typedef enum { @@ -181,16 +186,16 @@ struct wpa_auth_callbacks { 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); + int (*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); + const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk); int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); - int (*set_key)(void *ctx, int vlan_id, const char *alg, const u8 *addr, - int idx, u8 *key, size_t key_len); + int (*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 (*get_seqnum_igtk)(void *ctx, const u8 *addr, int idx, u8 *seq); int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data, size_t data_len, int encrypt); int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm, @@ -203,12 +208,15 @@ struct wpa_auth_callbacks { 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); + int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, + size_t tspec_ielen); #endif /* CONFIG_IEEE80211R */ }; struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, struct wpa_auth_callbacks *cb); +int wpa_init_keys(struct wpa_authenticator *wpa_auth); void wpa_deinit(struct wpa_authenticator *wpa_auth); int wpa_reconfig(struct wpa_authenticator *wpa_auth, struct wpa_auth_config *conf); @@ -226,9 +234,10 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, const u8 *mdie, size_t mdie_len); int wpa_auth_uses_mfp(struct wpa_state_machine *sm); struct wpa_state_machine * -wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr); -void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm); +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *p2p_dev_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, @@ -239,7 +248,7 @@ typedef enum { WPA_REAUTH_EAPOL, WPA_ASSOC_FT } wpa_event; void wpa_remove_ptk(struct wpa_state_machine *sm); -void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); +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); @@ -262,11 +271,16 @@ 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); +void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); +void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int ack); #ifdef CONFIG_IEEE80211R u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, - size_t max_len, int auth_alg); + 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, @@ -281,4 +295,14 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); #endif /* CONFIG_IEEE80211R */ +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos); +int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos); + +int wpa_auth_uses_sae(struct wpa_state_machine *sm); +int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm); + +int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr); + #endif /* WPA_AUTH_H */ diff --git a/contrib/hostapd/hostapd/wpa_ft.c b/contrib/hostapd/src/ap/wpa_auth_ft.c similarity index 75% rename from contrib/hostapd/hostapd/wpa_ft.c rename to contrib/hostapd/src/ap/wpa_auth_ft.c index 31391051fe..c22c4ccae3 100644 --- a/contrib/hostapd/hostapd/wpa_ft.c +++ b/contrib/hostapd/src/ap/wpa_auth_ft.c @@ -1,27 +1,23 @@ /* * hostapd - IEEE 802.11r - Fast BSS Transition - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" -#include "common.h" -#include "config.h" -#include "wpa.h" -#include "aes_wrap.h" +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "crypto/aes_wrap.h" +#include "crypto/random.h" +#include "ap_config.h" #include "ieee802_11.h" -#include "defs.h" +#include "wmm.h" +#include "wpa_auth.h" #include "wpa_auth_i.h" -#include "wpa_auth_ie.h" #ifdef CONFIG_IEEE80211R @@ -31,6 +27,7 @@ static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, { 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); } @@ -55,6 +52,19 @@ wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) } +static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, + u8 *tspec_ie, size_t tspec_ielen) +{ + if (wpa_auth->cb.add_tspec == NULL) { + wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); + return -1; + } + return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, + tspec_ielen); +} + + int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) { u8 *pos = buf; @@ -66,18 +76,20 @@ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) *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; + capab = 0; + if (conf->ft_over_ds) + capab |= RSN_FT_CAPAB_FT_OVER_DS; *pos++ = capab; return pos - buf; } -static int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, - size_t r0kh_id_len, - const u8 *anonce, const u8 *snonce, - u8 *buf, size_t len, const u8 *subelem, - size_t subelem_len) +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; @@ -127,6 +139,7 @@ struct wpa_ft_pmk_r0_sa { 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; }; @@ -136,6 +149,7 @@ struct wpa_ft_pmk_r1_sa { 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 */ }; @@ -181,7 +195,7 @@ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *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) + 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; @@ -195,6 +209,7 @@ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, 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; @@ -205,7 +220,7 @@ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0_name, - u8 *pmk_r0) + u8 *pmk_r0, int *pairwise) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; @@ -216,6 +231,8 @@ static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, 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; } @@ -228,7 +245,7 @@ static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1, - const u8 *pmk_r1_name) + 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; @@ -242,6 +259,7 @@ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, 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; @@ -252,7 +270,7 @@ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1_name, - u8 *pmk_r1) + u8 *pmk_r1, int *pairwise) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; @@ -263,6 +281,8 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, 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; } @@ -301,7 +321,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, /* aes_wrap() does not support inplace encryption, so use a temporary * buffer for the data. */ - if (os_get_random(f.nonce, sizeof(f.nonce))) { + if (random_get_bytes(f.nonce, sizeof(f.nonce))) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; @@ -309,6 +329,7 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, 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); + os_memset(f.pad, 0, sizeof(f.pad)); if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, f.nonce, frame.nonce) < 0) @@ -324,7 +345,7 @@ 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], pmk_r1_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; @@ -344,16 +365,19 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name); + wpa_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, pmk_r1_name); + pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); - wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_name); + wpa_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, pmk_r1_name, + 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); @@ -371,17 +395,6 @@ static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, } -#ifdef CONFIG_IEEE80211W -static inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth, - const u8 *addr, int idx, u8 *seq) -{ - if (wpa_auth->cb.get_seqnum_igtk == NULL) - return -1; - return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq); -} -#endif /* CONFIG_IEEE80211W */ - - static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) { u8 *subelem; @@ -404,7 +417,7 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) pad_len = 8 - pad_len; if (key_len + pad_len < 16) pad_len += 8; - if (pad_len) { + if (pad_len && key_len < sizeof(keybuf)) { os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); os_memset(keybuf + key_len, 0, pad_len); keybuf[key_len] = 0xdd; @@ -414,20 +427,21 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) key = gsm->GTK[gsm->GN - 1]; /* - * Sub-elem ID[1] | Length[1] | Key Info[1] | Key Length[1] | RSC[8] | + * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | * Key[5..32]. */ - subelem_len = 12 + key_len + 8; + subelem_len = 13 + key_len + 8; subelem = os_zalloc(subelem_len); if (subelem == NULL) return NULL; subelem[0] = FTIE_SUBELEM_GTK; - subelem[1] = 10 + key_len + 8; - subelem[2] = gsm->GN & 0x03; /* Key ID in B0-B1 of Key Info */ - subelem[3] = gsm->GTK_len; - wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 4); - if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 12)) { + 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; } @@ -456,7 +470,7 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) *pos++ = subelem_len - 2; WPA_PUT_LE16(pos, gsm->GN_igtk); pos += 2; - wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN_igtk, pos); + 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, @@ -471,33 +485,171 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) #endif /* CONFIG_IEEE80211W */ +static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm, + 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 && sm->wpa_auth->conf.ap_mlme) { + 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 */ + + if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) { + struct wmm_tspec_element *tspec; + int res; + + tspec = (struct wmm_tspec_element *) pos; + os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); + res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos, + sizeof(*tspec)); + if (res >= 0) { + if (res) + rdie->status_code = host_to_le16(res); + else { + /* TSPEC accepted; include updated TSPEC in + * response */ + rdie->descr_count = 1; + pos += sizeof(*tspec); + } + return pos; + } + } + + 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(struct wpa_state_machine *sm, 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(sm, 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) + size_t max_len, int auth_alg, + const u8 *req_ies, size_t req_ies_len) { - u8 *end, *mdie, *ftie, *rsnie, *r0kh_id, *subelem = NULL; - size_t mdie_len, ftie_len, rsnie_len, r0kh_id_len, subelem_len = 0; + 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) + if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return pos; end = pos + max_len; - /* RSN */ - res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); - if (res < 0) - return pos; - rsnie = pos; - rsnie_len = res; - pos += res; + 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); @@ -512,6 +664,8 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, 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; @@ -537,8 +691,10 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, } 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, NULL, NULL, pos, + res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos, end - pos, subelem, subelem_len); os_free(subelem); if (res < 0) @@ -547,125 +703,42 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *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); - _ftie->mic_control[1] = 3; /* Information element count */ - if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6, + 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(sm, 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, NULL, 0, _ftie->mic) < 0) + 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; } -struct wpa_ft_ies { - const u8 *mdie; - size_t mdie_len; - const u8 *ftie; - size_t ftie_len; - const u8 *r1kh_id; - const u8 *gtk; - size_t gtk_len; - const u8 *r0kh_id; - size_t r0kh_id_len; - const u8 *rsn; - size_t rsn_len; - const u8 *rsn_pmkid; -}; - - -static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, - struct wpa_ft_ies *parse) -{ - const u8 *end, *pos; - - parse->ftie = ie; - parse->ftie_len = ie_len; - - pos = ie + sizeof(struct rsn_ftie); - end = ie + ie_len; - - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case FTIE_SUBELEM_R1KH_ID: - if (pos[1] != FT_R1KH_ID_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " - "length in FTIE: %d", pos[1]); - return -1; - } - parse->r1kh_id = pos + 2; - break; - case FTIE_SUBELEM_GTK: - parse->gtk = pos + 2; - parse->gtk_len = pos[1]; - break; - case FTIE_SUBELEM_R0KH_ID: - if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { - wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " - "length in FTIE: %d", pos[1]); - return -1; - } - parse->r0kh_id = pos + 2; - parse->r0kh_id_len = pos[1]; - break; - } - - pos += 2 + pos[1]; - } - - return 0; -} - - -static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse) -{ - const u8 *end, *pos; - struct wpa_ie_data data; - int ret; - - os_memset(parse, 0, sizeof(*parse)); - if (ies == NULL) - return 0; - - pos = ies; - end = ies + ies_len; - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case WLAN_EID_RSN: - parse->rsn = pos + 2; - parse->rsn_len = pos[1]; - ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, - parse->rsn_len + 2, - &data); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to parse " - "RSN IE: %d", ret); - return -1; - } - if (data.num_pmkid == 1 && data.pmkid) - parse->rsn_pmkid = data.pmkid; - break; - case WLAN_EID_MOBILITY_DOMAIN: - parse->mdie = pos + 2; - parse->mdie_len = pos[1]; - break; - case WLAN_EID_FAST_BSS_TRANSITION: - if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) - return -1; - break; - } - - pos += 2 + pos[1]; - } - - return 0; -} - - static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, int vlan_id, - const char *alg, const u8 *addr, int idx, + enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { if (wpa_auth->cb.set_key == NULL) @@ -675,25 +748,25 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, } -static void wpa_ft_install_ptk(struct wpa_state_machine *sm) +void wpa_ft_install_ptk(struct wpa_state_machine *sm) { - char *alg; + enum wpa_alg alg; int klen; /* MLME-SETKEYS.request(PTK) */ - if (sm->pairwise == WPA_CIPHER_TKIP) { - alg = "TKIP"; - klen = 32; - } else if (sm->pairwise == WPA_CIPHER_CCMP) { - alg = "CCMP"; - klen = 16; - } else + alg = wpa_cipher_to_alg(sm->pairwise); + klen = wpa_cipher_key_len(sm->pairwise); + if (!wpa_cipher_valid_pairwise(sm->pairwise)) { + 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. Alternatively, could - * re-configure PTK at that point(?). + * 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)) @@ -717,6 +790,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, size_t buflen, ptk_len; int ret; u8 *pos, *end; + int pairwise; *resp_ies = NULL; *resp_ies_len = 0; @@ -772,8 +846,8 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); - if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1) < - 0) { + if (wpa_ft_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 " @@ -793,7 +867,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, 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)) { + if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "ANonce"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -804,7 +878,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); - ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64; + ptk_len = pairwise == WPA_CIPHER_TKIP ? 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); @@ -812,6 +886,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, (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) + @@ -895,6 +970,7 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, struct rsn_mdie *mdie; struct rsn_ftie *ftie; u8 mic[16]; + unsigned int count; if (sm == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -938,20 +1014,79 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return WLAN_STATUS_INVALID_FTIE; } - /* - * Assume that MDIE, FTIE, and RSN IE are protected and that there is - * no RIC, so total of 3 protected IEs. - */ - if (ftie->mic_control[1] != 3) { - wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in FTIE (%d)", - ftie->mic_control[1]); - return WLAN_STATUS_INVALID_FTIE; + if (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 += ieee802_11_ie_count(parse.ric, parse.ric_len); + 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, NULL, 0, + 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; @@ -959,8 +1094,16 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, if (os_memcmp(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, + MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", + parse.mdie - 2, parse.mdie_len + 2); + wpa_hexdump(MSG_MSGDUMP, "FT: FTIE", + parse.ftie - 2, parse.ftie_len + 2); + wpa_hexdump(MSG_MSGDUMP, "FT: RSN", + parse.rsn - 2, parse.rsn_len + 2); return WLAN_STATUS_INVALID_FTIE; } @@ -1023,6 +1166,8 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) /* RRB - Forward action frame to the target AP */ frame = os_malloc(sizeof(*frame) + len); + if (frame == NULL) + return -1; frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame->packet_type = FT_PACKET_REQUEST; frame->action_length = host_to_le16(len); @@ -1073,6 +1218,10 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len; frame = os_malloc(sizeof(*frame) + rlen); + if (frame == NULL) { + os_free(resp_ies); + return -1; + } frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; frame->packet_type = FT_PACKET_RESPONSE; frame->action_length = host_to_le16(rlen); @@ -1107,6 +1256,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, 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"); @@ -1154,8 +1304,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); - if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0) < - 0) { + 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; @@ -1166,6 +1316,8 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, 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); + os_memset(r.pad, 0, sizeof(r.pad)); if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, r.nonce, resp.nonce) < 0) { @@ -1187,6 +1339,7 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, { 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"); @@ -1227,16 +1380,19 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, * 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, MAC2STR(f.r1kh_id), MAC2STR(f.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); + 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; @@ -1251,6 +1407,7 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, struct ft_remote_r0kh *r0kh; struct os_time now; os_time_t tsend; + int pairwise; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); @@ -1299,14 +1456,17 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, return -1; } + pairwise = le_to_host16(f.pairwise); wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.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); + 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; @@ -1436,7 +1596,7 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, 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) + const u8 *s1kh_id, int pairwise) { struct ft_r0kh_r1kh_push_frame frame, f; struct os_time now; @@ -1460,6 +1620,8 @@ static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, WPA_PMK_NAME_LEN); os_get_time(&now); WPA_PUT_LE32(f.timestamp, now.sec); + f.pairwise = host_to_le16(pairwise); + os_memset(f.pad, 0, sizeof(f.pad)); if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, f.timestamp, frame.timestamp) < 0) return; @@ -1492,7 +1654,7 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) r1kh = wpa_auth->conf.r1kh_list; while (r1kh) { - wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); + wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise); r1kh = r1kh->next; } } diff --git a/contrib/hostapd/src/ap/wpa_auth_glue.c b/contrib/hostapd/src/ap/wpa_auth_glue.c new file mode 100644 index 0000000000..5af1495030 --- /dev/null +++ b/contrib/hostapd/src/ap/wpa_auth_glue.c @@ -0,0 +1,628 @@ +/* + * hostapd / WPA authenticator glue code + * Copyright (c) 2002-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/sae.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 "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" +#include "wpa_auth_glue.h" + + +static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, + struct hostapd_config *iconf, + struct wpa_auth_config *wconf) +{ + os_memset(wconf, 0, sizeof(*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->disable_pmksa_caching = conf->disable_pmksa_caching; + 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; + wconf->ft_over_ds = conf->ft_over_ds; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_HS20 + wconf->disable_gtk = conf->disable_dgaf; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_TESTING_OPTIONS + wconf->corrupt_gtk_rekey_mic_probability = + iconf->corrupt_gtk_rekey_mic_probability; +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_P2P + os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4); + os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4); + os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4); + os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4); +#endif /* CONFIG_P2P */ +} + + +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 int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + return 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 *p2p_dev_addr, + const u8 *prev_psk) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + const u8 *psk; + +#ifdef CONFIG_SAE + if (sta && sta->auth_alg == WLAN_AUTH_SAE) { + if (!sta->sae || prev_psk) + return NULL; + return sta->sae->pmk; + } +#endif /* CONFIG_SAE */ + + psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); + /* + * This is about to iterate over all psks, prev_psk gives the last + * returned psk which should not be returned again. + * logic list (all hostapd_get_psk; all sta->psk) + */ + if (sta && sta->psk && !psk) { + struct hostapd_sta_wpa_psk_short *pos; + psk = sta->psk->psk; + for (pos = sta->psk; pos; pos = pos->next) { + if (pos->psk == prev_psk) { + psk = pos->next ? pos->next->psk : NULL; + break; + } + } + } + return 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 hostapd_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; + struct sta_info *sta; + u32 flags = 0; + + sta = ap_get_sta(hapd, addr); + if (sta) + flags = hostapd_sta_flags_to_drv(sta->flags); + + return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len, + encrypt, flags); +} + + +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->interfaces == NULL || + hapd->iface->interfaces->for_each_interface == NULL) + return -1; + data.cb = cb; + data.cb_ctx = cb_ctx; + return hapd->iface->interfaces->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); + wpa_ft_rrb_rx(hapd->wpa_auth, + 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; + struct l2_ethhdr *buf; + int ret; + +#ifdef CONFIG_IEEE80211R + if (proto == ETH_P_RRB && hapd->iface->interfaces && + hapd->iface->interfaces->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->interfaces->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; + + buf = os_malloc(sizeof(*buf) + data_len); + if (buf == NULL) + return -1; + os_memcpy(buf->h_dest, dst, ETH_ALEN); + os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN); + buf->h_proto = host_to_be16(proto); + os_memcpy(buf + 1, data, data_len); + ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf, + sizeof(*buf) + data_len); + os_free(buf); + return ret; +} + + +#ifdef CONFIG_IEEE80211R + +static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, + const u8 *data, size_t data_len) +{ + struct hostapd_data *hapd = ctx; + int res; + struct ieee80211_mgmt *m; + size_t mlen; + struct sta_info *sta; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL || sta->wpa_sm == NULL) + return -1; + + m = os_zalloc(sizeof(*m) + data_len); + if (m == NULL) + return -1; + mlen = ((u8 *) &m->u - (u8 *) m) + data_len; + m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(m->da, dst, ETH_ALEN); + os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); + os_memcpy(&m->u, data, data_len); + + res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen, 0); + os_free(m); + return res; +} + + +static struct wpa_state_machine * +hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0) + return NULL; + + 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, NULL); + 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; + struct l2_ethhdr *ethhdr; + if (len < sizeof(*ethhdr)) + return; + ethhdr = (struct l2_ethhdr *) buf; + wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " + MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest)); + wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr), + len - sizeof(*ethhdr)); +} + + +static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, + u8 *tspec_ie, size_t tspec_ielen) +{ + struct hostapd_data *hapd = ctx; + return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); +} + +#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, hapd->iconf, &_conf); + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS) + _conf.tx_status = 1; + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) + _conf.ap_mlme = 1; + 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; + cb.add_tspec = hostapd_wpa_auth_add_tspec; +#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, 1); + 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, hapd->iconf, &wpa_auth_conf); + wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); +} + + +void hostapd_deinit_wpa(struct hostapd_data *hapd) +{ + ieee80211_tkip_countermeasures_deinit(hapd); + rsn_preauth_iface_deinit(hapd); + if (hapd->wpa_auth) { + wpa_deinit(hapd->wpa_auth); + hapd->wpa_auth = NULL; + + if (hostapd_set_privacy(hapd, 0)) { + wpa_printf(MSG_DEBUG, "Could not disable " + "PrivacyInvoked for interface %s", + hapd->conf->iface); + } + + if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { + wpa_printf(MSG_DEBUG, "Could not remove generic " + "information element from interface %s", + hapd->conf->iface); + } + } + ieee802_1x_deinit(hapd); + +#ifdef CONFIG_IEEE80211R + l2_packet_deinit(hapd->l2); + hapd->l2 = NULL; +#endif /* CONFIG_IEEE80211R */ +} diff --git a/contrib/hostapd/src/ap/wpa_auth_glue.h b/contrib/hostapd/src/ap/wpa_auth_glue.h new file mode 100644 index 0000000000..1b13ae7bef --- /dev/null +++ b/contrib/hostapd/src/ap/wpa_auth_glue.h @@ -0,0 +1,16 @@ +/* + * hostapd / WPA authenticator glue code + * Copyright (c) 2002-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/hostapd/wpa_auth_i.h b/contrib/hostapd/src/ap/wpa_auth_i.h similarity index 85% rename from contrib/hostapd/hostapd/wpa_auth_i.h rename to contrib/hostapd/src/ap/wpa_auth_i.h index 925d3ee459..fcd5878e74 100644 --- a/contrib/hostapd/hostapd/wpa_auth_i.h +++ b/contrib/hostapd/src/ap/wpa_auth_i.h @@ -2,14 +2,8 @@ * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_I_H @@ -32,6 +26,7 @@ struct wpa_state_machine { struct wpa_group *group; u8 addr[ETH_ALEN]; + u8 p2p_dev_addr[ETH_ALEN]; enum { WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, @@ -69,10 +64,11 @@ struct wpa_state_machine { Boolean pairwise_set; int keycount; Boolean Pair; - struct { + struct wpa_key_replay_counter { u8 counter[WPA_REPLAY_COUNTER_LEN]; Boolean valid; - } key_replay[RSNA_MAX_EAPOL_RETRIES]; + } key_replay[RSNA_MAX_EAPOL_RETRIES], + prev_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; @@ -86,10 +82,13 @@ struct wpa_state_machine { unsigned int pending_deinit:1; unsigned int started:1; unsigned int mgmt_frame_prot:1; + unsigned int rx_eapol_key_secure:1; + unsigned int update_snonce:1; #ifdef CONFIG_IEEE80211R unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; #endif /* CONFIG_IEEE80211R */ + unsigned int is_wnmsleep:1; u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; @@ -116,7 +115,16 @@ struct wpa_state_machine { * 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 */ + + int pending_1_of_4_timeout; + +#ifdef CONFIG_P2P + u8 ip_addr[4]; +#endif /* CONFIG_P2P */ }; @@ -135,13 +143,16 @@ struct wpa_group { enum { WPA_GROUP_GTK_INIT = 0, - WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE + WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE, + WPA_GROUP_FATAL_FAILURE } wpa_group_state; u8 GMK[WPA_GMK_LEN]; u8 GTK[2][WPA_GTK_MAX_LEN]; u8 GNonce[WPA_NONCE_LEN]; Boolean changed; + Boolean first_sta_seen; + Boolean reject_4way_hs_for_entropy; #ifdef CONFIG_IEEE80211W u8 IGTK[2][WPA_IGTK_LEN]; int GN_igtk, GM_igtk; @@ -178,6 +189,10 @@ struct wpa_authenticator { struct rsn_pmksa_cache *pmksa; struct wpa_ft_pmk_cache *ft_pmk_cache; + +#ifdef CONFIG_P2P + struct bitfield *ip_pool; +#endif /* CONFIG_P2P */ }; @@ -212,10 +227,16 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth, #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/contrib/hostapd/hostapd/wpa_auth_ie.c b/contrib/hostapd/src/ap/wpa_auth_ie.c similarity index 71% rename from contrib/hostapd/hostapd/wpa_auth_ie.c rename to contrib/hostapd/src/ap/wpa_auth_ie.c index 7e01635284..274f4d6216 100644 --- a/contrib/hostapd/hostapd/wpa_auth_ie.c +++ b/contrib/hostapd/src/ap/wpa_auth_ie.c @@ -2,33 +2,34 @@ * hostapd - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" -#include "common.h" -#include "config.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 "eapol_sm.h" -#include "wpa.h" -#include "pmksa_cache.h" +#include "wpa_auth.h" +#include "pmksa_cache_auth.h" #include "wpa_auth_ie.h" #include "wpa_auth_i.h" +#ifdef CONFIG_RSN_TESTING +int rsn_testing = 0; +#endif /* CONFIG_RSN_TESTING */ + + 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; + u32 suite; hdr = (struct wpa_ie_hdr *) buf; hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; @@ -36,46 +37,25 @@ static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) 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 { + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group); + if (suite == 0) { wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", conf->wpa_group); return -1; } + RSN_SELECTOR_PUT(pos, suite); 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++; - } - + num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise); if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", conf->wpa_pairwise); return -1; } + pos += num_suites * WPA_SELECTOR_LEN; WPA_PUT_LE16(count, num_suites); num_suites = 0; @@ -112,49 +92,48 @@ 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; + int num_suites, res; u8 *pos, *count; u16 capab; + u32 suite; 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 { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group); + if (suite == 0) { wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", conf->wpa_group); return -1; } + RSN_SELECTOR_PUT(pos, suite); 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); +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1)); 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); +#endif /* CONFIG_RSN_TESTING */ + + res = rsn_cipher_put_suites(pos, conf->rsn_pairwise); + num_suites += res; + pos += res * RSN_SELECTOR_LEN; + +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); pos += RSN_SELECTOR_LEN; num_suites++; } +#endif /* CONFIG_RSN_TESTING */ if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", @@ -167,6 +146,14 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, count = pos; pos += 2; +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); pos += RSN_SELECTOR_LEN; @@ -201,6 +188,26 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, num_suites++; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2)); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_RSN_TESTING */ if (num_suites == 0) { wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", @@ -220,12 +227,16 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); } #ifdef CONFIG_IEEE80211W - if (conf->ieee80211w != WPA_NO_IEEE80211W) { + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { capab |= WPA_CAPABILITY_MFPC; - if (conf->ieee80211w == IEEE80211W_REQUIRED) + if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) capab |= WPA_CAPABILITY_MFPR; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) + capab |= BIT(8) | BIT(14) | BIT(15); +#endif /* CONFIG_RSN_TESTING */ WPA_PUT_LE16(pos, capab); pos += 2; @@ -240,7 +251,7 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } #ifdef CONFIG_IEEE80211W - if (conf->ieee80211w != WPA_NO_IEEE80211W) { + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { if (pos + 2 + 4 > buf + len) return -1; if (pmkid == NULL) { @@ -255,6 +266,29 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_RSN_TESTING + if (rsn_testing) { + /* + * Fill in any defined fields and add extra data to the end of + * the element. + */ + int pmkid_count_set = pmkid != NULL; + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) + pmkid_count_set = 1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) { + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } + + os_memset(pos, 0x12, 17); + pos += 17; + } +#endif /* CONFIG_RSN_TESTING */ + hdr->len = (pos - buf) - 2; return pos - buf; @@ -276,8 +310,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) pos += res; } #ifdef CONFIG_IEEE80211R - if (wpa_auth->conf.wpa_key_mgmt & - (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) { + if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { res = wpa_write_mdie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); if (res < 0) @@ -321,114 +354,6 @@ u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, } -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; @@ -494,36 +419,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) selector = RSN_AUTH_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + else if (data.key_mgmt & WPA_KEY_MGMT_SAE) + selector = RSN_AUTH_KEY_MGMT_SAE; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) + selector = RSN_AUTH_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ 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 = wpa_cipher_to_suite(WPA_PROTO_RSN, + data.pairwise_cipher); + if (!selector) 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 = wpa_cipher_to_suite(WPA_PROTO_RSN, + data.group_cipher); + if (!selector) 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); @@ -535,30 +452,16 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, 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; + selector = wpa_cipher_to_suite(WPA_PROTO_WPA, + data.pairwise_cipher); + if (!selector) + selector = RSN_CIPHER_SUITE_TKIP; 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_to_suite(WPA_PROTO_WPA, + data.group_cipher); + if (!selector) 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) { @@ -594,6 +497,12 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + else if (key_mgmt & WPA_KEY_MGMT_SAE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE; + else if (key_mgmt & WPA_KEY_MGMT_FT_SAE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; else @@ -612,7 +521,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } #ifdef CONFIG_IEEE80211W - if (wpa_auth->conf.ieee80211w == WPA_IEEE80211W_REQUIRED) { + 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"); @@ -632,7 +541,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } } - if (wpa_auth->conf.ieee80211w == WPA_NO_IEEE80211W || + if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || !(data.capabilities & WPA_CAPABILITY_MFPC)) sm->mgmt_frame_prot = 0; else @@ -655,10 +564,9 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } #endif /* CONFIG_IEEE80211R */ - if (ciphers & WPA_CIPHER_CCMP) - sm->pairwise = WPA_CIPHER_CCMP; - else - sm->pairwise = WPA_CIPHER_TKIP; + sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); + if (sm->pairwise < 0) + return WPA_INVALID_PAIRWISE; /* TODO: clear WPA/WPA2 state if STA changes from one to another */ if (wpa_ie[0] == WLAN_EID_RSN) @@ -670,8 +578,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, for (i = 0; i < data.num_pmkid; i++) { wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", &data.pmkid[i * PMKID_LEN], PMKID_LEN); - sm->pmksa = pmksa_cache_get(wpa_auth->pmksa, sm->addr, - &data.pmkid[i * PMKID_LEN]); + sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr, + &data.pmkid[i * PMKID_LEN]); if (sm->pmksa) { pmkid = sm->pmksa->pmkid; break; @@ -800,6 +708,25 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_P2P + if (pos[1] >= RSN_SELECTOR_LEN + 1 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { + ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", + ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); + return 0; + } + + if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { + ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, + "WPA: IP Address Allocation in EAPOL-Key", + ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); + return 0; + } +#endif /* CONFIG_P2P */ + return 0; } @@ -839,6 +766,9 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) } 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); diff --git a/contrib/hostapd/hostapd/wpa_auth_ie.h b/contrib/hostapd/src/ap/wpa_auth_ie.h similarity index 76% rename from contrib/hostapd/hostapd/wpa_auth_ie.h rename to contrib/hostapd/src/ap/wpa_auth_ie.h index 9968d2d92a..f945882522 100644 --- a/contrib/hostapd/hostapd/wpa_auth_ie.h +++ b/contrib/hostapd/src/ap/wpa_auth_ie.h @@ -2,14 +2,8 @@ * hostapd - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_AUTH_IE_H @@ -42,7 +36,13 @@ struct wpa_eapol_ie_parse { #ifdef CONFIG_IEEE80211R const u8 *mdie; size_t mdie_len; + const u8 *ftie; + size_t ftie_len; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_P2P + const u8 *ip_addr_req; + const u8 *ip_addr_alloc; +#endif /* CONFIG_P2P */ }; int wpa_parse_kde_ies(const u8 *buf, size_t len, diff --git a/contrib/hostapd/src/ap/wps_hostapd.c b/contrib/hostapd/src/ap/wps_hostapd.c new file mode 100644 index 0000000000..1b1dce4c4a --- /dev/null +++ b/contrib/hostapd/src/ap/wps_hostapd.c @@ -0,0 +1,2002 @@ +/* + * hostapd / WPS integration + * Copyright (c) 2008-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/uuid.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 "wps/wps_attr_parse.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.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 *da, + const u8 *bssid, + const u8 *ie, size_t ie_len, + int ssi_signal); +static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); + + +struct wps_for_each_data { + int (*func)(struct hostapd_data *h, void *ctx); + void *ctx; + struct hostapd_data *calling_hapd; +}; + + +static int wps_for_each(struct hostapd_iface *iface, void *ctx) +{ + struct wps_for_each_data *data = ctx; + size_t j; + + if (iface == NULL) + return 0; + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + int ret; + + if (hapd != data->calling_hapd && + (hapd->conf->wps_independent || + data->calling_hapd->conf->wps_independent)) + continue; + + ret = data->func(hapd, data->ctx); + if (ret) + return ret; + } + + return 0; +} + + +static int hostapd_wps_for_each(struct hostapd_data *hapd, + int (*func)(struct hostapd_data *h, void *ctx), + void *ctx) +{ + struct hostapd_iface *iface = hapd->iface; + struct wps_for_each_data data; + data.func = func; + data.ctx = ctx; + data.calling_hapd = hapd; + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) + return wps_for_each(iface, &data); + return iface->interfaces->for_each_interface(iface->interfaces, + wps_for_each, &data); +} + + +static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, + const u8 *p2p_dev_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; + + if (is_zero_ether_addr(p2p_dev_addr)) { + wpa_printf(MSG_DEBUG, + "Received new WPA/WPA2-PSK from WPS for STA " MACSTR, + MAC2STR(mac_addr)); + } else { + wpa_printf(MSG_DEBUG, + "Received new WPA/WPA2-PSK from WPS for STA " MACSTR + " P2P Device Addr " MACSTR, + MAC2STR(mac_addr), MAC2STR(p2p_dev_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->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); + os_memcpy(p->psk, psk, PMK_LEN); + + if (hapd->new_psk_cb) { + hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr, + psk, psk_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; + if (hapd->beacon_set_done) + ieee802_11_set_beacon(hapd); + return hostapd_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); + } +} + + +struct wps_stop_reg_data { + struct hostapd_data *current_hapd; + const u8 *uuid_e; + const u8 *dev_pw; + size_t dev_pw_len; +}; + +static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx) +{ + struct wps_stop_reg_data *data = ctx; + if (hapd != data->current_hapd && hapd->wps != NULL) + wps_registrar_complete(hapd->wps->registrar, data->uuid_e, + data->dev_pw, data->dev_pw_len); + return 0; +} + + +static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, + const u8 *uuid_e, const u8 *dev_pw, + size_t dev_pw_len) +{ + struct hostapd_data *hapd = ctx; + char uuid[40]; + struct wps_stop_reg_data data; + 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); + data.current_hapd = hapd; + data.uuid_e = uuid_e; + data.dev_pw = dev_pw; + data.dev_pw_len = dev_pw_len; + hostapd_wps_for_each(hapd, wps_stop_registrar, &data); +} + + +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->interfaces == NULL || + iface->interfaces->reload_config(iface) < 0) { + wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " + "configuration"); + } +} + + +void hostapd_wps_eap_completed(struct hostapd_data *hapd) +{ + /* + * Reduce race condition of the station trying to reconnect immediately + * after AP reconfiguration through WPS by rescheduling the reload + * timeout to happen after EAP completion rather than the originally + * scheduled 100 ms after new configuration became known. + */ + if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) == + 1) + wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload"); +} + + +static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr, + size_t attr_len) +{ + size_t blen = attr_len * 2 + 1; + char *buf = os_malloc(blen); + if (buf) { + wpa_snprintf_hex(buf, blen, attr, attr_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_NEW_AP_SETTINGS "%s", buf); + os_free(buf); + } +} + + +static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd, + const struct wps_credential *cred) +{ + struct hostapd_bss_config *bss = hapd->conf; + + wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration"); + + bss->wps_state = 2; + if (cred->ssid_len <= HOSTAPD_MAX_SSID_LEN) { + os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len); + bss->ssid.ssid_len = cred->ssid_len; + bss->ssid.ssid_set = 1; + } + + if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && + (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) + bss->wpa = 3; + else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) + bss->wpa = 2; + else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) + bss->wpa = 1; + else + bss->wpa = 0; + + if (bss->wpa) { + if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) + bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) + bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + + bss->wpa_pairwise = 0; + if (cred->encr_type & WPS_ENCR_AES) + bss->wpa_pairwise |= WPA_CIPHER_CCMP; + if (cred->encr_type & WPS_ENCR_TKIP) + bss->wpa_pairwise |= WPA_CIPHER_TKIP; + bss->rsn_pairwise = bss->wpa_pairwise; + bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, + bss->wpa_pairwise, + bss->rsn_pairwise); + + if (cred->key_len >= 8 && cred->key_len < 64) { + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1); + if (bss->ssid.wpa_passphrase) + os_memcpy(bss->ssid.wpa_passphrase, cred->key, + cred->key_len); + os_free(bss->ssid.wpa_psk); + bss->ssid.wpa_psk = NULL; + } else if (cred->key_len == 64) { + os_free(bss->ssid.wpa_psk); + bss->ssid.wpa_psk = + os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (bss->ssid.wpa_psk && + hexstr2bin((const char *) cred->key, + bss->ssid.wpa_psk->psk, PMK_LEN) == 0) { + bss->ssid.wpa_psk->group = 1; + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = NULL; + } + } + bss->auth_algs = 1; + } else { + if ((cred->auth_type & WPS_AUTH_OPEN) && + (cred->auth_type & WPS_AUTH_SHARED)) + bss->auth_algs = 3; + else if (cred->auth_type & WPS_AUTH_SHARED) + bss->auth_algs = 2; + else + bss->auth_algs = 1; + if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx > 0 && + cred->key_idx <= 4) { + struct hostapd_wep_keys *wep = &bss->ssid.wep; + int idx = cred->key_idx; + if (idx) + idx--; + wep->idx = idx; + if (cred->key_len == 10 || cred->key_len == 26) { + os_free(wep->key[idx]); + wep->key[idx] = os_malloc(cred->key_len / 2); + if (wep->key[idx] == NULL || + hexstr2bin((const char *) cred->key, + wep->key[idx], + cred->key_len / 2)) + return -1; + wep->len[idx] = cred->key_len / 2; + } else { + os_free(wep->key[idx]); + wep->key[idx] = os_malloc(cred->key_len); + if (wep->key[idx] == NULL) + return -1; + os_memcpy(wep->key[idx], cred->key, + cred->key_len); + wep->len[idx] = cred->key_len; + } + wep->keys_set = 1; + } + } + + /* 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); + + return 0; +} + + +static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx) +{ + const struct wps_credential *cred = ctx; + FILE *oconf, *nconf; + size_t len, i; + char *tmp_fname; + char buf[1024]; + int multi_bss; + int wpa; + + if (hapd->wps == NULL) + return 0; + + 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) { + hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len); + } else if (hapd->conf->wps_cred_processing == 1 || + hapd->conf->wps_cred_processing == 2) { + struct wpabuf *attr; + attr = wpabuf_alloc(200); + if (attr && wps_build_credential_wrap(attr, cred) == 0) + hapd_new_ap_event(hapd, wpabuf_head_u8(attr), + wpabuf_len(attr)); + wpabuf_free(attr); + } 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; + + if (hapd->iface->config_fname == NULL) + return hapd_wps_reconfig_in_memory(hapd, cred); + 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"); + + if (is_hex(cred->ssid, cred->ssid_len)) { + fprintf(nconf, "ssid2="); + for (i = 0; i < cred->ssid_len; i++) + fprintf(nconf, "%02x", cred->ssid[i]); + fprintf(nconf, "\n"); + } else { + 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, "ssid2=") || + str_starts(buf, "auth_algs=") || + str_starts(buf, "wep_default_key=") || + str_starts(buf, "wep_key") || + 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); + + wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); + + return 0; +} + + +static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) +{ + struct hostapd_data *hapd = ctx; + return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred); +} + + +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; + if (hapd->ap_pin_failures_consecutive >= 10) + 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 int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx) +{ + struct wps_event_pwd_auth_fail *data = ctx; + + if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL) + return 0; + + /* + * 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++; + hapd->ap_pin_failures_consecutive++; + wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u " + "(%u consecutive)", + hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); + if (hapd->ap_pin_failures < 3) + return 0; + + 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 && + hapd->ap_pin_failures_consecutive >= 10) { + /* + * In indefinite lockdown - disable automatic AP PIN + * reenablement. + */ + eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); + wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely"); + } else 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); + } + + return 0; +} + + +static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, + struct wps_event_pwd_auth_fail *data) +{ + /* Update WPS Status - Authentication Failure */ + wpa_printf(MSG_DEBUG, "WPS: Authentication failure update"); + hapd->wps_stats.status = WPS_STATUS_FAILURE; + hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE; + os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN); + + hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data); +} + + +static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx) +{ + if (hapd->conf->ap_pin == NULL || hapd->wps == NULL) + return 0; + + if (hapd->ap_pin_failures_consecutive == 0) + return 0; + + wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter " + "- total validation failures %u (%u consecutive)", + hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive); + hapd->ap_pin_failures_consecutive = 0; + + return 0; +} + + +static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd) +{ + hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL); +} + + +static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd) +{ + /* Update WPS Status - PBC Overlap */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP; +} + + +static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd) +{ + /* Update WPS PBC Status:PBC Timeout */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT; +} + + +static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd) +{ + /* Update WPS PBC status - Active */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE; +} + + +static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd) +{ + /* Update WPS PBC status - Active */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; +} + + +static void hostapd_wps_event_success(struct hostapd_data *hapd, + struct wps_event_success *success) +{ + /* Update WPS status - Success */ + hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE; + hapd->wps_stats.status = WPS_STATUS_SUCCESS; + os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN); +} + + +static void hostapd_wps_event_fail(struct hostapd_data *hapd, + struct wps_event_fail *fail) +{ + /* Update WPS status - Failure */ + hapd->wps_stats.status = WPS_STATUS_FAILURE; + os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN); + + hapd->wps_stats.failure_reason = fail->error_indication; + + if (fail->error_indication > 0 && + fail->error_indication < NUM_WPS_EI_VALUES) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", + fail->msg, fail->config_error, fail->error_indication, + wps_ei_str(fail->error_indication)); + } else { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPS_EVENT_FAIL "msg=%d config_error=%d", + fail->msg, fail->config_error); + } +} + + +static void hostapd_wps_event_cb(void *ctx, enum wps_event event, + union wps_event_data *data) +{ + struct hostapd_data *hapd = ctx; + + switch (event) { + case WPS_EV_M2D: + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D); + break; + case WPS_EV_FAIL: + hostapd_wps_event_fail(hapd, &data->fail); + break; + case WPS_EV_SUCCESS: + hostapd_wps_event_success(hapd, &data->success); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS); + break; + case WPS_EV_PWD_AUTH_FAIL: + hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); + break; + case WPS_EV_PBC_OVERLAP: + hostapd_wps_event_pbc_overlap(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP); + break; + case WPS_EV_PBC_TIMEOUT: + hostapd_wps_event_pbc_timeout(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT); + break; + case WPS_EV_PBC_ACTIVE: + hostapd_wps_event_pbc_active(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE); + break; + case WPS_EV_PBC_DISABLE: + hostapd_wps_event_pbc_disable(hapd); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE); + break; + case WPS_EV_ER_AP_ADD: + break; + case WPS_EV_ER_AP_REMOVE: + break; + case WPS_EV_ER_ENROLLEE_ADD: + break; + case WPS_EV_ER_ENROLLEE_REMOVE: + break; + case WPS_EV_ER_AP_SETTINGS: + break; + case WPS_EV_ER_SET_SELECTED_REGISTRAR: + break; + case WPS_EV_AP_PIN_SUCCESS: + hostapd_wps_ap_pin_success(hapd); + break; + } + if (hapd->wps_event_cb) + hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data); +} + + +static int hostapd_wps_rf_band_cb(void *ctx) +{ + struct hostapd_data *hapd = ctx; + + return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? + WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ +} + + +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; + + hostapd_set_ap_wps_ie(hapd); +} + + +static int get_uuid_cb(struct hostapd_iface *iface, void *ctx) +{ + const u8 **uuid = ctx; + size_t j; + + if (iface == NULL) + return 0; + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + if (hapd->wps && !hapd->conf->wps_independent && + !is_nil_uuid(hapd->wps->uuid)) { + *uuid = hapd->wps->uuid; + return 1; + } + } + + return 0; +} + + +static const u8 * get_own_uuid(struct hostapd_iface *iface) +{ + const u8 *uuid; + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) + return NULL; + uuid = NULL; + iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb, + &uuid); + return uuid; +} + + +static int count_interface_cb(struct hostapd_iface *iface, void *ctx) +{ + int *count= ctx; + (*count)++; + return 0; +} + + +static int interface_count(struct hostapd_iface *iface) +{ + int count = 0; + if (iface->interfaces == NULL || + iface->interfaces->for_each_interface == NULL) + return 0; + iface->interfaces->for_each_interface(iface->interfaces, + count_interface_cb, &count); + return count; +} + + +static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd, + struct wps_context *wps) +{ + int i; + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + wpabuf_free(wps->dev.vendor_ext[i]); + wps->dev.vendor_ext[i] = NULL; + + if (hapd->conf->wps_vendor_ext[i] == NULL) + continue; + + wps->dev.vendor_ext[i] = + wpabuf_dup(hapd->conf->wps_vendor_ext[i]); + if (wps->dev.vendor_ext[i] == NULL) { + while (--i >= 0) + wpabuf_free(wps->dev.vendor_ext[i]); + return -1; + } + } + + return 0; +} + + +int hostapd_init_wps(struct hostapd_data *hapd, + struct hostapd_bss_config *conf) +{ + struct wps_context *wps; + struct wps_registrar_config cfg; + + if (conf->wps_state == 0) { + hostapd_wps_clear_ies(hapd); + return 0; + } + + wps = os_zalloc(sizeof(*wps)); + if (wps == NULL) + return -1; + + wps->cred_cb = hostapd_wps_cred_cb; + wps->event_cb = hostapd_wps_event_cb; + wps->rf_band_cb = hostapd_wps_rf_band_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)) { + const u8 *uuid; + uuid = get_own_uuid(hapd->iface); + if (uuid && !conf->wps_independent) { + os_memcpy(wps->uuid, uuid, UUID_LEN); + wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another " + "interface", wps->uuid, UUID_LEN); + } else { + 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); + wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID", + wps->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); +#ifdef CONFIG_WPS2 + if ((wps->config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY | + WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) { + wpa_printf(MSG_INFO, "WPS: Converting display to " + "virtual_display for WPS 2.0 compliance"); + wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY; + } + if ((wps->config_methods & + (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON | + WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) { + wpa_printf(MSG_INFO, "WPS: Converting push_button to " + "virtual_push_button for WPS 2.0 compliance"); + wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; + } +#endif /* CONFIG_WPS2 */ + os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type, + WPS_DEV_TYPE_LEN); + + if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) { + os_free(wps); + return -1; + } + + wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); + + if (conf->wps_rf_bands) { + wps->dev.rf_bands = conf->wps_rf_bands; + } else { + 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; + cfg.dualband = interface_count(hapd->iface) > 1; + if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) == + (WPS_RF_50GHZ | WPS_RF_24GHZ)) + cfg.dualband = 1; + if (cfg.dualband) + wpa_printf(MSG_DEBUG, "WPS: Dualband AP"); + cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk; + + wps->registrar = wps_registrar_init(wps, &cfg); + if (wps->registrar == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar"); + 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; +#endif /* CONFIG_WPS_UPNP */ + + hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); + + hapd->wps = wps; + + return 0; +} + + +int hostapd_init_wps_complete(struct hostapd_data *hapd) +{ + struct wps_context *wps = hapd->wps; + + if (wps == NULL) + return 0; + +#ifdef CONFIG_WPS_UPNP + 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); + hapd->wps = NULL; + return -1; + } +#endif /* CONFIG_WPS_UPNP */ + + return 0; +} + + +static void hostapd_wps_nfc_clear(struct wps_context *wps) +{ +#ifdef CONFIG_WPS_NFC + wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps); + wps->ap_nfc_dev_pw_id = 0; + wpabuf_free(wps->ap_nfc_dh_pubkey); + wps->ap_nfc_dh_pubkey = NULL; + wpabuf_free(wps->ap_nfc_dh_privkey); + wps->ap_nfc_dh_privkey = NULL; + wpabuf_free(wps->ap_nfc_dev_pw); + wps->ap_nfc_dev_pw = NULL; +#endif /* CONFIG_WPS_NFC */ +} + + +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); + eloop_cancel_timeout(wps_reload_config, hapd->iface, 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); + wps_free_pending_msgs(hapd->wps->upnp_msgs); + hostapd_wps_nfc_clear(hapd->wps); + 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; + +#ifdef CONFIG_WPS_UPNP + hapd->wps->friendly_name = hapd->conf->friendly_name; + hapd->wps->manufacturer_url = hapd->conf->manufacturer_url; + hapd->wps->model_description = hapd->conf->model_description; + hapd->wps->model_url = hapd->conf->model_url; + hapd->wps->upc = hapd->conf->upc; +#endif /* CONFIG_WPS_UPNP */ + + hostapd_wps_set_vendor_ext(hapd, hapd->wps); + + if (hapd->conf->wps_state) + wps_registrar_update_ie(hapd->wps->registrar); + else + hostapd_deinit_wps(hapd); +} + + +struct wps_add_pin_data { + const u8 *addr; + const u8 *uuid; + const u8 *pin; + size_t pin_len; + int timeout; + int added; +}; + + +static int wps_add_pin(struct hostapd_data *hapd, void *ctx) +{ + struct wps_add_pin_data *data = ctx; + int ret; + + if (hapd->wps == NULL) + return 0; + ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr, + data->uuid, data->pin, data->pin_len, + data->timeout); + if (ret == 0) + data->added++; + return ret; +} + + +int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, + const char *uuid, const char *pin, int timeout) +{ + u8 u[UUID_LEN]; + struct wps_add_pin_data data; + + data.addr = addr; + data.uuid = u; + data.pin = (const u8 *) pin; + data.pin_len = os_strlen(pin); + data.timeout = timeout; + data.added = 0; + + if (os_strcmp(uuid, "any") == 0) + data.uuid = NULL; + else { + if (uuid_str2bin(uuid, u)) + return -1; + data.uuid = u; + } + if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0) + return -1; + return data.added ? 0 : -1; +} + + +static int wps_button_pushed(struct hostapd_data *hapd, void *ctx) +{ + const u8 *p2p_dev_addr = ctx; + if (hapd->wps == NULL) + return 0; + return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr); +} + + +int hostapd_wps_button_pushed(struct hostapd_data *hapd, + const u8 *p2p_dev_addr) +{ + return hostapd_wps_for_each(hapd, wps_button_pushed, + (void *) p2p_dev_addr); +} + + +static int wps_cancel(struct hostapd_data *hapd, void *ctx) +{ + if (hapd->wps == NULL) + return 0; + + wps_registrar_wps_cancel(hapd->wps->registrar); + ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL); + + return 0; +} + + +int hostapd_wps_cancel(struct hostapd_data *hapd) +{ + return hostapd_wps_for_each(hapd, wps_cancel, NULL); +} + + +static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da, + const u8 *bssid, + const u8 *ie, size_t ie_len, + int ssi_signal) +{ + 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 (wps_validate_probe_req(wps_ie, addr) < 0) { + wpabuf_free(wps_ie); + return 0; + } + + if (wpabuf_len(wps_ie) > 0) { + int p2p_wildcard = 0; +#ifdef CONFIG_P2P + if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN && + os_memcmp(elems.ssid, P2P_WILDCARD_SSID, + P2P_WILDCARD_SSID_LEN) == 0) + p2p_wildcard = 1; +#endif /* CONFIG_P2P */ + wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie, + p2p_wildcard); +#ifdef CONFIG_WPS_UPNP + /* FIX: what exactly should be included in the WLANEvent? + * WPS attributes? Full ProbeReq frame? */ + if (!p2p_wildcard) + 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); +#ifndef CONFIG_WPS_STRICT + 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; + } + } +#endif /* CONFIG_WPS_STRICT */ + + if (!sta || !(sta->flags & WLAN_STA_WPS)) { + 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, + hapd->conf->upnp_iface); + if (hapd->wps_upnp == NULL) + return -1; + wps->wps_upnp = hapd->wps_upnp; + + return 0; +} + + +static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd) +{ + upnp_wps_device_deinit(hapd->wps_upnp, hapd); +} + +#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); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED); +} + + +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->ap_pin_failures_consecutive = 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); +} + + +static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx) +{ + 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); + return 0; +} + + +void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); + hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL); +} + + +struct wps_ap_pin_data { + char pin_txt[9]; + int timeout; +}; + + +static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx) +{ + struct wps_ap_pin_data *data = ctx; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(data->pin_txt); +#ifdef CONFIG_WPS_UPNP + upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt); +#endif /* CONFIG_WPS_UPNP */ + hostapd_wps_ap_pin_enable(hapd, data->timeout); + return 0; +} + + +const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) +{ + unsigned int pin; + struct wps_ap_pin_data data; + + pin = wps_generate_pin(); + os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin); + data.timeout = timeout; + hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); + 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) +{ + struct wps_ap_pin_data data; + int ret; + + ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin); + if (ret < 0 || ret >= (int) sizeof(data.pin_txt)) + return -1; + data.timeout = timeout; + return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); +} + + +static int wps_update_ie(struct hostapd_data *hapd, void *ctx) +{ + if (hapd->wps) + wps_registrar_update_ie(hapd->wps->registrar); + return 0; +} + + +void hostapd_wps_update_ie(struct hostapd_data *hapd) +{ + hostapd_wps_for_each(hapd, wps_update_ie, NULL); +} + + +int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, + const char *auth, const char *encr, const char *key) +{ + struct wps_credential cred; + size_t len; + + os_memset(&cred, 0, sizeof(cred)); + + len = os_strlen(ssid); + if ((len & 1) || len > 2 * sizeof(cred.ssid) || + hexstr2bin(ssid, cred.ssid, len / 2)) + return -1; + cred.ssid_len = len / 2; + + if (os_strncmp(auth, "OPEN", 4) == 0) + cred.auth_type = WPS_AUTH_OPEN; + else if (os_strncmp(auth, "WPAPSK", 6) == 0) + cred.auth_type = WPS_AUTH_WPAPSK; + else if (os_strncmp(auth, "WPA2PSK", 7) == 0) + cred.auth_type = WPS_AUTH_WPA2PSK; + else + return -1; + + if (encr) { + if (os_strncmp(encr, "NONE", 4) == 0) + cred.encr_type = WPS_ENCR_NONE; + else if (os_strncmp(encr, "WEP", 3) == 0) + cred.encr_type = WPS_ENCR_WEP; + else if (os_strncmp(encr, "TKIP", 4) == 0) + cred.encr_type = WPS_ENCR_TKIP; + else if (os_strncmp(encr, "CCMP", 4) == 0) + cred.encr_type = WPS_ENCR_AES; + else + return -1; + } else + cred.encr_type = WPS_ENCR_NONE; + + if (key) { + len = os_strlen(key); + if ((len & 1) || len > 2 * sizeof(cred.key) || + hexstr2bin(key, cred.key, len / 2)) + return -1; + cred.key_len = len / 2; + } + + return wps_registrar_config_ap(hapd->wps->registrar, &cred); +} + + +#ifdef CONFIG_WPS_NFC + +struct wps_nfc_password_token_data { + const u8 *oob_dev_pw; + size_t oob_dev_pw_len; + int added; +}; + + +static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx) +{ + struct wps_nfc_password_token_data *data = ctx; + int ret; + + if (hapd->wps == NULL) + return 0; + ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar, + data->oob_dev_pw, + data->oob_dev_pw_len); + if (ret == 0) + data->added++; + return ret; +} + + +static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd, + struct wps_parse_attr *attr) +{ + struct wps_nfc_password_token_data data; + + data.oob_dev_pw = attr->oob_dev_password; + data.oob_dev_pw_len = attr->oob_dev_password_len; + data.added = 0; + if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0) + return -1; + return data.added ? 0 : -1; +} + + +static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd, + const struct wpabuf *wps) +{ + struct wps_parse_attr attr; + + wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps); + + if (wps_parse_msg(wps, &attr)) { + wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag"); + return -1; + } + + if (attr.oob_dev_password) + return hostapd_wps_add_nfc_password_token(hapd, &attr); + + wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag"); + return -1; +} + + +int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, + const struct wpabuf *data) +{ + const struct wpabuf *wps = data; + struct wpabuf *tmp = NULL; + int ret; + + if (wpabuf_len(data) < 4) + return -1; + + if (*wpabuf_head_u8(data) != 0x10) { + /* Assume this contains full NDEF record */ + tmp = ndef_parse_wifi(data); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); + return -1; + } + wps = tmp; + } + + ret = hostapd_wps_nfc_tag_process(hapd, wps); + wpabuf_free(tmp); + return ret; +} + + +struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, + int ndef) +{ + struct wpabuf *ret; + + if (hapd->wps == NULL) + return NULL; + + ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd), + hapd->iconf->channel); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} + + +struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef) +{ + struct wpabuf *ret; + + if (hapd->wps == NULL) + return NULL; + + if (hapd->conf->wps_nfc_dh_pubkey == NULL) { + struct wps_context *wps = hapd->wps; + if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey, + &hapd->conf->wps_nfc_dh_privkey) < 0) + return NULL; + hostapd_wps_nfc_clear(wps); + wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; + wps->ap_nfc_dh_pubkey = + wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); + wps->ap_nfc_dh_privkey = + wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); + if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { + hostapd_wps_nfc_clear(wps); + return NULL; + } + } + + ret = wps_build_nfc_handover_sel(hapd->wps, + hapd->conf->wps_nfc_dh_pubkey, + hapd->own_addr, hapd->iface->freq); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} + + +int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + struct wpabuf *wps; + int ret = -1; + u16 wsc_len; + const u8 *pos; + struct wpabuf msg; + struct wps_parse_attr attr; + u16 dev_pw_id; + + /* + * Enrollee/station is always initiator of the NFC connection handover, + * so use the request message here to find Enrollee public key hash. + */ + wps = ndef_parse_wifi(req); + if (wps == NULL) + return -1; + wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " + "payload from NFC connection handover"); + wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); + if (wpabuf_len(wps) < 2) { + wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request " + "Message"); + goto out; + } + pos = wpabuf_head(wps); + wsc_len = WPA_GET_BE16(pos); + if (wsc_len > wpabuf_len(wps) - 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " + "in rt Wi-Fi Handover Request Message", wsc_len); + goto out; + } + pos += 2; + + wpa_hexdump(MSG_DEBUG, + "WPS: WSC attributes in Wi-Fi Handover Request Message", + pos, wsc_len); + if (wsc_len < wpabuf_len(wps) - 2) { + wpa_hexdump(MSG_DEBUG, + "WPS: Ignore extra data after WSC attributes", + pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); + } + + wpabuf_set(&msg, pos, wsc_len); + ret = wps_parse_msg(&msg, &attr); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " + "Wi-Fi Handover Request Message"); + goto out; + } + + if (attr.oob_dev_password == NULL || + attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " + "included in Wi-Fi Handover Request Message"); + ret = -1; + goto out; + } + + if (attr.uuid_e == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi " + "Handover Request Message"); + ret = -1; + goto out; + } + + wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN); + + wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", + attr.oob_dev_password, attr.oob_dev_password_len); + dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + + WPS_OOB_PUBKEY_HASH_LEN); + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " + "%u in Wi-Fi Handover Request Message", dev_pw_id); + ret = -1; + goto out; + } + wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash", + attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); + + ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar, + attr.oob_dev_password, + DEV_PW_NFC_CONNECTION_HANDOVER, + NULL, 0, 1); + +out: + wpabuf_free(wps); + return ret; +} + + +struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef) +{ + if (hapd->conf->wps_nfc_pw_from_config) { + return wps_nfc_token_build(ndef, + hapd->conf->wps_nfc_dev_pw_id, + hapd->conf->wps_nfc_dh_pubkey, + hapd->conf->wps_nfc_dev_pw); + } + + return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id, + &hapd->conf->wps_nfc_dh_pubkey, + &hapd->conf->wps_nfc_dh_privkey, + &hapd->conf->wps_nfc_dev_pw); +} + + +int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd) +{ + struct wps_context *wps = hapd->wps; + struct wpabuf *pw; + + if (wps == NULL) + return -1; + + if (!hapd->conf->wps_nfc_dh_pubkey || + !hapd->conf->wps_nfc_dh_privkey || + !hapd->conf->wps_nfc_dev_pw || + !hapd->conf->wps_nfc_dev_pw_id) + return -1; + + hostapd_wps_nfc_clear(wps); + wpa_printf(MSG_DEBUG, + "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)", + hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps); + wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id; + wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey); + wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey); + pw = hapd->conf->wps_nfc_dev_pw; + wps->ap_nfc_dev_pw = wpabuf_alloc( + wpabuf_len(pw) * 2 + 1); + if (wps->ap_nfc_dev_pw) { + wpa_snprintf_hex_uppercase( + (char *) wpabuf_put(wps->ap_nfc_dev_pw, + wpabuf_len(pw) * 2), + wpabuf_len(pw) * 2 + 1, + wpabuf_head(pw), wpabuf_len(pw)); + } + + if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey || + !wps->ap_nfc_dev_pw) { + hostapd_wps_nfc_clear(wps); + return -1; + } + + return 0; +} + + +void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s", + hapd->conf->iface); + hostapd_wps_nfc_clear(hapd->wps); +} + +#endif /* CONFIG_WPS_NFC */ diff --git a/contrib/hostapd/src/ap/wps_hostapd.h b/contrib/hostapd/src/ap/wps_hostapd.h new file mode 100644 index 0000000000..204bd820a5 --- /dev/null +++ b/contrib/hostapd/src/ap/wps_hostapd.h @@ -0,0 +1,92 @@ +/* + * hostapd / WPS integration + * Copyright (c) 2008-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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); +int hostapd_init_wps_complete(struct hostapd_data *hapd); +void hostapd_deinit_wps(struct hostapd_data *hapd); +void hostapd_update_wps(struct hostapd_data *hapd); +void hostapd_wps_eap_completed(struct hostapd_data *hapd); +int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, + const char *uuid, const char *pin, int timeout); +int hostapd_wps_button_pushed(struct hostapd_data *hapd, + const u8 *p2p_dev_addr); +int hostapd_wps_cancel(struct hostapd_data *hapd); +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); +void hostapd_wps_update_ie(struct hostapd_data *hapd); +int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid, + const char *auth, const char *encr, const char *key); +int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd, + const struct wpabuf *data); +struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd, + int ndef); +struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef); +int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd, + const struct wpabuf *req, + const struct wpabuf *sel); +struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef); +int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd); +void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd); + +#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 int hostapd_init_wps_complete(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void hostapd_update_wps(struct hostapd_data *hapd) +{ +} + +static inline void hostapd_wps_eap_completed(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, + const u8 *p2p_dev_addr) +{ + return 0; +} + +static inline int hostapd_wps_cancel(struct hostapd_data *hapd) +{ + return 0; +} + +#endif /* CONFIG_WPS */ + +#endif /* WPS_HOSTAPD_H */ diff --git a/contrib/hostapd/src/common/defs.h b/contrib/hostapd/src/common/defs.h index 4930e73e75..4811e8e909 100644 --- a/contrib/hostapd/src/common/defs.h +++ b/contrib/hostapd/src/common/defs.h @@ -2,14 +2,8 @@ * WPA Supplicant - Common definitions * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DEFS_H @@ -29,9 +23,15 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #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_CIPHER_GCMP BIT(6) +#define WPA_CIPHER_SMS4 BIT(7) +#define WPA_CIPHER_GCMP_256 BIT(8) +#define WPA_CIPHER_CCMP_256 BIT(9) +#define WPA_CIPHER_BIP_GMAC_128 BIT(11) +#define WPA_CIPHER_BIP_GMAC_256 BIT(12) +#define WPA_CIPHER_BIP_CMAC_256 BIT(13) +#define WPA_CIPHER_GTK_NOT_USED BIT(14) #define WPA_KEY_MGMT_IEEE8021X BIT(0) #define WPA_KEY_MGMT_PSK BIT(1) @@ -43,52 +43,93 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7) #define WPA_KEY_MGMT_PSK_SHA256 BIT(8) #define WPA_KEY_MGMT_WPS BIT(9) +#define WPA_KEY_MGMT_SAE BIT(10) +#define WPA_KEY_MGMT_FT_SAE BIT(11) +#define WPA_KEY_MGMT_WAPI_PSK BIT(12) +#define WPA_KEY_MGMT_WAPI_CERT BIT(13) +#define WPA_KEY_MGMT_CCKM BIT(14) 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; + return !!(akm & (WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_CCKM | + 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; + return !!(akm & (WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE)); } static inline int wpa_key_mgmt_ft(int akm) { - return akm == WPA_KEY_MGMT_FT_PSK || - akm == WPA_KEY_MGMT_FT_IEEE8021X; + return !!(akm & (WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_SAE)); +} + +static inline int wpa_key_mgmt_sae(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE)); } static inline int wpa_key_mgmt_sha256(int akm) { - return akm == WPA_KEY_MGMT_PSK_SHA256 || - akm == WPA_KEY_MGMT_IEEE8021X_SHA256; + return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SHA256)); +} + +static inline int wpa_key_mgmt_wpa(int akm) +{ + return wpa_key_mgmt_wpa_ieee8021x(akm) || + wpa_key_mgmt_wpa_psk(akm) || + wpa_key_mgmt_sae(akm); +} + +static inline int wpa_key_mgmt_wpa_any(int akm) +{ + return wpa_key_mgmt_wpa(akm) || (akm & WPA_KEY_MGMT_WPA_NONE); +} + +static inline int wpa_key_mgmt_cckm(int akm) +{ + return akm == WPA_KEY_MGMT_CCKM; } #define WPA_PROTO_WPA BIT(0) #define WPA_PROTO_RSN BIT(1) +#define WPA_PROTO_WAPI BIT(2) #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) +#define WPA_AUTH_ALG_SAE BIT(4) -typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP, - WPA_ALG_IGTK, WPA_ALG_PMK } wpa_alg; -typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, - CIPHER_WEP104 } wpa_cipher; -typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, - KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE, - KEY_MGMT_FT_802_1X, KEY_MGMT_FT_PSK, - KEY_MGMT_802_1X_SHA256, KEY_MGMT_PSK_SHA256, - KEY_MGMT_WPS -} wpa_key_mgmt; +enum wpa_alg { + WPA_ALG_NONE, + WPA_ALG_WEP, + WPA_ALG_TKIP, + WPA_ALG_CCMP, + WPA_ALG_IGTK, + WPA_ALG_PMK, + WPA_ALG_GCMP, + WPA_ALG_SMS4, + WPA_ALG_KRK, + WPA_ALG_GCMP_256, + WPA_ALG_CCMP_256, + WPA_ALG_BIP_GMAC_128, + WPA_ALG_BIP_GMAC_256, + WPA_ALG_BIP_CMAC_256 +}; /** * enum wpa_states - wpa_supplicant state @@ -100,7 +141,7 @@ typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used * to access the state variable. */ -typedef enum { +enum wpa_states { /** * WPA_DISCONNECTED - Disconnected state * @@ -110,6 +151,15 @@ typedef enum { */ WPA_DISCONNECTED, + /** + * WPA_INTERFACE_DISABLED - Interface disabled + * + * This stat eis entered if the network interface is disabled, e.g., + * due to rfkill. wpa_supplicant refuses any new operations that would + * use the radio until the interface has been enabled. + */ + WPA_INTERFACE_DISABLED, + /** * WPA_INACTIVE - Inactive state (wpa_supplicant disabled) * @@ -128,6 +178,16 @@ typedef enum { */ 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 * @@ -186,7 +246,7 @@ typedef enum { * fully configured. */ WPA_COMPLETED -} wpa_states; +}; #define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0 #define MLME_SETPROTECTION_PROTECT_TYPE_RX 1 @@ -196,4 +256,44 @@ typedef enum { #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, +}; +#define MGMT_FRAME_PROTECTION_DEFAULT 3 + +/** + * enum hostapd_hw_mode - Hardware mode + */ +enum hostapd_hw_mode { + HOSTAPD_MODE_IEEE80211B, + HOSTAPD_MODE_IEEE80211G, + HOSTAPD_MODE_IEEE80211A, + HOSTAPD_MODE_IEEE80211AD, + NUM_HOSTAPD_MODES +}; + +/** + * enum wpa_ctrl_req_type - Control interface request types + */ +enum wpa_ctrl_req_type { + WPA_CTRL_REQ_UNKNOWN, + WPA_CTRL_REQ_EAP_IDENTITY, + WPA_CTRL_REQ_EAP_PASSWORD, + WPA_CTRL_REQ_EAP_NEW_PASSWORD, + WPA_CTRL_REQ_EAP_PIN, + WPA_CTRL_REQ_EAP_OTP, + WPA_CTRL_REQ_EAP_PASSPHRASE, + WPA_CTRL_REQ_SIM, + NUM_WPA_CTRL_REQS +}; + +/* Maximum number of EAP methods to store for EAP server user information */ +#define EAP_MAX_METHODS 8 + #endif /* DEFS_H */ diff --git a/contrib/hostapd/src/common/eapol_common.h b/contrib/hostapd/src/common/eapol_common.h index d70e62d2f6..4811f38aab 100644 --- a/contrib/hostapd/src/common/eapol_common.h +++ b/contrib/hostapd/src/common/eapol_common.h @@ -2,14 +2,8 @@ * EAPOL definitions shared between hostapd and wpa_supplicant * Copyright (c) 2002-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAPOL_COMMON_H @@ -44,4 +38,44 @@ enum { IEEE802_1X_TYPE_EAP_PACKET = 0, enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, EAPOL_KEY_TYPE_WPA = 254 }; + +#define IEEE8021X_REPLAY_COUNTER_LEN 8 +#define IEEE8021X_KEY_SIGN_LEN 16 +#define IEEE8021X_KEY_IV_LEN 16 + +#define IEEE8021X_KEY_INDEX_FLAG 0x80 +#define IEEE8021X_KEY_INDEX_MASK 0x03 + +#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 */ + #endif /* EAPOL_COMMON_H */ diff --git a/contrib/hostapd/src/common/gas.c b/contrib/hostapd/src/common/gas.c new file mode 100644 index 0000000000..cff9254b74 --- /dev/null +++ b/contrib/hostapd/src/common/gas.c @@ -0,0 +1,273 @@ +/* + * Generic advertisement service (GAS) (IEEE 802.11u) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2012, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ieee802_11_defs.h" +#include "gas.h" + + +static struct wpabuf * +gas_build_req(u8 action, u8 dialog_token, size_t size) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100 + size); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, action); + wpabuf_put_u8(buf, dialog_token); + + return buf; +} + + +struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size) +{ + return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token, + size); +} + + +struct wpabuf * gas_build_comeback_req(u8 dialog_token) +{ + return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0); +} + + +static struct wpabuf * +gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id, + u8 more, u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100 + size); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, action); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_le16(buf, status_code); + if (action == WLAN_PA_GAS_COMEBACK_RESP) + wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0)); + wpabuf_put_le16(buf, comeback_delay); + + return buf; +} + + +struct wpabuf * +gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, + size_t size) +{ + return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token, + status_code, 0, 0, comeback_delay, size); +} + + +static struct wpabuf * +gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, + u16 comeback_delay, size_t size) +{ + return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token, + status_code, frag_id, more, comeback_delay, + size); +} + + +/** + * gas_add_adv_proto_anqp - Add an Advertisement Protocol element + * @buf: Buffer to which the element is added + * @query_resp_len_limit: Query Response Length Limit in units of 256 octets + * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1) + * + * + * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means + * that the maximum limit is determined by the maximum allowable number of + * fragments in the GAS Query Response Fragment ID. + */ +static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit, + u8 pame_bi) +{ + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 2); /* Length */ + wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | + (pame_bi ? 0x80 : 0)); + /* Advertisement Protocol */ + wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL); +} + + +struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_initial_req(dialog_token, 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0, 0); + + wpabuf_put(buf, 2); /* Query Request Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay, + 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0x7f, 0); + + wpabuf_put(buf, 2); /* Query Response Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, + u16 status_code, + u16 comeback_delay, + struct wpabuf *payload) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_initial_resp(dialog_token, status_code, + comeback_delay, + payload ? wpabuf_len(payload) : 0); + if (buf == NULL) + return NULL; + + if (payload) + wpabuf_put_buf(buf, payload); + + gas_anqp_set_len(buf); + + return buf; +} + + +struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, size_t size) +{ + struct wpabuf *buf; + + buf = gas_build_comeback_resp(dialog_token, status_code, + frag_id, more, comeback_delay, 4 + size); + if (buf == NULL) + return NULL; + + gas_add_adv_proto_anqp(buf, 0x7f, 0); + + wpabuf_put(buf, 2); /* Query Response Length to be filled */ + + return buf; +} + + +struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, + u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, + struct wpabuf *payload) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, + more, comeback_delay, + payload ? wpabuf_len(payload) : 0); + if (buf == NULL) + return NULL; + + if (payload) + wpabuf_put_buf(buf, payload); + + gas_anqp_set_len(buf); + + return buf; +} + + +/** + * gas_anqp_set_len - Set Query Request/Response Length + * @buf: GAS message + * + * This function is used to update the Query Request/Response Length field once + * the payload has been filled. + */ +void gas_anqp_set_len(struct wpabuf *buf) +{ + u8 action; + size_t offset; + u8 *len; + + if (buf == NULL || wpabuf_len(buf) < 2) + return; + + action = *(wpabuf_head_u8(buf) + 1); + switch (action) { + case WLAN_PA_GAS_INITIAL_REQ: + offset = 3 + 4; + break; + case WLAN_PA_GAS_INITIAL_RESP: + offset = 7 + 4; + break; + case WLAN_PA_GAS_COMEBACK_RESP: + offset = 8 + 4; + break; + default: + return; + } + + if (wpabuf_len(buf) < offset + 2) + return; + + len = wpabuf_mhead_u8(buf) + offset; + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); +} + + +/** + * gas_anqp_add_element - Add ANQP element header + * @buf: GAS message + * @info_id: ANQP Info ID + * Returns: Pointer to the Length field for gas_anqp_set_element_len() + */ +u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id) +{ + wpabuf_put_le16(buf, info_id); + return wpabuf_put(buf, 2); /* Length to be filled */ +} + + +/** + * gas_anqp_set_element_len - Update ANQP element Length field + * @buf: GAS message + * @len_pos: Length field position from gas_anqp_add_element() + * + * This function is called after the ANQP element payload has been added to the + * buffer. + */ +void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos) +{ + WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2); +} diff --git a/contrib/hostapd/src/common/gas.h b/contrib/hostapd/src/common/gas.h new file mode 100644 index 0000000000..306adc58c6 --- /dev/null +++ b/contrib/hostapd/src/common/gas.h @@ -0,0 +1,37 @@ +/* + * Generic advertisement service (GAS) (IEEE 802.11u) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2012, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_H +#define GAS_H + +struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size); +struct wpabuf * gas_build_comeback_req(u8 dialog_token); +struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size); +struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token, + u16 status_code, + u16 comeback_delay, + struct wpabuf *payload); +struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, size_t size); +struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token, + u16 status_code, + u8 frag_id, u8 more, + u16 comeback_delay, + struct wpabuf *payload); +void gas_anqp_set_len(struct wpabuf *buf); + +u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id); +void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos); + +#endif /* GAS_H */ diff --git a/contrib/hostapd/src/common/ieee802_11_common.c b/contrib/hostapd/src/common/ieee802_11_common.c index 242f933b03..809089faca 100644 --- a/contrib/hostapd/src/common/ieee802_11_common.c +++ b/contrib/hostapd/src/common/ieee802_11_common.c @@ -1,25 +1,20 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "defs.h" #include "ieee802_11_defs.h" #include "ieee802_11_common.h" -static int ieee802_11_parse_vendor_specific(u8 *pos, size_t elen, +static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, struct ieee802_11_elems *elems, int show_errors) { @@ -75,7 +70,7 @@ static int ieee802_11_parse_vendor_specific(u8 *pos, size_t elen, elems->wmm_tspec_len = elen; break; default: - wpa_printf(MSG_MSGDUMP, "unknown WMM " + wpa_printf(MSG_EXCESSIVE, "unknown WMM " "information element ignored " "(subtype=%d len=%lu)", pos[4], (unsigned long) elen); @@ -88,7 +83,33 @@ static int ieee802_11_parse_vendor_specific(u8 *pos, size_t elen, elems->wps_ie_len = elen; break; default: - wpa_printf(MSG_MSGDUMP, "Unknown Microsoft " + wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft " + "information element ignored " + "(type=%d len=%lu)", + pos[3], (unsigned long) elen); + return -1; + } + break; + + case OUI_WFA: + switch (pos[3]) { + case P2P_OUI_TYPE: + /* Wi-Fi Alliance - P2P IE */ + elems->p2p = pos; + elems->p2p_len = elen; + break; + case WFD_OUI_TYPE: + /* Wi-Fi Alliance - WFD IE */ + elems->wfd = pos; + elems->wfd_len = elen; + break; + case HS20_INDICATION_OUI_TYPE: + /* Hotspot 2.0 */ + elems->hs20 = pos; + elems->hs20_len = elen; + break; + default: + wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " "(type=%d len=%lu)\n", pos[3], (unsigned long) elen); @@ -103,18 +124,18 @@ static int ieee802_11_parse_vendor_specific(u8 *pos, size_t elen, elems->vendor_ht_cap_len = elen; break; default: - wpa_printf(MSG_MSGDUMP, "Unknown Broadcom " + wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " "information element ignored " - "(type=%d len=%lu)\n", + "(type=%d len=%lu)", 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)", + wpa_printf(MSG_EXCESSIVE, "unknown vendor specific " + "information element ignored (vendor OUI " + "%02x:%02x:%02x len=%lu)", pos[0], pos[1], pos[2], (unsigned long) elen); return -1; } @@ -131,12 +152,12 @@ static int ieee802_11_parse_vendor_specific(u8 *pos, size_t elen, * @show_errors: Whether to show parsing errors in debug log * Returns: Parsing result */ -ParseRes ieee802_11_parse_elems(u8 *start, size_t len, +ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors) { size_t left = len; - u8 *pos = start; + const u8 *pos = start; int unknown = 0; os_memset(elems, 0, sizeof(*elems)); @@ -168,25 +189,12 @@ ParseRes ieee802_11_parse_elems(u8 *start, size_t len, 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; @@ -211,8 +219,6 @@ ParseRes ieee802_11_parse_elems(u8 *start, size_t len, 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; @@ -238,6 +244,42 @@ ParseRes ieee802_11_parse_elems(u8 *start, size_t len, elems->ht_operation = pos; elems->ht_operation_len = elen; break; + case WLAN_EID_VHT_CAP: + elems->vht_capabilities = pos; + elems->vht_capabilities_len = elen; + break; + case WLAN_EID_VHT_OPERATION: + elems->vht_operation = pos; + elems->vht_operation_len = elen; + break; + case WLAN_EID_LINK_ID: + if (elen < 18) + break; + elems->link_id = pos; + break; + case WLAN_EID_INTERWORKING: + elems->interworking = pos; + elems->interworking_len = elen; + break; + case WLAN_EID_QOS_MAP_SET: + if (elen < 16) + break; + elems->qos_map_set = pos; + elems->qos_map_set_len = elen; + break; + case WLAN_EID_EXT_CAPAB: + elems->ext_capab = pos; + elems->ext_capab_len = elen; + break; + case WLAN_EID_BSS_MAX_IDLE_PERIOD: + if (elen < 3) + break; + elems->bss_max_idle_period = pos; + break; + case WLAN_EID_SSID_LIST: + elems->ssid_list = pos; + elems->ssid_list_len = elen; + break; default: unknown++; if (!show_errors) @@ -257,3 +299,240 @@ ParseRes ieee802_11_parse_elems(u8 *start, size_t len, 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; +} + + +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; + } +} + + +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], + const char *name, const char *val) +{ + int num, v; + const char *pos; + struct hostapd_wmm_ac_params *ac; + + /* skip 'wme_ac_' or 'wmm_ac_' prefix */ + pos = name + 7; + if (os_strncmp(pos, "be_", 3) == 0) { + num = 0; + pos += 3; + } else if (os_strncmp(pos, "bk_", 3) == 0) { + num = 1; + pos += 3; + } else if (os_strncmp(pos, "vi_", 3) == 0) { + num = 2; + pos += 3; + } else if (os_strncmp(pos, "vo_", 3) == 0) { + num = 3; + pos += 3; + } else { + wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos); + return -1; + } + + ac = &wmm_ac_params[num]; + + if (os_strcmp(pos, "aifs") == 0) { + v = atoi(val); + if (v < 1 || v > 255) { + wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v); + return -1; + } + ac->aifs = v; + } else if (os_strcmp(pos, "cwmin") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v); + return -1; + } + ac->cwmin = v; + } else if (os_strcmp(pos, "cwmax") == 0) { + v = atoi(val); + if (v < 0 || v > 12) { + wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v); + return -1; + } + ac->cwmax = v; + } else if (os_strcmp(pos, "txop_limit") == 0) { + v = atoi(val); + if (v < 0 || v > 0xffff) { + wpa_printf(MSG_ERROR, "Invalid txop value %d", v); + return -1; + } + ac->txop_limit = v; + } else if (os_strcmp(pos, "acm") == 0) { + v = atoi(val); + if (v < 0 || v > 1) { + wpa_printf(MSG_ERROR, "Invalid acm value %d", v); + return -1; + } + ac->admission_control_mandatory = v; + } else { + wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos); + return -1; + } + + return 0; +} + + +enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) +{ + enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES; + + if (freq >= 2412 && freq <= 2472) { + mode = HOSTAPD_MODE_IEEE80211G; + *channel = (freq - 2407) / 5; + } else if (freq == 2484) { + mode = HOSTAPD_MODE_IEEE80211B; + *channel = 14; + } else if (freq >= 4900 && freq < 5000) { + mode = HOSTAPD_MODE_IEEE80211A; + *channel = (freq - 4000) / 5; + } else if (freq >= 5000 && freq < 5900) { + mode = HOSTAPD_MODE_IEEE80211A; + *channel = (freq - 5000) / 5; + } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) { + mode = HOSTAPD_MODE_IEEE80211AD; + *channel = (freq - 56160) / 2160; + } + + return mode; +} + + +static int is_11b(u8 rate) +{ + return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; +} + + +int supp_rates_11b_only(struct ieee802_11_elems *elems) +{ + int num_11b = 0, num_others = 0; + int i; + + if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) + return 0; + + for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { + if (is_11b(elems->supp_rates[i])) + num_11b++; + else + num_others++; + } + + for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; + i++) { + if (is_11b(elems->ext_supp_rates[i])) + num_11b++; + else + num_others++; + } + + return num_11b > 0 && num_others == 0; +} diff --git a/contrib/hostapd/src/common/ieee802_11_common.h b/contrib/hostapd/src/common/ieee802_11_common.h index b7e497b6cc..b84dd9e757 100644 --- a/contrib/hostapd/src/common/ieee802_11_common.h +++ b/contrib/hostapd/src/common/ieee802_11_common.h @@ -1,15 +1,9 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_11_COMMON_H @@ -17,58 +11,88 @@ /* Parsed Information Elements */ struct ieee802_11_elems { - u8 *ssid; + const u8 *ssid; + const u8 *supp_rates; + const u8 *ds_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 *supp_channels; + const u8 *mdie; + const u8 *ftie; + const u8 *timeout_int; + const u8 *ht_capabilities; + const u8 *ht_operation; + const u8 *vht_capabilities; + const u8 *vht_operation; + const u8 *vendor_ht_cap; + const u8 *p2p; + const u8 *wfd; + const u8 *link_id; + const u8 *interworking; + const u8 *qos_map_set; + const u8 *hs20; + const u8 *ext_capab; + const u8 *bss_max_idle_period; + const u8 *ssid_list; + u8 ssid_len; - u8 *supp_rates; u8 supp_rates_len; - u8 *fh_params; - u8 fh_params_len; - u8 *ds_params; u8 ds_params_len; - u8 *cf_params; - u8 cf_params_len; - u8 *tim; - u8 tim_len; - u8 *ibss_params; - u8 ibss_params_len; - u8 *challenge; u8 challenge_len; - u8 *erp_info; u8 erp_info_len; - u8 *ext_supp_rates; u8 ext_supp_rates_len; - u8 *wpa_ie; u8 wpa_ie_len; - u8 *rsn_ie; u8 rsn_ie_len; - u8 *wmm; /* WMM Information or Parameter Element */ u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */ - u8 *wmm_tspec; u8 wmm_tspec_len; - u8 *wps_ie; u8 wps_ie_len; - u8 *power_cap; - u8 power_cap_len; - u8 *supp_channels; u8 supp_channels_len; - u8 *mdie; u8 mdie_len; - u8 *ftie; u8 ftie_len; - u8 *timeout_int; u8 timeout_int_len; - u8 *ht_capabilities; u8 ht_capabilities_len; - u8 *ht_operation; u8 ht_operation_len; - u8 *vendor_ht_cap; + u8 vht_capabilities_len; + u8 vht_operation_len; u8 vendor_ht_cap_len; + u8 p2p_len; + u8 wfd_len; + u8 interworking_len; + u8 qos_map_set_len; + u8 hs20_len; + u8 ext_capab_len; + u8 ssid_list_len; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; -ParseRes ieee802_11_parse_elems(u8 *start, size_t len, +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); +struct ieee80211_hdr; +const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len); + +struct hostapd_wmm_ac_params { + int cwmin; + int cwmax; + int aifs; + int txop_limit; /* in units of 32us */ + int admission_control_mandatory; +}; + +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], + const char *name, const char *val); +enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel); + +int supp_rates_11b_only(struct ieee802_11_elems *elems); #endif /* IEEE802_11_COMMON_H */ diff --git a/contrib/hostapd/src/common/ieee802_11_defs.h b/contrib/hostapd/src/common/ieee802_11_defs.h index d9e54a99ed..6f7f7779da 100644 --- a/contrib/hostapd/src/common/ieee802_11_defs.h +++ b/contrib/hostapd/src/common/ieee802_11_defs.h @@ -1,16 +1,10 @@ /* * IEEE 802.11 Frame type definitions - * Copyright (c) 2002-2007, Jouni Malinen + * Copyright (c) 2002-2009, Jouni Malinen * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IEEE802_11_DEFS_H @@ -71,11 +65,18 @@ #define WLAN_FC_STYPE_CFPOLL 6 #define WLAN_FC_STYPE_CFACKPOLL 7 #define WLAN_FC_STYPE_QOS_DATA 8 +#define WLAN_FC_STYPE_QOS_DATA_CFACK 9 +#define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10 +#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11 +#define WLAN_FC_STYPE_QOS_NULL 12 +#define WLAN_FC_STYPE_QOS_CFPOLL 14 +#define WLAN_FC_STYPE_QOS_CFACKPOLL 15 /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 #define WLAN_AUTH_SHARED_KEY 1 #define WLAN_AUTH_FT 2 +#define WLAN_AUTH_SAE 3 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 @@ -95,6 +96,11 @@ /* 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_TDLS_WAKEUP_ALTERNATE 2 +#define WLAN_STATUS_TDLS_WAKEUP_REJECT 3 +#define WLAN_STATUS_SECURITY_DISABLED 5 +#define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6 +#define WLAN_STATUS_NOT_IN_SAME_BSS 7 #define WLAN_STATUS_CAPS_UNSUPPORTED 10 #define WLAN_STATUS_REASSOC_NO_ASSOC 11 #define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 @@ -114,11 +120,16 @@ #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_ASSOC_DENIED_NO_DSSS_OFDM 26 +#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27 +#define WLAN_STATUS_R0KH_UNREACHABLE 28 +#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29 /* 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 @@ -137,6 +148,20 @@ #define WLAN_STATUS_INVALID_PMKID 53 #define WLAN_STATUS_INVALID_MDIE 54 #define WLAN_STATUS_INVALID_FTIE 55 +#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59 +#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60 +#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61 +#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62 +#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63 +#define WLAN_STATUS_REQ_REFUSED_HOME 64 +#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65 +#define WLAN_STATUS_REQ_REFUSED_SSPN 67 +#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68 +#define WLAN_STATUS_INVALID_RSNIE 72 +#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 +#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 +#define WLAN_STATUS_TRANSMISSION_FAILURE 79 +#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ #define WLAN_REASON_UNSPECIFIED 1 @@ -164,6 +189,10 @@ #define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 #define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 #define WLAN_REASON_CIPHER_SUITE_REJECTED 24 +#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25 +#define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26 +/* IEEE 802.11e */ +#define WLAN_REASON_DISASSOC_LOW_ACK 34 /* Information Element IDs */ @@ -175,6 +204,7 @@ #define WLAN_EID_TIM 5 #define WLAN_EID_IBSS_PARAMS 6 #define WLAN_EID_COUNTRY 7 +#define WLAN_EID_BSS_LOAD 11 #define WLAN_EID_CHALLENGE 16 /* EIDs defined by IEEE 802.11h - START */ #define WLAN_EID_PWR_CONSTRAINT 32 @@ -190,18 +220,44 @@ /* EIDs defined by IEEE 802.11h - END */ #define WLAN_EID_ERP_INFO 42 #define WLAN_EID_HT_CAP 45 +#define WLAN_EID_QOS 46 #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_SUPPORTED_OPERATING_CLASSES 59 #define WLAN_EID_HT_OPERATION 61 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 +#define WLAN_EID_WAPI 68 +#define WLAN_EID_TIME_ADVERTISEMENT 69 #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_SSID_LIST 84 +#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90 +#define WLAN_EID_TFS_REQ 91 +#define WLAN_EID_TFS_RESP 92 +#define WLAN_EID_WNMSLEEP 93 +#define WLAN_EID_TIME_ZONE 98 +#define WLAN_EID_LINK_ID 101 +#define WLAN_EID_INTERWORKING 107 +#define WLAN_EID_ADV_PROTO 108 +#define WLAN_EID_QOS_MAP_SET 110 +#define WLAN_EID_ROAMING_CONSORTIUM 111 +#define WLAN_EID_EXT_CAPAB 127 +#define WLAN_EID_CCKM 156 +#define WLAN_EID_VHT_CAP 191 +#define WLAN_EID_VHT_OPERATION 192 +#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 +#define WLAN_EID_VHT_WIDE_BW_CHSWITCH 194 +#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195 +#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196 +#define WLAN_EID_VHT_AID 197 +#define WLAN_EID_VHT_QUIET_CHANNEL 198 +#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199 #define WLAN_EID_VENDOR_SPECIFIC 221 @@ -215,7 +271,34 @@ #define WLAN_ACTION_FT 6 #define WLAN_ACTION_HT 7 #define WLAN_ACTION_SA_QUERY 8 +#define WLAN_ACTION_PROTECTED_DUAL 9 +#define WLAN_ACTION_WNM 10 +#define WLAN_ACTION_UNPROTECTED_WNM 11 +#define WLAN_ACTION_TDLS 12 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ +#define WLAN_ACTION_VENDOR_SPECIFIC 127 + +/* Public action codes */ +#define WLAN_PA_20_40_BSS_COEX 0 +#define WLAN_PA_VENDOR_SPECIFIC 9 +#define WLAN_PA_GAS_INITIAL_REQ 10 +#define WLAN_PA_GAS_INITIAL_RESP 11 +#define WLAN_PA_GAS_COMEBACK_REQ 12 +#define WLAN_PA_GAS_COMEBACK_RESP 13 +#define WLAN_TDLS_DISCOVERY_RESPONSE 14 + +/* Protected Dual of Public Action frames */ +#define WLAN_PROT_DSE_ENABLEMENT 1 +#define WLAN_PROT_DSE_DEENABLEMENT 2 +#define WLAN_PROT_EXT_CSA 4 +#define WLAN_PROT_MEASUREMENT_REQ 5 +#define WLAN_PROT_MEASUREMENT_REPORT 6 +#define WLAN_PROT_DSE_POWER_CONSTRAINT 8 +#define WLAN_PROT_VENDOR_SPECIFIC 9 +#define WLAN_PROT_GAS_INITIAL_REQ 10 +#define WLAN_PROT_GAS_INITIAL_RESP 11 +#define WLAN_PROT_GAS_COMEBACK_REQ 12 +#define WLAN_PROT_GAS_COMEBACK_RESP 13 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ #define WLAN_SA_QUERY_REQUEST 0 @@ -223,16 +306,123 @@ #define WLAN_SA_QUERY_TR_ID_LEN 2 +/* TDLS action codes */ +#define WLAN_TDLS_SETUP_REQUEST 0 +#define WLAN_TDLS_SETUP_RESPONSE 1 +#define WLAN_TDLS_SETUP_CONFIRM 2 +#define WLAN_TDLS_TEARDOWN 3 +#define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4 +#define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5 +#define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6 +#define WLAN_TDLS_PEER_PSM_REQUEST 7 +#define WLAN_TDLS_PEER_PSM_RESPONSE 8 +#define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9 +#define WLAN_TDLS_DISCOVERY_REQUEST 10 + /* Timeout Interval Type */ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1 #define WLAN_TIMEOUT_KEY_LIFETIME 2 #define WLAN_TIMEOUT_ASSOC_COMEBACK 3 +/* Interworking element (IEEE 802.11u) - Access Network Options */ +#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f +#define INTERWORKING_ANO_INTERNET 0x10 +#define INTERWORKING_ANO_ASRA 0x20 +#define INTERWORKING_ANO_ESR 0x40 +#define INTERWORKING_ANO_UESA 0x80 + +#define INTERWORKING_ANT_PRIVATE 0 +#define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1 +#define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2 +#define INTERWORKING_ANT_FREE_PUBLIC 3 +#define INTERWORKING_ANT_PERSONAL_DEVICE 4 +#define INTERWORKING_ANT_EMERGENCY_SERVICES 5 +#define INTERWORKING_ANT_TEST 6 +#define INTERWORKING_ANT_WILDCARD 15 + +/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */ +enum adv_proto_id { + ACCESS_NETWORK_QUERY_PROTOCOL = 0, + MIH_INFO_SERVICE = 1, + MIH_CMD_AND_EVENT_DISCOVERY = 2, + EMERGENCY_ALERT_SYSTEM = 3, + ADV_PROTO_VENDOR_SPECIFIC = 221 +}; + +/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */ +enum anqp_info_id { + ANQP_QUERY_LIST = 256, + ANQP_CAPABILITY_LIST = 257, + ANQP_VENUE_NAME = 258, + ANQP_EMERGENCY_CALL_NUMBER = 259, + ANQP_NETWORK_AUTH_TYPE = 260, + ANQP_ROAMING_CONSORTIUM = 261, + ANQP_IP_ADDR_TYPE_AVAILABILITY = 262, + ANQP_NAI_REALM = 263, + ANQP_3GPP_CELLULAR_NETWORK = 264, + ANQP_AP_GEOSPATIAL_LOCATION = 265, + ANQP_AP_CIVIC_LOCATION = 266, + ANQP_AP_LOCATION_PUBLIC_URI = 267, + ANQP_DOMAIN_NAME = 268, + ANQP_EMERGENCY_ALERT_URI = 269, + ANQP_EMERGENCY_NAI = 271, + ANQP_VENDOR_SPECIFIC = 56797 +}; + +/* NAI Realm list - EAP Method subfield - Authentication Parameter ID */ +enum nai_realm_eap_auth_param { + NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1, + NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2, + NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3, + NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4, + NAI_REALM_EAP_AUTH_CRED_TYPE = 5, + NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6, + NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221 +}; + +enum nai_realm_eap_auth_inner_non_eap { + NAI_REALM_INNER_NON_EAP_PAP = 1, + NAI_REALM_INNER_NON_EAP_CHAP = 2, + NAI_REALM_INNER_NON_EAP_MSCHAP = 3, + NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4 +}; + +enum nai_realm_eap_cred_type { + NAI_REALM_CRED_TYPE_SIM = 1, + NAI_REALM_CRED_TYPE_USIM = 2, + NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3, + NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4, + NAI_REALM_CRED_TYPE_SOFTOKEN = 5, + NAI_REALM_CRED_TYPE_CERTIFICATE = 6, + NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7, + NAI_REALM_CRED_TYPE_NONE = 8, + NAI_REALM_CRED_TYPE_ANONYMOUS = 9, + NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10 +}; #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; @@ -250,6 +440,7 @@ struct ieee80211_mgmt { } STRUCT_PACKED auth; struct { le16 reason_code; + u8 variable[0]; } STRUCT_PACKED deauth; struct { le16 capab_info; @@ -273,6 +464,7 @@ struct ieee80211_mgmt { } STRUCT_PACKED reassoc_req; struct { le16 reason_code; + u8 variable[0]; } STRUCT_PACKED disassoc; struct { u8 timestamp[8]; @@ -332,50 +524,73 @@ struct ieee80211_mgmt { u8 action; /* */ u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; } STRUCT_PACKED sa_query_resp; + struct { + u8 action; + u8 dialogtoken; + u8 variable[0]; + } STRUCT_PACKED wnm_sleep_req; + struct { + u8 action; + u8 dialogtoken; + le16 keydata_len; + u8 variable[0]; + } STRUCT_PACKED wnm_sleep_resp; + struct { + u8 action; + u8 variable[0]; + } STRUCT_PACKED public_action; + struct { + u8 action; /* 9 */ + u8 oui[3]; + /* Vendor-specific content */ + u8 variable[0]; + } STRUCT_PACKED vs_public_action; + struct { + u8 action; /* 7 */ + u8 dialog_token; + u8 req_mode; + le16 disassoc_timer; + u8 validity_interval; + /* BSS Termination Duration (optional), + * Session Information URL (optional), + * BSS Transition Candidate List + * Entries */ + u8 variable[0]; + } STRUCT_PACKED bss_tm_req; + struct { + u8 action; /* 8 */ + u8 dialog_token; + u8 status_code; + u8 bss_termination_delay; + /* Target BSSID (optional), + * BSS Transition Candidate List + * Entries (optional) */ + u8 variable[0]; + } STRUCT_PACKED bss_tm_resp; + struct { + u8 action; /* 6 */ + u8 dialog_token; + u8 query_reason; + /* BSS Transition Candidate List + * Entries (optional) */ + u8 variable[0]; + } STRUCT_PACKED bss_tm_query; } u; } STRUCT_PACKED action; } u; } 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) - - -/* HT Capability element */ - -enum { - MAX_RX_AMPDU_FACTOR_8KB = 0, - MAX_RX_AMPDU_FACTOR_16KB, - MAX_RX_AMPDU_FACTOR_32KB, - MAX_RX_AMPDU_FACTOR_64KB -}; - -enum { - CALIBRATION_NOT_SUPPORTED = 0, - CALIBRATION_CANNOT_INIT, - CALIBRATION_CAN_INIT, - CALIBRATION_FULL_SUPPORT -}; - -enum { - MCS_FEEDBACK_NOT_PROVIDED = 0, - MCS_FEEDBACK_UNSOLICITED, - MCS_FEEDBACK_MRQ_RESPONSE -}; +/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ +#define IEEE80211_HT_MCS_MASK_LEN 10 -struct ieee80211_ht_capability { - le16 capabilities_info; - u8 mac_ht_params_info; +struct ieee80211_ht_capabilities { + le16 ht_capabilities_info; + u8 a_mpdu_params; u8 supported_mcs_set[16]; - le16 extended_ht_capability_info; - le32 tx_BF_capability_info; - u8 antenna_selection_info; + le16 ht_extended_capabilities; + le32 tx_bf_capability_info; + u8 asel_capabilities; } STRUCT_PACKED; @@ -387,47 +602,42 @@ struct ieee80211_ht_operation { u8 basic_set[16]; } STRUCT_PACKED; -/* auxiliary bit manipulation macros FIXME: move it to common later... */ -#define SET_2BIT_U8(_ptr_, _shift_, _val_) \ - ((*(_ptr_) &= ~(3 << (_shift_))), \ - (*(_ptr_) |= (*(_ptr_) & (((u8)3) << (_shift_))) | \ - (((u8)(_val_) & 3) << _shift_))) -#define GET_2BIT_U8(_var_, _shift_) \ - (((_var_) & (((u8)3) << (_shift_))) >> (_shift_)) - -#define SET_2BIT_LE16(_u16ptr_, _shift_, _val_) \ - ((*(_u16ptr_) &= ~(3 << (_shift_))), \ - (*(_u16ptr_) |= \ - (((*(_u16ptr_)) & (((u16)3) << ((u16)_shift_))) | \ - (((u16)(_val_) & (u16)3) << (u16)(_shift_))))) - -#define GET_2BIT_LE16(_var_, _shift_) \ - (((_var_) & (((u16)3) << (_shift_))) >> (_shift_)) - -#define SET_2BIT_LE32(_u32ptr_, _shift_, _val_) \ - ((*(_u32ptr_) &= ~(3 << (_shift_))), \ - (*(_u32ptr_) |= (((*(_u32ptr_)) & (((u32)3) << (_shift_))) | \ - (((u32)(_val_) & 3) << _shift_)))) +struct ieee80211_obss_scan_parameters { + le16 scan_passive_dwell; + le16 scan_active_dwell; + le16 width_trigger_scan_interval; + le16 scan_passive_total_per_channel; + le16 scan_active_total_per_channel; + le16 channel_transition_delay_factor; + le16 scan_activity_threshold; +} STRUCT_PACKED; -#define GET_2BIT_LE32(_var_, _shift_) \ - (((_var_) & (((u32)3) << (_shift_))) >> (_shift_)) -#define SET_3BIT_LE16(_u16ptr_, _shift_, _val_) \ - ((*(_u16ptr_) &= ~(7 << (_shift_))), \ - (*(_u16ptr_) |= (((*(_u16ptr_)) & (((u16)7) << (_shift_))) | \ - (((u16)(_val_) & 7) << _shift_)))) +struct ieee80211_vht_capabilities { + le32 vht_capabilities_info; + struct { + le16 rx_map; + le16 rx_highest; + le16 tx_map; + le16 tx_highest; + } vht_supported_mcs_set; +} STRUCT_PACKED; -#define GET_3BIT_LE16(_var_, _shift_) \ - (((_var_) & (((u16)7) << (_shift_))) >> (_shift_)) +struct ieee80211_vht_operation { + u8 vht_op_info_chwidth; + u8 vht_op_info_chan_center_freq_seg0_idx; + u8 vht_op_info_chan_center_freq_seg1_idx; + le16 vht_basic_mcs_set; +} STRUCT_PACKED; -#define SET_3BIT_LE32(_u32ptr_, _shift_, _val_) \ - ((*(_u32ptr_) &= ~(7 << (_shift_))), \ - (*(_u32ptr_) |= (((*(_u32ptr_)) & (((u32)7) << (_shift_))) | \ - (((u32)(_val_) & 7) << _shift_)))) +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ -#define GET_3BIT_LE32(_var_, _shift_) \ - (((_var_) & (((u32)7) << (_shift_))) >> (_shift_)) +#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)) @@ -452,9 +662,6 @@ struct ieee80211_ht_operation { #define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15)) -#define MAC_HT_PARAM_INFO_MAX_RX_AMPDU_FACTOR_OFFSET 0 -#define MAC_HT_PARAM_INFO_MAX_MPDU_DENSITY_OFFSET 2 - #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 @@ -490,22 +697,6 @@ struct ieee80211_ht_operation { #define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5)) #define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6)) - -struct ht_cap_ie { - u8 id; - u8 length; - struct ieee80211_ht_capability data; -} STRUCT_PACKED; - - -#define REC_TRANS_CHNL_WIDTH_20 0 -#define REC_TRANS_CHNL_WIDTH_ANY 1 - -#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_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)) @@ -514,8 +705,14 @@ struct ht_cap_ie { #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)) + (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)) @@ -528,40 +725,60 @@ struct ht_cap_ie { #define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10)) #define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) - -/* Secondary channel offset element */ -#define SECONDARY_CHANNEL_OFFSET_NONE 0 -#define SECONDARY_CHANNEL_OFFSET_ABOVE 1 -#define SECONDARY_CHANNEL_OFFSET_BELOW 3 -struct secondary_channel_offset_ie { - u8 id; - u8 length; - u8 secondary_offset_offset; -} STRUCT_PACKED; - - -/* body of Recommended Transmit Channel Width action frame */ -#define CHANNEL_WIDTH_20 0 -#define CHANNEL_WIDTH_ANY 1 -struct recommended_tx_channel_width_action { - u8 category; - u8 action; - u8 channel_width; -} STRUCT_PACKED; - -/* body of MIMO Power Save action frame */ -#define PWR_SAVE_MODE_STATIC 0 -#define PWR_SAVE_MODE_DYNAMIC 1 -struct mimo_pwr_save_action { - u8 category; - u8 action; - u8 enable; - u8 mode; -} STRUCT_PACKED; - +#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126 +#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 + +/* VHT Defines */ +#define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0)) +#define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1)) +#define VHT_CAP_MAX_MPDU_LENGTH_MASK ((u32) BIT(0) | BIT(1)) +#define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2)) +#define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3)) +#define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3)) +#define VHT_CAP_RXLDPC ((u32) BIT(4)) +#define VHT_CAP_SHORT_GI_80 ((u32) BIT(5)) +#define VHT_CAP_SHORT_GI_160 ((u32) BIT(6)) +#define VHT_CAP_TXSTBC ((u32) BIT(7)) +#define VHT_CAP_RXSTBC_1 ((u32) BIT(8)) +#define VHT_CAP_RXSTBC_2 ((u32) BIT(9)) +#define VHT_CAP_RXSTBC_3 ((u32) BIT(8) | BIT(9)) +#define VHT_CAP_RXSTBC_4 ((u32) BIT(10)) +#define VHT_CAP_RXSTBC_MASK ((u32) BIT(8) | BIT(9) | \ + BIT(10)) +#define VHT_CAP_SU_BEAMFORMER_CAPABLE ((u32) BIT(11)) +#define VHT_CAP_SU_BEAMFORMEE_CAPABLE ((u32) BIT(12)) +#define VHT_CAP_BEAMFORMEE_STS_MAX ((u32) BIT(13) | \ + BIT(14) | BIT(15)) +#define VHT_CAP_BEAMFORMEE_STS_OFFSET 13 +#define VHT_CAP_SOUNDING_DIMENSION_MAX ((u32) BIT(16) | \ + BIT(17) | BIT(18)) +#define VHT_CAP_SOUNDING_DIMENSION_OFFSET 16 +#define VHT_CAP_MU_BEAMFORMER_CAPABLE ((u32) BIT(19)) +#define VHT_CAP_MU_BEAMFORMEE_CAPABLE ((u32) BIT(20)) +#define VHT_CAP_VHT_TXOP_PS ((u32) BIT(21)) +#define VHT_CAP_HTC_VHT ((u32) BIT(22)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT ((u32) BIT(23) | \ + BIT(24) | BIT(25)) +#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB ((u32) BIT(27)) +#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27)) +#define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28)) +#define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29)) + +/* VHT channel widths */ +#define VHT_CHANWIDTH_USE_HT 0 +#define VHT_CHANWIDTH_80MHZ 1 +#define VHT_CHANWIDTH_160MHZ 2 +#define VHT_CHANWIDTH_80P80MHZ 3 #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 OUI_WFA 0x506f9a +#define P2P_IE_VENDOR_TYPE 0x506f9a09 +#define WFD_IE_VENDOR_TYPE 0x506f9a0a +#define WFD_OUI_TYPE 10 +#define HS20_IE_VENDOR_TYPE 0x506f9a10 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -585,9 +802,392 @@ struct mimo_pwr_save_action { /* 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_QOSINFO_STA_AC_MASK 0x0f +#define WMM_QOSINFO_STA_SP_MASK 0x03 +#define WMM_QOSINFO_STA_SP_SHIFT 5 + +#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 specific 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 HS20_INDICATION_OUI_TYPE 16 +#define HS20_ANQP_OUI_TYPE 17 +#define HS20_STYPE_QUERY_LIST 1 +#define HS20_STYPE_CAPABILITY_LIST 2 +#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3 +#define HS20_STYPE_WAN_METRICS 4 +#define HS20_STYPE_CONNECTION_CAPABILITY 5 +#define HS20_STYPE_NAI_HOME_REALM_QUERY 6 +#define HS20_STYPE_OPERATING_CLASS 7 + +/* Wi-Fi Direct (P2P) */ + +#define P2P_OUI_TYPE 9 + +enum p2p_attr_id { + P2P_ATTR_STATUS = 0, + P2P_ATTR_MINOR_REASON_CODE = 1, + P2P_ATTR_CAPABILITY = 2, + P2P_ATTR_DEVICE_ID = 3, + P2P_ATTR_GROUP_OWNER_INTENT = 4, + P2P_ATTR_CONFIGURATION_TIMEOUT = 5, + P2P_ATTR_LISTEN_CHANNEL = 6, + P2P_ATTR_GROUP_BSSID = 7, + P2P_ATTR_EXT_LISTEN_TIMING = 8, + P2P_ATTR_INTENDED_INTERFACE_ADDR = 9, + P2P_ATTR_MANAGEABILITY = 10, + P2P_ATTR_CHANNEL_LIST = 11, + P2P_ATTR_NOTICE_OF_ABSENCE = 12, + P2P_ATTR_DEVICE_INFO = 13, + P2P_ATTR_GROUP_INFO = 14, + P2P_ATTR_GROUP_ID = 15, + P2P_ATTR_INTERFACE = 16, + P2P_ATTR_OPERATING_CHANNEL = 17, + P2P_ATTR_INVITATION_FLAGS = 18, + P2P_ATTR_OOB_GO_NEG_CHANNEL = 19, + P2P_ATTR_VENDOR_SPECIFIC = 221 +}; + +#define P2P_MAX_GO_INTENT 15 + +/* P2P Capability - Device Capability bitmap */ +#define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0) +#define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1) +#define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2) +#define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3) +#define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4) +#define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5) + +/* P2P Capability - Group Capability bitmap */ +#define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0) +#define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1) +#define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2) +#define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3) +#define P2P_GROUP_CAPAB_CROSS_CONN BIT(4) +#define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5) +#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6) +#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7) + +/* Invitation Flags */ +#define P2P_INVITATION_FLAGS_TYPE BIT(0) + +/* P2P Manageability */ +#define P2P_MAN_DEVICE_MANAGEMENT BIT(0) +#define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1) +#define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2) + +enum p2p_status_code { + P2P_SC_SUCCESS = 0, + P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1, + P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2, + P2P_SC_FAIL_LIMIT_REACHED = 3, + P2P_SC_FAIL_INVALID_PARAMS = 4, + P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5, + P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6, + P2P_SC_FAIL_NO_COMMON_CHANNELS = 7, + P2P_SC_FAIL_UNKNOWN_GROUP = 8, + P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9, + P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10, + P2P_SC_FAIL_REJECTED_BY_USER = 11, +}; + +enum p2p_role_indication { + P2P_DEVICE_NOT_IN_GROUP = 0x00, + P2P_CLIENT_IN_A_GROUP = 0x01, + P2P_GO_IN_A_GROUP = 0x02, +}; + +#define P2P_WILDCARD_SSID "DIRECT-" +#define P2P_WILDCARD_SSID_LEN 7 + +/* P2P action frames */ +enum p2p_act_frame_type { + P2P_NOA = 0, + P2P_PRESENCE_REQ = 1, + P2P_PRESENCE_RESP = 2, + P2P_GO_DISC_REQ = 3 +}; + +/* P2P public action frames */ +enum p2p_action_frame_type { + P2P_GO_NEG_REQ = 0, + P2P_GO_NEG_RESP = 1, + P2P_GO_NEG_CONF = 2, + P2P_INVITATION_REQ = 3, + P2P_INVITATION_RESP = 4, + P2P_DEV_DISC_REQ = 5, + P2P_DEV_DISC_RESP = 6, + P2P_PROV_DISC_REQ = 7, + P2P_PROV_DISC_RESP = 8 +}; + +enum p2p_service_protocol_type { + P2P_SERV_ALL_SERVICES = 0, + P2P_SERV_BONJOUR = 1, + P2P_SERV_UPNP = 2, + P2P_SERV_WS_DISCOVERY = 3, + P2P_SERV_WIFI_DISPLAY = 4, + P2P_SERV_VENDOR_SPECIFIC = 255 +}; + +enum p2p_sd_status { + P2P_SD_SUCCESS = 0, + P2P_SD_PROTO_NOT_AVAILABLE = 1, + P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2, + P2P_SD_BAD_REQUEST = 3 +}; + + +enum wifi_display_subelem { + WFD_SUBELEM_DEVICE_INFO = 0, + WFD_SUBELEM_ASSOCIATED_BSSID = 1, + WFD_SUBELEM_AUDIO_FORMATS = 2, + WFD_SUBELEM_VIDEO_FORMATS = 3, + WFD_SUBELEM_3D_VIDEO_FORMATS = 4, + WFD_SUBELEM_CONTENT_PROTECTION = 5, + WFD_SUBELEM_COUPLED_SINK = 6, + WFD_SUBELEM_EXT_CAPAB = 7, + WFD_SUBELEM_LOCAL_IP_ADDRESS = 8, + WFD_SUBELEM_SESSION_INFO = 9 +}; + #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 +#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07 +#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 +#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09 +#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A +#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B +#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C +#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D + +#define WLAN_CIPHER_SUITE_SMS4 0x00147201 + +#define WLAN_CIPHER_SUITE_CKIP 0x00409600 +#define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601 +#define WLAN_CIPHER_SUITE_CMIC 0x00409602 +#define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */ + +/* AKM suite selectors */ +#define WLAN_AKM_SUITE_8021X 0x000FAC01 +#define WLAN_AKM_SUITE_PSK 0x000FAC02 +#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03 +#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04 +#define WLAN_AKM_SUITE_CCKM 0x00409600 + + +/* IEEE 802.11v - WNM Action field values */ +enum wnm_action { + WNM_EVENT_REQ = 0, + WNM_EVENT_REPORT = 1, + WNM_DIAGNOSTIC_REQ = 2, + WNM_DIAGNOSTIC_REPORT = 3, + WNM_LOCATION_CFG_REQ = 4, + WNM_LOCATION_CFG_RESP = 5, + WNM_BSS_TRANS_MGMT_QUERY = 6, + WNM_BSS_TRANS_MGMT_REQ = 7, + WNM_BSS_TRANS_MGMT_RESP = 8, + WNM_FMS_REQ = 9, + WNM_FMS_RESP = 10, + WNM_COLLOCATED_INTERFERENCE_REQ = 11, + WNM_COLLOCATED_INTERFERENCE_REPORT = 12, + WNM_TFS_REQ = 13, + WNM_TFS_RESP = 14, + WNM_TFS_NOTIFY = 15, + WNM_SLEEP_MODE_REQ = 16, + WNM_SLEEP_MODE_RESP = 17, + WNM_TIM_BROADCAST_REQ = 18, + WNM_TIM_BROADCAST_RESP = 19, + WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20, + WNM_CHANNEL_USAGE_REQ = 21, + WNM_CHANNEL_USAGE_RESP = 22, + WNM_DMS_REQ = 23, + WNM_DMS_RESP = 24, + WNM_TIMING_MEASUREMENT_REQ = 25, + WNM_NOTIFICATION_REQ = 26, + WNM_NOTIFICATION_RESP = 27 +}; + +/* IEEE 802.11v - BSS Transition Management Request - Request Mode */ +#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0) +#define WNM_BSS_TM_REQ_ABRIDGED BIT(1) +#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2) +#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3) +#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4) + +/* IEEE Std 802.11-2012 - Table 8-253 */ +enum bss_trans_mgmt_status_code { + WNM_BSS_TM_ACCEPT = 0, + WNM_BSS_TM_REJECT_UNSPECIFIED = 1, + WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON = 2, + WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY = 3, + WNM_BSS_TM_REJECT_UNDESIRED = 4, + WNM_BSS_TM_REJECT_DELAY_REQUEST = 5, + WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED = 6, + WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES = 7, + WNM_BSS_TM_REJECT_LEAVING_ESS = 8 +}; + +#define WNM_NEIGHBOR_TSF 1 +#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2 +#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3 +#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4 +#define WNM_NEIGHBOR_BEARING 5 +#define WNM_NEIGHBOR_MEASUREMENT_PILOT 66 +#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70 +#define WNM_NEIGHBOR_MULTIPLE_BSSID 71 + +/* QoS action */ +enum qos_action { + QOS_ADDTS_REQ = 0, + QOS_ADDTS_RESP = 1, + QOS_DELTS = 2, + QOS_SCHEDULE = 3, + QOS_QOS_MAP_CONFIG = 4, +}; + +/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */ +#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0) +#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1) +#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2) +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ BIT(3) +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT BIT(4) + +struct ieee80211_2040_bss_coex_ie { + u8 element_id; + u8 length; + u8 coex_param; +} STRUCT_PACKED; + +struct ieee80211_2040_intol_chan_report { + u8 element_id; + u8 length; + u8 op_class; + u8 variable[0]; /* Channel List */ +} STRUCT_PACKED; + +/* IEEE 802.11v - WNM-Sleep Mode element */ +struct wnm_sleep_element { + u8 eid; /* WLAN_EID_WNMSLEEP */ + u8 len; + u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */ + u8 status; + le16 intval; +} STRUCT_PACKED; + +#define WNM_SLEEP_MODE_ENTER 0 +#define WNM_SLEEP_MODE_EXIT 1 + +enum wnm_sleep_mode_response_status { + WNM_STATUS_SLEEP_ACCEPT = 0, + WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1, + WNM_STATUS_DENIED_ACTION = 2, + WNM_STATUS_DENIED_TMP = 3, + WNM_STATUS_DENIED_KEY = 4, + WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5 +}; + +/* WNM-Sleep Mode subelement IDs */ +enum wnm_sleep_mode_subelement_id { + WNM_SLEEP_SUBELEM_GTK = 0, + WNM_SLEEP_SUBELEM_IGTK = 1 +}; + +/* Channel Switch modes (802.11h) */ +#define CHAN_SWITCH_MODE_ALLOW_TX 0 +#define CHAN_SWITCH_MODE_BLOCK_TX 1 + #endif /* IEEE802_11_DEFS_H */ diff --git a/contrib/hostapd/src/common/nl80211_copy.h b/contrib/hostapd/src/common/nl80211_copy.h deleted file mode 100644 index 45db17f81a..0000000000 --- a/contrib/hostapd/src/common/nl80211_copy.h +++ /dev/null @@ -1,1434 +0,0 @@ -#ifndef __LINUX_NL80211_H -#define __LINUX_NL80211_H -/* - * 802.11 netlink interface public header - * - * Copyright 2006, 2007, 2008 Johannes Berg - * Copyright 2008 Michael Wu - * Copyright 2008 Luis Carlos Cobo - * Copyright 2008 Michael Buesch - * Copyright 2008, 2009 Luis R. Rodriguez - * Copyright 2008 Jouni Malinen - * Copyright 2008 Colin McCabe - * - * 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 - -/** - * 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_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, - - /* 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//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_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_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, - - /* 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) - * @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_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, - - /* 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 -}; - -#endif /* __LINUX_NL80211_H */ diff --git a/contrib/hostapd/src/common/privsep_commands.h b/contrib/hostapd/src/common/privsep_commands.h index 81b7f5432e..858b51d388 100644 --- a/contrib/hostapd/src/common/privsep_commands.h +++ b/contrib/hostapd/src/common/privsep_commands.h @@ -2,14 +2,8 @@ * WPA Supplicant - privilege separation commands * Copyright (c) 2007-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PRIVSEP_COMMANDS_H @@ -18,7 +12,6 @@ enum privsep_cmd { PRIVSEP_CMD_REGISTER, PRIVSEP_CMD_UNREGISTER, - PRIVSEP_CMD_SET_WPA, PRIVSEP_CMD_SCAN, PRIVSEP_CMD_GET_SCAN_RESULTS, PRIVSEP_CMD_ASSOCIATE, @@ -30,7 +23,6 @@ enum privsep_cmd { PRIVSEP_CMD_L2_UNREGISTER, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, PRIVSEP_CMD_L2_SEND, - PRIVSEP_CMD_SET_MODE, PRIVSEP_CMD_SET_COUNTRY, }; @@ -72,7 +64,6 @@ enum privsep_event { PRIVSEP_EVENT_STKSTART, PRIVSEP_EVENT_FT_RESPONSE, PRIVSEP_EVENT_RX_EAPOL, - PRIVSEP_EVENT_STA_RX, }; #endif /* PRIVSEP_COMMANDS_H */ diff --git a/contrib/hostapd/src/common/qca-vendor.h b/contrib/hostapd/src/common/qca-vendor.h new file mode 100644 index 0000000000..0d83920a60 --- /dev/null +++ b/contrib/hostapd/src/common/qca-vendor.h @@ -0,0 +1,51 @@ +/* + * Qualcomm Atheros OUI and vendor specific assignments + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef QCA_VENDOR_H +#define QCA_VENDOR_H + +/* + * This file is a registry of identifier assignments from the Qualcomm Atheros + * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers + * can be assigned through normal review process for changes to the upstream + * hostap.git repository. + */ + +#define OUI_QCA 0x001374 + +/** + * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers + * + * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0 + * + * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event + * + * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency + * ranges to avoid to reduce issues due to interference or internal + * co-existence information in the driver. The event data structure is + * defined in struct qca_avoid_freq_list. + */ +enum qca_nl80211_vendor_subcmds { + QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, + QCA_NL80211_VENDOR_SUBCMD_TEST = 1, + /* subcmds 2..9 not yet allocated */ + QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10, +}; + + +struct qca_avoid_freq_range { + u32 start_freq; + u32 end_freq; +} STRUCT_PACKED; + +struct qca_avoid_freq_list { + u32 count; + struct qca_avoid_freq_range range[0]; +} STRUCT_PACKED; + +#endif /* QCA_VENDOR_H */ diff --git a/contrib/hostapd/src/common/sae.c b/contrib/hostapd/src/common/sae.c new file mode 100644 index 0000000000..08bf054cbd --- /dev/null +++ b/contrib/hostapd/src/common/sae.c @@ -0,0 +1,1047 @@ +/* + * Simultaneous authentication of equals + * Copyright (c) 2012-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/crypto.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "crypto/dh_groups.h" +#include "ieee802_11_defs.h" +#include "sae.h" + + +int sae_set_group(struct sae_data *sae, int group) +{ + struct sae_temporary_data *tmp; + + sae_clear_data(sae); + tmp = sae->tmp = os_zalloc(sizeof(*tmp)); + if (tmp == NULL) + return -1; + + /* First, check if this is an ECC group */ + tmp->ec = crypto_ec_init(group); + if (tmp->ec) { + sae->group = group; + tmp->prime_len = crypto_ec_prime_len(tmp->ec); + tmp->prime = crypto_ec_get_prime(tmp->ec); + tmp->order = crypto_ec_get_order(tmp->ec); + return 0; + } + + /* Not an ECC group, check FFC */ + tmp->dh = dh_groups_get(group); + if (tmp->dh) { + sae->group = group; + tmp->prime_len = tmp->dh->prime_len; + if (tmp->prime_len > SAE_MAX_PRIME_LEN) { + sae_clear_data(sae); + return -1; + } + + tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, + tmp->prime_len); + if (tmp->prime_buf == NULL) { + sae_clear_data(sae); + return -1; + } + tmp->prime = tmp->prime_buf; + + tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, + tmp->dh->order_len); + if (tmp->order_buf == NULL) { + sae_clear_data(sae); + return -1; + } + tmp->order = tmp->order_buf; + + return 0; + } + + /* Unsupported group */ + return -1; +} + + +void sae_clear_temp_data(struct sae_data *sae) +{ + struct sae_temporary_data *tmp; + if (sae == NULL || sae->tmp == NULL) + return; + tmp = sae->tmp; + crypto_ec_deinit(tmp->ec); + crypto_bignum_deinit(tmp->prime_buf, 0); + crypto_bignum_deinit(tmp->order_buf, 0); + crypto_bignum_deinit(tmp->sae_rand, 1); + crypto_bignum_deinit(tmp->pwe_ffc, 1); + crypto_bignum_deinit(tmp->own_commit_scalar, 0); + crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); + crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); + crypto_ec_point_deinit(tmp->pwe_ecc, 1); + crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); + crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); + os_free(sae->tmp); + sae->tmp = NULL; +} + + +void sae_clear_data(struct sae_data *sae) +{ + if (sae == NULL) + return; + sae_clear_temp_data(sae); + crypto_bignum_deinit(sae->peer_commit_scalar, 0); + os_memset(sae, 0, sizeof(*sae)); +} + + +static void buf_shift_right(u8 *buf, size_t len, size_t bits) +{ + size_t i; + for (i = len - 1; i > 0; i--) + buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); + buf[0] >>= bits; +} + + +static struct crypto_bignum * sae_get_rand(struct sae_data *sae) +{ + u8 val[SAE_MAX_PRIME_LEN]; + int iter = 0; + struct crypto_bignum *bn = NULL; + int order_len_bits = crypto_bignum_bits(sae->tmp->order); + size_t order_len = (order_len_bits + 7) / 8; + + if (order_len > sizeof(val)) + return NULL; + + for (;;) { + if (iter++ > 100) + return NULL; + if (random_get_bytes(val, order_len) < 0) + return NULL; + if (order_len_bits % 8) + buf_shift_right(val, order_len, 8 - order_len_bits % 8); + bn = crypto_bignum_init_set(val, order_len); + if (bn == NULL) + return NULL; + if (crypto_bignum_is_zero(bn) || + crypto_bignum_is_one(bn) || + crypto_bignum_cmp(bn, sae->tmp->order) >= 0) + continue; + break; + } + + os_memset(val, 0, order_len); + return bn; +} + + +static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae) +{ + crypto_bignum_deinit(sae->tmp->sae_rand, 1); + sae->tmp->sae_rand = sae_get_rand(sae); + if (sae->tmp->sae_rand == NULL) + return NULL; + return sae_get_rand(sae); +} + + +static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) +{ + wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR + " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); + if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { + os_memcpy(key, addr1, ETH_ALEN); + os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); + } else { + os_memcpy(key, addr2, ETH_ALEN); + os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); + } +} + + +static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, + struct crypto_ec_point *pwe) +{ + u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN]; + struct crypto_bignum *x; + int y_bit; + size_t bits; + + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + sae->tmp->prime_len) < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); + + /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ + bits = crypto_ec_prime_len_bits(sae->tmp->ec); + sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + prime, sae->tmp->prime_len, pwd_value, bits); + if (bits % 8) + buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", + pwd_value, sae->tmp->prime_len); + + if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) + return 0; + + y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; + + x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + if (x == NULL) + return -1; + if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) { + crypto_bignum_deinit(x, 0); + wpa_printf(MSG_DEBUG, "SAE: No solution found"); + return 0; + } + crypto_bignum_deinit(x, 0); + + wpa_printf(MSG_DEBUG, "SAE: PWE found"); + + return 1; +} + + +static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, + struct crypto_bignum *pwe) +{ + u8 pwd_value[SAE_MAX_PRIME_LEN]; + size_t bits = sae->tmp->prime_len * 8; + u8 exp[1]; + struct crypto_bignum *a, *b; + int res; + + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); + + /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ + sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", + sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, + bits); + if (bits % 8) + buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, + sae->tmp->prime_len); + + if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0) + { + wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p"); + return 0; + } + + /* PWE = pwd-value^((p-1)/r) modulo p */ + + a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); + + if (sae->tmp->dh->safe_prime) { + /* + * r = (p-1)/2 for the group used here, so this becomes: + * PWE = pwd-value^2 modulo p + */ + exp[0] = 2; + b = crypto_bignum_init_set(exp, sizeof(exp)); + } else { + /* Calculate exponent: (p-1)/r */ + exp[0] = 1; + b = crypto_bignum_init_set(exp, sizeof(exp)); + if (b == NULL || + crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || + crypto_bignum_div(b, sae->tmp->order, b) < 0) { + crypto_bignum_deinit(b, 0); + b = NULL; + } + } + + if (a == NULL || b == NULL) + res = -1; + else + res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); + + crypto_bignum_deinit(a, 0); + crypto_bignum_deinit(b, 0); + + if (res < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE"); + return -1; + } + + /* if (PWE > 1) --> found */ + if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) { + wpa_printf(MSG_DEBUG, "SAE: PWE <= 1"); + return 0; + } + + wpa_printf(MSG_DEBUG, "SAE: PWE found"); + return 1; +} + + +static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, + const u8 *addr2, const u8 *password, + size_t password_len) +{ + u8 counter, k = 4; + u8 addrs[2 * ETH_ALEN]; + const u8 *addr[2]; + size_t len[2]; + int found = 0; + struct crypto_ec_point *pwe_tmp; + + if (sae->tmp->pwe_ecc == NULL) { + sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); + if (sae->tmp->pwe_ecc == NULL) + return -1; + } + pwe_tmp = crypto_ec_point_init(sae->tmp->ec); + if (pwe_tmp == NULL) + return -1; + + wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", + password, password_len); + + /* + * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), + * password || counter) + */ + sae_pwd_seed_key(addr1, addr2, addrs); + + addr[0] = password; + len[0] = password_len; + addr[1] = &counter; + len[1] = sizeof(counter); + + /* + * Continue for at least k iterations to protect against side-channel + * attacks that attempt to determine the number of iterations required + * in the loop. + */ + for (counter = 1; counter < k || !found; counter++) { + u8 pwd_seed[SHA256_MAC_LEN]; + int res; + + if (counter > 200) { + /* This should not happen in practice */ + wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); + break; + } + + wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, + pwd_seed) < 0) + break; + res = sae_test_pwd_seed_ecc(sae, pwd_seed, + found ? pwe_tmp : + sae->tmp->pwe_ecc); + if (res < 0) + break; + if (res == 0) + continue; + if (found) { + wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was " + "already selected)"); + } else { + wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); + found = 1; + } + } + + crypto_ec_point_deinit(pwe_tmp, 1); + + return found ? 0 : -1; +} + + +static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, + const u8 *addr2, const u8 *password, + size_t password_len) +{ + u8 counter; + u8 addrs[2 * ETH_ALEN]; + const u8 *addr[2]; + size_t len[2]; + int found = 0; + + if (sae->tmp->pwe_ffc == NULL) { + sae->tmp->pwe_ffc = crypto_bignum_init(); + if (sae->tmp->pwe_ffc == NULL) + return -1; + } + + wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", + password, password_len); + + /* + * H(salt, ikm) = HMAC-SHA256(salt, ikm) + * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), + * password || counter) + */ + sae_pwd_seed_key(addr1, addr2, addrs); + + addr[0] = password; + len[0] = password_len; + addr[1] = &counter; + len[1] = sizeof(counter); + + for (counter = 1; !found; counter++) { + u8 pwd_seed[SHA256_MAC_LEN]; + int res; + + if (counter > 200) { + /* This should not happen in practice */ + wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); + break; + } + + wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); + if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, + pwd_seed) < 0) + break; + res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); + if (res < 0) + break; + if (res > 0) { + wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); + found = 1; + } + } + + return found ? 0 : -1; +} + + +static int sae_derive_commit_element_ecc(struct sae_data *sae, + struct crypto_bignum *mask) +{ + /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ + if (!sae->tmp->own_commit_element_ecc) { + sae->tmp->own_commit_element_ecc = + crypto_ec_point_init(sae->tmp->ec); + if (!sae->tmp->own_commit_element_ecc) + return -1; + } + + if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, + sae->tmp->own_commit_element_ecc) < 0 || + crypto_ec_point_invert(sae->tmp->ec, + sae->tmp->own_commit_element_ecc) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); + return -1; + } + + return 0; +} + + +static int sae_derive_commit_element_ffc(struct sae_data *sae, + struct crypto_bignum *mask) +{ + /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ + if (!sae->tmp->own_commit_element_ffc) { + sae->tmp->own_commit_element_ffc = crypto_bignum_init(); + if (!sae->tmp->own_commit_element_ffc) + return -1; + } + + if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, + sae->tmp->own_commit_element_ffc) < 0 || + crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, + sae->tmp->prime, + sae->tmp->own_commit_element_ffc) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); + return -1; + } + + return 0; +} + + +static int sae_derive_commit(struct sae_data *sae) +{ + struct crypto_bignum *mask; + int ret = -1; + + mask = sae_get_rand_and_mask(sae); + if (mask == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); + return -1; + } + + /* commit-scalar = (rand + mask) modulo r */ + if (!sae->tmp->own_commit_scalar) { + sae->tmp->own_commit_scalar = crypto_bignum_init(); + if (!sae->tmp->own_commit_scalar) + goto fail; + } + crypto_bignum_add(sae->tmp->sae_rand, mask, + sae->tmp->own_commit_scalar); + crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, + sae->tmp->own_commit_scalar); + + if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) + goto fail; + if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0) + goto fail; + + ret = 0; +fail: + crypto_bignum_deinit(mask, 1); + return ret; +} + + +int sae_prepare_commit(const u8 *addr1, const u8 *addr2, + const u8 *password, size_t password_len, + struct sae_data *sae) +{ + if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, + password_len) < 0) + return -1; + if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, + password_len) < 0) + return -1; + if (sae_derive_commit(sae) < 0) + return -1; + return 0; +} + + +static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) +{ + struct crypto_ec_point *K; + int ret = -1; + + K = crypto_ec_point_init(sae->tmp->ec); + if (K == NULL) + goto fail; + + /* + * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), + * PEER-COMMIT-ELEMENT))) + * If K is identity element (point-at-infinity), reject + * k = F(K) (= x coordinate) + */ + + if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, + sae->peer_commit_scalar, K) < 0 || + crypto_ec_point_add(sae->tmp->ec, K, + sae->tmp->peer_commit_element_ecc, K) < 0 || + crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || + crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || + crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); + + ret = 0; +fail: + crypto_ec_point_deinit(K, 1); + return ret; +} + + +static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) +{ + struct crypto_bignum *K; + int ret = -1; + + K = crypto_bignum_init(); + if (K == NULL) + goto fail; + + /* + * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), + * PEER-COMMIT-ELEMENT))) + * If K is identity element (one), reject. + * k = F(K) (= x coordinate) + */ + + if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, + sae->tmp->prime, K) < 0 || + crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, + sae->tmp->prime, K) < 0 || + crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 + || + crypto_bignum_is_one(K) || + crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < + 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); + + ret = 0; +fail: + crypto_bignum_deinit(K, 1); + return ret; +} + + +static int sae_derive_keys(struct sae_data *sae, const u8 *k) +{ + u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN]; + u8 keyseed[SHA256_MAC_LEN]; + u8 keys[SAE_KCK_LEN + SAE_PMK_LEN]; + struct crypto_bignum *tmp; + int ret = -1; + + tmp = crypto_bignum_init(); + if (tmp == NULL) + goto fail; + + /* keyseed = H(<0>32, k) + * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK", + * (commit-scalar + peer-commit-scalar) modulo r) + * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) + */ + + os_memset(null_key, 0, sizeof(null_key)); + hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len, + keyseed); + wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); + + crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, + tmp); + crypto_bignum_mod(tmp, sae->tmp->order, tmp); + crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); + sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", + val, sae->tmp->prime_len, keys, sizeof(keys)); + os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); + os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); + + ret = 0; +fail: + crypto_bignum_deinit(tmp, 0); + return ret; +} + + +int sae_process_commit(struct sae_data *sae) +{ + u8 k[SAE_MAX_PRIME_LEN]; + if ((sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || + (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || + sae_derive_keys(sae, k) < 0) + return -1; + return 0; +} + + +void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, + const struct wpabuf *token) +{ + u8 *pos; + wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ + if (token) + wpabuf_put_buf(buf, token); + pos = wpabuf_put(buf, sae->tmp->prime_len); + crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, + sae->tmp->prime_len, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", + pos, sae->tmp->prime_len); + if (sae->tmp->ec) { + pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); + crypto_ec_point_to_bin(sae->tmp->ec, + sae->tmp->own_commit_element_ecc, + pos, pos + sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", + pos, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", + pos + sae->tmp->prime_len, sae->tmp->prime_len); + } else { + pos = wpabuf_put(buf, sae->tmp->prime_len); + crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, + sae->tmp->prime_len, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", + pos, sae->tmp->prime_len); + } +} + + +static u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, + u16 group) +{ + if (allowed_groups) { + int i; + for (i = 0; allowed_groups[i] > 0; i++) { + if (allowed_groups[i] == group) + break; + } + if (allowed_groups[i] != group) { + wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " + "enabled in the current configuration", + group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + } + + if (sae->state == SAE_COMMITTED && group != sae->group) { + wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + if (group != sae->group && sae_set_group(sae, group) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", + group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + if (sae->tmp == NULL) { + wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (sae->tmp->dh && !allowed_groups) { + wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " + "explicit configuration enabling it", group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + + return WLAN_STATUS_SUCCESS; +} + + +static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, + const u8 *end, const u8 **token, + size_t *token_len) +{ + if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) { + size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * + sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); + if (token) + *token = *pos; + if (token_len) + *token_len = tlen; + *pos += tlen; + } else { + if (token) + *token = NULL; + if (token_len) + *token_len = 0; + } +} + + +static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, + const u8 *end) +{ + struct crypto_bignum *peer_scalar; + + if (*pos + sae->tmp->prime_len > end) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); + if (peer_scalar == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* + * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for + * the peer and it is in Authenticated state, the new Commit Message + * shall be dropped if the peer-scalar is identical to the one used in + * the existing protocol instance. + */ + if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && + crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { + wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " + "peer-commit-scalar"); + crypto_bignum_deinit(peer_scalar, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* 0 < scalar < r */ + if (crypto_bignum_is_zero(peer_scalar) || + crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); + crypto_bignum_deinit(peer_scalar, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + + crypto_bignum_deinit(sae->peer_commit_scalar, 0); + sae->peer_commit_scalar = peer_scalar; + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", + *pos, sae->tmp->prime_len); + *pos += sae->tmp->prime_len; + + return WLAN_STATUS_SUCCESS; +} + + +static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, + const u8 *end) +{ + u8 prime[SAE_MAX_ECC_PRIME_LEN]; + + if (pos + 2 * sae->tmp->prime_len > end) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for " + "commit-element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), + sae->tmp->prime_len) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* element x and y coordinates < p */ + if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 || + os_memcmp(pos + sae->tmp->prime_len, prime, + sae->tmp->prime_len) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " + "element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", + pos, sae->tmp->prime_len); + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", + pos + sae->tmp->prime_len, sae->tmp->prime_len); + + crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); + sae->tmp->peer_commit_element_ecc = + crypto_ec_point_from_bin(sae->tmp->ec, pos); + if (sae->tmp->peer_commit_element_ecc == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (!crypto_ec_point_is_on_curve(sae->tmp->ec, + sae->tmp->peer_commit_element_ecc)) { + wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + return WLAN_STATUS_SUCCESS; +} + + +static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, + const u8 *end) +{ + struct crypto_bignum *res; + + if (pos + sae->tmp->prime_len > end) { + wpa_printf(MSG_DEBUG, "SAE: Not enough data for " + "commit-element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos, + sae->tmp->prime_len); + + crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); + sae->tmp->peer_commit_element_ffc = + crypto_bignum_init_set(pos, sae->tmp->prime_len); + if (sae->tmp->peer_commit_element_ffc == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || + crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || + crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, + sae->tmp->prime) >= 0) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* scalar-op(r, ELEMENT) = 1 modulo p */ + res = crypto_bignum_init(); + if (res == NULL || + crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, + sae->tmp->order, sae->tmp->prime, res) < 0 || + !crypto_bignum_is_one(res)) { + wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); + crypto_bignum_deinit(res, 0); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + crypto_bignum_deinit(res, 0); + + return WLAN_STATUS_SUCCESS; +} + + +static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, + const u8 *end) +{ + if (sae->tmp->dh) + return sae_parse_commit_element_ffc(sae, pos, end); + return sae_parse_commit_element_ecc(sae, pos, end); +} + + +u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, + const u8 **token, size_t *token_len, int *allowed_groups) +{ + const u8 *pos = data, *end = data + len; + u16 res; + + /* Check Finite Cyclic Group */ + if (pos + 2 > end) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); + if (res != WLAN_STATUS_SUCCESS) + return res; + pos += 2; + + /* Optional Anti-Clogging Token */ + sae_parse_commit_token(sae, &pos, end, token, token_len); + + /* commit-scalar */ + res = sae_parse_commit_scalar(sae, &pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* commit-element */ + return sae_parse_commit_element(sae, pos, end); +} + + +static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const u8 *element1, size_t element1_len, + const struct crypto_bignum *scalar2, + const u8 *element2, size_t element2_len, + u8 *confirm) +{ + const u8 *addr[5]; + size_t len[5]; + u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; + + /* Confirm + * CN(key, X, Y, Z, ...) = + * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) + * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, + * peer-commit-scalar, PEER-COMMIT-ELEMENT) + * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, + * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) + */ + addr[0] = sc; + len[0] = 2; + crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), + sae->tmp->prime_len); + addr[1] = scalar_b1; + len[1] = sae->tmp->prime_len; + addr[2] = element1; + len[2] = element1_len; + crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), + sae->tmp->prime_len); + addr[3] = scalar_b2; + len[3] = sae->tmp->prime_len; + addr[4] = element2; + len[4] = element2_len; + hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, + confirm); +} + + +static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const struct crypto_ec_point *element1, + const struct crypto_bignum *scalar2, + const struct crypto_ec_point *element2, + u8 *confirm) +{ + u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; + u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; + + crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, + element_b1 + sae->tmp->prime_len); + crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, + element_b2 + sae->tmp->prime_len); + + sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len, + scalar2, element_b2, 2 * sae->tmp->prime_len, confirm); +} + + +static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const struct crypto_bignum *element1, + const struct crypto_bignum *scalar2, + const struct crypto_bignum *element2, + u8 *confirm) +{ + u8 element_b1[SAE_MAX_PRIME_LEN]; + u8 element_b2[SAE_MAX_PRIME_LEN]; + + crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), + sae->tmp->prime_len); + crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), + sae->tmp->prime_len); + + sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, + scalar2, element_b2, sae->tmp->prime_len, confirm); +} + + +void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) +{ + const u8 *sc; + + /* Send-Confirm */ + sc = wpabuf_put(buf, 0); + wpabuf_put_le16(buf, sae->send_confirm); + sae->send_confirm++; + + if (sae->tmp->ec) + sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ecc, + sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ecc, + wpabuf_put(buf, SHA256_MAC_LEN)); + else + sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ffc, + sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ffc, + wpabuf_put(buf, SHA256_MAC_LEN)); +} + + +int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) +{ + u8 verifier[SHA256_MAC_LEN]; + + if (len < 2 + SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); + + if (sae->tmp->ec) + sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ecc, + sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ecc, + verifier); + else + sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ffc, + sae->tmp->own_commit_scalar, + sae->tmp->own_commit_element_ffc, + verifier); + + if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); + wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", + data + 2, SHA256_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", + verifier, SHA256_MAC_LEN); + return -1; + } + + return 0; +} diff --git a/contrib/hostapd/src/common/sae.h b/contrib/hostapd/src/common/sae.h new file mode 100644 index 0000000000..d82a98e888 --- /dev/null +++ b/contrib/hostapd/src/common/sae.h @@ -0,0 +1,64 @@ +/* + * Simultaneous authentication of equals + * Copyright (c) 2012-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SAE_H +#define SAE_H + +#define SAE_KCK_LEN 32 +#define SAE_PMK_LEN 32 +#define SAE_PMKID_LEN 16 +#define SAE_KEYSEED_KEY_LEN 32 +#define SAE_MAX_PRIME_LEN 512 +#define SAE_MAX_ECC_PRIME_LEN 66 +#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN) +#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN) + +struct sae_temporary_data { + u8 kck[SAE_KCK_LEN]; + struct crypto_bignum *own_commit_scalar; + struct crypto_bignum *own_commit_element_ffc; + struct crypto_ec_point *own_commit_element_ecc; + struct crypto_bignum *peer_commit_element_ffc; + struct crypto_ec_point *peer_commit_element_ecc; + struct crypto_ec_point *pwe_ecc; + struct crypto_bignum *pwe_ffc; + struct crypto_bignum *sae_rand; + struct crypto_ec *ec; + int prime_len; + const struct dh_group *dh; + const struct crypto_bignum *prime; + const struct crypto_bignum *order; + struct crypto_bignum *prime_buf; + struct crypto_bignum *order_buf; +}; + +struct sae_data { + enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state; + u16 send_confirm; + u8 pmk[SAE_PMK_LEN]; + struct crypto_bignum *peer_commit_scalar; + int group; + struct sae_temporary_data *tmp; +}; + +int sae_set_group(struct sae_data *sae, int group); +void sae_clear_temp_data(struct sae_data *sae); +void sae_clear_data(struct sae_data *sae); + +int sae_prepare_commit(const u8 *addr1, const u8 *addr2, + const u8 *password, size_t password_len, + struct sae_data *sae); +int sae_process_commit(struct sae_data *sae); +void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, + const struct wpabuf *token); +u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, + const u8 **token, size_t *token_len, int *allowed_groups); +void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf); +int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len); + +#endif /* SAE_H */ diff --git a/contrib/hostapd/src/common/version.h b/contrib/hostapd/src/common/version.h index b79c4949c4..0edf11cc9d 100644 --- a/contrib/hostapd/src/common/version.h +++ b/contrib/hostapd/src/common/version.h @@ -1,6 +1,10 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION_STR "0.6.10" +#ifndef VERSION_STR_POSTFIX +#define VERSION_STR_POSTFIX "" +#endif /* VERSION_STR_POSTFIX */ + +#define VERSION_STR "2.1" VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/contrib/hostapd/src/common/wireless_copy.h b/contrib/hostapd/src/common/wireless_copy.h deleted file mode 100644 index ad76466376..0000000000 --- a/contrib/hostapd/src/common/wireless_copy.h +++ /dev/null @@ -1,1099 +0,0 @@ -/* 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 - - * 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 /* for "caddr_t" et al */ -#include /* for "struct sockaddr" et al */ -#include /* for IFNAMSIZ and co... */ -#else -#include -#include -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 ) - * ---------- - * - 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/contrib/hostapd/src/common/wpa_common.c b/contrib/hostapd/src/common/wpa_common.c index 074cb80640..37b265d05f 100644 --- a/contrib/hostapd/src/common/wpa_common.c +++ b/contrib/hostapd/src/common/wpa_common.c @@ -1,25 +1,19 @@ /* * WPA/RSN - Shared functions for supplicant and authenticator - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" -#include "sha256.h" -#include "aes_wrap.h" -#include "crypto.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" @@ -49,11 +43,13 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, u8 hash[SHA1_MAC_LEN]; switch (ver) { +#ifndef CONFIG_FIPS case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: - hmac_md5(key, 16, buf, len, mic); - break; + return hmac_md5(key, 16, buf, len, mic); +#endif /* CONFIG_FIPS */ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: - hmac_sha1(key, 16, buf, len, hash); + 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) @@ -126,6 +122,8 @@ void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); + wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN); wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len); } @@ -186,10 +184,157 @@ int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr, return 0; } + + +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; +} + + +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; + break; + } + + 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; +} #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) @@ -206,6 +351,18 @@ static int rsn_selector_to_bitfield(const u8 *s) if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) return WPA_CIPHER_AES_128_CMAC; #endif /* CONFIG_IEEE80211W */ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP) + return WPA_CIPHER_GCMP; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256) + return WPA_CIPHER_CCMP_256; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256) + return WPA_CIPHER_GCMP_256; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128) + return WPA_CIPHER_BIP_GMAC_128; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256) + return WPA_CIPHER_BIP_GMAC_256; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256) + return WPA_CIPHER_BIP_CMAC_256; return 0; } @@ -228,9 +385,14 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256) return WPA_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE) + return WPA_KEY_MGMT_SAE; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE) + return WPA_KEY_MGMT_FT_SAE; +#endif /* CONFIG_SAE */ return 0; } -#endif /* CONFIG_NO_WPA2 */ /** @@ -243,7 +405,6 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) 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; @@ -397,9 +558,144 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, } return 0; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ +} + + +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; +} + + +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 -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) { + 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 * WPA_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 |= 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 -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) { + 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 |= 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 -7; + } + + 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; } @@ -568,3 +864,573 @@ void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, } #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"; + case WPA_CIPHER_GCMP: + return "GCMP"; + case WPA_CIPHER_GCMP_256: + return "GCMP-256"; + case WPA_CIPHER_CCMP_256: + return "CCMP-256"; + case WPA_CIPHER_GTK_NOT_USED: + return "GTK_NOT_USED"; + 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 */ + + +int wpa_cipher_key_len(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + case WPA_CIPHER_GCMP_256: + return 32; + case WPA_CIPHER_CCMP: + case WPA_CIPHER_GCMP: + return 16; + case WPA_CIPHER_TKIP: + return 32; + case WPA_CIPHER_WEP104: + return 13; + case WPA_CIPHER_WEP40: + return 5; + } + + return 0; +} + + +int wpa_cipher_rsc_len(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + case WPA_CIPHER_GCMP_256: + case WPA_CIPHER_CCMP: + case WPA_CIPHER_GCMP: + case WPA_CIPHER_TKIP: + return 6; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return 0; + } + + return 0; +} + + +int wpa_cipher_to_alg(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + return WPA_ALG_CCMP_256; + case WPA_CIPHER_GCMP_256: + return WPA_ALG_GCMP_256; + case WPA_CIPHER_CCMP: + return WPA_ALG_CCMP; + case WPA_CIPHER_GCMP: + return WPA_ALG_GCMP; + case WPA_CIPHER_TKIP: + return WPA_ALG_TKIP; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return WPA_ALG_WEP; + } + return WPA_ALG_NONE; +} + + +int wpa_cipher_valid_pairwise(int cipher) +{ + return cipher == WPA_CIPHER_CCMP_256 || + cipher == WPA_CIPHER_GCMP_256 || + cipher == WPA_CIPHER_CCMP || + cipher == WPA_CIPHER_GCMP || + cipher == WPA_CIPHER_TKIP; +} + + +u32 wpa_cipher_to_suite(int proto, int cipher) +{ + if (cipher & WPA_CIPHER_CCMP_256) + return RSN_CIPHER_SUITE_CCMP_256; + if (cipher & WPA_CIPHER_GCMP_256) + return RSN_CIPHER_SUITE_GCMP_256; + if (cipher & WPA_CIPHER_CCMP) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP); + if (cipher & WPA_CIPHER_GCMP) + return RSN_CIPHER_SUITE_GCMP; + if (cipher & WPA_CIPHER_TKIP) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP); + if (cipher & WPA_CIPHER_WEP104) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104); + if (cipher & WPA_CIPHER_WEP40) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40); + if (cipher & WPA_CIPHER_NONE) + return (proto == WPA_PROTO_RSN ? + RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE); + if (cipher & WPA_CIPHER_GTK_NOT_USED) + return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED; + return 0; +} + + +int rsn_cipher_put_suites(u8 *pos, int ciphers) +{ + int num_suites = 0; + + if (ciphers & WPA_CIPHER_CCMP_256) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP_256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_GCMP_256) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP_256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_GCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + + return num_suites; +} + + +int wpa_cipher_put_suites(u8 *pos, int ciphers) +{ + int num_suites = 0; + + if (ciphers & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (ciphers & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + return num_suites; +} + + +int wpa_pick_pairwise_cipher(int ciphers, int none_allowed) +{ + if (ciphers & WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; + if (ciphers & WPA_CIPHER_GCMP_256) + return WPA_CIPHER_GCMP_256; + if (ciphers & WPA_CIPHER_CCMP) + return WPA_CIPHER_CCMP; + if (ciphers & WPA_CIPHER_GCMP) + return WPA_CIPHER_GCMP; + if (ciphers & WPA_CIPHER_TKIP) + return WPA_CIPHER_TKIP; + if (none_allowed && (ciphers & WPA_CIPHER_NONE)) + return WPA_CIPHER_NONE; + return -1; +} + + +int wpa_pick_group_cipher(int ciphers) +{ + if (ciphers & WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; + if (ciphers & WPA_CIPHER_GCMP_256) + return WPA_CIPHER_GCMP_256; + if (ciphers & WPA_CIPHER_CCMP) + return WPA_CIPHER_CCMP; + if (ciphers & WPA_CIPHER_GCMP) + return WPA_CIPHER_GCMP; + if (ciphers & WPA_CIPHER_GTK_NOT_USED) + return WPA_CIPHER_GTK_NOT_USED; + if (ciphers & WPA_CIPHER_TKIP) + return WPA_CIPHER_TKIP; + if (ciphers & WPA_CIPHER_WEP104) + return WPA_CIPHER_WEP104; + if (ciphers & WPA_CIPHER_WEP40) + return WPA_CIPHER_WEP40; + return -1; +} + + +int wpa_parse_cipher(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-256") == 0) + val |= WPA_CIPHER_CCMP_256; + else if (os_strcmp(start, "GCMP-256") == 0) + val |= WPA_CIPHER_GCMP_256; + else if (os_strcmp(start, "CCMP") == 0) + val |= WPA_CIPHER_CCMP; + else if (os_strcmp(start, "GCMP") == 0) + val |= WPA_CIPHER_GCMP; + 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 if (os_strcmp(start, "GTK_NOT_USED") == 0) + val |= WPA_CIPHER_GTK_NOT_USED; + else { + os_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + os_free(buf); + + return val; +} + + +int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) +{ + char *pos = start; + int ret; + + if (ciphers & WPA_CIPHER_CCMP_256) { + ret = os_snprintf(pos, end - pos, "%sCCMP-256", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_GCMP_256) { + ret = os_snprintf(pos, end - pos, "%sGCMP-256", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_CCMP) { + ret = os_snprintf(pos, end - pos, "%sCCMP", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_GCMP) { + ret = os_snprintf(pos, end - pos, "%sGCMP", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_TKIP) { + ret = os_snprintf(pos, end - pos, "%sTKIP", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_WEP104) { + ret = os_snprintf(pos, end - pos, "%sWEP104", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_WEP40) { + ret = os_snprintf(pos, end - pos, "%sWEP40", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_NONE) { + ret = os_snprintf(pos, end - pos, "%sNONE", + pos == start ? "" : delim); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + } + + return pos - start; +} + + +int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise) +{ + int pairwise = 0; + + /* Select group cipher based on the enabled pairwise cipher suites */ + if (wpa & 1) + pairwise |= wpa_pairwise; + if (wpa & 2) + pairwise |= rsn_pairwise; + + if (pairwise & WPA_CIPHER_TKIP) + return WPA_CIPHER_TKIP; + if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP) + return WPA_CIPHER_GCMP; + if ((pairwise & (WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP | + WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP_256) + return WPA_CIPHER_GCMP_256; + if ((pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP | + WPA_CIPHER_GCMP)) == WPA_CIPHER_CCMP_256) + return WPA_CIPHER_CCMP_256; + return WPA_CIPHER_CCMP; +} diff --git a/contrib/hostapd/src/common/wpa_common.h b/contrib/hostapd/src/common/wpa_common.h index 3074cd44a1..dcc035c7d8 100644 --- a/contrib/hostapd/src/common/wpa_common.h +++ b/contrib/hostapd/src/common/wpa_common.h @@ -1,15 +1,9 @@ /* * WPA definitions shared between hostapd and wpa_supplicant - * Copyright (c) 2002-2008, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_COMMON_H @@ -26,6 +20,14 @@ #define WPA_GMK_LEN 32 #define WPA_GTK_MAX_LEN 32 +#define WPA_ALLOWED_PAIRWISE_CIPHERS \ +(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \ +WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) +#define WPA_ALLOWED_GROUP_CIPHERS \ +(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \ +WPA_CIPHER_WEP40 | WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ +WPA_CIPHER_GTK_NOT_USED) + #define WPA_SELECTOR_LEN 4 #define WPA_VERSION 1 #define RSN_SELECTOR_LEN 4 @@ -38,6 +40,7 @@ #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_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0) #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) @@ -56,6 +59,14 @@ #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_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_384 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) +#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_384 \ +RSN_SELECTOR(0x00, 0x0f, 0xac, 13) +#define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) @@ -68,6 +79,13 @@ #ifdef CONFIG_IEEE80211W #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #endif /* CONFIG_IEEE80211W */ +#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#define RSN_CIPHER_SUITE_GCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#define RSN_CIPHER_SUITE_CCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 10) +#define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) +#define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13) /* EAPOL-Key Key Data Encapsulation * GroupKey and PeerKey require encryption, otherwise, encryption is optional. @@ -87,6 +105,12 @@ #ifdef CONFIG_IEEE80211W #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #endif /* CONFIG_IEEE80211W */ +#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10) +#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11) +#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12) + +#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4) +#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5) #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) @@ -115,7 +139,13 @@ /* B4-B5: GTKSA Replay Counter */ #define WPA_CAPABILITY_MFPR BIT(6) #define WPA_CAPABILITY_MFPC BIT(7) +/* B8: Reserved */ #define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9) +#define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10) +#define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11) +#define WPA_CAPABILITY_PBAC BIT(12) +#define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13) +/* B14-B15: Reserved */ /* IEEE 802.11r */ @@ -282,6 +312,12 @@ struct rsn_ftie { #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 @@ -331,5 +367,53 @@ struct wpa_ie_data { int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data); +int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_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); + +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; +}; + +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); + +int wpa_cipher_key_len(int cipher); +int wpa_cipher_rsc_len(int cipher); +int wpa_cipher_to_alg(int cipher); +int wpa_cipher_valid_pairwise(int cipher); +u32 wpa_cipher_to_suite(int proto, int cipher); +int rsn_cipher_put_suites(u8 *pos, int ciphers); +int wpa_cipher_put_suites(u8 *pos, int ciphers); +int wpa_pick_pairwise_cipher(int ciphers, int none_allowed); +int wpa_pick_group_cipher(int ciphers); +int wpa_parse_cipher(const char *value); +int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim); +int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise); #endif /* WPA_COMMON_H */ diff --git a/contrib/hostapd/src/common/wpa_ctrl.c b/contrib/hostapd/src/common/wpa_ctrl.c index 2b4e3aa5ee..f4af94aa1c 100644 --- a/contrib/hostapd/src/common/wpa_ctrl.c +++ b/contrib/hostapd/src/common/wpa_ctrl.c @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd control interface library * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,7 +12,18 @@ #ifdef CONFIG_CTRL_IFACE_UNIX #include +#include +#include #endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE +#include +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef ANDROID +#include +#include +#include "private/android_filesystem_config.h" +#endif /* ANDROID */ #include "wpa_ctrl.h" #include "common.h" @@ -44,6 +49,8 @@ struct wpa_ctrl { struct sockaddr_in local; struct sockaddr_in dest; char *cookie; + char *remote_ifname; + char *remote_ip; #endif /* CONFIG_CTRL_IFACE_UDP */ #ifdef CONFIG_CTRL_IFACE_UNIX int s; @@ -58,6 +65,14 @@ struct wpa_ctrl { #ifdef CONFIG_CTRL_IFACE_UNIX +#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR +#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp" +#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */ +#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX +#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_" +#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */ + + struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) { struct wpa_ctrl *ctrl; @@ -65,6 +80,10 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) int ret; size_t res; int tries = 0; + int flags; + + if (ctrl_path == NULL) + return NULL; ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) @@ -81,7 +100,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) counter++; try_again: ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), - "/tmp/wpa_ctrl_%d-%d", getpid(), counter); + CONFIG_CTRL_IFACE_CLIENT_DIR "/" + CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + (int) getpid(), counter); if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) { close(ctrl->s); os_free(ctrl); @@ -105,13 +126,58 @@ try_again: return NULL; } +#ifdef ANDROID + chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); + + if (os_strncmp(ctrl_path, "@android:", 9) == 0) { + if (socket_local_client_connect( + ctrl->s, ctrl_path + 9, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_DGRAM) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + os_free(ctrl); + return NULL; + } + return ctrl; + } + + /* + * If the ctrl_path isn't an absolute pathname, assume that + * it's the name of a socket in the Android reserved namespace. + * Otherwise, it's a normal UNIX domain socket appearing in the + * filesystem. + */ + if (*ctrl_path != '/') { + char buf[21]; + os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path); + if (socket_local_client_connect( + ctrl->s, buf, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_DGRAM) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + os_free(ctrl); + return NULL; + } + return ctrl; + } +#endif /* ANDROID */ + 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 (os_strncmp(ctrl_path, "@abstract:", 10) == 0) { + ctrl->dest.sun_path[0] = '\0'; + os_strlcpy(ctrl->dest.sun_path + 1, ctrl_path + 10, + sizeof(ctrl->dest.sun_path) - 1); + } else { + 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) { @@ -121,17 +187,79 @@ try_again: return NULL; } + /* + * Make socket non-blocking so that we don't hang forever if + * target dies unexpectedly. + */ + flags = fcntl(ctrl->s, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(ctrl->s, F_SETFL, flags) < 0) { + perror("fcntl(ctrl->s, O_NONBLOCK)"); + /* Not fatal, continue on.*/ + } + } + return ctrl; } void wpa_ctrl_close(struct wpa_ctrl *ctrl) { + if (ctrl == NULL) + return; unlink(ctrl->local.sun_path); - close(ctrl->s); + if (ctrl->s >= 0) + close(ctrl->s); os_free(ctrl); } + +#ifdef ANDROID +/** + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that + * may be left over from clients that were previously connected to + * wpa_supplicant. This keeps these files from being orphaned in the + * event of crashes that prevented them from being removed as part + * of the normal orderly shutdown. + */ +void wpa_ctrl_cleanup(void) +{ + DIR *dir; + struct dirent entry; + struct dirent *result; + size_t dirnamelen; + size_t maxcopy; + char pathname[PATH_MAX]; + char *namep; + + if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL) + return; + + dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/", + CONFIG_CTRL_IFACE_CLIENT_DIR); + if (dirnamelen >= sizeof(pathname)) { + closedir(dir); + return; + } + namep = pathname + dirnamelen; + maxcopy = PATH_MAX - dirnamelen; + while (readdir_r(dir, &entry, &result) == 0 && result != NULL) { + if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy) + unlink(pathname); + } + closedir(dir); +} +#endif /* ANDROID */ + +#else /* CONFIG_CTRL_IFACE_UNIX */ + +#ifdef ANDROID +void wpa_ctrl_cleanup(void) +{ +} +#endif /* ANDROID */ + #endif /* CONFIG_CTRL_IFACE_UNIX */ @@ -142,6 +270,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) struct wpa_ctrl *ctrl; char buf[128]; size_t len; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + struct hostent *h; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) @@ -156,7 +287,11 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) } ctrl->local.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + ctrl->local.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, sizeof(ctrl->local)) < 0) { close(ctrl->s); @@ -167,10 +302,48 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) 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); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + if (ctrl_path) { + char *port, *name; + int port_id; + + name = os_strdup(ctrl_path); + if (name == NULL) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } + port = os_strchr(name, ':'); + + if (port) { + port_id = atoi(&port[1]); + port[0] = '\0'; + } else + port_id = WPA_CTRL_IFACE_PORT; + + h = gethostbyname(name); + ctrl->remote_ip = os_strdup(name); + os_free(name); + if (h == NULL) { + perror("gethostbyname"); + close(ctrl->s); + os_free(ctrl->remote_ip); + os_free(ctrl); + return NULL; + } + ctrl->dest.sin_port = htons(port_id); + os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr, + h->h_length); + } else + ctrl->remote_ip = os_strdup("localhost"); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { perror("connect"); close(ctrl->s); + os_free(ctrl->remote_ip); os_free(ctrl); return NULL; } @@ -181,14 +354,31 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) ctrl->cookie = os_strdup(buf); } + if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) { + buf[len] = '\0'; + ctrl->remote_ifname = os_strdup(buf); + } + return ctrl; } +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl) +{ +#define WPA_CTRL_MAX_PS_NAME 100 + static char ps[WPA_CTRL_MAX_PS_NAME] = {}; + os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s", + ctrl->remote_ip, ctrl->remote_ifname); + return ps; +} + + void wpa_ctrl_close(struct wpa_ctrl *ctrl) { close(ctrl->s); os_free(ctrl->cookie); + os_free(ctrl->remote_ifname); + os_free(ctrl->remote_ip); os_free(ctrl); } @@ -201,6 +391,7 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, void (*msg_cb)(char *msg, size_t len)) { struct timeval tv; + struct os_reltime started_at; int res; fd_set rfds; const char *_cmd; @@ -227,18 +418,43 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, _cmd_len = cmd_len; } + errno = 0; + started_at.sec = 0; + started_at.usec = 0; +retry_send: if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) { + if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK) + { + /* + * Must be a non-blocking socket... Try for a bit + * longer before giving up. + */ + if (started_at.sec == 0) + os_get_reltime(&started_at); + else { + struct os_reltime n; + os_get_reltime(&n); + /* Try for a few seconds. */ + if (os_reltime_expired(&n, &started_at, 5)) + goto send_err; + } + os_sleep(1, 0); + goto retry_send; + } + send_err: os_free(cmd_buf); return -1; } os_free(cmd_buf); for (;;) { - tv.tv_sec = 2; + tv.tv_sec = 10; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(ctrl->s, &rfds); res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (res < 0) + return res; if (FD_ISSET(ctrl->s, &rfds)) { res = recv(ctrl->s, reply, *reply_len, 0); if (res < 0) diff --git a/contrib/hostapd/src/common/wpa_ctrl.h b/contrib/hostapd/src/common/wpa_ctrl.h index 1bfc0d6453..759cee47dd 100644 --- a/contrib/hostapd/src/common/wpa_ctrl.h +++ b/contrib/hostapd/src/common/wpa_ctrl.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd control interface library * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_CTRL_H @@ -32,6 +26,8 @@ extern "C" { #define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " /** Disconnected, data connection is not available */ #define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " +/** Association rejected during connection attempt */ +#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT " /** wpa_supplicant is exiting */ #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " /** Password change was completed successfully */ @@ -40,19 +36,52 @@ extern "C" { #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 status */ +#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS " /** 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 " +/** Network block temporarily disabled (e.g., due to authentication failure) */ +#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " +/** Temporarily disabled network block re-enabled */ +#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED " +/** New scan started */ +#define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED " /** New scan results available */ #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " +/** wpa_supplicant state change */ +#define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE " +/** 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 " +/** RSN IBSS 4-way handshakes completed with specified peer */ +#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED " + +/** Notification of frequency conflict due to a concurrent operation. + * + * The indicated network is disabled and needs to be re-enabled before it can + * be used again. + */ +#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT " +/** Frequency ranges that the driver recommends to avoid */ +#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ " /** 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 our address as authorized in scan results */ +#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH " /** Available WPS AP with recently selected PIN registrar found in scan results */ #define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN " @@ -68,12 +97,130 @@ extern "C" { #define WPS_EVENT_SUCCESS "WPS-SUCCESS " /** WPS enrollment attempt timed out and was terminated */ #define WPS_EVENT_TIMEOUT "WPS-TIMEOUT " +/* PBC mode was activated */ +#define WPS_EVENT_ACTIVE "WPS-PBC-ACTIVE " +/* PBC mode was disabled */ +#define WPS_EVENT_DISABLE "WPS-PBC-DISABLE " + +#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN " + +#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK " + +/* 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 " +#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS " +#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG " + +/** P2P device found */ +#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND " + +/** P2P device lost */ +#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST " + +/** A P2P device requested GO negotiation, but we were not ready to start the + * negotiation */ +#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST " +#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS " +#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE " +#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS " +#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE " +#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED " +#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED " +#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE " +#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE" +/* parameters: */ +#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ " +/* parameters: */ +#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP " +#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " +#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " +#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED " +#define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id=" +#define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE " +#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO " +#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT " +#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT " + +/* parameters: */ +#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT " +#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP " + +#define INTERWORKING_AP "INTERWORKING-AP " +#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " +#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED " + +#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO " +/* parameters: */ +#define GAS_QUERY_START "GAS-QUERY-START " +/* parameters: */ +#define GAS_QUERY_DONE "GAS-QUERY-DONE " + +#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START " +#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT " /* 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 " + +#define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " +#define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " + +#define AP_EVENT_ENABLED "AP-ENABLED " +#define AP_EVENT_DISABLED "AP-DISABLED " + +#define ACS_EVENT_STARTED "ACS-STARTED " +#define ACS_EVENT_COMPLETED "ACS-COMPLETED " +#define ACS_EVENT_FAILED "ACS-FAILED " + +#define DFS_EVENT_RADAR_DETECTED "DFS-RADAR-DETECTED " +#define DFS_EVENT_NEW_CHANNEL "DFS-NEW-CHANNEL " +#define DFS_EVENT_CAC_START "DFS-CAC-START " +#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED " +#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED " + +#define AP_CSA_FINISHED "AP-CSA-FINISHED " + +/* BSS command information masks */ + +#define WPA_BSS_MASK_ALL 0xFFFDFFFF +#define WPA_BSS_MASK_ID BIT(0) +#define WPA_BSS_MASK_BSSID BIT(1) +#define WPA_BSS_MASK_FREQ BIT(2) +#define WPA_BSS_MASK_BEACON_INT BIT(3) +#define WPA_BSS_MASK_CAPABILITIES BIT(4) +#define WPA_BSS_MASK_QUAL BIT(5) +#define WPA_BSS_MASK_NOISE BIT(6) +#define WPA_BSS_MASK_LEVEL BIT(7) +#define WPA_BSS_MASK_TSF BIT(8) +#define WPA_BSS_MASK_AGE BIT(9) +#define WPA_BSS_MASK_IE BIT(10) +#define WPA_BSS_MASK_FLAGS BIT(11) +#define WPA_BSS_MASK_SSID BIT(12) +#define WPA_BSS_MASK_WPS_SCAN BIT(13) +#define WPA_BSS_MASK_P2P_SCAN BIT(14) +#define WPA_BSS_MASK_INTERNETW BIT(15) +#define WPA_BSS_MASK_WIFI_DISPLAY BIT(16) +#define WPA_BSS_MASK_DELIM BIT(17) /* wpa_supplicant/hostapd control interface access */ @@ -200,9 +347,25 @@ int wpa_ctrl_pending(struct wpa_ctrl *ctrl); */ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); + +#ifdef ANDROID +/** + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that + * may be left over from clients that were previously connected to + * wpa_supplicant. This keeps these files from being orphaned in the + * event of crashes that prevented them from being removed as part + * of the normal orderly shutdown. + */ +void wpa_ctrl_cleanup(void); +#endif /* ANDROID */ + #ifdef CONFIG_CTRL_IFACE_UDP +/* Port range for multiple wpa_supplicant instances and multiple VIFs */ #define WPA_CTRL_IFACE_PORT 9877 +#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */ #define WPA_GLOBAL_CTRL_IFACE_PORT 9878 +#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */ #endif /* CONFIG_CTRL_IFACE_UDP */ diff --git a/contrib/hostapd/src/crypto/aes-cbc.c b/contrib/hostapd/src/crypto/aes-cbc.c new file mode 100644 index 0000000000..2833cfcc84 --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-cbc.c @@ -0,0 +1,80 @@ +/* + * AES-128 CBC + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/aes-ccm.c b/contrib/hostapd/src/crypto/aes-ccm.c new file mode 100644 index 0000000000..d14670db8d --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-ccm.c @@ -0,0 +1,212 @@ +/* + * Counter with CBC-MAC (CCM) with AES + * + * Copyright (c) 2010-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + + +static void xor_aes_block(u8 *dst, const u8 *src) +{ + u32 *d = (u32 *) dst; + u32 *s = (u32 *) src; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; +} + + +static void aes_ccm_auth_start(void *aes, size_t M, size_t L, const u8 *nonce, + const u8 *aad, size_t aad_len, size_t plain_len, + u8 *x) +{ + u8 aad_buf[2 * AES_BLOCK_SIZE]; + u8 b[AES_BLOCK_SIZE]; + + /* Authentication */ + /* B_0: Flags | Nonce N | l(m) */ + b[0] = aad_len ? 0x40 : 0 /* Adata */; + b[0] |= (((M - 2) / 2) /* M' */ << 3); + b[0] |= (L - 1) /* L' */; + os_memcpy(&b[1], nonce, 15 - L); + WPA_PUT_BE16(&b[AES_BLOCK_SIZE - L], plain_len); + + wpa_hexdump_key(MSG_EXCESSIVE, "CCM B_0", b, AES_BLOCK_SIZE); + aes_encrypt(aes, b, x); /* X_1 = E(K, B_0) */ + + if (!aad_len) + return; + + WPA_PUT_BE16(aad_buf, aad_len); + os_memcpy(aad_buf + 2, aad, aad_len); + os_memset(aad_buf + 2 + aad_len, 0, sizeof(aad_buf) - 2 - aad_len); + + xor_aes_block(aad_buf, x); + aes_encrypt(aes, aad_buf, x); /* X_2 = E(K, X_1 XOR B_1) */ + + if (aad_len > AES_BLOCK_SIZE - 2) { + xor_aes_block(&aad_buf[AES_BLOCK_SIZE], x); + /* X_3 = E(K, X_2 XOR B_2) */ + aes_encrypt(aes, &aad_buf[AES_BLOCK_SIZE], x); + } +} + + +static void aes_ccm_auth(void *aes, const u8 *data, size_t len, u8 *x) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + + for (i = 0; i < len / AES_BLOCK_SIZE; i++) { + /* X_i+1 = E(K, X_i XOR B_i) */ + xor_aes_block(x, data); + data += AES_BLOCK_SIZE; + aes_encrypt(aes, x, x); + } + if (last) { + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + x[i] ^= *data++; + aes_encrypt(aes, x, x); + } +} + + +static void aes_ccm_encr_start(size_t L, const u8 *nonce, u8 *a) +{ + /* A_i = Flags | Nonce N | Counter i */ + a[0] = L - 1; /* Flags = L' */ + os_memcpy(&a[1], nonce, 15 - L); +} + + +static void aes_ccm_encr(void *aes, size_t L, const u8 *in, size_t len, u8 *out, + u8 *a) +{ + size_t last = len % AES_BLOCK_SIZE; + size_t i; + + /* crypt = msg XOR (S_1 | S_2 | ... | S_n) */ + for (i = 1; i <= len / AES_BLOCK_SIZE; i++) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + /* S_i = E(K, A_i) */ + aes_encrypt(aes, a, out); + xor_aes_block(out, in); + out += AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + } + if (last) { + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], i); + aes_encrypt(aes, a, out); + /* XOR zero-padded last block */ + for (i = 0; i < last; i++) + *out++ ^= *in++; + } +} + + +static void aes_ccm_encr_auth(void *aes, size_t M, u8 *x, u8 *a, u8 *auth) +{ + size_t i; + u8 tmp[AES_BLOCK_SIZE]; + + wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", x, M); + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + aes_encrypt(aes, a, tmp); + for (i = 0; i < M; i++) + auth[i] = x[i] ^ tmp[i]; + wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M); +} + + +static void aes_ccm_decr_auth(void *aes, size_t M, u8 *a, const u8 *auth, u8 *t) +{ + size_t i; + u8 tmp[AES_BLOCK_SIZE]; + + wpa_hexdump_key(MSG_EXCESSIVE, "CCM U", auth, M); + /* U = T XOR S_0; S_0 = E(K, A_0) */ + WPA_PUT_BE16(&a[AES_BLOCK_SIZE - 2], 0); + aes_encrypt(aes, a, tmp); + for (i = 0; i < M; i++) + t[i] = auth[i] ^ tmp[i]; + wpa_hexdump_key(MSG_EXCESSIVE, "CCM T", t, M); +} + + +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth) +{ + const size_t L = 2; + void *aes; + u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return -1; + + aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, plain_len, x); + aes_ccm_auth(aes, plain, plain_len, x); + + /* Encryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_encr(aes, L, plain, plain_len, crypt, a); + aes_ccm_encr_auth(aes, M, x, a, auth); + + aes_encrypt_deinit(aes); + + return 0; +} + + +/* AES-CCM with fixed L=2 and aad_len <= 30 assumption */ +int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *auth, u8 *plain) +{ + const size_t L = 2; + void *aes; + u8 x[AES_BLOCK_SIZE], a[AES_BLOCK_SIZE]; + u8 t[AES_BLOCK_SIZE]; + + if (aad_len > 30 || M > AES_BLOCK_SIZE) + return -1; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return -1; + + /* Decryption */ + aes_ccm_encr_start(L, nonce, a); + aes_ccm_decr_auth(aes, M, a, auth, t); + + /* plaintext = msg XOR (S_1 | S_2 | ... | S_n) */ + aes_ccm_encr(aes, L, crypt, crypt_len, plain, a); + + aes_ccm_auth_start(aes, M, L, nonce, aad, aad_len, crypt_len, x); + aes_ccm_auth(aes, plain, crypt_len, x); + + aes_encrypt_deinit(aes); + + if (os_memcmp(x, t, M) != 0) { + wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch"); + return -1; + } + + return 0; +} diff --git a/contrib/hostapd/src/crypto/aes-ctr.c b/contrib/hostapd/src/crypto/aes-ctr.c new file mode 100644 index 0000000000..d4d874daac --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-ctr.c @@ -0,0 +1,55 @@ +/* + * AES-128 CTR + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/aes-eax.c b/contrib/hostapd/src/crypto/aes-eax.c new file mode 100644 index 0000000000..21941c66de --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-eax.c @@ -0,0 +1,145 @@ +/* + * AES-128 EAX + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/aes-encblock.c b/contrib/hostapd/src/crypto/aes-encblock.c new file mode 100644 index 0000000000..a5216211dd --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-encblock.c @@ -0,0 +1,32 @@ +/* + * AES encrypt_block + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/aes-gcm.c b/contrib/hostapd/src/crypto/aes-gcm.c new file mode 100644 index 0000000000..3d91c71de2 --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-gcm.c @@ -0,0 +1,327 @@ +/* + * Galois/Counter Mode (GCM) and GMAC with AES + * + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +static void inc32(u8 *block) +{ + u32 val; + val = WPA_GET_BE32(block + AES_BLOCK_SIZE - 4); + val++; + WPA_PUT_BE32(block + AES_BLOCK_SIZE - 4, val); +} + + +static void xor_block(u8 *dst, const u8 *src) +{ + u32 *d = (u32 *) dst; + u32 *s = (u32 *) src; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; + *d++ ^= *s++; +} + + +static void shift_right_block(u8 *v) +{ + u32 val; + + val = WPA_GET_BE32(v + 12); + val >>= 1; + if (v[11] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 12, val); + + val = WPA_GET_BE32(v + 8); + val >>= 1; + if (v[7] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 8, val); + + val = WPA_GET_BE32(v + 4); + val >>= 1; + if (v[3] & 0x01) + val |= 0x80000000; + WPA_PUT_BE32(v + 4, val); + + val = WPA_GET_BE32(v); + val >>= 1; + WPA_PUT_BE32(v, val); +} + + +/* Multiplication in GF(2^128) */ +static void gf_mult(const u8 *x, const u8 *y, u8 *z) +{ + u8 v[16]; + int i, j; + + os_memset(z, 0, 16); /* Z_0 = 0^128 */ + os_memcpy(v, y, 16); /* V_0 = Y */ + + for (i = 0; i < 16; i++) { + for (j = 0; j < 8; j++) { + if (x[i] & BIT(7 - j)) { + /* Z_(i + 1) = Z_i XOR V_i */ + xor_block(z, v); + } else { + /* Z_(i + 1) = Z_i */ + } + + if (v[15] & 0x01) { + /* V_(i + 1) = (V_i >> 1) XOR R */ + shift_right_block(v); + /* R = 11100001 || 0^120 */ + v[0] ^= 0xe1; + } else { + /* V_(i + 1) = V_i >> 1 */ + shift_right_block(v); + } + } + } +} + + +static void ghash_start(u8 *y) +{ + /* Y_0 = 0^128 */ + os_memset(y, 0, 16); +} + + +static void ghash(const u8 *h, const u8 *x, size_t xlen, u8 *y) +{ + size_t m, i; + const u8 *xpos = x; + u8 tmp[16]; + + m = xlen / 16; + + for (i = 0; i < m; i++) { + /* Y_i = (Y^(i-1) XOR X_i) dot H */ + xor_block(y, xpos); + xpos += 16; + + /* dot operation: + * multiplication operation for binary Galois (finite) field of + * 2^128 elements */ + gf_mult(y, h, tmp); + os_memcpy(y, tmp, 16); + } + + if (x + xlen > xpos) { + /* Add zero padded last block */ + size_t last = x + xlen - xpos; + os_memcpy(tmp, xpos, last); + os_memset(tmp + last, 0, sizeof(tmp) - last); + + /* Y_i = (Y^(i-1) XOR X_i) dot H */ + xor_block(y, tmp); + + /* dot operation: + * multiplication operation for binary Galois (finite) field of + * 2^128 elements */ + gf_mult(y, h, tmp); + os_memcpy(y, tmp, 16); + } + + /* Return Y_m */ +} + + +static void aes_gctr(void *aes, const u8 *icb, const u8 *x, size_t xlen, u8 *y) +{ + size_t i, n, last; + u8 cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; + const u8 *xpos = x; + u8 *ypos = y; + + if (xlen == 0) + return; + + n = xlen / 16; + + os_memcpy(cb, icb, AES_BLOCK_SIZE); + /* Full blocks */ + for (i = 0; i < n; i++) { + aes_encrypt(aes, cb, ypos); + xor_block(ypos, xpos); + xpos += AES_BLOCK_SIZE; + ypos += AES_BLOCK_SIZE; + inc32(cb); + } + + last = x + xlen - xpos; + if (last) { + /* Last, partial block */ + aes_encrypt(aes, cb, tmp); + for (i = 0; i < last; i++) + *ypos++ = *xpos++ ^ tmp[i]; + } +} + + +static void * aes_gcm_init_hash_subkey(const u8 *key, size_t key_len, u8 *H) +{ + void *aes; + + aes = aes_encrypt_init(key, key_len); + if (aes == NULL) + return NULL; + + /* Generate hash subkey H = AES_K(0^128) */ + os_memset(H, 0, AES_BLOCK_SIZE); + aes_encrypt(aes, H, H); + wpa_hexdump_key(MSG_EXCESSIVE, "Hash subkey H for GHASH", + H, AES_BLOCK_SIZE); + return aes; +} + + +static void aes_gcm_prepare_j0(const u8 *iv, size_t iv_len, const u8 *H, u8 *J0) +{ + u8 len_buf[16]; + + if (iv_len == 12) { + /* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */ + os_memcpy(J0, iv, iv_len); + os_memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len); + J0[AES_BLOCK_SIZE - 1] = 0x01; + } else { + /* + * s = 128 * ceil(len(IV)/128) - len(IV) + * J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64) + */ + ghash_start(J0); + ghash(H, iv, iv_len, J0); + WPA_PUT_BE64(len_buf, 0); + WPA_PUT_BE64(len_buf + 8, iv_len * 8); + ghash(H, len_buf, sizeof(len_buf), J0); + } +} + + +static void aes_gcm_gctr(void *aes, const u8 *J0, const u8 *in, size_t len, + u8 *out) +{ + u8 J0inc[AES_BLOCK_SIZE]; + + if (len == 0) + return; + + os_memcpy(J0inc, J0, AES_BLOCK_SIZE); + inc32(J0inc); + aes_gctr(aes, J0inc, in, len, out); +} + + +static void aes_gcm_ghash(const u8 *H, const u8 *aad, size_t aad_len, + const u8 *crypt, size_t crypt_len, u8 *S) +{ + u8 len_buf[16]; + + /* + * u = 128 * ceil[len(C)/128] - len(C) + * v = 128 * ceil[len(A)/128] - len(A) + * S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64) + * (i.e., zero padded to block size A || C and lengths of each in bits) + */ + ghash_start(S); + ghash(H, aad, aad_len, S); + ghash(H, crypt, crypt_len, S); + WPA_PUT_BE64(len_buf, aad_len * 8); + WPA_PUT_BE64(len_buf + 8, crypt_len * 8); + ghash(H, len_buf, sizeof(len_buf), S); + + wpa_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16); +} + + +/** + * aes_gcm_ae - GCM-AE_K(IV, P, A) + */ +int aes_gcm_ae(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, + const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *tag) +{ + u8 H[AES_BLOCK_SIZE]; + u8 J0[AES_BLOCK_SIZE]; + u8 S[16]; + void *aes; + + aes = aes_gcm_init_hash_subkey(key, key_len, H); + if (aes == NULL) + return -1; + + aes_gcm_prepare_j0(iv, iv_len, H, J0); + + /* C = GCTR_K(inc_32(J_0), P) */ + aes_gcm_gctr(aes, J0, plain, plain_len, crypt); + + aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S); + + /* T = MSB_t(GCTR_K(J_0, S)) */ + aes_gctr(aes, J0, S, sizeof(S), tag); + + /* Return (C, T) */ + + aes_encrypt_deinit(aes); + + return 0; +} + + +/** + * aes_gcm_ad - GCM-AD_K(IV, C, A, T) + */ +int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, + const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *tag, u8 *plain) +{ + u8 H[AES_BLOCK_SIZE]; + u8 J0[AES_BLOCK_SIZE]; + u8 S[16], T[16]; + void *aes; + + aes = aes_gcm_init_hash_subkey(key, key_len, H); + if (aes == NULL) + return -1; + + aes_gcm_prepare_j0(iv, iv_len, H, J0); + + /* P = GCTR_K(inc_32(J_0), C) */ + aes_gcm_gctr(aes, J0, crypt, crypt_len, plain); + + aes_gcm_ghash(H, aad, aad_len, crypt, crypt_len, S); + + /* T' = MSB_t(GCTR_K(J_0, S)) */ + aes_gctr(aes, J0, S, sizeof(S), T); + + aes_encrypt_deinit(aes); + + if (os_memcmp(tag, T, 16) != 0) { + wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch"); + return -1; + } + + return 0; +} + + +int aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len, + const u8 *aad, size_t aad_len, u8 *tag) +{ + return aes_gcm_ae(key, key_len, iv, iv_len, NULL, 0, aad, aad_len, NULL, + tag); +} diff --git a/contrib/hostapd/src/crypto/aes-internal-dec.c b/contrib/hostapd/src/crypto/aes-internal-dec.c new file mode 100644 index 0000000000..720c7036e4 --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-internal-dec.c @@ -0,0 +1,161 @@ +/* + * AES (Rijndael) cipher - decrypt + * + * Modifications to public domain implementation: + * - 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-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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. + */ +static int rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[], int keyBits) +{ + int Nr, i, j; + u32 temp; + + /* expand the cipher key: */ + Nr = rijndaelKeySetupEnc(rk, cipherKey, keyBits); + if (Nr < 0) + return Nr; + /* 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)); + } + } + + return Nr; +} + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + int res; + rk = os_malloc(AES_PRIV_SIZE); + if (rk == NULL) + return NULL; + res = rijndaelKeySetupDec(rk, key, len * 8); + if (res < 0) { + os_free(rk); + return NULL; + } + rk[AES_PRIV_NR_POS] = res; + return rk; +} + +static void rijndaelDecrypt(const u32 rk[/*44*/], int Nr, const u8 ct[16], + u8 pt[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#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); + if (Nr > 10) { + ROUND(10,s,t); + ROUND(11,t,s); + if (Nr > 12) { + ROUND(12,s,t); + ROUND(13,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) +{ + u32 *rk = ctx; + rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain); +} + + +void aes_decrypt_deinit(void *ctx) +{ + os_memset(ctx, 0, AES_PRIV_SIZE); + os_free(ctx); +} diff --git a/contrib/hostapd/src/crypto/aes-internal-enc.c b/contrib/hostapd/src/crypto/aes-internal-enc.c new file mode 100644 index 0000000000..f3c61b8508 --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-internal-enc.c @@ -0,0 +1,126 @@ +/* + * AES (Rijndael) cipher - encrypt + * + * Modifications to public domain implementation: + * - 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-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "aes_i.h" + +static void rijndaelEncrypt(const u32 rk[], int Nr, const u8 pt[16], u8 ct[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; +#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); + if (Nr > 10) { + ROUND(10,s,t); + ROUND(11,t,s); + if (Nr > 12) { + ROUND(12,s,t); + ROUND(13,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; + int res; + rk = os_malloc(AES_PRIV_SIZE); + if (rk == NULL) + return NULL; + res = rijndaelKeySetupEnc(rk, key, len * 8); + if (res < 0) { + os_free(rk); + return NULL; + } + rk[AES_PRIV_NR_POS] = res; + return rk; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + u32 *rk = ctx; + rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt); +} + + +void aes_encrypt_deinit(void *ctx) +{ + os_memset(ctx, 0, AES_PRIV_SIZE); + os_free(ctx); +} diff --git a/contrib/hostapd-0.4.9/aes.c b/contrib/hostapd/src/crypto/aes-internal.c similarity index 82% rename from contrib/hostapd-0.4.9/aes.c rename to contrib/hostapd/src/crypto/aes-internal.c index ce94778dd6..bd4535d209 100644 --- a/contrib/hostapd-0.4.9/aes.c +++ b/contrib/hostapd/src/crypto/aes-internal.c @@ -2,25 +2,24 @@ * 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 + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "aes_i.h" + /* * rijndael-alg-fst.c * @@ -47,9 +46,6 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* #define FULL_UNROLL */ -#define AES_SMALL_TABLES - /* Te0[x] = S [x].[02, 01, 01, 03]; @@ -65,7 +61,7 @@ Td3[x] = Si[x].[09, 0d, 0b, 0e]; Td4[x] = Si[x].[01, 01, 01, 01]; */ -static const u32 Te0[256] = { +const u32 Te0[256] = { 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, @@ -132,7 +128,7 @@ static const u32 Te0[256] = { 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; #ifndef AES_SMALL_TABLES -static const u32 Te1[256] = { +const u32 Te1[256] = { 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, @@ -198,7 +194,7 @@ static const u32 Te1[256] = { 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, }; -static const u32 Te2[256] = { +const u32 Te2[256] = { 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, @@ -264,7 +260,7 @@ static const u32 Te2[256] = { 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, }; -static const u32 Te3[256] = { +const u32 Te3[256] = { 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, @@ -331,7 +327,7 @@ static const u32 Te3[256] = { 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, }; -static const u32 Te4[256] = { +const u32 Te4[256] = { 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, @@ -398,7 +394,7 @@ static const u32 Te4[256] = { 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, }; #endif /* AES_SMALL_TABLES */ -static const u32 Td0[256] = { +const u32 Td0[256] = { 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, @@ -465,7 +461,7 @@ static const u32 Td0[256] = { 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, }; #ifndef AES_SMALL_TABLES -static const u32 Td1[256] = { +const u32 Td1[256] = { 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, @@ -531,7 +527,7 @@ static const u32 Td1[256] = { 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, }; -static const u32 Td2[256] = { +const u32 Td2[256] = { 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, @@ -598,7 +594,7 @@ static const u32 Td2[256] = { 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, }; -static const u32 Td3[256] = { +const u32 Td3[256] = { 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, @@ -664,7 +660,7 @@ static const u32 Td3[256] = { 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, }; -static const u32 Td4[256] = { +const u32 Td4[256] = { 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, @@ -730,13 +726,13 @@ static const u32 Td4[256] = { 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, }; -static const u32 rcon[] = { +const u32 rcon[] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; #else /* AES_SMALL_TABLES */ -static const u8 Td4s[256] = { +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, @@ -770,101 +766,17 @@ static const u8 Td4s[256] = { 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, }; -static const u8 rcons[] = { +const u8 rcons[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; #endif /* AES_SMALL_TABLES */ - - -#ifndef AES_SMALL_TABLES - -#define RCON(i) rcon[(i)] - -#define TE0(i) Te0[((i) >> 24) & 0xff] -#define TE1(i) Te1[((i) >> 16) & 0xff] -#define TE2(i) Te2[((i) >> 8) & 0xff] -#define TE3(i) Te3[(i) & 0xff] -#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) -#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) -#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) -#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff) -#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000) -#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000) -#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00) -#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff) -#define TE4(i) (Te4[(i)] & 0x000000ff) - -#define TD0(i) Td0[((i) >> 24) & 0xff] -#define TD1(i) Td1[((i) >> 16) & 0xff] -#define TD2(i) Td2[((i) >> 8) & 0xff] -#define TD3(i) Td3[(i) & 0xff] -#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000) -#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000) -#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00) -#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff) -#define TD0_(i) Td0[(i) & 0xff] -#define TD1_(i) Td1[(i) & 0xff] -#define TD2_(i) Td2[(i) & 0xff] -#define TD3_(i) Td3[(i) & 0xff] - -#else /* AES_SMALL_TABLES */ - -#define RCON(i) (rcons[(i)] << 24) - -static inline u32 rotr(u32 val, int bits) -{ - return (val >> bits) | (val << (32 - bits)); -} - -#define TE0(i) Te0[((i) >> 24) & 0xff] -#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8) -#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16) -#define TE3(i) rotr(Te0[(i) & 0xff], 24) -#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) -#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) -#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) -#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) -#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000) -#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) -#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) -#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) -#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) - -#define TD0(i) Td0[((i) >> 24) & 0xff] -#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8) -#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16) -#define TD3(i) rotr(Td0[(i) & 0xff], 24) -#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24) -#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16) -#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8) -#define TD44(i) (Td4s[(i) & 0xff]) -#define TD0_(i) Td0[(i) & 0xff] -#define TD1_(i) rotr(Td0[(i) & 0xff], 8) -#define TD2_(i) rotr(Td0[(i) & 0xff], 16) -#define TD3_(i) rotr(Td0[(i) & 0xff], 24) - -#endif /* AES_SMALL_TABLES */ - -#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) - -#ifdef _MSC_VER -#define GETU32(p) SWAP(*((u32 *)(p))) -#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } -#else -#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \ -((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) -#define PUTU32(ct, st) { \ -(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ -(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } -#endif - /** * Expand the cipher key into the encryption key schedule. * * @return the number of rounds for the given cipher key size. */ -void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) +int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits) { int i; u32 temp; @@ -873,233 +785,61 @@ void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) rk[1] = GETU32(cipherKey + 4); rk[2] = GETU32(cipherKey + 8); rk[3] = GETU32(cipherKey + 12); - for (i = 0; i < 10; i++) { - temp = rk[3]; - rk[4] = rk[0] ^ - TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ - RCON(i); - rk[5] = rk[1] ^ rk[4]; - rk[6] = rk[2] ^ rk[5]; - rk[7] = rk[3] ^ rk[6]; - rk += 4; - } -} - -/** - * Expand the cipher key into the decryption key schedule. - * - * @return the number of rounds for the given cipher key size. - */ -void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) -{ - int Nr = 10, i, j; - u32 temp; - /* expand the cipher key: */ - rijndaelKeySetupEnc(rk, cipherKey); - /* invert the order of the round keys: */ - for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { - temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; - temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; - temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; - temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; - } - /* apply the inverse MixColumn transform to all round keys but the - * first and the last: */ - for (i = 1; i < Nr; i++) { - rk += 4; - for (j = 0; j < 4; j++) { - rk[j] = TD0_(TE4((rk[j] >> 24) )) ^ - TD1_(TE4((rk[j] >> 16) & 0xff)) ^ - TD2_(TE4((rk[j] >> 8) & 0xff)) ^ - TD3_(TE4((rk[j] ) & 0xff)); + if (keyBits == 128) { + 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; } + return 10; } -} - -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); + rk[4] = GETU32(cipherKey + 16); + rk[5] = GETU32(cipherKey + 20); + + if (keyBits == 192) { + for (i = 0; i < 8; i++) { + temp = rk[5]; + rk[6] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[7] = rk[1] ^ rk[6]; + rk[8] = rk[2] ^ rk[7]; + rk[9] = rk[3] ^ rk[8]; + if (i == 7) + return 12; + rk[10] = rk[4] ^ rk[9]; + rk[11] = rk[5] ^ rk[10]; + rk += 6; + } } -#endif /* ?FULL_UNROLL */ - -#undef ROUND - - /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; - PUTU32(ct , s0); - s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; - PUTU32(ct + 4, s1); - s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; - PUTU32(ct + 8, s2); - s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; - PUTU32(ct + 12, s3); -} - -void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) -{ - u32 s0, s1, s2, s3, t0, t1, t2, t3; - const int Nr = 10; -#ifndef FULL_UNROLL - int r; -#endif /* ?FULL_UNROLL */ - - /* - * map byte array block to cipher state - * and add initial round key: - */ - s0 = GETU32(ct ) ^ rk[0]; - s1 = GETU32(ct + 4) ^ rk[1]; - s2 = GETU32(ct + 8) ^ rk[2]; - s3 = GETU32(ct + 12) ^ rk[3]; - -#define ROUND(i,d,s) \ -d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \ -d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \ -d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \ -d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] - -#ifdef FULL_UNROLL - - ROUND(1,t,s); - ROUND(2,s,t); - ROUND(3,t,s); - ROUND(4,s,t); - ROUND(5,t,s); - ROUND(6,s,t); - ROUND(7,t,s); - ROUND(8,s,t); - ROUND(9,t,s); - - rk += Nr << 2; - -#else /* !FULL_UNROLL */ - - /* Nr - 1 full rounds: */ - r = Nr >> 1; - for (;;) { - ROUND(1,t,s); - rk += 8; - if (--r == 0) - break; - ROUND(0,s,t); + rk[6] = GETU32(cipherKey + 24); + rk[7] = GETU32(cipherKey + 28); + + if (keyBits == 256) { + for (i = 0; i < 7; i++) { + temp = rk[7]; + rk[8] = rk[0] ^ TE421(temp) ^ TE432(temp) ^ + TE443(temp) ^ TE414(temp) ^ RCON(i); + rk[9] = rk[1] ^ rk[8]; + rk[10] = rk[2] ^ rk[9]; + rk[11] = rk[3] ^ rk[10]; + if (i == 6) + return 14; + temp = rk[11]; + rk[12] = rk[4] ^ TE411(temp) ^ TE422(temp) ^ + TE433(temp) ^ TE444(temp); + rk[13] = rk[5] ^ rk[12]; + rk[14] = rk[6] ^ rk[13]; + rk[15] = rk[7] ^ rk[14]; + rk += 8; + } } -#endif /* ?FULL_UNROLL */ - -#undef ROUND - - /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0]; - PUTU32(pt , s0); - s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1]; - PUTU32(pt + 4, s1); - s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2]; - PUTU32(pt + 8, s2); - s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3]; - PUTU32(pt + 12, s3); -} - - - -/* Generic wrapper functions for AES functions */ - -void * aes_encrypt_init(const u8 *key, size_t len) -{ - u32 *rk; - if (len != 16) - return NULL; - rk = malloc(4 * 44); - if (rk == NULL) - return NULL; - rijndaelKeySetupEnc(rk, key); - return rk; -} - - -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) -{ - rijndaelEncrypt(ctx, plain, crypt); -} - - -void aes_encrypt_deinit(void *ctx) -{ - free(ctx); -} - - -void * aes_decrypt_init(const u8 *key, size_t len) -{ - u32 *rk; - if (len != 16) - return NULL; - rk = malloc(4 * 44); - if (rk == NULL) - return NULL; - rijndaelKeySetupDec(rk, key); - return rk; -} - - -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) -{ - rijndaelDecrypt(ctx, crypt, plain); -} - - -void aes_decrypt_deinit(void *ctx) -{ - free(ctx); + return -1; } diff --git a/contrib/hostapd/src/crypto/aes-omac1.c b/contrib/hostapd/src/crypto/aes-omac1.c new file mode 100644 index 0000000000..27895eb007 --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-omac1.c @@ -0,0 +1,118 @@ +/* + * One-key CBC MAC (OMAC1) hash with AES-128 + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/aes-unwrap.c b/contrib/hostapd/src/crypto/aes-unwrap.c new file mode 100644 index 0000000000..9dd51602f3 --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-unwrap.c @@ -0,0 +1,73 @@ +/* + * AES key unwrap (128-bit KEK, RFC3394) + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/aes-wrap.c b/contrib/hostapd/src/crypto/aes-wrap.c new file mode 100644 index 0000000000..89d6f94bf7 --- /dev/null +++ b/contrib/hostapd/src/crypto/aes-wrap.c @@ -0,0 +1,70 @@ +/* + * AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/aes.c b/contrib/hostapd/src/crypto/aes.c deleted file mode 100644 index 8b8f2a04de..0000000000 --- a/contrib/hostapd/src/crypto/aes.c +++ /dev/null @@ -1,1127 +0,0 @@ -/* - * AES (Rijndael) cipher - * - * Modifications to public domain implementation: - * - support only 128-bit keys - * - cleanup - * - use C pre-processor to make it easier to change S table access - * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at - * cost of reduced throughput (quite small difference on Pentium 4, - * 10-25% when using -O1 or -O2 optimization) - * - * Copyright (c) 2003-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" - -#ifdef INTERNAL_AES - -#include "crypto.h" - -/* - * rijndael-alg-fst.c - * - * @version 3.0 (December 2000) - * - * Optimised ANSI C code for the Rijndael cipher (now AES) - * - * @author Vincent Rijmen - * @author Antoon Bosselaers - * @author Paulo Barreto - * - * This code is hereby placed in the public domain. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* #define FULL_UNROLL */ -#define AES_SMALL_TABLES - - -/* -Te0[x] = S [x].[02, 01, 01, 03]; -Te1[x] = S [x].[03, 02, 01, 01]; -Te2[x] = S [x].[01, 03, 02, 01]; -Te3[x] = S [x].[01, 01, 03, 02]; -Te4[x] = S [x].[01, 01, 01, 01]; - -Td0[x] = Si[x].[0e, 09, 0d, 0b]; -Td1[x] = Si[x].[0b, 0e, 09, 0d]; -Td2[x] = Si[x].[0d, 0b, 0e, 09]; -Td3[x] = Si[x].[09, 0d, 0b, 0e]; -Td4[x] = Si[x].[01, 01, 01, 01]; -*/ - -static const u32 Te0[256] = { - 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, - 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, - 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, - 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, - 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, - 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, - 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, - 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, - 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, - 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, - 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, - 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, - 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, - 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, - 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, - 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, - 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, - 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, - 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, - 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, - 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, - 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, - 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, - 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, - 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, - 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, - 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, - 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, - 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, - 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, - 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, - 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, - 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, - 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, - 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, - 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, - 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, - 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, - 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, - 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, - 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, - 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, - 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, - 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, - 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, - 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, - 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, - 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, - 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, - 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, - 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, - 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, - 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, - 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, - 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, - 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, - 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, - 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, - 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, - 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, - 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, - 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, - 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, - 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, -}; -#ifndef AES_SMALL_TABLES -static const u32 Te1[256] = { - 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, - 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, - 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, - 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, - 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, - 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, - 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, - 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, - 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, - 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, - 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, - 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, - 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, - 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, - 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, - 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, - 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, - 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, - 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, - 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, - 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, - 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, - 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, - 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, - 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, - 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, - 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, - 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, - 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, - 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, - 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, - 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, - 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, - 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, - 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, - 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, - 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, - 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, - 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, - 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, - 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, - 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, - 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, - 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, - 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, - 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, - 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, - 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, - 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, - 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, - 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, - 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, - 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, - 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, - 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, - 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, - 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, - 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, - 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, - 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, - 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, - 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, - 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, - 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, -}; -static const u32 Te2[256] = { - 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, - 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, - 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, - 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, - 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, - 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, - 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, - 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, - 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, - 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, - 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, - 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, - 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, - 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, - 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, - 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, - 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, - 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, - 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, - 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, - 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, - 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, - 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, - 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, - 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, - 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, - 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, - 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, - 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, - 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, - 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, - 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, - 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, - 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, - 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, - 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, - 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, - 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, - 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, - 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, - 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, - 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, - 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, - 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, - 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, - 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, - 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, - 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, - 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, - 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, - 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, - 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, - 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, - 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, - 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, - 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, - 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, - 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, - 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, - 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, - 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, - 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, - 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, - 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, -}; -static const u32 Te3[256] = { - - 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, - 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, - 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, - 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, - 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, - 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, - 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, - 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, - 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, - 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, - 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, - 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, - 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, - 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, - 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, - 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, - 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, - 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, - 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, - 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, - 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, - 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, - 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, - 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, - 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, - 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, - 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, - 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, - 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, - 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, - 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, - 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, - 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, - 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, - 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, - 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, - 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, - 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, - 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, - 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, - 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, - 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, - 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, - 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, - 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, - 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, - 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, - 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, - 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, - 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, - 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, - 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, - 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, - 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, - 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, - 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, - 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, - 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, - 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, - 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, - 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, - 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, - 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, - 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, -}; -static const u32 Te4[256] = { - 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, - 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, - 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, - 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, - 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, - 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, - 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, - 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, - 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, - 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, - 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, - 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, - 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, - 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, - 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, - 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, - 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, - 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, - 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, - 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, - 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, - 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, - 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, - 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, - 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, - 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, - 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, - 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, - 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, - 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, - 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, - 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, - 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, - 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, - 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, - 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, - 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, - 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, - 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, - 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, - 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, - 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, - 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, - 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, - 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, - 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, - 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, - 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, - 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, - 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, - 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, - 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, - 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, - 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, - 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, - 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, - 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, - 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, - 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, - 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, - 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, - 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, - 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, - 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, -}; -#endif /* AES_SMALL_TABLES */ -static const u32 Td0[256] = { - 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, - 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, - 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, - 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, - 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, - 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, - 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, - 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, - 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, - 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, - 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, - 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, - 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, - 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, - 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, - 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, - 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, - 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, - 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, - 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, - 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, - 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, - 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, - 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, - 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, - 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, - 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, - 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, - 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, - 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, - 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, - 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, - 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, - 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, - 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, - 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, - 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, - 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, - 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, - 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, - 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, - 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, - 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, - 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, - 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, - 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, - 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, - 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, - 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, - 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, - 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, - 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, - 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, - 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, - 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, - 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, - 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, - 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, - 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, - 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, - 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, - 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, - 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, - 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, -}; -#ifndef AES_SMALL_TABLES -static const u32 Td1[256] = { - 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, - 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, - 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, - 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, - 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, - 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, - 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, - 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, - 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, - 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, - 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, - 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, - 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, - 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, - 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, - 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, - 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, - 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, - 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, - 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, - 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, - 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, - 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, - 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, - 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, - 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, - 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, - 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, - 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, - 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, - 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, - 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, - 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, - 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, - 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, - 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, - 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, - 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, - 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, - 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, - 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, - 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, - 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, - 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, - 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, - 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, - 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, - 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, - 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, - 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, - 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, - 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, - 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, - 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, - 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, - 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, - 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, - 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, - 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, - 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, - 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, - 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, - 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, - 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, -}; -static const u32 Td2[256] = { - 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, - 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, - 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, - 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, - 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, - 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, - 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, - 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, - 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, - 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, - 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, - 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, - 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, - 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, - 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, - 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, - 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, - 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, - 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, - 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, - - 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, - 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, - 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, - 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, - 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, - 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, - 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, - 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, - 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, - 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, - 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, - 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, - 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, - 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, - 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, - 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, - 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, - 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, - 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, - 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, - 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, - 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, - 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, - 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, - 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, - 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, - 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, - 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, - 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, - 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, - 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, - 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, - 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, - 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, - 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, - 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, - 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, - 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, - 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, - 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, - 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, - 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, - 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, - 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, -}; -static const u32 Td3[256] = { - 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, - 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, - 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, - 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, - 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, - 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, - 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, - 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, - 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, - 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, - 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, - 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, - 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, - 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, - 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, - 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, - 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, - 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, - 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, - 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, - 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, - 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, - 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, - 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, - 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, - 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, - 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, - 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, - 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, - 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, - 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, - 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, - 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, - 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, - 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, - 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, - 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, - 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, - 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, - 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, - 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, - 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, - 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, - 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, - 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, - 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, - 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, - 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, - 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, - 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, - 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, - 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, - 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, - 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, - 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, - 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, - 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, - 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, - 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, - 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, - 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, - 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, - 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, - 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, -}; -static const u32 Td4[256] = { - 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, - 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, - 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, - 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, - 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, - 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, - 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, - 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, - 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, - 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, - 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, - 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, - 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, - 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, - 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, - 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, - 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, - 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, - 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, - 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, - 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, - 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, - 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, - 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, - 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, - 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, - 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, - 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, - 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, - 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, - 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, - 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, - 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, - 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, - 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, - 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, - 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, - 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, - 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, - 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, - 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, - 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, - 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, - 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, - 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, - 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, - 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, - 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, - 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, - 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, - 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, - 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, - 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, - 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, - 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, - 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, - 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, - 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, - 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, - 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, - 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, - 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, - 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, - 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, -}; -static const u32 rcon[] = { - 0x01000000, 0x02000000, 0x04000000, 0x08000000, - 0x10000000, 0x20000000, 0x40000000, 0x80000000, - 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ -}; -#else /* AES_SMALL_TABLES */ -static const u8 Td4s[256] = { - 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, - 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, - 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, - 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, - 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, - 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, - 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, - 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, - 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, - 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, - 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, - 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, - 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, - 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, - 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, - 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, - 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, - 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, - 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, - 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, - 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, - 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, - 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, - 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, - 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, - 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, - 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, - 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, - 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, - 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, - 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, - 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, -}; -static const u8 rcons[] = { - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 - /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ -}; -#endif /* AES_SMALL_TABLES */ - - -#ifndef AES_SMALL_TABLES - -#define RCON(i) rcon[(i)] - -#define TE0(i) Te0[((i) >> 24) & 0xff] -#define TE1(i) Te1[((i) >> 16) & 0xff] -#define TE2(i) Te2[((i) >> 8) & 0xff] -#define TE3(i) Te3[(i) & 0xff] -#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) -#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) -#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) -#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff) -#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000) -#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000) -#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00) -#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff) -#define TE4(i) (Te4[(i)] & 0x000000ff) - -#define TD0(i) Td0[((i) >> 24) & 0xff] -#define TD1(i) Td1[((i) >> 16) & 0xff] -#define TD2(i) Td2[((i) >> 8) & 0xff] -#define TD3(i) Td3[(i) & 0xff] -#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000) -#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000) -#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00) -#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff) -#define TD0_(i) Td0[(i) & 0xff] -#define TD1_(i) Td1[(i) & 0xff] -#define TD2_(i) Td2[(i) & 0xff] -#define TD3_(i) Td3[(i) & 0xff] - -#else /* AES_SMALL_TABLES */ - -#define RCON(i) (rcons[(i)] << 24) - -static inline u32 rotr(u32 val, int bits) -{ - return (val >> bits) | (val << (32 - bits)); -} - -#define TE0(i) Te0[((i) >> 24) & 0xff] -#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8) -#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16) -#define TE3(i) rotr(Te0[(i) & 0xff], 24) -#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) -#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) -#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) -#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) -#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000) -#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) -#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) -#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) -#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) - -#define TD0(i) Td0[((i) >> 24) & 0xff] -#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8) -#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16) -#define TD3(i) rotr(Td0[(i) & 0xff], 24) -#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24) -#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16) -#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8) -#define TD44(i) (Td4s[(i) & 0xff]) -#define TD0_(i) Td0[(i) & 0xff] -#define TD1_(i) rotr(Td0[(i) & 0xff], 8) -#define TD2_(i) rotr(Td0[(i) & 0xff], 16) -#define TD3_(i) rotr(Td0[(i) & 0xff], 24) - -#endif /* AES_SMALL_TABLES */ - -#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) - -#ifdef _MSC_VER -#define GETU32(p) SWAP(*((u32 *)(p))) -#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } -#else -#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \ -((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) -#define PUTU32(ct, st) { \ -(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ -(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } -#endif - -/** - * Expand the cipher key into the encryption key schedule. - * - * @return the number of rounds for the given cipher key size. - */ -void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) -{ - int i; - u32 temp; - - rk[0] = GETU32(cipherKey ); - rk[1] = GETU32(cipherKey + 4); - rk[2] = GETU32(cipherKey + 8); - rk[3] = GETU32(cipherKey + 12); - for (i = 0; i < 10; i++) { - temp = rk[3]; - rk[4] = rk[0] ^ - TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ - RCON(i); - rk[5] = rk[1] ^ rk[4]; - rk[6] = rk[2] ^ rk[5]; - rk[7] = rk[3] ^ rk[6]; - rk += 4; - } -} - -#ifndef CONFIG_NO_AES_DECRYPT -/** - * 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)); - } - } -} -#endif /* CONFIG_NO_AES_DECRYPT */ - -#ifndef CONFIG_NO_AES_ENCRYPT -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); -} -#endif /* CONFIG_NO_AES_ENCRYPT */ - -void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) -{ - u32 s0, s1, s2, s3, t0, t1, t2, t3; - const int Nr = 10; -#ifndef FULL_UNROLL - int r; -#endif /* ?FULL_UNROLL */ - - /* - * map byte array block to cipher state - * and add initial round key: - */ - s0 = GETU32(ct ) ^ rk[0]; - s1 = GETU32(ct + 4) ^ rk[1]; - s2 = GETU32(ct + 8) ^ rk[2]; - s3 = GETU32(ct + 12) ^ rk[3]; - -#define ROUND(i,d,s) \ -d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \ -d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \ -d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \ -d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] - -#ifdef FULL_UNROLL - - ROUND(1,t,s); - ROUND(2,s,t); - ROUND(3,t,s); - ROUND(4,s,t); - ROUND(5,t,s); - ROUND(6,s,t); - ROUND(7,t,s); - ROUND(8,s,t); - ROUND(9,t,s); - - rk += Nr << 2; - -#else /* !FULL_UNROLL */ - - /* Nr - 1 full rounds: */ - r = Nr >> 1; - for (;;) { - ROUND(1,t,s); - rk += 8; - if (--r == 0) - break; - ROUND(0,s,t); - } - -#endif /* ?FULL_UNROLL */ - -#undef ROUND - - /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0]; - PUTU32(pt , s0); - s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1]; - PUTU32(pt + 4, s1); - s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2]; - PUTU32(pt + 8, s2); - s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3]; - PUTU32(pt + 12, s3); -} - - - -/* Generic wrapper functions for AES functions */ - -#define AES_PRIV_SIZE (4 * 44) - -#ifndef CONFIG_NO_AES_ENCRYPT -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); -} -#endif /* CONFIG_NO_AES_ENCRYPT */ - - -#ifndef CONFIG_NO_AES_DECRYPT -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; -} - - -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); -} -#endif /* CONFIG_NO_AES_DECRYPT */ - -#endif /* INTERNAL_AES */ diff --git a/contrib/hostapd/src/crypto/aes.h b/contrib/hostapd/src/crypto/aes.h index 6b9f4147af..2de59e04ef 100644 --- a/contrib/hostapd/src/crypto/aes.h +++ b/contrib/hostapd/src/crypto/aes.h @@ -2,19 +2,15 @@ * AES functions * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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); diff --git a/contrib/hostapd/src/crypto/aes_i.h b/contrib/hostapd/src/crypto/aes_i.h new file mode 100644 index 0000000000..54375cf355 --- /dev/null +++ b/contrib/hostapd/src/crypto/aes_i.h @@ -0,0 +1,125 @@ +/* + * AES (Rijndael) cipher + * Copyright (c) 2003-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 TE411(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) +#define TE422(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE433(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE444(i) (Te4[(i) & 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 TE411(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) +#define TE422(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE433(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE444(i) ((Te0[(i) & 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 * 4 * 15 + 4) +#define AES_PRIV_NR_POS (4 * 15) + +int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits); + +#endif /* AES_I_H */ diff --git a/contrib/hostapd/src/crypto/aes_wrap.c b/contrib/hostapd/src/crypto/aes_wrap.c deleted file mode 100644 index b1448b0d9a..0000000000 --- a/contrib/hostapd/src/crypto/aes_wrap.c +++ /dev/null @@ -1,533 +0,0 @@ -/* - * AES-based functions - * - * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * - One-Key CBC MAC (OMAC1, i.e., CMAC) hash with AES-128 - * - AES-128 CTR mode encryption - * - AES-128 EAX mode encryption/decryption - * - AES-128 CBC - * - * Copyright (c) 2003-2007, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "aes_wrap.h" -#include "crypto.h" - -#ifndef CONFIG_NO_AES_WRAP - -/** - * 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; -} - -#endif /* CONFIG_NO_AES_WRAP */ - - -#ifndef CONFIG_NO_AES_UNWRAP - -/** - * 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; -} - -#endif /* CONFIG_NO_AES_UNWRAP */ - - -#define BLOCK_SIZE 16 - -#ifndef CONFIG_NO_AES_OMAC1 - -static void gf_mulx(u8 *pad) -{ - int i, carry; - - carry = pad[0] & 0x80; - for (i = 0; i < BLOCK_SIZE - 1; i++) - pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); - pad[BLOCK_SIZE - 1] <<= 1; - if (carry) - pad[BLOCK_SIZE - 1] ^= 0x87; -} - - -/** - * omac1_aes_128_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[BLOCK_SIZE], pad[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, 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 >= BLOCK_SIZE) { - for (i = 0; i < BLOCK_SIZE; i++) { - cbc[i] ^= *pos++; - if (pos >= end) { - e++; - pos = addr[e]; - end = pos + len[e]; - } - } - if (left > BLOCK_SIZE) - aes_encrypt(ctx, cbc, cbc); - left -= BLOCK_SIZE; - } - - os_memset(pad, 0, 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 < 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); -} - -#endif /* CONFIG_NO_AES_OMAC1 */ - - -#ifndef CONFIG_NO_AES_ENCRYPT_BLOCK -/** - * 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; -} -#endif /* CONFIG_NO_AES_ENCRYPT_BLOCK */ - - -#ifndef CONFIG_NO_AES_CTR - -/** - * 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[BLOCK_SIZE], buf[BLOCK_SIZE]; - - ctx = aes_encrypt_init(key, 16); - if (ctx == NULL) - return -1; - os_memcpy(counter, nonce, BLOCK_SIZE); - - while (left > 0) { - aes_encrypt(ctx, counter, buf); - - len = (left < BLOCK_SIZE) ? left : BLOCK_SIZE; - for (j = 0; j < len; j++) - pos[j] ^= buf[j]; - pos += len; - left -= len; - - for (i = BLOCK_SIZE - 1; i >= 0; i--) { - counter[i]++; - if (counter[i]) - break; - } - } - aes_encrypt_deinit(ctx); - return 0; -} - -#endif /* CONFIG_NO_AES_CTR */ - - -#ifndef CONFIG_NO_AES_EAX - -/** - * aes_128_eax_encrypt - AES-128 EAX mode encryption - * @key: Key for encryption (16 bytes) - * @nonce: Nonce for counter mode - * @nonce_len: Nonce length in bytes - * @hdr: Header data to be authenticity protected - * @hdr_len: Length of the header data bytes - * @data: Data to encrypt in-place - * @data_len: Length of data in bytes - * @tag: 16-byte tag value - * Returns: 0 on success, -1 on failure - */ -int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, - const u8 *hdr, size_t hdr_len, - u8 *data, size_t data_len, u8 *tag) -{ - u8 *buf; - size_t buf_len; - u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; - int i, 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 < 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[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; - int i; - - if (nonce_len > data_len) - buf_len = nonce_len; - else - buf_len = data_len; - if (hdr_len > buf_len) - buf_len = hdr_len; - buf_len += 16; - - buf = 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 < 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); -} - -#endif /* CONFIG_NO_AES_EAX */ - - -#ifndef CONFIG_NO_AES_CBC - -/** - * aes_128_cbc_encrypt - AES-128 CBC encryption - * @key: Encryption key - * @iv: Encryption IV for CBC mode (16 bytes) - * @data: Data to encrypt in-place - * @data_len: Length of data in bytes (must be divisible by 16) - * Returns: 0 on success, -1 on failure - */ -int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) -{ - void *ctx; - u8 cbc[BLOCK_SIZE]; - u8 *pos = data; - int i, j, blocks; - - ctx = aes_encrypt_init(key, 16); - if (ctx == NULL) - return -1; - os_memcpy(cbc, iv, BLOCK_SIZE); - - blocks = data_len / BLOCK_SIZE; - for (i = 0; i < blocks; i++) { - for (j = 0; j < BLOCK_SIZE; j++) - cbc[j] ^= pos[j]; - aes_encrypt(ctx, cbc, cbc); - os_memcpy(pos, cbc, BLOCK_SIZE); - pos += BLOCK_SIZE; - } - aes_encrypt_deinit(ctx); - return 0; -} - - -/** - * aes_128_cbc_decrypt - AES-128 CBC decryption - * @key: Decryption key - * @iv: Decryption IV for CBC mode (16 bytes) - * @data: Data to decrypt in-place - * @data_len: Length of data in bytes (must be divisible by 16) - * Returns: 0 on success, -1 on failure - */ -int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) -{ - void *ctx; - u8 cbc[BLOCK_SIZE], tmp[BLOCK_SIZE]; - u8 *pos = data; - int i, j, blocks; - - ctx = aes_decrypt_init(key, 16); - if (ctx == NULL) - return -1; - os_memcpy(cbc, iv, BLOCK_SIZE); - - blocks = data_len / BLOCK_SIZE; - for (i = 0; i < blocks; i++) { - os_memcpy(tmp, pos, BLOCK_SIZE); - aes_decrypt(ctx, pos, pos); - for (j = 0; j < BLOCK_SIZE; j++) - pos[j] ^= cbc[j]; - os_memcpy(cbc, tmp, BLOCK_SIZE); - pos += BLOCK_SIZE; - } - aes_decrypt_deinit(ctx); - return 0; -} - -#endif /* CONFIG_NO_AES_CBC */ diff --git a/contrib/hostapd/src/crypto/aes_wrap.h b/contrib/hostapd/src/crypto/aes_wrap.h index 4b1c7b083b..0433c0434e 100644 --- a/contrib/hostapd/src/crypto/aes_wrap.h +++ b/contrib/hostapd/src/crypto/aes_wrap.h @@ -6,17 +6,13 @@ * - AES-128 CTR mode encryption * - AES-128 EAX mode encryption/decryption * - AES-128 CBC + * - AES-GCM + * - AES-CCM * - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef AES_WRAP_H @@ -44,5 +40,25 @@ 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); +int __must_check aes_gcm_ae(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, + u8 *crypt, u8 *tag); +int __must_check aes_gcm_ad(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *tag, + u8 *plain); +int __must_check aes_gmac(const u8 *key, size_t key_len, + const u8 *iv, size_t iv_len, + const u8 *aad, size_t aad_len, u8 *tag); +int __must_check aes_ccm_ae(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *plain, size_t plain_len, + const u8 *aad, size_t aad_len, u8 *crypt, u8 *auth); +int __must_check aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce, + size_t M, const u8 *crypt, size_t crypt_len, + const u8 *aad, size_t aad_len, const u8 *auth, + u8 *plain); #endif /* AES_WRAP_H */ diff --git a/contrib/hostapd/src/crypto/crypto.h b/contrib/hostapd/src/crypto/crypto.h index a5129bbd05..9bccaaa8f8 100644 --- a/contrib/hostapd/src/crypto/crypto.h +++ b/contrib/hostapd/src/crypto/crypto.h @@ -1,15 +1,9 @@ /* - * WPA Supplicant / wrapper functions for crypto libraries - * Copyright (c) 2004-2007, Jouni Malinen + * Wrapper functions for crypto libraries + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -33,8 +27,9 @@ * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure */ -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); /** * md5_vector - MD5 hash for data vector @@ -42,8 +37,10 @@ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure */ -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + /** * sha1_vector - SHA-1 hash for data vector @@ -51,9 +48,10 @@ void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure */ -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac); +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 @@ -76,9 +74,10 @@ int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure */ -void sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac); +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); /** * des_encrypt - Encrypt one block with DES @@ -135,7 +134,8 @@ 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 + CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1, + CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 }; struct crypto_hash; @@ -275,6 +275,7 @@ 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 @@ -282,7 +283,8 @@ struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len); * to implement this. */ struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len); + size_t len, + const char *passwd); /** * crypto_public_key_from_cert - Import an RSA public key from a certificate @@ -428,4 +430,356 @@ int __must_check crypto_mod_exp(const u8 *base, size_t base_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); + +/** + * crypto_get_random - Generate cryptographically strong pseudy-random bytes + * @buf: Buffer for data + * @len: Number of bytes to generate + * Returns: 0 on success, -1 on failure + * + * If the PRNG does not have enough entropy to ensure unpredictable byte + * sequence, this functions must return -1. + */ +int crypto_get_random(void *buf, size_t len); + + +/** + * struct crypto_bignum - bignum + * + * Internal data structure for bignum implementation. The contents is specific + * to the used crypto library. + */ +struct crypto_bignum; + +/** + * crypto_bignum_init - Allocate memory for bignum + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct crypto_bignum * crypto_bignum_init(void); + +/** + * crypto_bignum_init_set - Allocate memory for bignum and set the value + * @buf: Buffer with unsigned binary value + * @len: Length of buf in octets + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len); + +/** + * crypto_bignum_deinit - Free bignum + * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set() + * @clear: Whether to clear the value from memory + */ +void crypto_bignum_deinit(struct crypto_bignum *n, int clear); + +/** + * crypto_bignum_to_bin - Set binary buffer to unsigned bignum + * @a: Bignum + * @buf: Buffer for the binary number + * @len: Length of @buf in octets + * @padlen: Length in octets to pad the result to or 0 to indicate no padding + * Returns: Number of octets written on success, -1 on failure + */ +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen); + +/** + * crypto_bignum_add - c = a + b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_mod - c = a % b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a % b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c) + * @a: Bignum; base + * @b: Bignum; exponent + * @c: Bignum; modulus + * @d: Bignum; used to store the result of a^b (mod c) + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_exptmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d); + +/** + * crypto_bignum_rshift - b = a >> n + * @a: Bignum + * @n: Number of bits to shift + * @b: Bignum; used to store the result of a >> n + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *b); + +/** + * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b) + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_sub - c = a - b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a - b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_div - c = a / b + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a / b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); + +/** + * crypto_bignum_mulmod - d = a * b (mod c) + * @a: Bignum + * @b: Bignum + * @c: Bignum + * @d: Bignum; used to store the result of (a * b) % c + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d); + +/** + * crypto_bignum_cmp - Compare two bignums + * @a: Bignum + * @b: Bignum + * Returns: -1 if a < b, 0 if a == b, or 1 if a > b + */ +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b); + +/** + * crypto_bignum_bits - Get size of a bignum in bits + * @a: Bignum + * Returns: Number of bits in the bignum + */ +int crypto_bignum_bits(const struct crypto_bignum *a); + +/** + * crypto_bignum_is_zero - Is the given bignum zero + * @a: Bignum + * Returns: 1 if @a is zero or 0 if not + */ +int crypto_bignum_is_zero(const struct crypto_bignum *a); + +/** + * crypto_bignum_is_one - Is the given bignum one + * @a: Bignum + * Returns: 1 if @a is one or 0 if not + */ +int crypto_bignum_is_one(const struct crypto_bignum *a); + +/** + * struct crypto_ec - Elliptic curve context + * + * Internal data structure for EC implementation. The contents is specific + * to the used crypto library. + */ +struct crypto_ec; + +/** + * crypto_ec_init - Initialize elliptic curve context + * @group: Identifying number for the ECC group (IANA "Group Description" + * attribute registrty for RFC 2409) + * Returns: Pointer to EC context or %NULL on failure + */ +struct crypto_ec * crypto_ec_init(int group); + +/** + * crypto_ec_deinit - Deinitialize elliptic curve context + * @e: EC context from crypto_ec_init() + */ +void crypto_ec_deinit(struct crypto_ec *e); + +/** + * crypto_ec_prime_len - Get length of the prime in octets + * @e: EC context from crypto_ec_init() + * Returns: Length of the prime defining the group + */ +size_t crypto_ec_prime_len(struct crypto_ec *e); + +/** + * crypto_ec_prime_len_bits - Get length of the prime in bits + * @e: EC context from crypto_ec_init() + * Returns: Length of the prime defining the group in bits + */ +size_t crypto_ec_prime_len_bits(struct crypto_ec *e); + +/** + * crypto_ec_get_prime - Get prime defining an EC group + * @e: EC context from crypto_ec_init() + * Returns: Prime (bignum) defining the group + */ +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e); + +/** + * crypto_ec_get_order - Get order of an EC group + * @e: EC context from crypto_ec_init() + * Returns: Order (bignum) of the group + */ +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e); + +/** + * struct crypto_ec_point - Elliptic curve point + * + * Internal data structure for EC implementation to represent a point. The + * contents is specific to the used crypto library. + */ +struct crypto_ec_point; + +/** + * crypto_ec_point_init - Initialize data for an EC point + * @e: EC context from crypto_ec_init() + * Returns: Pointer to EC point data or %NULL on failure + */ +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e); + +/** + * crypto_ec_point_deinit - Deinitialize EC point data + * @p: EC point data from crypto_ec_point_init() + * @clear: Whether to clear the EC point value from memory + */ +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear); + +/** + * crypto_ec_point_to_bin - Write EC point value as binary data + * @e: EC context from crypto_ec_init() + * @p: EC point data from crypto_ec_point_init() + * @x: Buffer for writing the binary data for x coordinate or %NULL if not used + * @y: Buffer for writing the binary data for y coordinate or %NULL if not used + * Returns: 0 on success, -1 on failure + * + * This function can be used to write an EC point as binary data in a format + * that has the x and y coordinates in big endian byte order fields padded to + * the length of the prime defining the group. + */ +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y); + +/** + * crypto_ec_point_from_bin - Create EC point from binary data + * @e: EC context from crypto_ec_init() + * @val: Binary data to read the EC point from + * Returns: Pointer to EC point data or %NULL on failure + * + * This function readers x and y coordinates of the EC point from the provided + * buffer assuming the values are in big endian byte order with fields padded to + * the length of the prime defining the group. + */ +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val); + +/** + * crypto_bignum_add - c = a + b + * @e: EC context from crypto_ec_init() + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c); + +/** + * crypto_bignum_mul - res = b * p + * @e: EC context from crypto_ec_init() + * @p: EC point + * @b: Bignum + * @res: EC point; used to store the result of b * p + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res); + +/** + * crypto_ec_point_invert - Compute inverse of an EC point + * @e: EC context from crypto_ec_init() + * @p: EC point to invert (and result of the operation) + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p); + +/** + * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate + * @e: EC context from crypto_ec_init() + * @p: EC point to use for the returning the result + * @x: x coordinate + * @y_bit: y-bit (0 or 1) for selecting the y value to use + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit); + +/** + * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element + * @e: EC context from crypto_ec_init() + * @p: EC point + * Returns: 1 if the specified EC point is the neutral element of the group or + * 0 if not + */ +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p); + +/** + * crypto_ec_point_is_on_curve - Check whether EC point is on curve + * @e: EC context from crypto_ec_init() + * @p: EC point + * Returns: 1 if the specified EC point is on the curve or 0 if not + */ +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p); + #endif /* CRYPTO_H */ diff --git a/contrib/hostapd/src/crypto/crypto_cryptoapi.c b/contrib/hostapd/src/crypto/crypto_cryptoapi.c index 45333dd2e3..55a069b0d0 100644 --- a/contrib/hostapd/src/crypto/crypto_cryptoapi.c +++ b/contrib/hostapd/src/crypto/crypto_cryptoapi.c @@ -2,14 +2,8 @@ * Crypto wrapper for Microsoft CryptoAPI * Copyright (c) 2005-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -33,7 +27,6 @@ L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" #define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) #endif -#ifdef CONFIG_TLS_INTERNAL #ifdef __MINGW32_VERSION /* * MinGW does not yet include all the needed definitions for CryptoAPI, so @@ -83,7 +76,6 @@ static int mingw_load_crypto_func(void) } #endif /* __MINGW32_VERSION */ -#endif /* CONFIG_TLS_INTERNAL */ static void cryptoapi_report_error(const char *msg) @@ -152,9 +144,9 @@ int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem, } -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); + return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); } @@ -223,16 +215,15 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -#ifdef EAP_TLS_FUNCS -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); + return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); } -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); + return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); } @@ -349,7 +340,6 @@ void aes_decrypt_deinit(void *ctx) aes_encrypt_deinit(ctx); } -#ifdef CONFIG_TLS_INTERNAL struct crypto_hash { enum crypto_hash_alg alg; @@ -657,7 +647,8 @@ struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len) + size_t len, + const char *passwd) { /* TODO */ return NULL; @@ -781,6 +772,12 @@ void crypto_global_deinit(void) { } -#endif /* CONFIG_TLS_INTERNAL */ -#endif /* EAP_TLS_FUNCS */ +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/contrib/hostapd/src/crypto/crypto_gnutls.c b/contrib/hostapd/src/crypto/crypto_gnutls.c index 8f8611c0fb..0dfd54d22d 100644 --- a/contrib/hostapd/src/crypto/crypto_gnutls.c +++ b/contrib/hostapd/src/crypto/crypto_gnutls.c @@ -2,14 +2,8 @@ * WPA Supplicant / wrapper functions for libgcrypt * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,20 +12,21 @@ #include "common.h" #include "crypto.h" -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +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; + 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; } @@ -57,49 +52,42 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +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; + 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; } -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +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; + 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; } -#ifndef CONFIG_NO_FIPS186_2_PRF -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; -} -#endif /* CONFIG_NO_FIPS186_2_PRF */ - - void * aes_encrypt_init(const u8 *key, size_t len) { gcry_cipher_hd_t hd; diff --git a/contrib/hostapd/src/crypto/crypto_internal-cipher.c b/contrib/hostapd/src/crypto/crypto_internal-cipher.c new file mode 100644 index 0000000000..ad0930a5a9 --- /dev/null +++ b/contrib/hostapd/src/crypto/crypto_internal-cipher.c @@ -0,0 +1,243 @@ +/* + * Crypto wrapper for internal crypto implementation - Cipher wrappers + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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]; + 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: + 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; + } + os_memcpy(ctx->u.aes.cbc, iv, 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 % AES_BLOCK_SIZE) + return -1; + blocks = len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + for (j = 0; j < 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, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += 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 % AES_BLOCK_SIZE) + return -1; + blocks = len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, AES_BLOCK_SIZE); + aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain); + for (j = 0; j < AES_BLOCK_SIZE; j++) + plain[j] ^= ctx->u.aes.cbc[j]; + os_memcpy(ctx->u.aes.cbc, tmp, AES_BLOCK_SIZE); + plain += AES_BLOCK_SIZE; + crypt += 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/contrib/hostapd/src/crypto/crypto_internal-modexp.c b/contrib/hostapd/src/crypto/crypto_internal-modexp.c new file mode 100644 index 0000000000..9dcabb95bd --- /dev/null +++ b/contrib/hostapd/src/crypto/crypto_internal-modexp.c @@ -0,0 +1,49 @@ +/* + * Crypto wrapper for internal crypto implementation - modexp + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/crypto_internal-rsa.c b/contrib/hostapd/src/crypto/crypto_internal-rsa.c new file mode 100644 index 0000000000..54209fad3c --- /dev/null +++ b/contrib/hostapd/src/crypto/crypto_internal-rsa.c @@ -0,0 +1,108 @@ +/* + * Crypto wrapper for internal crypto implementation - RSA parts + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "tls/rsa.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/contrib/hostapd/src/crypto/crypto_internal.c b/contrib/hostapd/src/crypto/crypto_internal.c index cddfb4de8e..f3602dac34 100644 --- a/contrib/hostapd/src/crypto/crypto_internal.c +++ b/contrib/hostapd/src/crypto/crypto_internal.c @@ -1,63 +1,27 @@ /* - * WPA Supplicant / Crypto wrapper for internal crypto implementation - * Copyright (c) 2006-2007, Jouni Malinen + * Crypto wrapper for internal crypto implementation + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto.h" -#include "md5.h" -#include "sha1.h" -#include "rc4.h" -#include "aes.h" -#include "tls/rsa.h" -#include "tls/bignum.h" -#include "tls/asn1.h" - - -#ifdef CONFIG_CRYPTO_INTERNAL - -#ifdef CONFIG_TLS_INTERNAL - -/* 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); - - -struct MD5Context { - u32 buf[4]; - u32 bits[2]; - u8 in[64]; -}; - -struct SHA1Context { - u32 state[5]; - u32 count[2]; - unsigned char buffer[64]; -}; - +#include "sha256_i.h" +#include "sha1_i.h" +#include "md5_i.h" struct crypto_hash { enum crypto_hash_alg alg; union { struct MD5Context md5; struct SHA1Context sha1; +#ifdef CONFIG_SHA256 + struct sha256_state sha256; +#endif /* CONFIG_SHA256 */ } u; u8 key[64]; size_t key_len; @@ -69,7 +33,7 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, { struct crypto_hash *ctx; u8 k_pad[64]; - u8 tk[20]; + u8 tk[32]; size_t i; ctx = os_zalloc(sizeof(*ctx)); @@ -85,6 +49,11 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, case CRYPTO_HASH_ALG_SHA1: SHA1Init(&ctx->u.sha1); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + sha256_init(&ctx->u.sha256); + break; +#endif /* CONFIG_SHA256 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (key_len > sizeof(k_pad)) { MD5Init(&ctx->u.md5); @@ -97,7 +66,8 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ctx->key_len = key_len; os_memcpy(k_pad, key, key_len); - os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + if (key_len < sizeof(k_pad)) + 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); @@ -115,12 +85,34 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, ctx->key_len = key_len; os_memcpy(k_pad, key, key_len); - os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + if (key_len < sizeof(k_pad)) + 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; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (key_len > sizeof(k_pad)) { + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, key, key_len); + sha256_done(&ctx->u.sha256, tk); + key = tk; + key_len = 32; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + if (key_len < sizeof(k_pad)) + os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); + break; +#endif /* CONFIG_SHA256 */ default: os_free(ctx); return NULL; @@ -144,6 +136,14 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) case CRYPTO_HASH_ALG_HMAC_SHA1: SHA1Update(&ctx->u.sha1, data, len); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + case CRYPTO_HASH_ALG_HMAC_SHA256: + sha256_process(&ctx->u.sha256, data, len); + break; +#endif /* CONFIG_SHA256 */ + default: + break; } } @@ -180,6 +180,17 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) *len = 20; SHA1Final(mac, &ctx->u.sha1); break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_SHA256: + if (*len < 32) { + *len = 32; + os_free(ctx); + return -1; + } + *len = 32; + sha256_done(&ctx->u.sha256, mac); + break; +#endif /* CONFIG_SHA256 */ case CRYPTO_HASH_ALG_HMAC_MD5: if (*len < 16) { *len = 16; @@ -220,562 +231,34 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) SHA1Update(&ctx->u.sha1, mac, 20); SHA1Final(mac, &ctx->u.sha1); break; - } - - os_free(ctx); - - return 0; -} - - -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; - } 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) { +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (*len < 32) { + *len = 32; os_free(ctx); - return NULL; - } - des3_key_setup(key, &ctx->u.des3.key); - os_memcpy(ctx->u.des3.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; - default: - return -1; - } - - return 0; -} + *len = 32; + sha256_done(&ctx->u.sha256, mac); -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; - } + 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; + sha256_init(&ctx->u.sha256); + sha256_process(&ctx->u.sha256, k_pad, sizeof(k_pad)); + sha256_process(&ctx->u.sha256, mac, 32); + sha256_done(&ctx->u.sha256, mac); break; +#endif /* CONFIG_SHA256 */ default: + os_free(ctx); 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); -} - - -/* 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); -} - - -#ifdef EAP_TLS_FUNCS -static struct crypto_private_key * -crypto_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); -} -#endif /* EAP_TLS_FUNCS */ - - -struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len) -{ - struct crypto_private_key *res; - - /* First, check for possible PKCS #8 encoding */ - res = crypto_pkcs8_key_import(key, len); - 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; -} - - -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, 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 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, (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) -{ - struct crypto_rsa_key *rkey = (struct crypto_rsa_key *) key; - int res; - u8 *pos, *end; - - res = crypto_rsa_exptmod(in, inlen, out, outlen, rkey, 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 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, (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) -{ - size_t len; - u8 *pos; - - len = *plain_len; - if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, - (struct crypto_rsa_key *) 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; } @@ -790,47 +273,3 @@ int crypto_global_init(void) void crypto_global_deinit(void) { } -#endif /* CONFIG_TLS_INTERNAL */ - - -#if defined(EAP_FAST) || defined(CONFIG_WPS) - -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; -} - -#endif /* EAP_FAST || CONFIG_WPS */ - - -#endif /* CONFIG_CRYPTO_INTERNAL */ diff --git a/contrib/hostapd/src/crypto/crypto_libtomcrypt.c b/contrib/hostapd/src/crypto/crypto_libtomcrypt.c index e82097f10a..a55edd14e2 100644 --- a/contrib/hostapd/src/crypto/crypto_libtomcrypt.c +++ b/contrib/hostapd/src/crypto/crypto_libtomcrypt.c @@ -2,21 +2,14 @@ * WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1) * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include #include "common.h" -#include "rc4.h" #include "crypto.h" #ifndef mp_init_multi @@ -29,7 +22,7 @@ #endif -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { hash_state md; size_t i; @@ -38,6 +31,7 @@ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) for (i = 0; i < num_elem; i++) md4_process(&md, addr[i], len[i]); md4_done(&md, mac); + return 0; } @@ -62,8 +56,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -#ifdef EAP_TLS_FUNCS -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { hash_state md; size_t i; @@ -72,10 +65,11 @@ void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) for (i = 0; i < num_elem; i++) md5_process(&md, addr[i], len[i]); md5_done(&md, mac); + return 0; } -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { hash_state md; size_t i; @@ -84,6 +78,7 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) for (i = 0; i < num_elem; i++) sha1_process(&md, addr[i], len[i]); sha1_done(&md, mac); + return 0; } @@ -145,8 +140,6 @@ void aes_decrypt_deinit(void *ctx) } -#ifdef CONFIG_TLS_INTERNAL - struct crypto_hash { enum crypto_hash_alg alg; int error; @@ -451,7 +444,8 @@ struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len) + size_t len, + const char *passwd) { int res; struct crypto_private_key *pk; @@ -697,7 +691,7 @@ void crypto_global_deinit(void) } -#ifdef EAP_FAST +#ifdef CONFIG_MODEXP int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, @@ -729,8 +723,4 @@ fail: return -1; } -#endif /* EAP_FAST */ - -#endif /* CONFIG_TLS_INTERNAL */ - -#endif /* EAP_TLS_FUNCS */ +#endif /* CONFIG_MODEXP */ diff --git a/contrib/hostapd/src/crypto/crypto_none.c b/contrib/hostapd/src/crypto/crypto_none.c index f18c2a8ded..011f3f35a0 100644 --- a/contrib/hostapd/src/crypto/crypto_none.c +++ b/contrib/hostapd/src/crypto/crypto_none.c @@ -2,14 +2,8 @@ * WPA Supplicant / Empty template functions for crypto wrapper * Copyright (c) 2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,8 +12,9 @@ #include "crypto.h" -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { + return 0; } diff --git a/contrib/hostapd/src/crypto/crypto_nss.c b/contrib/hostapd/src/crypto/crypto_nss.c new file mode 100644 index 0000000000..acd0a55281 --- /dev/null +++ b/contrib/hostapd/src/crypto/crypto_nss.c @@ -0,0 +1,207 @@ +/* + * Crypto wrapper functions for NSS + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/contrib/hostapd/src/crypto/crypto_openssl.c b/contrib/hostapd/src/crypto/crypto_openssl.c index a4c3415c4d..1da2b9f4a3 100644 --- a/contrib/hostapd/src/crypto/crypto_openssl.c +++ b/contrib/hostapd/src/crypto/crypto_openssl.c @@ -1,28 +1,33 @@ /* - * WPA Supplicant / wrapper functions for libcrypto - * Copyright (c) 2004-2005, Jouni Malinen + * Wrapper functions for OpenSSL libcrypto + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include -#include -#include -#include +#include #include #include #include #include +#include +#include +#include +#ifdef CONFIG_OPENSSL_CMAC +#include +#endif /* CONFIG_OPENSSL_CMAC */ +#ifdef CONFIG_ECC +#include +#endif /* CONFIG_ECC */ #include "common.h" +#include "wpabuf.h" +#include "dh_group5.h" +#include "sha1.h" +#include "sha256.h" #include "crypto.h" #if OPENSSL_VERSION_NUMBER < 0x00907000 @@ -33,16 +38,80 @@ 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 -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { - MD4_CTX ctx; + EVP_MD_CTX ctx; size_t i; + unsigned int mac_len; - MD4_Init(&ctx); - for (i = 0; i < num_elem; i++) - MD4_Update(&ctx, addr[i], len[i]); - MD4_Final(mac, &ctx); + EVP_MD_CTX_init(&ctx); + 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(), num_elem, addr, len, mac); } @@ -67,144 +136,177 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) { - MD5_CTX ctx; - size_t i; +#ifdef OPENSSL_NO_RC4 + return -1; +#else /* OPENSSL_NO_RC4 */ + EVP_CIPHER_CTX ctx; + int outl; + int res = -1; + unsigned char skip_buf[16]; - MD5_Init(&ctx); - for (i = 0; i < num_elem; i++) - MD5_Update(&ctx, addr[i], len[i]); - MD5_Final(mac, &ctx); -} + 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; + } -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - SHA_CTX ctx; - size_t i; + if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len)) + res = 0; - SHA1_Init(&ctx); - for (i = 0; i < num_elem; i++) - SHA1_Update(&ctx, addr[i], len[i]); - SHA1_Final(mac, &ctx); +out: + EVP_CIPHER_CTX_cleanup(&ctx); + return res; +#endif /* OPENSSL_NO_RC4 */ } -#ifndef CONFIG_NO_FIPS186_2_PRF -static void sha1_transform(u8 *state, const u8 data[64]) +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - 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); + return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); } -int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - 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; + return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac); +} - 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); +#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(), num_elem, addr, len, mac); +} +#endif /* NO_SHA256_WRAPPER */ - /* 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 */ +static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) +{ + switch (keylen) { + case 16: + return EVP_aes_128_ecb(); + case 24: + return EVP_aes_192_ecb(); + case 32: + return EVP_aes_256_ecb(); } - return 0; + return NULL; } -#endif /* CONFIG_NO_FIPS186_2_PRF */ void * aes_encrypt_init(const u8 *key, size_t len) { - AES_KEY *ak; - ak = os_malloc(sizeof(*ak)); - if (ak == NULL) + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + type = aes_get_evp_cipher(len); + if (type == NULL) + return NULL; + + ctx = os_malloc(sizeof(*ctx)); + if (ctx == NULL) return NULL; - if (AES_set_encrypt_key(key, 8 * len, ak) < 0) { - os_free(ak); + EVP_CIPHER_CTX_init(ctx); + if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); return NULL; } - return ak; + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; } void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { - AES_encrypt(plain, crypt, ctx); + EVP_CIPHER_CTX *c = ctx; + int clen = 16; + if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } } void aes_encrypt_deinit(void *ctx) { - os_free(ctx); + EVP_CIPHER_CTX *c = ctx; + u8 buf[16]; + int len = sizeof(buf); + if (EVP_EncryptFinal_ex(c, buf, &len) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: " + "%s", ERR_error_string(ERR_get_error(), NULL)); + } + if (len != 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " + "in AES encrypt", len); + } + EVP_CIPHER_CTX_cleanup(c); + os_free(c); } void * aes_decrypt_init(const u8 *key, size_t len) { - AES_KEY *ak; - ak = os_malloc(sizeof(*ak)); - if (ak == NULL) + EVP_CIPHER_CTX *ctx; + const EVP_CIPHER *type; + + type = aes_get_evp_cipher(len); + if (type == NULL) return NULL; - if (AES_set_decrypt_key(key, 8 * len, ak) < 0) { - os_free(ak); + + ctx = os_malloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + EVP_CIPHER_CTX_init(ctx); + if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); return NULL; } - return ak; + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; } void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { - AES_decrypt(crypt, plain, ctx); + EVP_CIPHER_CTX *c = ctx; + int plen = 16; + if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } } void aes_decrypt_deinit(void *ctx) { + EVP_CIPHER_CTX *c = ctx; + u8 buf[16]; + int len = sizeof(buf); + if (EVP_DecryptFinal_ex(c, buf, &len) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: " + "%s", ERR_error_string(ERR_get_error(), NULL)); + } + if (len != 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d " + "in AES decrypt", len); + } + EVP_CIPHER_CTX_cleanup(c); os_free(ctx); } @@ -310,7 +412,7 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, 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, cipher, NULL, key, iv)) { + !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) { EVP_CIPHER_CTX_cleanup(&ctx->enc); os_free(ctx); return NULL; @@ -320,7 +422,7 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, 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, cipher, NULL, key, iv)) { + !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) { EVP_CIPHER_CTX_cleanup(&ctx->enc); EVP_CIPHER_CTX_cleanup(&ctx->dec); os_free(ctx); @@ -358,3 +460,776 @@ void crypto_cipher_deinit(struct crypto_cipher *ctx) 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; +} + + +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) +{ + DH *dh; + + 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; + + dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL); + if (dh->priv_key == NULL) + goto err; + + dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL); + if (dh->pub_key == NULL) + goto err; + + if (DH_generate_key(dh) != 1) + goto err; + + return dh; + +err: + 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); +} + + +struct crypto_hash { + HMAC_CTX ctx; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + const EVP_MD *md; + + switch (alg) { +#ifndef OPENSSL_NO_MD5 + case CRYPTO_HASH_ALG_HMAC_MD5: + md = EVP_md5(); + break; +#endif /* OPENSSL_NO_MD5 */ +#ifndef OPENSSL_NO_SHA + case CRYPTO_HASH_ALG_HMAC_SHA1: + md = EVP_sha1(); + break; +#endif /* OPENSSL_NO_SHA */ +#ifndef OPENSSL_NO_SHA256 +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + md = EVP_sha256(); + break; +#endif /* CONFIG_SHA256 */ +#endif /* OPENSSL_NO_SHA256 */ + default: + return NULL; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + HMAC_CTX_init(&ctx->ctx); + +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) { + os_free(ctx); + return NULL; + } +#endif /* openssl < 0.9.9 */ + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL) + return; + HMAC_Update(&ctx->ctx, data, len); +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + unsigned int mdlen; + int res; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) { + os_free(ctx); + return 0; + } + + mdlen = *len; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx->ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx->ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx->ctx); + os_free(ctx); + + if (res == 1) { + *len = mdlen; + return 0; + } + + return -1; +} + + +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ +#if OPENSSL_VERSION_NUMBER < 0x00908000 + if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), + (unsigned char *) ssid, + ssid_len, 4096, buflen, buf) != 1) + return -1; +#else /* openssl < 0.9.8 */ + if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid, + ssid_len, 4096, buflen, buf) != 1) + return -1; +#endif /* openssl < 0.9.8 */ + return 0; +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + HMAC_CTX ctx; + size_t i; + unsigned int mdlen; + int res; + + HMAC_CTX_init(&ctx); +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1) + return -1; +#endif /* openssl < 0.9.9 */ + + for (i = 0; i < num_elem; i++) + HMAC_Update(&ctx, addr[i], len[i]); + + mdlen = 20; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx); + + return res == 1 ? 0 : -1; +} + + +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); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + HMAC_CTX ctx; + size_t i; + unsigned int mdlen; + int res; + + HMAC_CTX_init(&ctx); +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL); +#else /* openssl < 0.9.9 */ + if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1) + return -1; +#endif /* openssl < 0.9.9 */ + + for (i = 0; i < num_elem; i++) + HMAC_Update(&ctx, addr[i], len[i]); + + mdlen = 32; +#if OPENSSL_VERSION_NUMBER < 0x00909000 + HMAC_Final(&ctx, mac, &mdlen); + res = 1; +#else /* openssl < 0.9.9 */ + res = HMAC_Final(&ctx, mac, &mdlen); +#endif /* openssl < 0.9.9 */ + HMAC_CTX_cleanup(&ctx); + + return res == 1 ? 0 : -1; +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +int crypto_get_random(void *buf, size_t len) +{ + if (RAND_bytes(buf, len) != 1) + return -1; + return 0; +} + + +#ifdef CONFIG_OPENSSL_CMAC +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + CMAC_CTX *ctx; + int ret = -1; + size_t outlen, i; + + ctx = CMAC_CTX_new(); + if (ctx == NULL) + return -1; + + if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL)) + goto fail; + for (i = 0; i < num_elem; i++) { + if (!CMAC_Update(ctx, addr[i], len[i])) + goto fail; + } + if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16) + goto fail; + + ret = 0; +fail: + CMAC_CTX_free(ctx); + return ret; +} + + +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); +} +#endif /* CONFIG_OPENSSL_CMAC */ + + +struct crypto_bignum * crypto_bignum_init(void) +{ + return (struct crypto_bignum *) BN_new(); +} + + +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len) +{ + BIGNUM *bn = BN_bin2bn(buf, len, NULL); + return (struct crypto_bignum *) bn; +} + + +void crypto_bignum_deinit(struct crypto_bignum *n, int clear) +{ + if (clear) + BN_clear_free((BIGNUM *) n); + else + BN_free((BIGNUM *) n); +} + + +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen) +{ + int num_bytes, offset; + + if (padlen > buflen) + return -1; + + num_bytes = BN_num_bytes((const BIGNUM *) a); + if ((size_t) num_bytes > buflen) + return -1; + if (padlen > (size_t) num_bytes) + offset = padlen - num_bytes; + else + offset = 0; + + os_memset(buf, 0, offset); + BN_bn2bin((const BIGNUM *) a, buf + offset); + + return num_bytes + offset; +} + + +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? + 0 : -1; +} + + +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + int res; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b, + bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_exptmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d) +{ + int res; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, + (const BIGNUM *) c, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *b) +{ + return BN_rshift((BIGNUM *) b, (const BIGNUM *) a, n) ? 0 : -1; +} + + +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + BIGNUM *res; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a, + (const BIGNUM *) b, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ? + 0 : -1; +} + + +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + int res; + + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a, + (const BIGNUM *) b, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *c, + struct crypto_bignum *d) +{ + int res; + + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + if (bnctx == NULL) + return -1; + res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b, + (const BIGNUM *) c, bnctx); + BN_CTX_free(bnctx); + + return res ? 0 : -1; +} + + +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b) +{ + return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b); +} + + +int crypto_bignum_bits(const struct crypto_bignum *a) +{ + return BN_num_bits((const BIGNUM *) a); +} + + +int crypto_bignum_is_zero(const struct crypto_bignum *a) +{ + return BN_is_zero((const BIGNUM *) a); +} + + +int crypto_bignum_is_one(const struct crypto_bignum *a) +{ + return BN_is_one((const BIGNUM *) a); +} + + +#ifdef CONFIG_ECC + +struct crypto_ec { + EC_GROUP *group; + BN_CTX *bnctx; + BIGNUM *prime; + BIGNUM *order; +}; + +struct crypto_ec * crypto_ec_init(int group) +{ + struct crypto_ec *e; + int nid; + + /* Map from IANA registry for IKE D-H groups to OpenSSL NID */ + switch (group) { + case 19: + nid = NID_X9_62_prime256v1; + break; + case 20: + nid = NID_secp384r1; + break; + case 21: + nid = NID_secp521r1; + break; + case 25: + nid = NID_X9_62_prime192v1; + break; + case 26: + nid = NID_secp224r1; + break; + default: + return NULL; + } + + e = os_zalloc(sizeof(*e)); + if (e == NULL) + return NULL; + + e->bnctx = BN_CTX_new(); + e->group = EC_GROUP_new_by_curve_name(nid); + e->prime = BN_new(); + e->order = BN_new(); + if (e->group == NULL || e->bnctx == NULL || e->prime == NULL || + e->order == NULL || + !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, e->bnctx) || + !EC_GROUP_get_order(e->group, e->order, e->bnctx)) { + crypto_ec_deinit(e); + e = NULL; + } + + return e; +} + + +void crypto_ec_deinit(struct crypto_ec *e) +{ + if (e == NULL) + return; + BN_free(e->order); + EC_GROUP_free(e->group); + BN_CTX_free(e->bnctx); + os_free(e); +} + + +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) +{ + if (e == NULL) + return NULL; + return (struct crypto_ec_point *) EC_POINT_new(e->group); +} + + +size_t crypto_ec_prime_len(struct crypto_ec *e) +{ + return BN_num_bytes(e->prime); +} + + +size_t crypto_ec_prime_len_bits(struct crypto_ec *e) +{ + return BN_num_bits(e->prime); +} + + +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) e->prime; +} + + +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) e->order; +} + + +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) +{ + if (clear) + EC_POINT_clear_free((EC_POINT *) p); + else + EC_POINT_free((EC_POINT *) p); +} + + +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y) +{ + BIGNUM *x_bn, *y_bn; + int ret = -1; + int len = BN_num_bytes(e->prime); + + x_bn = BN_new(); + y_bn = BN_new(); + + if (x_bn && y_bn && + EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point, + x_bn, y_bn, e->bnctx)) { + if (x) { + crypto_bignum_to_bin((struct crypto_bignum *) x_bn, + x, len, len); + } + if (y) { + crypto_bignum_to_bin((struct crypto_bignum *) y_bn, + y, len, len); + } + ret = 0; + } + + BN_free(x_bn); + BN_free(y_bn); + return ret; +} + + +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val) +{ + BIGNUM *x, *y; + EC_POINT *elem; + int len = BN_num_bytes(e->prime); + + x = BN_bin2bn(val, len, NULL); + y = BN_bin2bn(val + len, len, NULL); + elem = EC_POINT_new(e->group); + if (x == NULL || y == NULL || elem == NULL) { + BN_free(x); + BN_free(y); + EC_POINT_free(elem); + return NULL; + } + + if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y, + e->bnctx)) { + EC_POINT_free(elem); + elem = NULL; + } + + BN_free(x); + BN_free(y); + + return (struct crypto_ec_point *) elem; +} + + +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c) +{ + return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a, + (const EC_POINT *) b, e->bnctx) ? 0 : -1; +} + + +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res) +{ + return EC_POINT_mul(e->group, (EC_POINT *) res, NULL, + (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx) + ? 0 : -1; +} + + +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) +{ + return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1; +} + + +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit) +{ + if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p, + (const BIGNUM *) x, y_bit, + e->bnctx) || + !EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx)) + return -1; + return 0; +} + + +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p); +} + + +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, e->bnctx); +} + +#endif /* CONFIG_ECC */ diff --git a/contrib/hostapd/src/crypto/des.c b/contrib/hostapd/src/crypto/des-internal.c similarity index 95% rename from contrib/hostapd/src/crypto/des.c rename to contrib/hostapd/src/crypto/des-internal.c index 103e592461..dec39ef8c6 100644 --- a/contrib/hostapd/src/crypto/des.c +++ b/contrib/hostapd/src/crypto/des-internal.c @@ -2,25 +2,17 @@ * DES and 3DES-EDE ciphers * * Modifications to LibTomCrypt implementation: - * Copyright (c) 2006, Jouni Malinen + * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto.h" - - -#ifdef INTERNAL_DES +#include "des_i.h" /* * This implementation is based on a DES implementation included in @@ -432,10 +424,34 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -struct des3_key_s { - u32 ek[3][32]; - u32 dk[3][32]; -}; +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) { @@ -475,5 +491,3 @@ void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain) WPA_PUT_BE32(plain, work[0]); WPA_PUT_BE32(plain + 4, work[1]); } - -#endif /* INTERNAL_DES */ diff --git a/contrib/hostapd/src/crypto/des_i.h b/contrib/hostapd/src/crypto/des_i.h new file mode 100644 index 0000000000..c9563d2204 --- /dev/null +++ b/contrib/hostapd/src/crypto/des_i.h @@ -0,0 +1,25 @@ +/* + * DES and 3DES-EDE ciphers + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/dh_group5.c b/contrib/hostapd/src/crypto/dh_group5.c new file mode 100644 index 0000000000..ccdbfc8129 --- /dev/null +++ b/contrib/hostapd/src/crypto/dh_group5.c @@ -0,0 +1,40 @@ +/* + * Diffie-Hellman group 5 operations + * Copyright (c) 2009, 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 == NULL) + return NULL; + return (void *) 1; +} + + +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) +{ + 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/contrib/hostapd/src/crypto/dh_group5.h b/contrib/hostapd/src/crypto/dh_group5.h new file mode 100644 index 0000000000..abee8eaafb --- /dev/null +++ b/contrib/hostapd/src/crypto/dh_group5.h @@ -0,0 +1,18 @@ +/* + * Diffie-Hellman group 5 operations + * Copyright (c) 2009, 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DH_GROUP5_H +#define DH_GROUP5_H + +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ); +void * dh5_init_fixed(const struct wpabuf *priv, const 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/contrib/hostapd/src/crypto/dh_groups.c b/contrib/hostapd/src/crypto/dh_groups.c index 5f6008a6e2..58e94c393c 100644 --- a/contrib/hostapd/src/crypto/dh_groups.c +++ b/contrib/hostapd/src/crypto/dh_groups.c @@ -2,20 +2,15 @@ * Diffie-Hellman groups * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "crypto.h" +#include "random.h" #include "dh_groups.h" @@ -40,6 +35,20 @@ static const u8 dh_group1_prime[96] = { 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group1_order[96] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1D, 0x1B, 0x10, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 4306, B.2. Group 2 - 1024 Bit MODP * Generator: 2 @@ -64,6 +73,24 @@ static const u8 dh_group2_prime[128] = { 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group2_order[128] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; #endif /* ALL_DH_GROUPS */ @@ -98,6 +125,32 @@ static const u8 dh_group5_prime[192] = { 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group5_order[192] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x11, 0xB9, 0x93, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; #ifdef ALL_DH_GROUPS @@ -140,6 +193,40 @@ static const u8 dh_group14_prime[256] = { 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group14_order[256] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 4. Group 15 - 3072 Bit MODP * Generator: 2 @@ -196,6 +283,56 @@ static const u8 dh_group15_prime[384] = { 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group15_order[384] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x9D, 0x69, 0x65, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 5. Group 16 - 4096 Bit MODP * Generator: 2 @@ -268,6 +405,72 @@ static const u8 dh_group16_prime[512] = { 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group16_order[512] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, + 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, + 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, + 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, + 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, + 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, + 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, + 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, + 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, + 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, + 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, + 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, + 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, + 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, + 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, + 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, + 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 6. Group 17 - 6144 Bit MODP * Generator: 2 @@ -372,6 +575,104 @@ static const u8 dh_group17_prime[768] = { 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group17_order[768] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, + 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, + 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, + 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, + 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, + 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, + 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, + 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, + 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, + 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, + 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, + 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, + 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, + 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, + 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, + 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, + 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49, + 0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13, + 0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F, + 0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE, + 0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87, + 0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7, + 0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18, + 0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C, + 0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76, + 0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D, + 0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5, + 0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1, + 0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F, + 0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76, + 0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81, + 0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B, + 0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41, + 0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F, + 0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79, + 0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F, + 0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62, + 0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55, + 0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC, + 0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0, + 0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94, + 0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B, + 0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8, + 0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE, + 0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19, + 0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34, + 0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77, + 0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B, + 0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xE6, 0x20, 0x12, + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; /* RFC 3526, 7. Group 18 - 8192 Bit MODP * Generator: 2 @@ -508,29 +809,367 @@ static const u8 dh_group18_prime[1024] = { 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 dh_group18_order[1024] = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A, + 0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68, + 0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A, + 0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91, + 0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E, + 0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D, + 0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B, + 0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22, + 0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63, + 0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5, + 0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6, + 0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2, + 0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3, + 0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E, + 0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82, + 0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD, + 0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF, + 0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB, + 0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D, + 0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36, + 0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02, + 0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE, + 0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D, + 0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01, + 0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47, + 0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64, + 0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C, + 0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72, + 0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88, + 0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16, + 0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19, + 0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32, + 0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85, + 0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E, + 0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63, + 0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB, + 0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE, + 0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35, + 0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32, + 0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32, + 0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06, + 0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6, + 0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71, + 0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98, + 0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47, + 0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00, + 0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B, + 0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93, + 0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E, + 0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED, + 0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74, + 0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C, + 0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53, + 0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E, + 0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1, + 0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6, + 0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7, + 0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E, + 0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54, + 0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0, + 0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47, + 0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49, + 0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13, + 0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F, + 0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE, + 0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87, + 0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7, + 0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18, + 0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C, + 0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76, + 0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D, + 0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5, + 0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1, + 0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F, + 0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76, + 0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81, + 0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B, + 0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41, + 0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F, + 0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79, + 0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F, + 0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62, + 0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55, + 0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC, + 0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0, + 0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94, + 0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B, + 0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8, + 0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE, + 0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19, + 0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34, + 0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77, + 0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B, + 0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xDF, 0x08, 0xAC, + 0xBA, 0x51, 0xC9, 0x37, 0x89, 0x7F, 0x72, 0xF2, + 0x1C, 0x3B, 0xBE, 0x5B, 0x54, 0x99, 0x6F, 0xC6, + 0x6C, 0x5F, 0x62, 0x68, 0x39, 0xDC, 0x98, 0xDD, + 0x1D, 0xE4, 0x19, 0x5B, 0x46, 0xCE, 0xE9, 0x80, + 0x3A, 0x0F, 0xD3, 0xDF, 0xC5, 0x7E, 0x23, 0xF6, + 0x92, 0xBB, 0x7B, 0x49, 0xB5, 0xD2, 0x12, 0x33, + 0x1D, 0x55, 0xB1, 0xCE, 0x2D, 0x72, 0x7A, 0xB4, + 0x1A, 0x11, 0xDA, 0x3A, 0x15, 0xF8, 0xE4, 0xBC, + 0x11, 0xC7, 0x8B, 0x65, 0xF1, 0xCE, 0xB2, 0x96, + 0xF1, 0xFE, 0xDC, 0x5F, 0x7E, 0x42, 0x45, 0x6C, + 0x91, 0x11, 0x17, 0x02, 0x52, 0x01, 0xBE, 0x03, + 0x89, 0xF5, 0xAB, 0xD4, 0x0D, 0x11, 0xF8, 0x63, + 0x9A, 0x39, 0xFE, 0x32, 0x36, 0x75, 0x18, 0x35, + 0xA5, 0xE5, 0xE4, 0x43, 0x17, 0xC1, 0xC2, 0xEE, + 0xFD, 0x4E, 0xA5, 0xBF, 0xD1, 0x60, 0x43, 0xF4, + 0x3C, 0xB4, 0x19, 0x81, 0xF6, 0xAD, 0xEE, 0x9D, + 0x03, 0x15, 0x9E, 0x7A, 0xD9, 0xD1, 0x3C, 0x53, + 0x36, 0x95, 0x09, 0xFC, 0x1F, 0xA2, 0x7C, 0x16, + 0xEF, 0x98, 0x87, 0x70, 0x3A, 0x55, 0xB5, 0x1B, + 0x22, 0xCB, 0xF4, 0x4C, 0xD0, 0x12, 0xAE, 0xE0, + 0xB2, 0x79, 0x8E, 0x62, 0x84, 0x23, 0x42, 0x8E, + 0xFC, 0xD5, 0xA4, 0x0C, 0xAE, 0xF6, 0xBF, 0x50, + 0xD8, 0xEA, 0x88, 0x5E, 0xBF, 0x73, 0xA6, 0xB9, + 0xFD, 0x79, 0xB5, 0xE1, 0x8F, 0x67, 0xD1, 0x34, + 0x1A, 0xC8, 0x23, 0x7A, 0x75, 0xC3, 0xCF, 0xC9, + 0x20, 0x04, 0xA1, 0xC5, 0xA4, 0x0E, 0x36, 0x6B, + 0xC4, 0x4D, 0x00, 0x17, 0x6A, 0xF7, 0x1C, 0x15, + 0xE4, 0x8C, 0x86, 0xD3, 0x7E, 0x01, 0x37, 0x23, + 0xCA, 0xAC, 0x72, 0x23, 0xAB, 0x3B, 0xF4, 0xD5, + 0x4F, 0x18, 0x28, 0x71, 0x3B, 0x2B, 0x4A, 0x6F, + 0xE4, 0x0F, 0xAB, 0x74, 0x40, 0x5C, 0xB7, 0x38, + 0xB0, 0x64, 0xC0, 0x6E, 0xCC, 0x76, 0xE9, 0xEF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* + * RFC 5114, 2.1. + * Group 22 - 1024-bit MODP Group with 160-bit Prime Order Subgroup + */ +static const u8 dh_group22_generator[] = { + 0xA4, 0xD1, 0xCB, 0xD5, 0xC3, 0xFD, 0x34, 0x12, + 0x67, 0x65, 0xA4, 0x42, 0xEF, 0xB9, 0x99, 0x05, + 0xF8, 0x10, 0x4D, 0xD2, 0x58, 0xAC, 0x50, 0x7F, + 0xD6, 0x40, 0x6C, 0xFF, 0x14, 0x26, 0x6D, 0x31, + 0x26, 0x6F, 0xEA, 0x1E, 0x5C, 0x41, 0x56, 0x4B, + 0x77, 0x7E, 0x69, 0x0F, 0x55, 0x04, 0xF2, 0x13, + 0x16, 0x02, 0x17, 0xB4, 0xB0, 0x1B, 0x88, 0x6A, + 0x5E, 0x91, 0x54, 0x7F, 0x9E, 0x27, 0x49, 0xF4, + 0xD7, 0xFB, 0xD7, 0xD3, 0xB9, 0xA9, 0x2E, 0xE1, + 0x90, 0x9D, 0x0D, 0x22, 0x63, 0xF8, 0x0A, 0x76, + 0xA6, 0xA2, 0x4C, 0x08, 0x7A, 0x09, 0x1F, 0x53, + 0x1D, 0xBF, 0x0A, 0x01, 0x69, 0xB6, 0xA2, 0x8A, + 0xD6, 0x62, 0xA4, 0xD1, 0x8E, 0x73, 0xAF, 0xA3, + 0x2D, 0x77, 0x9D, 0x59, 0x18, 0xD0, 0x8B, 0xC8, + 0x85, 0x8F, 0x4D, 0xCE, 0xF9, 0x7C, 0x2A, 0x24, + 0x85, 0x5E, 0x6E, 0xEB, 0x22, 0xB3, 0xB2, 0xE5 +}; +static const u8 dh_group22_prime[] = { + 0xB1, 0x0B, 0x8F, 0x96, 0xA0, 0x80, 0xE0, 0x1D, + 0xDE, 0x92, 0xDE, 0x5E, 0xAE, 0x5D, 0x54, 0xEC, + 0x52, 0xC9, 0x9F, 0xBC, 0xFB, 0x06, 0xA3, 0xC6, + 0x9A, 0x6A, 0x9D, 0xCA, 0x52, 0xD2, 0x3B, 0x61, + 0x60, 0x73, 0xE2, 0x86, 0x75, 0xA2, 0x3D, 0x18, + 0x98, 0x38, 0xEF, 0x1E, 0x2E, 0xE6, 0x52, 0xC0, + 0x13, 0xEC, 0xB4, 0xAE, 0xA9, 0x06, 0x11, 0x23, + 0x24, 0x97, 0x5C, 0x3C, 0xD4, 0x9B, 0x83, 0xBF, + 0xAC, 0xCB, 0xDD, 0x7D, 0x90, 0xC4, 0xBD, 0x70, + 0x98, 0x48, 0x8E, 0x9C, 0x21, 0x9A, 0x73, 0x72, + 0x4E, 0xFF, 0xD6, 0xFA, 0xE5, 0x64, 0x47, 0x38, + 0xFA, 0xA3, 0x1A, 0x4F, 0xF5, 0x5B, 0xCC, 0xC0, + 0xA1, 0x51, 0xAF, 0x5F, 0x0D, 0xC8, 0xB4, 0xBD, + 0x45, 0xBF, 0x37, 0xDF, 0x36, 0x5C, 0x1A, 0x65, + 0xE6, 0x8C, 0xFD, 0xA7, 0x6D, 0x4D, 0xA7, 0x08, + 0xDF, 0x1F, 0xB2, 0xBC, 0x2E, 0x4A, 0x43, 0x71 +}; +static const u8 dh_group22_order[] = { + 0xF5, 0x18, 0xAA, 0x87, 0x81, 0xA8, 0xDF, 0x27, + 0x8A, 0xBA, 0x4E, 0x7D, 0x64, 0xB7, 0xCB, 0x9D, + 0x49, 0x46, 0x23, 0x53 +}; + +/* + * RFC 5114, 2.2. + * Group 23 - 2048-bit MODP Group with 224-bit Prime Order Subgroup + */ +static const u8 dh_group23_generator[] = { + 0xAC, 0x40, 0x32, 0xEF, 0x4F, 0x2D, 0x9A, 0xE3, + 0x9D, 0xF3, 0x0B, 0x5C, 0x8F, 0xFD, 0xAC, 0x50, + 0x6C, 0xDE, 0xBE, 0x7B, 0x89, 0x99, 0x8C, 0xAF, + 0x74, 0x86, 0x6A, 0x08, 0xCF, 0xE4, 0xFF, 0xE3, + 0xA6, 0x82, 0x4A, 0x4E, 0x10, 0xB9, 0xA6, 0xF0, + 0xDD, 0x92, 0x1F, 0x01, 0xA7, 0x0C, 0x4A, 0xFA, + 0xAB, 0x73, 0x9D, 0x77, 0x00, 0xC2, 0x9F, 0x52, + 0xC5, 0x7D, 0xB1, 0x7C, 0x62, 0x0A, 0x86, 0x52, + 0xBE, 0x5E, 0x90, 0x01, 0xA8, 0xD6, 0x6A, 0xD7, + 0xC1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4A, + 0xF4, 0xD0, 0x27, 0x27, 0x5A, 0xC1, 0x34, 0x8B, + 0xB8, 0xA7, 0x62, 0xD0, 0x52, 0x1B, 0xC9, 0x8A, + 0xE2, 0x47, 0x15, 0x04, 0x22, 0xEA, 0x1E, 0xD4, + 0x09, 0x93, 0x9D, 0x54, 0xDA, 0x74, 0x60, 0xCD, + 0xB5, 0xF6, 0xC6, 0xB2, 0x50, 0x71, 0x7C, 0xBE, + 0xF1, 0x80, 0xEB, 0x34, 0x11, 0x8E, 0x98, 0xD1, + 0x19, 0x52, 0x9A, 0x45, 0xD6, 0xF8, 0x34, 0x56, + 0x6E, 0x30, 0x25, 0xE3, 0x16, 0xA3, 0x30, 0xEF, + 0xBB, 0x77, 0xA8, 0x6F, 0x0C, 0x1A, 0xB1, 0x5B, + 0x05, 0x1A, 0xE3, 0xD4, 0x28, 0xC8, 0xF8, 0xAC, + 0xB7, 0x0A, 0x81, 0x37, 0x15, 0x0B, 0x8E, 0xEB, + 0x10, 0xE1, 0x83, 0xED, 0xD1, 0x99, 0x63, 0xDD, + 0xD9, 0xE2, 0x63, 0xE4, 0x77, 0x05, 0x89, 0xEF, + 0x6A, 0xA2, 0x1E, 0x7F, 0x5F, 0x2F, 0xF3, 0x81, + 0xB5, 0x39, 0xCC, 0xE3, 0x40, 0x9D, 0x13, 0xCD, + 0x56, 0x6A, 0xFB, 0xB4, 0x8D, 0x6C, 0x01, 0x91, + 0x81, 0xE1, 0xBC, 0xFE, 0x94, 0xB3, 0x02, 0x69, + 0xED, 0xFE, 0x72, 0xFE, 0x9B, 0x6A, 0xA4, 0xBD, + 0x7B, 0x5A, 0x0F, 0x1C, 0x71, 0xCF, 0xFF, 0x4C, + 0x19, 0xC4, 0x18, 0xE1, 0xF6, 0xEC, 0x01, 0x79, + 0x81, 0xBC, 0x08, 0x7F, 0x2A, 0x70, 0x65, 0xB3, + 0x84, 0xB8, 0x90, 0xD3, 0x19, 0x1F, 0x2B, 0xFA +}; +static const u8 dh_group23_prime[] = { + 0xAD, 0x10, 0x7E, 0x1E, 0x91, 0x23, 0xA9, 0xD0, + 0xD6, 0x60, 0xFA, 0xA7, 0x95, 0x59, 0xC5, 0x1F, + 0xA2, 0x0D, 0x64, 0xE5, 0x68, 0x3B, 0x9F, 0xD1, + 0xB5, 0x4B, 0x15, 0x97, 0xB6, 0x1D, 0x0A, 0x75, + 0xE6, 0xFA, 0x14, 0x1D, 0xF9, 0x5A, 0x56, 0xDB, + 0xAF, 0x9A, 0x3C, 0x40, 0x7B, 0xA1, 0xDF, 0x15, + 0xEB, 0x3D, 0x68, 0x8A, 0x30, 0x9C, 0x18, 0x0E, + 0x1D, 0xE6, 0xB8, 0x5A, 0x12, 0x74, 0xA0, 0xA6, + 0x6D, 0x3F, 0x81, 0x52, 0xAD, 0x6A, 0xC2, 0x12, + 0x90, 0x37, 0xC9, 0xED, 0xEF, 0xDA, 0x4D, 0xF8, + 0xD9, 0x1E, 0x8F, 0xEF, 0x55, 0xB7, 0x39, 0x4B, + 0x7A, 0xD5, 0xB7, 0xD0, 0xB6, 0xC1, 0x22, 0x07, + 0xC9, 0xF9, 0x8D, 0x11, 0xED, 0x34, 0xDB, 0xF6, + 0xC6, 0xBA, 0x0B, 0x2C, 0x8B, 0xBC, 0x27, 0xBE, + 0x6A, 0x00, 0xE0, 0xA0, 0xB9, 0xC4, 0x97, 0x08, + 0xB3, 0xBF, 0x8A, 0x31, 0x70, 0x91, 0x88, 0x36, + 0x81, 0x28, 0x61, 0x30, 0xBC, 0x89, 0x85, 0xDB, + 0x16, 0x02, 0xE7, 0x14, 0x41, 0x5D, 0x93, 0x30, + 0x27, 0x82, 0x73, 0xC7, 0xDE, 0x31, 0xEF, 0xDC, + 0x73, 0x10, 0xF7, 0x12, 0x1F, 0xD5, 0xA0, 0x74, + 0x15, 0x98, 0x7D, 0x9A, 0xDC, 0x0A, 0x48, 0x6D, + 0xCD, 0xF9, 0x3A, 0xCC, 0x44, 0x32, 0x83, 0x87, + 0x31, 0x5D, 0x75, 0xE1, 0x98, 0xC6, 0x41, 0xA4, + 0x80, 0xCD, 0x86, 0xA1, 0xB9, 0xE5, 0x87, 0xE8, + 0xBE, 0x60, 0xE6, 0x9C, 0xC9, 0x28, 0xB2, 0xB9, + 0xC5, 0x21, 0x72, 0xE4, 0x13, 0x04, 0x2E, 0x9B, + 0x23, 0xF1, 0x0B, 0x0E, 0x16, 0xE7, 0x97, 0x63, + 0xC9, 0xB5, 0x3D, 0xCF, 0x4B, 0xA8, 0x0A, 0x29, + 0xE3, 0xFB, 0x73, 0xC1, 0x6B, 0x8E, 0x75, 0xB9, + 0x7E, 0xF3, 0x63, 0xE2, 0xFF, 0xA3, 0x1F, 0x71, + 0xCF, 0x9D, 0xE5, 0x38, 0x4E, 0x71, 0xB8, 0x1C, + 0x0A, 0xC4, 0xDF, 0xFE, 0x0C, 0x10, 0xE6, 0x4F +}; +static const u8 dh_group23_order[] = { + 0x80, 0x1C, 0x0D, 0x34, 0xC5, 0x8D, 0x93, 0xFE, + 0x99, 0x71, 0x77, 0x10, 0x1F, 0x80, 0x53, 0x5A, + 0x47, 0x38, 0xCE, 0xBC, 0xBF, 0x38, 0x9A, 0x99, + 0xB3, 0x63, 0x71, 0xEB +}; + +/* + * RFC 5114, 2.3. + * Group 24 - 2048-bit MODP Group with 256-bit Prime Order Subgroup + */ +static const u8 dh_group24_generator[] = { + 0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B, + 0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48, + 0x4C, 0xA7, 0xB1, 0x8F, 0x21, 0xEF, 0x20, 0x54, + 0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25, + 0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F, + 0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55, + 0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1, + 0xBC, 0x37, 0x73, 0xBF, 0x7E, 0x8C, 0x6F, 0x62, + 0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18, + 0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65, + 0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2, + 0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B, + 0x77, 0x7D, 0xE6, 0x2A, 0xAA, 0xB8, 0xA8, 0x62, + 0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38, + 0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83, + 0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93, + 0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1, + 0xDF, 0xC9, 0x67, 0xC1, 0xFB, 0x3F, 0x2E, 0x55, + 0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80, + 0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A, + 0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14, + 0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9, + 0xB7, 0xD2, 0xBB, 0xD2, 0xDF, 0x01, 0x61, 0x99, + 0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15, + 0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37, + 0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52, + 0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6, + 0x18, 0x4B, 0x52, 0x3D, 0x1D, 0xB2, 0x46, 0xC3, + 0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8, + 0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51, + 0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82, + 0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59 +}; +static const u8 dh_group24_prime[] = { + 0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C, + 0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99, + 0x8C, 0xEE, 0xF6, 0x08, 0x66, 0x0D, 0xD0, 0xF2, + 0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00, + 0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4, + 0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30, + 0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA, + 0x3B, 0xF4, 0x29, 0x6D, 0x83, 0x0E, 0x9A, 0x7C, + 0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD, + 0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED, + 0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0, + 0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B, + 0x6C, 0x5B, 0xFC, 0x11, 0xD4, 0x5F, 0x90, 0x88, + 0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8, + 0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C, + 0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76, + 0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90, + 0x2D, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8A, 0x0E, + 0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB, + 0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E, + 0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9, + 0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25, + 0x1C, 0xCA, 0xCB, 0x83, 0xE6, 0xB4, 0x86, 0xF6, + 0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26, + 0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56, + 0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21, + 0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3, + 0x75, 0xF2, 0x63, 0x75, 0xD7, 0x01, 0x41, 0x03, + 0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12, + 0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F, + 0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA, + 0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97 +}; +static const u8 dh_group24_order[] = { + 0x8C, 0xF8, 0x36, 0x42, 0xA7, 0x09, 0xA0, 0x97, + 0xB4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9D, 0xA2, + 0x99, 0xB1, 0xA4, 0x7D, 0x1E, 0xB3, 0x75, 0x0B, + 0xA3, 0x08, 0xB0, 0xFE, 0x64, 0xF5, 0xFB, 0xD3 +}; #endif /* ALL_DH_GROUPS */ -#define DH_GROUP(id) \ +#define DH_GROUP(id,safe) \ { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ -dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) } +dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \ +dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe } static struct dh_group dh_groups[] = { - DH_GROUP(5), + DH_GROUP(5, 1), #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) + DH_GROUP(1, 1), + DH_GROUP(2, 1), + DH_GROUP(14, 1), + DH_GROUP(15, 1), + DH_GROUP(16, 1), + DH_GROUP(17, 1), + DH_GROUP(18, 1), + DH_GROUP(22, 0), + DH_GROUP(23, 0), + DH_GROUP(24, 0) #endif /* ALL_DH_GROUPS */ }; -#define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0])) +#define NUM_DH_GROUPS ARRAY_SIZE(dh_groups) const struct dh_group * dh_groups_get(int id) @@ -564,7 +1203,8 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) if (*priv == NULL) return NULL; - if (os_get_random(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) { + if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) + { wpabuf_free(*priv); *priv = NULL; return NULL; @@ -619,11 +1259,12 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, 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_put(shared, shared_len), &shared_len) < 0) { + 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/contrib/hostapd/src/crypto/dh_groups.h b/contrib/hostapd/src/crypto/dh_groups.h index 5c61539b70..d0e74b9206 100644 --- a/contrib/hostapd/src/crypto/dh_groups.h +++ b/contrib/hostapd/src/crypto/dh_groups.h @@ -2,14 +2,8 @@ * Diffie-Hellman groups * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DH_GROUPS_H @@ -21,6 +15,9 @@ struct dh_group { size_t generator_len; const u8 *prime; size_t prime_len; + const u8 *order; + size_t order_len; + unsigned int safe_prime:1; }; const struct dh_group * dh_groups_get(int id); diff --git a/contrib/hostapd/src/crypto/fips_prf_cryptoapi.c b/contrib/hostapd/src/crypto/fips_prf_cryptoapi.c new file mode 100644 index 0000000000..dca93a3d33 --- /dev/null +++ b/contrib/hostapd/src/crypto/fips_prf_cryptoapi.c @@ -0,0 +1,19 @@ +/* + * FIPS 186-2 PRF for Microsoft CryptoAPI + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/fips_prf_gnutls.c b/contrib/hostapd/src/crypto/fips_prf_gnutls.c new file mode 100644 index 0000000000..947e6f6414 --- /dev/null +++ b/contrib/hostapd/src/crypto/fips_prf_gnutls.c @@ -0,0 +1,20 @@ +/* + * FIPS 186-2 PRF for libgcrypt + * Copyright (c) 2004-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#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/contrib/hostapd/src/crypto/fips_prf_internal.c b/contrib/hostapd/src/crypto/fips_prf_internal.c new file mode 100644 index 0000000000..a4bf50a47f --- /dev/null +++ b/contrib/hostapd/src/crypto/fips_prf_internal.c @@ -0,0 +1,69 @@ +/* + * FIPS 186-2 PRF for internal crypto implementation + * Copyright (c) 2006-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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)) + os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); + else + seed_len = sizeof(xkey); + + /* FIPS 186-2 + change notice 1 */ + + os_memcpy(xkey, seed, 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/contrib/hostapd/src/crypto/fips_prf_nss.c b/contrib/hostapd/src/crypto/fips_prf_nss.c new file mode 100644 index 0000000000..2c962f4f13 --- /dev/null +++ b/contrib/hostapd/src/crypto/fips_prf_nss.c @@ -0,0 +1,19 @@ +/* + * FIPS 186-2 PRF for NSS + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#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/contrib/hostapd/src/crypto/fips_prf_openssl.c b/contrib/hostapd/src/crypto/fips_prf_openssl.c new file mode 100644 index 0000000000..d69eceabff --- /dev/null +++ b/contrib/hostapd/src/crypto/fips_prf_openssl.c @@ -0,0 +1,78 @@ +/* + * FIPS 186-2 PRF for libcrypto + * Copyright (c) 2004-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#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)) + os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); + else + seed_len = sizeof(xkey); + + /* FIPS 186-2 + change notice 1 */ + + os_memcpy(xkey, seed, 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/contrib/hostapd/src/crypto/md4.c b/contrib/hostapd/src/crypto/md4-internal.c similarity index 94% rename from contrib/hostapd/src/crypto/md4.c rename to contrib/hostapd/src/crypto/md4-internal.c index 41c84a3a7b..cd5e6ca8cc 100644 --- a/contrib/hostapd/src/crypto/md4.c +++ b/contrib/hostapd/src/crypto/md4-internal.c @@ -2,14 +2,8 @@ * MD4 hash implementation * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,9 +11,6 @@ #include "common.h" #include "crypto.h" - -#ifdef INTERNAL_MD4 - #define MD4_BLOCK_LENGTH 64 #define MD4_DIGEST_LENGTH 16 @@ -35,7 +26,7 @@ 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); -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD4_CTX ctx; size_t i; @@ -44,6 +35,7 @@ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) for (i = 0; i < num_elem; i++) MD4Update(&ctx, addr[i], len[i]); MD4Final(mac, &ctx); + return 0; } @@ -278,5 +270,3 @@ MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]) state[3] += d; } /* ===== end - public domain MD4 implementation ===== */ - -#endif /* INTERNAL_MD4 */ diff --git a/contrib/hostapd-0.4.9/md5.c b/contrib/hostapd/src/crypto/md5-internal.c similarity index 68% rename from contrib/hostapd-0.4.9/md5.c rename to contrib/hostapd/src/crypto/md5-internal.c index 82388e0867..f0a2a5d3a5 100644 --- a/contrib/hostapd-0.4.9/md5.c +++ b/contrib/hostapd/src/crypto/md5-internal.c @@ -1,129 +1,22 @@ /* * MD5 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include -#include -#include +#include "includes.h" #include "common.h" #include "md5.h" +#include "md5_i.h" #include "crypto.h" -/** - * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104) - * @key: Key for HMAC operations - * @key_len: Length of the key in bytes - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash (16 bytes) - */ -void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) -{ - u8 k_pad[64]; /* padding - key XORd with ipad/opad */ - u8 tk[16]; - int i; - const u8 *_addr[6]; - size_t _len[6]; - - if (num_elem > 5) { - /* - * Fixed limit on the number of fragments to avoid having to - * allocate memory (which could fail). - */ - return; - } - - /* if key is longer than 64 bytes reset it to key = MD5(key) */ - if (key_len > 64) { - md5_vector(1, &key, &key_len, tk); - key = tk; - key_len = 16; - } - - /* the HMAC_MD5 transform looks like: - * - * MD5(K XOR opad, MD5(K XOR ipad, text)) - * - * where K is an n byte key - * ipad is the byte 0x36 repeated 64 times - * opad is the byte 0x5c repeated 64 times - * and text is the data being protected */ - - /* start out by storing key in ipad */ - memset(k_pad, 0, sizeof(k_pad)); - memcpy(k_pad, key, key_len); - - /* XOR key with ipad values */ - for (i = 0; i < 64; i++) - k_pad[i] ^= 0x36; - - /* perform inner MD5 */ - _addr[0] = k_pad; - _len[0] = 64; - for (i = 0; i < num_elem; i++) { - _addr[i + 1] = addr[i]; - _len[i + 1] = len[i]; - } - md5_vector(1 + num_elem, _addr, _len, mac); - - memset(k_pad, 0, sizeof(k_pad)); - memcpy(k_pad, key, key_len); - /* XOR key with opad values */ - for (i = 0; i < 64; i++) - k_pad[i] ^= 0x5c; - - /* perform outer MD5 */ - _addr[0] = k_pad; - _len[0] = 64; - _addr[1] = mac; - _len[1] = MD5_MAC_LEN; - md5_vector(2, _addr, _len, mac); -} - - -/** - * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104) - * @key: Key for HMAC operations - * @key_len: Length of the key in bytes - * @data: Pointers to the data area - * @data_len: Length of the data area - * @mac: Buffer for the hash (16 bytes) - */ -void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac) -{ - hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); -} - - -#ifndef EAP_TLS_FUNCS - -struct MD5Context { - u32 buf[4]; - u32 bits[2]; - u8 in[64]; -}; - -static void MD5Init(struct MD5Context *context); -static void MD5Update(struct MD5Context *context, unsigned char const *buf, - unsigned len); -static void MD5Final(unsigned char digest[16], struct MD5Context *context); static void MD5Transform(u32 buf[4], u32 const in[16]); + typedef struct MD5Context MD5_CTX; @@ -133,16 +26,18 @@ typedef struct MD5Context MD5_CTX; * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure */ -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD5_CTX ctx; - int i; + size_t i; MD5Init(&ctx); for (i = 0; i < num_elem; i++) MD5Update(&ctx, addr[i], len[i]); MD5Final(mac, &ctx); + return 0; } @@ -186,7 +81,7 @@ static void byteReverse(unsigned char *buf, unsigned longs) * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ -static void MD5Init(struct MD5Context *ctx) +void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; @@ -201,8 +96,7 @@ static void MD5Init(struct MD5Context *ctx) * Update context to reflect the concatenation of another buffer full * of bytes. */ -static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, - unsigned len) +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) { u32 t; @@ -222,10 +116,10 @@ static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, t = 64 - t; if (len < t) { - memcpy(p, buf, len); + os_memcpy(p, buf, len); return; } - memcpy(p, buf, t); + os_memcpy(p, buf, t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (u32 *) ctx->in); buf += t; @@ -234,7 +128,7 @@ static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, /* Process data in 64-byte chunks */ while (len >= 64) { - memcpy(ctx->in, buf, 64); + os_memcpy(ctx->in, buf, 64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (u32 *) ctx->in); buf += 64; @@ -243,14 +137,14 @@ static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, /* Handle any remaining bytes of data. */ - memcpy(ctx->in, buf, len); + 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) */ -static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { unsigned count; unsigned char *p; @@ -269,26 +163,26 @@ static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); + os_memset(p, 0, count); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (u32 *) ctx->in); /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); + os_memset(ctx->in, 0, 56); } else { /* Pad block to 56 bytes */ - memset(p, 0, count - 8); + 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]; + ((u32 *) aliasing_hide_typecast(ctx->in, u32))[14] = ctx->bits[0]; + ((u32 *) aliasing_hide_typecast(ctx->in, u32))[15] = ctx->bits[1]; MD5Transform(ctx->buf, (u32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + 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 */ @@ -391,5 +285,3 @@ static void MD5Transform(u32 buf[4], u32 const in[16]) buf[3] += d; } /* ===== end - public domain MD5 implementation ===== */ - -#endif /* !EAP_TLS_FUNCS */ diff --git a/contrib/hostapd/src/crypto/md5.c b/contrib/hostapd/src/crypto/md5.c index a7db7aa9a7..db2b8cc316 100644 --- a/contrib/hostapd/src/crypto/md5.c +++ b/contrib/hostapd/src/crypto/md5.c @@ -2,14 +2,8 @@ * MD5 hash implementation and interface functions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -27,9 +21,10 @@ * @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 */ -void 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_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]; @@ -41,12 +36,13 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ - return; + return -1; } /* if key is longer than 64 bytes reset it to key = MD5(key) */ if (key_len > 64) { - md5_vector(1, &key, &key_len, tk); + if (md5_vector(1, &key, &key_len, tk)) + return -1; key = tk; key_len = 16; } @@ -75,7 +71,8 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } - md5_vector(1 + num_elem, _addr, _len, mac); + 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); @@ -88,7 +85,7 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, _len[0] = 64; _addr[1] = mac; _len[1] = MD5_MAC_LEN; - md5_vector(2, _addr, _len, mac); + return md5_vector(2, _addr, _len, mac); } @@ -99,296 +96,10 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, * @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 */ -void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { - hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); -} - - -#ifdef INTERNAL_MD5 - -struct MD5Context { - u32 buf[4]; - u32 bits[2]; - u8 in[64]; -}; - -#ifndef CONFIG_CRYPTO_INTERNAL -static void MD5Init(struct MD5Context *context); -static void MD5Update(struct MD5Context *context, unsigned char const *buf, - unsigned len); -static void MD5Final(unsigned char digest[16], struct MD5Context *context); -#endif /* CONFIG_CRYPTO_INTERNAL */ -static void MD5Transform(u32 buf[4], u32 const in[16]); - - -typedef struct MD5Context MD5_CTX; - - -/** - * md5_vector - MD5 hash for data vector - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash - */ -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) -{ - MD5_CTX ctx; - size_t i; - - MD5Init(&ctx); - for (i = 0; i < num_elem; i++) - MD5Update(&ctx, addr[i], len[i]); - MD5Final(mac, &ctx); + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); } - - -/* ===== 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<>(32-s), w += x ) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -static void MD5Transform(u32 buf[4], u32 const in[16]) -{ - register u32 a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} -/* ===== end - public domain MD5 implementation ===== */ - -#endif /* INTERNAL_MD5 */ diff --git a/contrib/hostapd/src/crypto/md5.h b/contrib/hostapd/src/crypto/md5.h index e82f3969ed..33f8426c57 100644 --- a/contrib/hostapd/src/crypto/md5.h +++ b/contrib/hostapd/src/crypto/md5.h @@ -1,15 +1,9 @@ /* * MD5 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MD5_H @@ -17,18 +11,9 @@ #define MD5_MAC_LEN 16 -void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac); -void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac); - -#ifdef CONFIG_CRYPTO_INTERNAL -struct MD5Context; - -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 /* CONFIG_CRYPTO_INTERNAL */ +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); #endif /* MD5_H */ diff --git a/contrib/hostapd/src/crypto/md5_i.h b/contrib/hostapd/src/crypto/md5_i.h new file mode 100644 index 0000000000..7dfc10037d --- /dev/null +++ b/contrib/hostapd/src/crypto/md5_i.h @@ -0,0 +1,23 @@ +/* + * MD5 internal definitions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/milenage.c b/contrib/hostapd/src/crypto/milenage.c new file mode 100644 index 0000000000..a7f9c6a286 --- /dev/null +++ b/contrib/hostapd/src/crypto/milenage.c @@ -0,0 +1,323 @@ +/* + * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) + * Copyright (c) 2006-2007 + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/hlr_auc_gw/milenage.h b/contrib/hostapd/src/crypto/milenage.h similarity index 67% rename from contrib/hostapd/src/hlr_auc_gw/milenage.h rename to contrib/hostapd/src/crypto/milenage.h index b35603ca86..62137d95ce 100644 --- a/contrib/hostapd/src/hlr_auc_gw/milenage.h +++ b/contrib/hostapd/src/crypto/milenage.h @@ -2,14 +2,8 @@ * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) * Copyright (c) 2006-2007 * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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 software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MILENAGE_H @@ -25,5 +19,9 @@ int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, 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/contrib/hostapd/src/crypto/ms_funcs.c b/contrib/hostapd/src/crypto/ms_funcs.c index 7e2f0fa377..b2bbab2b5c 100644 --- a/contrib/hostapd/src/crypto/ms_funcs.c +++ b/contrib/hostapd/src/crypto/ms_funcs.c @@ -1,15 +1,9 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,7 +12,60 @@ #include "sha1.h" #include "ms_funcs.h" #include "crypto.h" -#include "rc4.h" + +/** + * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding + * @utf8_string: UTF-8 string (IN) + * @utf8_string_len: Length of utf8_string (IN) + * @ucs2_buffer: UCS-2 buffer (OUT) + * @ucs2_buffer_size: Length of UCS-2 buffer (IN) + * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string + * Returns: 0 on success, -1 on failure + */ +static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len, + u8 *ucs2_buffer, size_t ucs2_buffer_size, + size_t *ucs2_string_size) +{ + size_t i, j; + + for (i = 0, j = 0; i < utf8_string_len; i++) { + u8 c = utf8_string[i]; + if (j >= ucs2_buffer_size) { + /* input too long */ + return -1; + } + if (c <= 0x7F) { + WPA_PUT_LE16(ucs2_buffer + j, c); + j += 2; + } else if (i == utf8_string_len - 1 || + j >= ucs2_buffer_size - 1) { + /* incomplete surrogate */ + return -1; + } else { + u8 c2 = utf8_string[++i]; + if ((c & 0xE0) == 0xC0) { + /* two-byte encoding */ + WPA_PUT_LE16(ucs2_buffer + j, + ((c & 0x1F) << 6) | (c2 & 0x3F)); + j += 2; + } else if (i == utf8_string_len || + j >= ucs2_buffer_size - 1) { + /* incomplete surrogate */ + return -1; + } else { + /* three-byte encoding */ + u8 c3 = utf8_string[++i]; + WPA_PUT_LE16(ucs2_buffer + j, + ((c & 0xF) << 12) | + ((c2 & 0x3F) << 6) | (c3 & 0x3F)); + } + } + } + + if (ucs2_string_size) + *ucs2_string_size = j / 2; + return 0; +} /** @@ -28,10 +75,11 @@ * @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 void challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, - const u8 *username, size_t username_len, - u8 *challenge) +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]; @@ -44,35 +92,33 @@ static void challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, addr[2] = username; len[2] = username_len; - sha1_vector(3, addr, len, hash); + 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: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (OUT) + * Returns: 0 on success, -1 on failure */ -void nt_password_hash(const u8 *password, size_t password_len, +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; + size_t len, max_len; - /* Convert password into unicode */ - for (i = 0; i < password_len; i++) { - buf[2 * i] = password[i]; - buf[2 * i + 1] = 0; - } + max_len = sizeof(buf); + if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0) + return -1; - len = password_len * 2; + len *= 2; pos = buf; - md4_vector(1, (const u8 **) &pos, &len, password_hash); + return md4_vector(1, (const u8 **) &pos, &len, password_hash); } @@ -80,11 +126,12 @@ void nt_password_hash(const u8 *password, size_t password_len, * 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 */ -void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) +int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) { size_t len = 16; - md4_vector(1, &password_hash, &len, password_hash_hash); + return md4_vector(1, &password_hash, &len, password_hash_hash); } @@ -113,22 +160,26 @@ void challenge_response(const u8 *challenge, const u8 *password_hash, * @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: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure */ -void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, - const u8 *username, size_t username_len, - const u8 *password, size_t password_len, - u8 *response) +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); - nt_password_hash(password, password_len, password_hash); + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; + if (nt_password_hash(password, password_len, password_hash)) + return -1; challenge_response(challenge, password_hash, response); + return 0; } @@ -140,18 +191,22 @@ void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, * @username_len: Length of username * @password_hash: 16-octet PasswordHash (IN) * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure */ -void 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_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]; - challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge); + if (challenge_hash(peer_challenge, auth_challenge, + username, username_len, + challenge)) + return -1; challenge_response(challenge, password_hash, response); + return 0; } @@ -165,8 +220,9 @@ void generate_nt_response_pwhash(const u8 *auth_challenge, * @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 */ -void generate_authenticator_response_pwhash( +int generate_authenticator_response_pwhash( const u8 *password_hash, const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, @@ -200,18 +256,21 @@ void generate_authenticator_response_pwhash( addr2[1] = challenge; addr2[2] = magic2; - hash_nt_password_hash(password_hash, password_hash_hash); - sha1_vector(3, addr1, len1, response); + 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); - sha1_vector(3, addr2, len2, response); + if (challenge_hash(peer_challenge, auth_challenge, username, + username_len, challenge)) + return -1; + 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: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @nt_response: 24-octet NT-Response (IN) * @peer_challenge: 16-octet PeerChallenge (IN) @@ -220,35 +279,39 @@ void generate_authenticator_response_pwhash( * @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 */ -void generate_authenticator_response(const u8 *password, size_t password_len, - const u8 *peer_challenge, - const u8 *auth_challenge, - const u8 *username, size_t username_len, - const u8 *nt_response, u8 *response) +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]; - nt_password_hash(password, password_len, password_hash); - generate_authenticator_response_pwhash(password_hash, - peer_challenge, auth_challenge, - username, username_len, - nt_response, response); + 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: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure */ -void nt_challenge_response(const u8 *challenge, const u8 *password, - size_t password_len, u8 *response) +int nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response) { u8 password_hash[16]; - nt_password_hash(password, password_len, password_hash); + if (nt_password_hash(password, password_len, password_hash)) + return -1; challenge_response(challenge, password_hash, response); + return 0; } @@ -257,9 +320,10 @@ void nt_challenge_response(const u8 *challenge, const u8 *password, * @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 */ -void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, - u8 *master_key) +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, @@ -274,8 +338,10 @@ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, addr[1] = nt_response; addr[2] = magic1; - sha1_vector(3, addr, len, hash); + if (sha1_vector(3, addr, len, hash)) + return -1; os_memcpy(master_key, hash, 16); + return 0; } @@ -286,10 +352,11 @@ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, * @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 */ -void get_asymetric_start_key(const u8 *master_key, u8 *session_key, - size_t session_key_len, int is_send, - int is_server) +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, @@ -339,11 +406,13 @@ void get_asymetric_start_key(const u8 *master_key, u8 *session_key, } addr[3] = shs_pad2; - sha1_vector(4, addr, len, digest); + 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; } @@ -351,7 +420,7 @@ void get_asymetric_start_key(const u8 *master_key, u8 *session_key, /** * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10 - * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (IN) * @pw_block: 516-byte PwBlock (OUT) @@ -361,18 +430,23 @@ 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; + size_t ucs2_len, offset; u8 *pos; - if (password_len > 256) + os_memset(pw_block, 0, PWBLOCK_LEN); + + if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0) return -1; - os_memset(pw_block, 0, PWBLOCK_LEN); - offset = (256 - password_len) * 2; - if (os_get_random(pw_block, offset) < 0) + if (ucs2_len > 256) return -1; - for (i = 0; i < password_len; i++) - pw_block[offset + i * 2] = password[i]; + + offset = (256 - ucs2_len) * 2; + if (offset != 0) { + os_memmove(pw_block + offset, pw_block, ucs2_len * 2); + if (os_get_random(pw_block, offset) < 0) + return -1; + } /* * PasswordLength is 4 octets, but since the maximum password length is * 256, only first two (in little endian byte order) can be non-zero. @@ -386,9 +460,9 @@ int encrypt_pw_block_with_password_hash( /** * 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: 0-to-256-unicode-char NewPassword (IN; UTF-8) * @new_password_len: Length of new_password - * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) * @old_password_len: Length of old_password * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) * Returns: 0 on success, -1 on failure @@ -400,7 +474,8 @@ int new_password_encrypted_with_old_nt_password_hash( { u8 password_hash[16]; - nt_password_hash(old_password, old_password_len, password_hash); + 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)) @@ -425,22 +500,27 @@ void nt_password_hash_encrypted_with_block(const u8 *password_hash, /** * 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: 0-to-256-unicode-char NewPassword (IN; UTF-8) * @new_password_len: Length of new_password - * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8) * @old_password_len: Length of old_password * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT) + * Returns: 0 on success, -1 on failure */ -void old_nt_password_hash_encrypted_with_new_nt_password_hash( +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]; - nt_password_hash(old_password, old_password_len, old_password_hash); - nt_password_hash(new_password, new_password_len, new_password_hash); + 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/contrib/hostapd/src/crypto/ms_funcs.h b/contrib/hostapd/src/crypto/ms_funcs.h index 6205bf68d3..bd9bfee95b 100644 --- a/contrib/hostapd/src/crypto/ms_funcs.h +++ b/contrib/hostapd/src/crypto/ms_funcs.h @@ -1,52 +1,46 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MS_FUNCS_H #define MS_FUNCS_H -void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, - const u8 *username, size_t username_len, - const u8 *password, size_t password_len, - u8 *response); -void generate_nt_response_pwhash(const u8 *auth_challenge, - const u8 *peer_challenge, - const u8 *username, size_t username_len, - const u8 *password_hash, - u8 *response); -void generate_authenticator_response(const u8 *password, size_t password_len, - const u8 *peer_challenge, - const u8 *auth_challenge, - const u8 *username, size_t username_len, - const u8 *nt_response, u8 *response); -void generate_authenticator_response_pwhash( +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); -void nt_challenge_response(const u8 *challenge, const u8 *password, - size_t password_len, 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); -void nt_password_hash(const u8 *password, size_t password_len, - u8 *password_hash); -void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); -void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, - u8 *master_key); -void get_asymetric_start_key(const u8 *master_key, u8 *session_key, - size_t session_key_len, int is_send, - int is_server); +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); @@ -56,7 +50,7 @@ int __must_check new_password_encrypted_with_old_nt_password_hash( u8 *encrypted_pw_block); void nt_password_hash_encrypted_with_block(const u8 *password_hash, const u8 *block, u8 *cypher); -void old_nt_password_hash_encrypted_with_new_nt_password_hash( +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); diff --git a/contrib/hostapd/src/crypto/random.c b/contrib/hostapd/src/crypto/random.c new file mode 100644 index 0000000000..053740e9bf --- /dev/null +++ b/contrib/hostapd/src/crypto/random.c @@ -0,0 +1,446 @@ +/* + * Random number generator + * Copyright (c) 2010-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This random number generator is used to provide additional entropy to the + * one provided by the operating system (os_get_random()) for session key + * generation. The os_get_random() output is expected to be secure and the + * implementation here is expected to provide only limited protection against + * cases where os_get_random() cannot provide strong randomness. This + * implementation shall not be assumed to be secure as the sole source of + * randomness. The random_get_bytes() function mixes in randomness from + * os_get_random() and as such, calls to os_get_random() can be replaced with + * calls to random_get_bytes() without reducing security. + * + * The design here follows partially the design used in the Linux + * drivers/char/random.c, but the implementation here is simpler and not as + * strong. This is a compromise to reduce duplicated CPU effort and to avoid + * extra code/memory size. As pointed out above, os_get_random() needs to be + * guaranteed to be secure for any of the security assumptions to hold. + */ + +#include "utils/includes.h" +#ifdef __linux__ +#include +#endif /* __linux__ */ + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/crypto.h" +#include "sha1.h" +#include "random.h" + +#define POOL_WORDS 32 +#define POOL_WORDS_MASK (POOL_WORDS - 1) +#define POOL_TAP1 26 +#define POOL_TAP2 20 +#define POOL_TAP3 14 +#define POOL_TAP4 7 +#define POOL_TAP5 1 +#define EXTRACT_LEN 16 +#define MIN_READY_MARK 2 + +static u32 pool[POOL_WORDS]; +static unsigned int input_rotate = 0; +static unsigned int pool_pos = 0; +static u8 dummy_key[20]; +#ifdef __linux__ +static size_t dummy_key_avail = 0; +static int random_fd = -1; +#endif /* __linux__ */ +static unsigned int own_pool_ready = 0; +#define RANDOM_ENTROPY_SIZE 20 +static char *random_entropy_file = NULL; +static int random_entropy_file_read = 0; + +#define MIN_COLLECT_ENTROPY 1000 +static unsigned int entropy = 0; +static unsigned int total_collected = 0; + + +static void random_write_entropy(void); + + +static u32 __ROL32(u32 x, u32 y) +{ + return (x << (y & 31)) | (x >> (32 - (y & 31))); +} + + +static void random_mix_pool(const void *buf, size_t len) +{ + static const u32 twist[8] = { + 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158, + 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 + }; + const u8 *pos = buf; + u32 w; + + wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len); + + while (len--) { + w = __ROL32(*pos++, input_rotate & 31); + input_rotate += pool_pos ? 7 : 14; + pool_pos = (pool_pos - 1) & POOL_WORDS_MASK; + w ^= pool[pool_pos]; + w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK]; + w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK]; + pool[pool_pos] = (w >> 3) ^ twist[w & 7]; + } +} + + +static void random_extract(u8 *out) +{ + unsigned int i; + u8 hash[SHA1_MAC_LEN]; + u32 *hash_ptr; + u32 buf[POOL_WORDS / 2]; + + /* First, add hash back to pool to make backtracking more difficult. */ + hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool, + sizeof(pool), hash); + random_mix_pool(hash, sizeof(hash)); + /* Hash half the pool to extra data */ + for (i = 0; i < POOL_WORDS / 2; i++) + buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK]; + hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf, + sizeof(buf), hash); + /* + * Fold the hash to further reduce any potential output pattern. + * Though, compromise this to reduce CPU use for the most common output + * length (32) and return 16 bytes from instead of only half. + */ + hash_ptr = (u32 *) hash; + hash_ptr[0] ^= hash_ptr[4]; + os_memcpy(out, hash, EXTRACT_LEN); +} + + +void random_add_randomness(const void *buf, size_t len) +{ + struct os_time t; + static unsigned int count = 0; + + count++; + if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) { + /* + * No need to add more entropy at this point, so save CPU and + * skip the update. + */ + return; + } + wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u", + count, entropy); + + os_get_time(&t); + wpa_hexdump_key(MSG_EXCESSIVE, "random pool", + (const u8 *) pool, sizeof(pool)); + random_mix_pool(&t, sizeof(t)); + random_mix_pool(buf, len); + wpa_hexdump_key(MSG_EXCESSIVE, "random pool", + (const u8 *) pool, sizeof(pool)); + entropy++; + total_collected++; +} + + +int random_get_bytes(void *buf, size_t len) +{ + int ret; + u8 *bytes = buf; + size_t left; + + wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u", + (unsigned int) len, entropy); + + /* Start with assumed strong randomness from OS */ + ret = os_get_random(buf, len); + wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random", + buf, len); + + /* Mix in additional entropy extracted from the internal pool */ + left = len; + while (left) { + size_t siz, i; + u8 tmp[EXTRACT_LEN]; + random_extract(tmp); + wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool", + tmp, sizeof(tmp)); + siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; + for (i = 0; i < siz; i++) + *bytes++ ^= tmp[i]; + left -= siz; + } + +#ifdef CONFIG_FIPS + /* Mix in additional entropy from the crypto module */ + left = len; + while (left) { + size_t siz, i; + u8 tmp[EXTRACT_LEN]; + if (crypto_get_random(tmp, sizeof(tmp)) < 0) { + wpa_printf(MSG_ERROR, "random: No entropy available " + "for generating strong random bytes"); + return -1; + } + wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module", + tmp, sizeof(tmp)); + siz = left > EXTRACT_LEN ? EXTRACT_LEN : left; + for (i = 0; i < siz; i++) + *bytes++ ^= tmp[i]; + left -= siz; + } +#endif /* CONFIG_FIPS */ + + wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len); + + if (entropy < len) + entropy = 0; + else + entropy -= len; + + return ret; +} + + +int random_pool_ready(void) +{ +#ifdef __linux__ + int fd; + ssize_t res; + + /* + * Make sure that there is reasonable entropy available before allowing + * some key derivation operations to proceed. + */ + + if (dummy_key_avail == sizeof(dummy_key)) + return 1; /* Already initialized - good to continue */ + + /* + * Try to fetch some more data from the kernel high quality + * /dev/random. There may not be enough data available at this point, + * so use non-blocking read to avoid blocking the application + * completely. + */ + fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (fd < 0) { +#ifndef CONFIG_NO_STDOUT_DEBUG + int error = errno; + perror("open(/dev/random)"); + wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", + strerror(error)); +#endif /* CONFIG_NO_STDOUT_DEBUG */ + return -1; + } + + res = read(fd, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " + "%s", strerror(errno)); + res = 0; + } + wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from " + "/dev/random", (unsigned) res, + (unsigned) (sizeof(dummy_key) - dummy_key_avail)); + dummy_key_avail += res; + close(fd); + + if (dummy_key_avail == sizeof(dummy_key)) { + if (own_pool_ready < MIN_READY_MARK) + own_pool_ready = MIN_READY_MARK; + random_write_entropy(); + return 1; + } + + wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong " + "random data available from /dev/random", + (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key)); + + if (own_pool_ready >= MIN_READY_MARK || + total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) { + wpa_printf(MSG_INFO, "random: Allow operation to proceed " + "based on internal entropy"); + return 1; + } + + wpa_printf(MSG_INFO, "random: Not enough entropy pool available for " + "secure operations"); + return 0; +#else /* __linux__ */ + /* TODO: could do similar checks on non-Linux platforms */ + return 1; +#endif /* __linux__ */ +} + + +void random_mark_pool_ready(void) +{ + own_pool_ready++; + wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be " + "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK); + random_write_entropy(); +} + + +#ifdef __linux__ + +static void random_close_fd(void) +{ + if (random_fd >= 0) { + eloop_unregister_read_sock(random_fd); + close(random_fd); + random_fd = -1; + } +} + + +static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx) +{ + ssize_t res; + + if (dummy_key_avail == sizeof(dummy_key)) { + random_close_fd(); + return; + } + + res = read(sock, dummy_key + dummy_key_avail, + sizeof(dummy_key) - dummy_key_avail); + if (res < 0) { + wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: " + "%s", strerror(errno)); + return; + } + + wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random", + (unsigned) res, + (unsigned) (sizeof(dummy_key) - dummy_key_avail)); + dummy_key_avail += res; + + if (dummy_key_avail == sizeof(dummy_key)) { + random_close_fd(); + if (own_pool_ready < MIN_READY_MARK) + own_pool_ready = MIN_READY_MARK; + random_write_entropy(); + } +} + +#endif /* __linux__ */ + + +static void random_read_entropy(void) +{ + char *buf; + size_t len; + + if (!random_entropy_file) + return; + + buf = os_readfile(random_entropy_file, &len); + if (buf == NULL) + return; /* entropy file not yet available */ + + if (len != 1 + RANDOM_ENTROPY_SIZE) { + wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s", + random_entropy_file); + os_free(buf); + return; + } + + own_pool_ready = (u8) buf[0]; + random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE); + random_entropy_file_read = 1; + os_free(buf); + wpa_printf(MSG_DEBUG, "random: Added entropy from %s " + "(own_pool_ready=%u)", + random_entropy_file, own_pool_ready); +} + + +static void random_write_entropy(void) +{ + char buf[RANDOM_ENTROPY_SIZE]; + FILE *f; + u8 opr; + int fail = 0; + + if (!random_entropy_file) + return; + + if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0) + return; + + f = fopen(random_entropy_file, "wb"); + if (f == NULL) { + wpa_printf(MSG_ERROR, "random: Could not open entropy file %s " + "for writing", random_entropy_file); + return; + } + + opr = own_pool_ready > 0xff ? 0xff : own_pool_ready; + if (fwrite(&opr, 1, 1, f) != 1 || + fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1) + fail = 1; + fclose(f); + if (fail) { + wpa_printf(MSG_ERROR, "random: Could not write entropy data " + "to %s", random_entropy_file); + return; + } + + wpa_printf(MSG_DEBUG, "random: Updated entropy file %s " + "(own_pool_ready=%u)", + random_entropy_file, own_pool_ready); +} + + +void random_init(const char *entropy_file) +{ + os_free(random_entropy_file); + if (entropy_file) + random_entropy_file = os_strdup(entropy_file); + else + random_entropy_file = NULL; + random_read_entropy(); + +#ifdef __linux__ + if (random_fd >= 0) + return; + + random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); + if (random_fd < 0) { +#ifndef CONFIG_NO_STDOUT_DEBUG + int error = errno; + perror("open(/dev/random)"); + wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", + strerror(error)); +#endif /* CONFIG_NO_STDOUT_DEBUG */ + return; + } + wpa_printf(MSG_DEBUG, "random: Trying to read entropy from " + "/dev/random"); + + eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL); +#endif /* __linux__ */ + + random_write_entropy(); +} + + +void random_deinit(void) +{ +#ifdef __linux__ + random_close_fd(); +#endif /* __linux__ */ + random_write_entropy(); + os_free(random_entropy_file); + random_entropy_file = NULL; +} diff --git a/contrib/hostapd/src/crypto/random.h b/contrib/hostapd/src/crypto/random.h new file mode 100644 index 0000000000..d13e1c4929 --- /dev/null +++ b/contrib/hostapd/src/crypto/random.h @@ -0,0 +1,28 @@ +/* + * Random number generator + * Copyright (c) 2010-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RANDOM_H +#define RANDOM_H + +#ifdef CONFIG_NO_RANDOM_POOL +#define random_init(e) do { } while (0) +#define random_deinit() do { } while (0) +#define random_add_randomness(b, l) do { } while (0) +#define random_get_bytes(b, l) os_get_random((b), (l)) +#define random_pool_ready() 1 +#define random_mark_pool_ready() do { } while (0) +#else /* CONFIG_NO_RANDOM_POOL */ +void random_init(const char *entropy_file); +void random_deinit(void); +void random_add_randomness(const void *buf, size_t len); +int random_get_bytes(void *buf, size_t len); +int random_pool_ready(void); +void random_mark_pool_ready(void); +#endif /* CONFIG_NO_RANDOM_POOL */ + +#endif /* RANDOM_H */ diff --git a/contrib/hostapd/src/crypto/rc4.c b/contrib/hostapd/src/crypto/rc4.c index 70c790e364..98ae269a63 100644 --- a/contrib/hostapd/src/crypto/rc4.c +++ b/contrib/hostapd/src/crypto/rc4.c @@ -2,37 +2,19 @@ * RC4 stream cipher * Copyright (c) 2002-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "rc4.h" +#include "crypto.h" #define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) -/** - * rc4 - XOR RC4 stream to given data with skip-stream-start - * @key: RC4 key - * @keylen: RC4 key length - * @skip: number of bytes to skip from the beginning of the RC4 stream - * @data: data to be XOR'ed with RC4 stream - * @data_len: buf length - * - * Generate RC4 pseudo random stream for the given key, skip beginning of the - * stream, and XOR the end result with the data buffer to perform RC4 - * encryption/decryption. - */ -void rc4_skip(const u8 *key, size_t keylen, size_t skip, - u8 *data, size_t data_len) +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; @@ -67,4 +49,6 @@ void rc4_skip(const u8 *key, size_t keylen, size_t skip, S_SWAP(i, j); *pos++ ^= S[(S[i] + S[j]) & 0xff]; } + + return 0; } diff --git a/contrib/hostapd/src/crypto/rc4.h b/contrib/hostapd/src/crypto/rc4.h deleted file mode 100644 index 35c7e41fba..0000000000 --- a/contrib/hostapd/src/crypto/rc4.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * RC4 stream cipher - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#ifndef RC4_H -#define RC4_H - -void rc4_skip(const u8 *key, size_t keylen, size_t skip, - u8 *data, size_t data_len); - -#endif /* RC4_H */ diff --git a/contrib/hostapd/src/crypto/sha1-internal.c b/contrib/hostapd/src/crypto/sha1-internal.c new file mode 100644 index 0000000000..10bf153ca3 --- /dev/null +++ b/contrib/hostapd/src/crypto/sha1-internal.c @@ -0,0 +1,302 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 4/01 +By Jouni Malinen +Minor changes to match the coding style used in Dynamics. + +Modified September 24, 2004 +By Jouni Malinen +Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined. + +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +#define SHA1HANDSOFF + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifndef WORDS_BIGENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ + (rol(block->l[i], 8) & 0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ + block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); +#define R3(v,w,x,y,z,i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w=rol(w, 30); + + +#ifdef VERBOSE /* SAK */ +void SHAPrintContext(SHA1_CTX *context, char *msg) +{ + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +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/contrib/hostapd/src/crypto/sha1-pbkdf2.c b/contrib/hostapd/src/crypto/sha1-pbkdf2.c new file mode 100644 index 0000000000..8effe2fe06 --- /dev/null +++ b/contrib/hostapd/src/crypto/sha1-pbkdf2.c @@ -0,0 +1,92 @@ +/* + * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i + * Copyright (c) 2003-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" + +static int pbkdf2_sha1_f(const char *passphrase, const u8 *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] = 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 u8 *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/contrib/hostapd/src/crypto/sha1-prf.c b/contrib/hostapd/src/crypto/sha1-prf.c new file mode 100644 index 0000000000..90b9e74b74 --- /dev/null +++ b/contrib/hostapd/src/crypto/sha1-prf.c @@ -0,0 +1,66 @@ +/* + * SHA1-based PRF + * Copyright (c) 2003-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "crypto.h" + + +/** + * 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/contrib/hostapd/src/crypto/sha1-tlsprf.c b/contrib/hostapd/src/crypto/sha1-tlsprf.c new file mode 100644 index 0000000000..0effd9b76d --- /dev/null +++ b/contrib/hostapd/src/crypto/sha1-tlsprf.c @@ -0,0 +1,99 @@ +/* + * TLS PRF (SHA1 + MD5) + * Copyright (c) 2003-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "md5.h" + + +/** + * tls_prf_sha1_md5 - 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_sha1_md5(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(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); + hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); + + MD5_pos = MD5_MAC_LEN; + SHA1_pos = SHA1_MAC_LEN; + for (i = 0; i < outlen; i++) { + if (MD5_pos == MD5_MAC_LEN) { + hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); + MD5_pos = 0; + hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); + } + if (SHA1_pos == SHA1_MAC_LEN) { + hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, + P_SHA1); + SHA1_pos = 0; + hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1); + } + + out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos]; + + MD5_pos++; + SHA1_pos++; + } + + return 0; +} diff --git a/contrib/hostapd/src/crypto/sha1-tprf.c b/contrib/hostapd/src/crypto/sha1-tprf.c new file mode 100644 index 0000000000..a52949462f --- /dev/null +++ b/contrib/hostapd/src/crypto/sha1-tprf.c @@ -0,0 +1,70 @@ +/* + * SHA1 T-PRF for EAP-FAST + * Copyright (c) 2003-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/sha1.c b/contrib/hostapd/src/crypto/sha1.c index 141e4f4ee6..d48c77d75c 100644 --- a/contrib/hostapd/src/crypto/sha1.c +++ b/contrib/hostapd/src/crypto/sha1.c @@ -2,21 +2,14 @@ * SHA1 hash implementation and interface functions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "sha1.h" -#include "md5.h" #include "crypto.h" @@ -28,9 +21,10 @@ * @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 */ -void 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_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]; @@ -42,12 +36,13 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ - return; + return -1; } /* if key is longer than 64 bytes reset it to key = SHA1(key) */ if (key_len > 64) { - sha1_vector(1, &key, &key_len, tk); + if (sha1_vector(1, &key, &key_len, tk)) + return -1; key = tk; key_len = 20; } @@ -75,7 +70,8 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } - sha1_vector(1 + num_elem, _addr, _len, mac); + 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); @@ -88,7 +84,7 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, _len[0] = 64; _addr[1] = mac; _len[1] = SHA1_MAC_LEN; - sha1_vector(2, _addr, _len, mac); + return sha1_vector(2, _addr, _len, mac); } @@ -99,635 +95,10 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, * @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 */ -void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { - hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); -} - - -/** - * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) - * @key: Key for PRF - * @key_len: Length of the key in bytes - * @label: A unique label for each purpose of the PRF - * @data: Extra data to bind into the key - * @data_len: Length of the data - * @buf: Buffer for the generated pseudo-random key - * @buf_len: Number of bytes of key to generate - * - * This function is used to derive new, cryptographically separate keys from a - * given key (e.g., PMK in IEEE 802.11i). - */ -void sha1_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) -{ - u8 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) { - hmac_sha1_vector(key, key_len, 3, addr, len, - &buf[pos]); - pos += SHA1_MAC_LEN; - } else { - hmac_sha1_vector(key, key_len, 3, addr, len, - hash); - os_memcpy(&buf[pos], hash, plen); - break; - } - counter++; - } -} - - -#ifndef CONFIG_NO_T_PRF -/** - * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) - * @key: Key for PRF - * @key_len: Length of the key in bytes - * @label: A unique label for each purpose of the PRF - * @seed: Seed value to bind into the key - * @seed_len: Length of the seed - * @buf: Buffer for the generated pseudo-random key - * @buf_len: Number of bytes of key to generate - * - * This function is used to derive new, cryptographically separate keys from a - * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5. - */ -void sha1_t_prf(const u8 *key, size_t key_len, const char *label, - const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len) -{ - unsigned char counter = 0; - size_t pos, plen; - u8 hash[SHA1_MAC_LEN]; - size_t label_len = 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; - 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; - } -} -#endif /* CONFIG_NO_T_PRF */ - - -#ifndef CONFIG_NO_TLS_PRF -/** - * 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(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); - hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); - - MD5_pos = MD5_MAC_LEN; - SHA1_pos = SHA1_MAC_LEN; - for (i = 0; i < outlen; i++) { - if (MD5_pos == MD5_MAC_LEN) { - hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); - MD5_pos = 0; - hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); - } - if (SHA1_pos == SHA1_MAC_LEN) { - hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, - P_SHA1); - SHA1_pos = 0; - hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1); - } - - out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos]; - - MD5_pos++; - SHA1_pos++; - } - - return 0; -} -#endif /* CONFIG_NO_TLS_PRF */ - - -#ifndef CONFIG_NO_PBKDF2 - -static void 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; - hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, tmp); - os_memcpy(digest, tmp, SHA1_MAC_LEN); - - for (i = 1; i < iterations; i++) { - hmac_sha1((u8 *) passphrase, passphrase_len, tmp, SHA1_MAC_LEN, - tmp2); - os_memcpy(tmp, tmp2, SHA1_MAC_LEN); - for (j = 0; j < SHA1_MAC_LEN; j++) - digest[j] ^= tmp2[j]; - } -} - - -/** - * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i - * @passphrase: ASCII passphrase - * @ssid: SSID - * @ssid_len: SSID length in bytes - * @iterations: Number of iterations to run - * @buf: Buffer for the generated key - * @buflen: Length of the buffer in bytes - * - * This function is used to derive PSK for WPA-PSK. For this protocol, - * iterations is set to 4096 and buflen to 32. This function is described in - * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. - */ -void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, - int iterations, u8 *buf, size_t buflen) -{ - unsigned int count = 0; - unsigned char *pos = buf; - size_t left = buflen, plen; - unsigned char digest[SHA1_MAC_LEN]; - - while (left > 0) { - count++; - pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, count, - digest); - plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left; - os_memcpy(pos, digest, plen); - pos += plen; - left -= plen; - } -} - -#endif /* CONFIG_NO_PBKDF2 */ - - -#ifdef INTERNAL_SHA1 - -struct SHA1Context { - u32 state[5]; - u32 count[2]; - unsigned char buffer[64]; -}; - -typedef struct SHA1Context SHA1_CTX; - -#ifndef CONFIG_CRYPTO_INTERNAL -static void SHA1Init(struct SHA1Context *context); -static void SHA1Update(struct SHA1Context *context, const void *data, u32 len); -static void SHA1Final(unsigned char digest[20], struct SHA1Context *context); -#endif /* CONFIG_CRYPTO_INTERNAL */ -static void SHA1Transform(u32 state[5], const unsigned char buffer[64]); - - -/** - * sha1_vector - SHA-1 hash for data vector - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash - */ -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac) -{ - SHA1_CTX ctx; - size_t i; - - SHA1Init(&ctx); - for (i = 0; i < num_elem; i++) - SHA1Update(&ctx, addr[i], len[i]); - SHA1Final(mac, &ctx); -} - - -#ifndef CONFIG_NO_FIPS186_2_PRF -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; -} -#endif /* CONFIG_NO_FIPS186_2_PRF */ - - -/* ===== start - public domain SHA1 implementation ===== */ - -/* -SHA-1 in C -By Steve Reid -100% Public Domain - ------------------ -Modified 7/98 -By James H. Brown -Still 100% Public Domain - -Corrected a problem which generated improper hash values on 16 bit machines -Routine SHA1Update changed from - void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int -len) -to - void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned -long len) - -The 'len' parameter was declared an int which works fine on 32 bit machines. -However, on 16 bit machines an int is too small for the shifts being done -against -it. This caused the hash function to generate incorrect values if len was -greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). - -Since the file IO in main() reads 16K at a time, any file 8K or larger would -be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million -"a"s). - -I also changed the declaration of variables i & j in SHA1Update to -unsigned long from unsigned int for the same reason. - -These changes should make no difference to any 32 bit implementations since -an -int and a long are the same size in those environments. - --- -I also corrected a few compiler warnings generated by Borland C. -1. Added #include for exit() prototype -2. Removed unused variable 'j' in SHA1Final -3. Changed exit(0) to return(0) at end of main. - -ALL changes I made can be located by searching for comments containing 'JHB' ------------------ -Modified 8/98 -By Steve Reid -Still 100% public domain - -1- Removed #include and used return() instead of exit() -2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) -3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net - ------------------ -Modified 4/01 -By Saul Kravitz -Still 100% PD -Modified to run on Compaq Alpha hardware. - ------------------ -Modified 4/01 -By Jouni Malinen -Minor changes to match the coding style used in Dynamics. - -Modified September 24, 2004 -By Jouni Malinen -Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined. - -*/ - -/* -Test Vectors (from FIPS PUB 180-1) -"abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ - -#define SHA1HANDSOFF - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ -#ifndef WORDS_BIGENDIAN -#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ - (rol(block->l[i], 8) & 0x00FF00FF)) -#else -#define blk0(i) block->l[i] -#endif -#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ - block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) \ - z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R1(v,w,x,y,z,i) \ - z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R2(v,w,x,y,z,i) \ - z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); -#define R3(v,w,x,y,z,i) \ - z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ - w = rol(w, 30); -#define R4(v,w,x,y,z,i) \ - z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ - w=rol(w, 30); - - -#ifdef VERBOSE /* SAK */ -void SHAPrintContext(SHA1_CTX *context, char *msg) -{ - printf("%s (%d,%d) %x %x %x %x %x\n", - msg, - context->count[0], context->count[1], - context->state[0], - context->state[1], - context->state[2], - context->state[3], - context->state[4]); -} -#endif - -/* Hash a single 512-bit block. This is the core of the algorithm. */ - -static void SHA1Transform(u32 state[5], const unsigned char buffer[64]) -{ - u32 a, b, c, d, e; - typedef union { - unsigned char c[64]; - u32 l[16]; - } CHAR64LONG16; - CHAR64LONG16* block; -#ifdef SHA1HANDSOFF - 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; + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); } - - -/* 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 ===== */ - -#endif /* INTERNAL_SHA1 */ diff --git a/contrib/hostapd/src/crypto/sha1.h b/contrib/hostapd/src/crypto/sha1.h index 9c365e2677..933cd81b95 100644 --- a/contrib/hostapd/src/crypto/sha1.h +++ b/contrib/hostapd/src/crypto/sha1.h @@ -1,15 +1,9 @@ /* * SHA1 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SHA1_H @@ -17,26 +11,17 @@ #define SHA1_MAC_LEN 20 -void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac); -void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, +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); -void sha1_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len); -void sha1_t_prf(const u8 *key, size_t key_len, const char *label, - const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); -int __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); -void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, - int iterations, u8 *buf, size_t buflen); - -#ifdef CONFIG_CRYPTO_INTERNAL -struct SHA1Context; - -void SHA1Init(struct SHA1Context *context); -void SHA1Update(struct SHA1Context *context, const void *data, u32 len); -void SHA1Final(unsigned char digest[20], struct SHA1Context *context); -#endif /* CONFIG_CRYPTO_INTERNAL */ - +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_sha1_md5(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 u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen); #endif /* SHA1_H */ diff --git a/contrib/hostapd/src/crypto/sha1_i.h b/contrib/hostapd/src/crypto/sha1_i.h new file mode 100644 index 0000000000..344387e973 --- /dev/null +++ b/contrib/hostapd/src/crypto/sha1_i.h @@ -0,0 +1,23 @@ +/* + * SHA1 internal definitions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/crypto/sha256.c b/contrib/hostapd/src/crypto/sha256-internal.c similarity index 50% copy from contrib/hostapd/src/crypto/sha256.c copy to contrib/hostapd/src/crypto/sha256-internal.c index 96dac0ea71..35299b0524 100644 --- a/contrib/hostapd/src/crypto/sha256.c +++ b/contrib/hostapd/src/crypto/sha256-internal.c @@ -1,193 +1,40 @@ /* * SHA-256 hash implementation and interface functions - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "sha256.h" +#include "sha256_i.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++; - } -} - - -#ifdef INTERNAL_SHA256 - -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 */ -void sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac) +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++) - sha256_process(&ctx, addr[i], len[i]); - sha256_done(&ctx, mac); + if (sha256_process(&ctx, addr[i], len[i])) + return -1; + if (sha256_done(&ctx, mac)) + return -1; + return 0; } @@ -274,7 +121,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf) /* Initialize the hash state */ -static void sha256_init(struct sha256_state *md) +void sha256_init(struct sha256_state *md) { md->curlen = 0; md->length = 0; @@ -295,32 +142,31 @@ static void sha256_init(struct sha256_state *md) @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) +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)) + if (md->curlen >= sizeof(md->buf)) return -1; while (inlen > 0) { - if (md->curlen == 0 && inlen >= block_size) { + if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) { if (sha256_compress(md, (unsigned char *) in) < 0) return -1; - md->length += block_size * 8; - in += block_size; - inlen -= block_size; + md->length += SHA256_BLOCK_SIZE * 8; + in += SHA256_BLOCK_SIZE; + inlen -= SHA256_BLOCK_SIZE; } else { - n = MIN(inlen, (block_size - md->curlen)); + n = MIN(inlen, (SHA256_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 (md->curlen == SHA256_BLOCK_SIZE) { if (sha256_compress(md, md->buf) < 0) return -1; - md->length += 8 * block_size; + md->length += 8 * SHA256_BLOCK_SIZE; md->curlen = 0; } } @@ -336,7 +182,7 @@ static int sha256_process(struct sha256_state *md, const unsigned char *in, @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 sha256_done(struct sha256_state *md, unsigned char *out) { int i; @@ -354,14 +200,14 @@ static int sha256_done(struct sha256_state *md, unsigned char *out) * encoding like normal. */ if (md->curlen > 56) { - while (md->curlen < 64) { + while (md->curlen < SHA256_BLOCK_SIZE) { md->buf[md->curlen++] = (unsigned char) 0; } sha256_compress(md, md->buf); md->curlen = 0; } - /* pad upto 56 bytes of zeroes */ + /* pad up to 56 bytes of zeroes */ while (md->curlen < 56) { md->buf[md->curlen++] = (unsigned char) 0; } @@ -378,5 +224,3 @@ static int sha256_done(struct sha256_state *md, unsigned char *out) } /* ===== end - public domain SHA256 implementation ===== */ - -#endif /* INTERNAL_SHA256 */ diff --git a/contrib/hostapd/src/crypto/sha256-prf.c b/contrib/hostapd/src/crypto/sha256-prf.c new file mode 100644 index 0000000000..9a11208f5a --- /dev/null +++ b/contrib/hostapd/src/crypto/sha256-prf.c @@ -0,0 +1,98 @@ +/* + * SHA256-based PRF (IEEE 802.11r) + * Copyright (c) 2003-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" +#include "crypto.h" + + +/** + * 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) +{ + sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); +} + + +/** + * sha256_prf_bits - IEEE Std 802.11-2012, 11.6.1.7.2 Key derivation function + * @key: Key for KDF + * @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 bits of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. If the requested buf_len is not divisible by eight, the least + * significant 1-7 bits of the last octet in the output are not part of the + * requested output. + */ +void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) +{ + 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]; + size_t buf_len = (buf_len_bits + 7) / 8; + + 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_bits); + 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); + pos += plen; + break; + } + counter++; + } + + /* + * Mask out unused bits in the last octet if it does not use all the + * bits. + */ + if (buf_len_bits % 8) { + u8 mask = 0xff << (8 - buf_len_bits % 8); + buf[pos - 1] &= mask; + } +} diff --git a/contrib/hostapd/src/crypto/sha256-tlsprf.c b/contrib/hostapd/src/crypto/sha256-tlsprf.c new file mode 100644 index 0000000000..0528dadfdc --- /dev/null +++ b/contrib/hostapd/src/crypto/sha256-tlsprf.c @@ -0,0 +1,66 @@ +/* + * TLS PRF P_SHA256 + * Copyright (c) 2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" + + +/** + * tls_prf_sha256 - Pseudo-Random Function for TLS v1.2 (P_SHA256, RFC 5246) + * @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. + */ +void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +{ + size_t clen; + u8 A[SHA256_MAC_LEN]; + u8 P[SHA256_MAC_LEN]; + size_t pos; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = A; + len[0] = SHA256_MAC_LEN; + addr[1] = (unsigned char *) label; + len[1] = os_strlen(label); + addr[2] = seed; + len[2] = seed_len; + + /* + * RFC 5246, 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(secret, label, seed) = P_SHA256(secret, label + seed) + */ + + hmac_sha256_vector(secret, secret_len, 2, &addr[1], &len[1], A); + + pos = 0; + while (pos < outlen) { + hmac_sha256_vector(secret, secret_len, 3, addr, len, P); + hmac_sha256(secret, secret_len, A, SHA256_MAC_LEN, A); + + clen = outlen - pos; + if (clen > SHA256_MAC_LEN) + clen = SHA256_MAC_LEN; + os_memcpy(out + pos, P, clen); + pos += clen; + } +} diff --git a/contrib/hostapd/src/crypto/sha256.c b/contrib/hostapd/src/crypto/sha256.c index 96dac0ea71..b55e976f37 100644 --- a/contrib/hostapd/src/crypto/sha256.c +++ b/contrib/hostapd/src/crypto/sha256.c @@ -1,15 +1,9 @@ /* * SHA-256 hash implementation and interface functions - * Copyright (c) 2003-2007, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -27,9 +21,10 @@ * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash (32 bytes) + * Returns: 0 on success, -1 on failure */ -void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) +int 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]; @@ -41,12 +36,13 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ - return; + return -1; } /* if key is longer than 64 bytes reset it to key = SHA256(key) */ if (key_len > 64) { - sha256_vector(1, &key, &key_len, tk); + if (sha256_vector(1, &key, &key_len, tk) < 0) + return -1; key = tk; key_len = 32; } @@ -74,7 +70,8 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } - sha256_vector(1 + num_elem, _addr, _len, mac); + if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); @@ -87,7 +84,7 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, _len[0] = 64; _addr[1] = mac; _len[1] = SHA256_MAC_LEN; - sha256_vector(2, _addr, _len, mac); + return sha256_vector(2, _addr, _len, mac); } @@ -97,286 +94,11 @@ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, * @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++; - } -} - - -#ifdef INTERNAL_SHA256 - -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 + * @mac: Buffer for the hash (32 bytes) + * Returns: 0 on success, -1 on failure */ -void sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac) +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) { - struct sha256_state ctx; - size_t i; - - sha256_init(&ctx); - for (i = 0; i < num_elem; i++) - sha256_process(&ctx, addr[i], len[i]); - sha256_done(&ctx, mac); + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); } - - -/* ===== 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 ===== */ - -#endif /* INTERNAL_SHA256 */ diff --git a/contrib/hostapd/src/crypto/sha256.h b/contrib/hostapd/src/crypto/sha256.h index dc597f09b5..7596a52219 100644 --- a/contrib/hostapd/src/crypto/sha256.h +++ b/contrib/hostapd/src/crypto/sha256.h @@ -1,15 +1,9 @@ /* * SHA256 hash implementation and interface functions - * Copyright (c) 2003-2006, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef SHA256_H @@ -17,11 +11,17 @@ #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); +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int 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); +void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); +void tls_prf_sha256(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); #endif /* SHA256_H */ diff --git a/contrib/hostapd/src/crypto/sha256_i.h b/contrib/hostapd/src/crypto/sha256_i.h new file mode 100644 index 0000000000..a502d2ba5d --- /dev/null +++ b/contrib/hostapd/src/crypto/sha256_i.h @@ -0,0 +1,25 @@ +/* + * SHA-256 internal definitions + * Copyright (c) 2003-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA256_I_H +#define SHA256_I_H + +#define SHA256_BLOCK_SIZE 64 + +struct sha256_state { + u64 length; + u32 state[8], curlen; + u8 buf[SHA256_BLOCK_SIZE]; +}; + +void sha256_init(struct sha256_state *md); +int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen); +int sha256_done(struct sha256_state *md, unsigned char *out); + +#endif /* SHA256_I_H */ diff --git a/contrib/hostapd/src/crypto/tls.h b/contrib/hostapd/src/crypto/tls.h index aafb799993..287fd333f6 100644 --- a/contrib/hostapd/src/crypto/tls.h +++ b/contrib/hostapd/src/crypto/tls.h @@ -1,15 +1,9 @@ /* - * WPA Supplicant / SSL/TLS interface definition - * Copyright (c) 2004-2007, Jouni Malinen + * SSL/TLS interface definition + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLS_H @@ -24,18 +18,74 @@ struct tls_keys { 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_SUCCESS, + TLS_CERT_CHAIN_FAILURE, + TLS_PEER_CERTIFICATE, + TLS_ALERT +}; + +/* + * 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, + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, + TLS_FAIL_SERVER_USED_CLIENT_CERT = 10 +}; + +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 { + int is_local; + const char *type; + const char *description; + } alert; }; struct tls_config { const char *opensc_engine_path; const char *pkcs11_engine_path; const char *pkcs11_module_path; + int fips_mode; + int cert_in_cb; + + 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) +#define TLS_CONN_DISABLE_SESSION_TICKET BIT(2) +#define TLS_CONN_REQUEST_OCSP BIT(3) +#define TLS_CONN_REQUIRE_OCSP BIT(4) /** * struct tls_connection_params - Parameters for TLS connection @@ -48,6 +98,8 @@ struct tls_config { * %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 + * @suffix_match: String to suffix match in the dNSName or CN of the peer + * certificate or %NULL to allow all domain names * @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 @@ -70,8 +122,9 @@ struct tls_config { * 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_*) + * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response + * or %NULL if OCSP is not enabled * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -88,6 +141,7 @@ struct tls_connection_params { const char *ca_path; const char *subject_match; const char *altsubject_match; + const char *suffix_match; const char *client_cert; const u8 *client_cert_blob; size_t client_cert_blob_len; @@ -98,7 +152,6 @@ struct tls_connection_params { const char *dh_file; const u8 *dh_blob; size_t dh_blob_len; - int tls_ia; /* OpenSSL specific variables */ int engine; @@ -109,6 +162,7 @@ struct tls_connection_params { const char *ca_cert_id; unsigned int flags; + const char *ocsp_stapling_response; }; @@ -237,20 +291,6 @@ 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() @@ -278,7 +318,7 @@ int __must_check tls_connection_get_keys(void *tls_ctx, * 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 + * return -1. Example implementation of this function is in tls_prf_sha1_md5() * when it is called with seed set to client_random|server_random (or * server_random|client_random). */ @@ -292,17 +332,14 @@ int __must_check tls_connection_prf(void *tls_ctx, * tls_connection_handshake - Process TLS handshake (client side) * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * @in_data: Input data from TLS peer - * @in_len: Input data length - * @out_len: Length of the output buffer. + * @in_data: Input data from TLS server * @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 + * Returns: Output data, %NULL on failure * - * Caller is responsible for freeing returned output data. If the final + * 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. Caller is responsible - * for freeing appl_data. + * 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. @@ -318,62 +355,66 @@ int __must_check tls_connection_prf(void *tls_ctx, * tls_connection_established() should return 1 once the TLS handshake has been * completed successfully. */ -u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len); +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); + +struct wpabuf * tls_connection_handshake2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, + int *more_data_needed); /** * tls_connection_server_handshake - Process TLS handshake (server side) * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Input data from TLS peer - * @in_len: Input data length - * @out_len: Length of the output buffer. - * Returns: pointer to output data, %NULL on failure + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * Returns: Output data, %NULL on failure * - * Caller is responsible for freeing returned output data. + * The caller is responsible for freeing the returned output data. */ -u8 * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len); +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: 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 + * @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. + * send data in the encrypted tunnel. The caller is responsible for freeing the + * returned output data. */ -int __must_check tls_connection_encrypt(void *tls_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len); +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: 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 + * @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. + * receive data from the encrypted tunnel. The caller is responsible for + * freeing the returned output data. */ -int __must_check tls_connection_decrypt(void *tls_ctx, +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); + +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len); + const struct wpabuf *in_data, + int *more_data_needed); /** * tls_connection_resumed - Was session resumption used @@ -480,7 +521,6 @@ int tls_connection_get_write_alerts(void *tls_ctx, 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() @@ -488,45 +528,6 @@ int tls_connection_get_keyblock_size(void *tls_ctx, */ 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 - * @out_data: Pointer to output buffer (encrypted TLS/IA data) - * @out_len: Maximum out_data length - * Returns: Number of bytes written to out_data on success, -1 on failure - * - * This function is used to send the TLS/IA end phase message, e.g., when the - * EAP server completes EAP-TTLSv1. - */ -int __must_check tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final, - u8 *out_data, size_t out_len); - -/** - * 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); diff --git a/contrib/hostapd/src/crypto/tls_gnutls.c b/contrib/hostapd/src/crypto/tls_gnutls.c index 2c5c5a2b64..cb23eb9c5f 100644 --- a/contrib/hostapd/src/crypto/tls_gnutls.c +++ b/contrib/hostapd/src/crypto/tls_gnutls.c @@ -1,15 +1,9 @@ /* - * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2007, Jouni Malinen + * SSL/TLS interface functions for GnuTLS + * Copyright (c) 2004-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -19,28 +13,12 @@ #include #endif /* PKCS12_FUNCS */ -#ifdef CONFIG_GNUTLS_EXTRA -#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 -#define GNUTLS_IA -#include -#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 +#define WPA_TLS_RANDOM_SIZE 32 +#define WPA_TLS_MASTER_SIZE 48 #if LIBGNUTLS_VERSION_NUMBER < 0x010302 @@ -77,9 +55,9 @@ typedef struct { 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]; + opaque master_secret[WPA_TLS_MASTER_SIZE]; + opaque client_random[WPA_TLS_RANDOM_SIZE]; + opaque server_random[WPA_TLS_RANDOM_SIZE]; /* followed by stuff we are not interested in */ } security_parameters_st; @@ -112,26 +90,12 @@ struct tls_connection { int established; int verify_peer; - u8 *push_buf, *pull_buf, *pull_buf_offset; - size_t push_buf_len, pull_buf_len; + 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 */ }; @@ -161,8 +125,6 @@ static void tls_log_func(int level, const char *msg) } -extern int wpa_debug_show_keys; - void * tls_init(const struct tls_config *conf) { struct tls_global *global; @@ -241,22 +203,22 @@ static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; - u8 *end; + const u8 *end; if (conn->pull_buf == NULL) { errno = EWOULDBLOCK; return -1; } - end = conn->pull_buf + conn->pull_buf_len; + 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__); - os_free(conn->pull_buf); - conn->pull_buf = conn->pull_buf_offset = NULL; - conn->pull_buf_len = 0; + 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__, @@ -270,16 +232,12 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; - u8 *nbuf; - nbuf = os_realloc(conn->push_buf, conn->push_buf_len + len); - if (nbuf == NULL) { + if (wpabuf_resize(&conn->push_buf, len) < 0) { errno = ENOMEM; return -1; } - os_memcpy(nbuf + conn->push_buf_len, buf, len); - conn->push_buf = nbuf; - conn->push_buf_len += len; + wpabuf_put_data(conn->push_buf, buf, len); return len; } @@ -288,8 +246,12 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, static int tls_gnutls_init_session(struct tls_global *global, struct tls_connection *conn) { +#if LIBGNUTLS_VERSION_NUMBER >= 0x020200 + const char *err; +#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ const int cert_types[2] = { GNUTLS_CRT_X509, 0 }; const int protos[2] = { GNUTLS_TLS1, 0 }; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ int ret; ret = gnutls_init(&conn->session, @@ -304,6 +266,15 @@ static int tls_gnutls_init_session(struct tls_global *global, if (ret < 0) goto fail; +#if LIBGNUTLS_VERSION_NUMBER >= 0x020200 + ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0", + &err); + if (ret < 0) { + wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at " + "'%s'", err); + goto fail; + } +#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */ ret = gnutls_certificate_type_set_priority(conn->session, cert_types); if (ret < 0) goto fail; @@ -311,6 +282,7 @@ static int tls_gnutls_init_session(struct tls_global *global, ret = gnutls_protocol_set_priority(conn->session, protos); if (ret < 0) goto fail; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */ gnutls_transport_set_pull_function(conn->session, tls_pull_func); gnutls_transport_set_push_function(conn->session, tls_push_func); @@ -367,24 +339,13 @@ 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); - os_free(conn->push_buf); - os_free(conn->pull_buf); + wpabuf_free(conn->push_buf); + wpabuf_free(conn->pull_buf); os_free(conn); } @@ -407,18 +368,9 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) * because the connection was already terminated in practice * and "close notify" shutdown alert would confuse AS. */ gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); - os_free(conn->push_buf); + wpabuf_free(conn->push_buf); conn->push_buf = NULL; - conn->push_buf_len = 0; 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)) { @@ -601,11 +553,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); } +#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { gnutls_certificate_set_verify_flags( conn->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); } +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ } if (params->client_cert && params->private_key) { @@ -650,7 +604,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } } - conn->tls_ia = params->tls_ia; conn->params_set = 1; ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, @@ -660,28 +613,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, 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; } @@ -733,11 +664,13 @@ int tls_global_set_params(void *tls_ctx, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); } +#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) { gnutls_certificate_set_verify_flags( global->xcred, GNUTLS_VERIFY_DISABLE_TIME_CHECKS); } +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ } if (params->client_cert && params->private_key) { @@ -826,10 +759,11 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, os_memset(keys, 0, sizeof(*keys)); +#if LIBGNUTLS_VERSION_NUMBER < 0x020c00 #ifdef GNUTLS_INTERNAL_STRUCTURE_HACK sec = &conn->session->security_parameters; keys->master_key = sec->master_secret; - keys->master_key_len = TLS_MASTER_SIZE; + keys->master_key_len = WPA_TLS_MASTER_SIZE; keys->client_random = sec->client_random; keys->server_random = sec->server_random; #else /* GNUTLS_INTERNAL_STRUCTURE_HACK */ @@ -839,16 +773,12 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, (u8 *) gnutls_session_get_server_random(conn->session); /* No access to master_secret */ #endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ -#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; +#if LIBGNUTLS_VERSION_NUMBER < 0x020c00 + keys->client_random_len = WPA_TLS_RANDOM_SIZE; + keys->server_random_len = WPA_TLS_RANDOM_SIZE; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ return 0; } @@ -887,11 +817,13 @@ static int tls_connection_verify_peer(struct tls_connection *conn, if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); + *err = GNUTLS_A_INTERNAL_ERROR; if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " "algorithm"); *err = GNUTLS_A_INSUFFICIENT_SECURITY; } +#if LIBGNUTLS_VERSION_NUMBER >= 0x020800 if (status & GNUTLS_CERT_NOT_ACTIVATED) { wpa_printf(MSG_INFO, "TLS: Certificate not yet " "activated"); @@ -901,6 +833,7 @@ static int tls_connection_verify_peer(struct tls_connection *conn, wpa_printf(MSG_INFO, "TLS: Certificate expired"); *err = GNUTLS_A_CERTIFICATE_EXPIRED; } +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ return -1; } @@ -979,31 +912,56 @@ static int tls_connection_verify_peer(struct tls_connection *conn, } -u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) +static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn) { - struct tls_global *global = ssl_ctx; - u8 *out_data; + 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_record_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 && in_len) { + 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); + (unsigned long) wpabuf_len(conn->pull_buf)); + wpabuf_free(conn->pull_buf); } - conn->pull_buf = os_malloc(in_len); + conn->pull_buf = wpabuf_dup(in_data); if (conn->pull_buf == NULL) return NULL; - os_memcpy(conn->pull_buf, in_data, in_len); - conn->pull_buf_offset = conn->pull_buf; - conn->pull_buf_len = in_len; + conn->pull_buf_offset = wpabuf_head(conn->pull_buf); } ret = gnutls_handshake(conn->session); @@ -1014,7 +972,7 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, conn->push_buf == NULL) { /* Need to return something to trigger * completion of EAP-TLS. */ - conn->push_buf = os_malloc(1); + conn->push_buf = wpabuf_alloc(0); } break; case GNUTLS_E_FATAL_ALERT_RECEIVED: @@ -1041,24 +999,11 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, 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"); - } + 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 = os_malloc(1); + conn->push_buf = wpabuf_alloc(0); } gnutls_session_get_data(conn->session, NULL, &size); @@ -1073,145 +1018,87 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, 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; - *out_len = conn->push_buf_len; conn->push_buf = NULL; - conn->push_buf_len = 0; return out_data; } -u8 * tls_connection_server_handshake(void *ssl_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) +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(ssl_ctx, conn, in_data, in_len, - out_len, NULL, NULL); + return tls_connection_handshake(tls_ctx, conn, in_data, appl_data); } -int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +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, (char *) in_data, in_len); - else -#endif /* GNUTLS_IA */ - res = gnutls_record_send(conn->session, in_data, in_len); + 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 -1; - } - if (conn->push_buf == NULL) - return -1; - if (conn->push_buf_len < out_len) - out_len = conn->push_buf_len; - else if (conn->push_buf_len > out_len) { - wpa_printf(MSG_INFO, "GnuTLS: Not enough buffer space for " - "encrypted message (in_len=%lu push_buf_len=%lu " - "out_len=%lu", - (unsigned long) in_len, - (unsigned long) conn->push_buf_len, - (unsigned long) out_len); + return NULL; } - os_memcpy(out_data, conn->push_buf, out_len); - os_free(conn->push_buf); + + buf = conn->push_buf; conn->push_buf = NULL; - conn->push_buf_len = 0; - return out_len; + return buf; } -int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +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) conn->pull_buf_len); - os_free(conn->pull_buf); + (unsigned long) wpabuf_len(conn->pull_buf)); + wpabuf_free(conn->pull_buf); } - conn->pull_buf = os_malloc(in_len); + conn->pull_buf = wpabuf_dup(in_data); if (conn->pull_buf == NULL) - return -1; - os_memcpy(conn->pull_buf, in_data, in_len); - conn->pull_buf_offset = conn->pull_buf; - conn->pull_buf_len = in_len; - -#ifdef GNUTLS_IA - if (conn->tls_ia) { - res = gnutls_ia_recv(conn->session, (char *) out_data, - out_len); - if (out_len >= 12 && - (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)); - return -1; - } - - res = gnutls_ia_verify_endphase(conn->session, - (char *) out_data); - 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)); - return -1; - } - - if (final) - conn->final_phase_finished = 1; - - return 0; - } - - if (res < 0) { - wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " - "(%s)", __func__, (int) res, - gnutls_strerror(res)); - } - return res; - } -#endif /* GNUTLS_IA */ + 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; - res = gnutls_record_recv(conn->session, out_data, out_len); + 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 res; + return out; } @@ -1243,7 +1130,7 @@ int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, int tls_connection_enable_workaround(void *ssl_ctx, struct tls_connection *conn) { - /* TODO: set SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ + gnutls_record_disable_padding(conn->session); return 0; } @@ -1291,138 +1178,13 @@ int tls_connection_get_keyblock_size(void *tls_ctx, 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 */ } -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, void *ctx) { -#ifdef GNUTLS_IA - int ret; - - if (conn == NULL || conn->session == NULL || !conn->tls_ia) - return -1; - - 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 -1; - } - - 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 -1; - } - - if (conn->push_buf == NULL) - return -1; - if (conn->push_buf_len < out_len) - out_len = conn->push_buf_len; - os_memcpy(out_data, conn->push_buf, out_len); - os_free(conn->push_buf); - conn->push_buf = NULL; - conn->push_buf_len = 0; - return out_len; -#else /* GNUTLS_IA */ - return -1; -#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 */ } diff --git a/contrib/hostapd/src/crypto/tls_internal.c b/contrib/hostapd/src/crypto/tls_internal.c index 42120c8a88..91f0690032 100644 --- a/contrib/hostapd/src/crypto/tls_internal.c +++ b/contrib/hostapd/src/crypto/tls_internal.c @@ -1,15 +1,9 @@ /* - * WPA Supplicant / TLS interface functions and an internal TLS implementation - * Copyright (c) 2004-2007, Jouni Malinen + * TLS interface functions and an internal TLS implementation + * Copyright (c) 2004-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file interface functions for hostapd/wpa_supplicant to use the * integrated TLSv1 implementation. @@ -211,6 +205,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + tlsv1_client_set_time_checks( + conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS)); + return 0; #else /* CONFIG_TLS_INTERNAL_CLIENT */ return -1; @@ -287,13 +284,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -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) { @@ -331,45 +321,88 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, } -u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data, + NULL); +} + + +struct wpabuf * tls_connection_handshake2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, + int *need_more_data) { #ifdef CONFIG_TLS_INTERNAL_CLIENT + u8 *res, *ad; + size_t res_len, ad_len; + struct wpabuf *out; + if (conn->client == NULL) return NULL; - if (appl_data) - *appl_data = 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, need_more_data); + 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); - wpa_printf(MSG_DEBUG, "TLS: %s(in_data=%p in_len=%lu)", - __func__, in_data, (unsigned long) in_len); - return tlsv1_client_handshake(conn->client, in_data, in_len, out_len, - appl_data, appl_data_len); + return out; #else /* CONFIG_TLS_INTERNAL_CLIENT */ return NULL; #endif /* CONFIG_TLS_INTERNAL_CLIENT */ } -u8 * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) +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 *out; + u8 *res; + size_t res_len; + struct wpabuf *out; + if (conn->server == NULL) return NULL; - wpa_printf(MSG_DEBUG, "TLS: %s(in_data=%p in_len=%lu)", - __func__, in_data, (unsigned long) in_len); - out = tlsv1_server_handshake(conn->server, in_data, in_len, out_len); - if (out == NULL && tlsv1_server_established(conn->server)) { - out = os_malloc(1); - *out_len = 0; + 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; @@ -377,43 +410,95 @@ u8 * tls_connection_server_handshake(void *tls_ctx, } -int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +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) { - return tlsv1_client_encrypt(conn->client, in_data, in_len, - out_data, out_len); + 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) { - return tlsv1_server_encrypt(conn->server, in_data, in_len, - out_data, out_len); + 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 -1; + return NULL; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL); } -int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + int *need_more_data) { + if (need_more_data) + *need_more_data = 0; + #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - return tlsv1_client_decrypt(conn->client, in_data, in_len, - out_data, out_len); + return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data), + wpabuf_len(in_data), + need_more_data); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - return tlsv1_server_decrypt(conn->server, in_data, in_len, - out_data, out_len); + 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 -1; + return NULL; } @@ -524,30 +609,6 @@ unsigned int tls_capabilities(void *tls_ctx) } -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) -{ - return -1; -} - - -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, diff --git a/contrib/hostapd/src/crypto/tls_none.c b/contrib/hostapd/src/crypto/tls_none.c index f731628b0e..1a1092a184 100644 --- a/contrib/hostapd/src/crypto/tls_none.c +++ b/contrib/hostapd/src/crypto/tls_none.c @@ -1,15 +1,9 @@ /* - * WPA Supplicant / SSL/TLS interface functions for no TLS case - * Copyright (c) 2004, Jouni Malinen + * SSL/TLS interface functions for no TLS case + * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -22,13 +16,12 @@ void * tls_init(const struct tls_config *conf) return (void *) 1; } + void tls_deinit(void *ssl_ctx) { } -#ifdef EAP_TLS_NONE - int tls_get_errors(void *tls_ctx) { return 0; @@ -85,13 +78,6 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, } -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) { @@ -107,37 +93,37 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, } -u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) { return NULL; } -u8 * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) { return NULL; } -int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { - return -1; + return NULL; } -int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { - return -1; + return NULL; } @@ -206,29 +192,3 @@ unsigned int tls_capabilities(void *tls_ctx) { return 0; } - - -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) -{ - return -1; -} - - -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; -} - -#endif /* EAP_TLS_NONE */ diff --git a/contrib/hostapd/src/crypto/tls_nss.c b/contrib/hostapd/src/crypto/tls_nss.c new file mode 100644 index 0000000000..c53c192a1c --- /dev/null +++ b/contrib/hostapd/src/crypto/tls_nss.c @@ -0,0 +1,645 @@ +/* + * SSL/TLS interface functions for NSS + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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_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; +} + + +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/contrib/hostapd/src/crypto/tls_openssl.c b/contrib/hostapd/src/crypto/tls_openssl.c index b5a1d64f53..d025ae0a8a 100644 --- a/contrib/hostapd/src/crypto/tls_openssl.c +++ b/contrib/hostapd/src/crypto/tls_openssl.c @@ -1,24 +1,20 @@ /* - * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2008, Jouni Malinen + * SSL/TLS interface functions for OpenSSL + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #ifndef CONFIG_SMARTCARD #ifndef OPENSSL_NO_ENGINE +#ifndef ANDROID #define OPENSSL_NO_ENGINE #endif #endif +#endif #include #include @@ -29,6 +25,7 @@ #endif /* OPENSSL_NO_ENGINE */ #include "common.h" +#include "crypto.h" #include "tls.h" #if OPENSSL_VERSION_NUMBER >= 0x0090800fL @@ -37,6 +34,10 @@ #define OPENSSL_d2i_TYPE unsigned char ** #endif +#if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data) +#define OPENSSL_SUPPORTS_CTX_APP_DATA +#endif + #ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT #ifdef SSL_OP_NO_TICKET /* @@ -47,16 +48,51 @@ #endif #endif +#ifdef SSL_set_tlsext_status_type +#ifndef OPENSSL_NO_TLSEXT +#define HAVE_OCSP +#include +#endif /* OPENSSL_NO_TLSEXT */ +#endif /* SSL_set_tlsext_status_type */ + +#ifdef ANDROID +#include +#include + +static BIO * BIO_from_keystore(const char *key) +{ + BIO *bio = NULL; + uint8_t *value = NULL; + int length = keystore_get(key, strlen(key), &value); + if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) + BIO_write(bio, value, length); + free(value); + return bio; +} +#endif /* ANDROID */ + static int tls_openssl_ref_count = 0; +struct tls_context { + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + int cert_in_cb; + char *ocsp_stapling_response; +}; + +static struct tls_context *tls_global = NULL; + + struct tls_connection { + struct tls_context *context; 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; + char *subject_match, *altsubject_match, *suffix_match; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; @@ -65,9 +101,36 @@ struct tls_connection { /* 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; + unsigned int server:1; + + u8 srv_cert_hash[32]; + + unsigned int flags; + + X509 *peer_cert; + X509 *peer_issuer; + X509 *peer_issuer_issuer; }; +static struct tls_context * tls_context_new(const struct tls_config *conf) +{ + struct tls_context *context = os_zalloc(sizeof(*context)); + if (context == NULL) + return NULL; + if (conf) { + context->event_cb = conf->event_cb; + context->cb_ctx = conf->cb_ctx; + context->cert_in_cb = conf->cert_in_cb; + } + return context; +} + + #ifdef CONFIG_NO_STDOUT_DEBUG static void _tls_show_errors(void) @@ -493,6 +556,7 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) wpa_printf(MSG_DEBUG, "SSL: %s:%s", str, SSL_state_string_long(ssl)); } else if (where & SSL_CB_ALERT) { + struct tls_connection *conn = SSL_get_app_data((SSL *) ssl); wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", where & SSL_CB_READ ? "read (remote end reported an error)" : @@ -500,13 +564,20 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) 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++; } + if (conn->context->event_cb != NULL) { + union tls_event_data ev; + struct tls_context *context = conn->context; + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = !(where & SSL_CB_READ); + ev.alert.type = SSL_alert_type_string_long(ret); + ev.alert.description = SSL_alert_desc_string_long(ret); + context->event_cb(context->cb_ctx, TLS_ALERT, &ev); + } } else if (where & SSL_CB_EXIT && ret <= 0) { wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", str, ret == 0 ? "failed" : "error", @@ -663,11 +734,39 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) void * tls_init(const struct tls_config *conf) { SSL_CTX *ssl; + struct tls_context *context; if (tls_openssl_ref_count == 0) { + tls_global = context = tls_context_new(conf); + if (context == NULL) + return NULL; +#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); + os_free(tls_global); + tls_global = NULL; + 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"); + os_free(tls_global); + tls_global = NULL; + return NULL; + } +#endif /* OPENSSL_FIPS */ +#endif /* CONFIG_FIPS */ SSL_load_error_strings(); SSL_library_init(); -#ifndef OPENSSL_NO_SHA256 +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) EVP_add_digest(EVP_sha256()); #endif /* OPENSSL_NO_SHA256 */ /* TODO: if /dev/urandom is available, PRNG is seeded @@ -686,14 +785,35 @@ void * tls_init(const struct tls_config *conf) #endif /* OPENSSL_NO_RC2 */ PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ + } else { + context = tls_global; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + /* Newer OpenSSL can store app-data per-SSL */ + context = tls_context_new(conf); + if (context == NULL) + return NULL; +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ } tls_openssl_ref_count++; ssl = SSL_CTX_new(TLSv1_method()); - if (ssl == NULL) + if (ssl == NULL) { + tls_openssl_ref_count--; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + if (context != tls_global) + os_free(context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + if (tls_openssl_ref_count == 0) { + os_free(tls_global); + tls_global = NULL; + } return NULL; + } SSL_CTX_set_info_callback(ssl, ssl_info_cb); +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + SSL_CTX_set_app_data(ssl, context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ #ifndef OPENSSL_NO_ENGINE if (conf && @@ -719,6 +839,11 @@ void * tls_init(const struct tls_config *conf) void tls_deinit(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + struct tls_context *context = SSL_CTX_get_app_data(ssl); + if (context != tls_global) + os_free(context); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ SSL_CTX_free(ssl); tls_openssl_ref_count--; @@ -730,6 +855,10 @@ void tls_deinit(void *ssl_ctx) ERR_remove_state(0); ERR_free_strings(); EVP_cleanup(); + os_free(tls_global->ocsp_stapling_response); + tls_global->ocsp_stapling_response = NULL; + os_free(tls_global); + tls_global = NULL; } } @@ -744,16 +873,21 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); return -1; } +#ifndef ANDROID if (pin == NULL) { wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set"); return -1; } +#endif if (key_id == NULL) { wpa_printf(MSG_ERROR, "ENGINE: Key Id not set"); return -1; } ERR_clear_error(); +#ifdef ANDROID + ENGINE_load_dynamic(); +#endif conn->engine = ENGINE_by_id(engine_id); if (!conn->engine) { wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]", @@ -768,11 +902,13 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, } wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); +#ifndef ANDROID 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; } +#endif /* load private key first in-case PIN is required for cert */ conn->private_key = ENGINE_load_private_key(conn->engine, key_id, NULL, NULL); @@ -853,6 +989,10 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) SSL_CTX *ssl = ssl_ctx; struct tls_connection *conn; long options; + struct tls_context *context = tls_global; +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + context = SSL_CTX_get_app_data(ssl); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ conn = os_zalloc(sizeof(*conn)); if (conn == NULL) @@ -865,6 +1005,7 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) return NULL; } + conn->context = context; SSL_set_app_data(conn->ssl, conn); options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE; @@ -906,6 +1047,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) tls_engine_deinit(conn); os_free(conn->subject_match); os_free(conn->altsubject_match); + os_free(conn->suffix_match); os_free(conn->session_ticket); os_free(conn); } @@ -996,6 +1138,224 @@ static int tls_match_altsubject(X509 *cert, const char *match) } +#ifndef CONFIG_NATIVE_WINDOWS +static int domain_suffix_match(const u8 *val, size_t len, const char *match) +{ + size_t i, match_len; + + /* Check for embedded nuls that could mess up suffix matching */ + for (i = 0; i < len; i++) { + if (val[i] == '\0') { + wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject"); + return 0; + } + } + + match_len = os_strlen(match); + if (match_len > len) + return 0; + + if (os_strncasecmp((const char *) val + len - match_len, match, + match_len) != 0) + return 0; /* no match */ + + if (match_len == len) + return 1; /* exact match */ + + if (val[len - match_len - 1] == '.') + return 1; /* full label match completes suffix match */ + + wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match"); + return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static int tls_match_suffix(X509 *cert, const char *match) +{ +#ifdef CONFIG_NATIVE_WINDOWS + /* wincrypt.h has conflicting X509_NAME definition */ + return -1; +#else /* CONFIG_NATIVE_WINDOWS */ + GENERAL_NAME *gen; + void *ext; + int i; + int dns_name = 0; + X509_NAME *name; + + wpa_printf(MSG_DEBUG, "TLS: Match domain against suffix %s", match); + + 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 != GEN_DNS) + continue; + dns_name++; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", + gen->d.dNSName->data, + gen->d.dNSName->length); + if (domain_suffix_match(gen->d.dNSName->data, + gen->d.dNSName->length, match) == 1) { + wpa_printf(MSG_DEBUG, "TLS: Suffix match in dNSName found"); + return 1; + } + } + + if (dns_name) { + wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); + return 0; + } + + name = X509_get_subject_name(cert); + i = -1; + for (;;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + + i = X509_NAME_get_index_by_NID(name, NID_commonName, i); + if (i == -1) + break; + e = X509_NAME_get_entry(name, i); + if (e == NULL) + continue; + cn = X509_NAME_ENTRY_get_data(e); + if (cn == NULL) + continue; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", + cn->data, cn->length); + if (domain_suffix_match(cn->data, cn->length, match) == 1) { + wpa_printf(MSG_DEBUG, "TLS: Suffix match in commonName found"); + return 1; + } + } + + wpa_printf(MSG_DEBUG, "TLS: No CommonName suffix match found"); + return 0; +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +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; + struct tls_context *context = conn->context; + + if (context->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; + context->event_cb(context->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; + struct tls_context *context = conn->context; +#ifdef CONFIG_SHA256 + u8 hash[32]; +#endif /* CONFIG_SHA256 */ + + if (context->event_cb == NULL) + return; + + os_memset(&ev, 0, sizeof(ev)); + if (conn->cert_probe || context->cert_in_cb) { + 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; + context->event_cb(context->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]; @@ -1003,7 +1363,9 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) int err, depth; SSL *ssl; struct tls_connection *conn; - char *match, *altmatch; + struct tls_context *context; + char *match, *altmatch, *suffix_match; + const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); err = X509_STORE_CTX_get_error(x509_ctx); @@ -1013,29 +1375,121 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) 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 (conn == NULL) + return 0; - 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 && os_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); + if (depth == 0) + conn->peer_cert = err_cert; + else if (depth == 1) + conn->peer_issuer = err_cert; + else if (depth == 2) + conn->peer_issuer_issuer = err_cert; + + context = conn->context; + match = conn->subject_match; + altmatch = conn->altsubject_match; + suffix_match = conn->suffix_match; + + if (!preverify_ok && !conn->ca_cert_verify) + preverify_ok = 1; + if (!preverify_ok && depth > 0 && conn->server_cert_only) + preverify_ok = 1; + if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) && + (err == X509_V_ERR_CERT_HAS_EXPIRED || + err == X509_V_ERR_CERT_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity " + "time mismatch"); + 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 if (depth == 0 && suffix_match && + !tls_match_suffix(err_cert, suffix_match)) { + wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", + suffix_match); + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain suffix mismatch", + TLS_FAIL_DOMAIN_SUFFIX_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); + } + + if (!conn->server && err_cert && preverify_ok && depth == 0 && + (err_cert->ex_flags & EXFLAG_XKUSAGE) && + (err_cert->ex_xkusage & XKU_SSL_CLIENT)) { + wpa_printf(MSG_WARNING, "TLS: Server used client certificate"); + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Server used client certificate", + TLS_FAIL_SERVER_USED_CLIENT_CERT); + preverify_ok = 0; + } + + if (preverify_ok && context->event_cb != NULL) + context->event_cb(context->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); return preverify_ok; } @@ -1092,6 +1546,47 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, 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); @@ -1120,16 +1615,44 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, X509_free(cert); wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob " "to certificate store", __func__); + return 0; + } + +#ifdef ANDROID + if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { + BIO *bio = BIO_from_keystore(&ca_cert[11]); + STACK_OF(X509_INFO) *stack = NULL; + int i; + + if (bio) { + stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (!stack) + return -1; + + for (i = 0; i < sk_X509_INFO_num(stack); ++i) { + X509_INFO *info = sk_X509_INFO_value(stack, i); + if (info->x509) { + X509_STORE_add_cert(ssl_ctx->cert_store, + info->x509); + } + if (info->crl) { + X509_STORE_add_crl(ssl_ctx->cert_store, + info->crl); + } + } + sk_X509_INFO_pop_free(stack, X509_INFO_free); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); return 0; } +#endif /* ANDROID */ #ifdef CONFIG_NATIVE_WINDOWS if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from " "system certificate store"); - SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); return 0; } #endif /* CONFIG_NATIVE_WINDOWS */ @@ -1152,7 +1675,6 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, "certificate(s) loaded"); tls_get_errors(ssl_ctx); } - SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); @@ -1161,7 +1683,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } else { /* No ca_cert configured - do not try to verify server * certificate */ - SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); + conn->ca_cert_verify = 0; } return 0; @@ -1215,7 +1737,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, - const char *altsubject_match) + const char *altsubject_match, + const char *suffix_match) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -1233,6 +1756,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->suffix_match); + conn->suffix_match = NULL; + if (suffix_match) { + conn->suffix_match = os_strdup(suffix_match); + if (conn->suffix_match == NULL) + return -1; + } + return 0; } @@ -1246,10 +1777,12 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, 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); } @@ -1294,26 +1827,42 @@ static int tls_connection_client_cert(struct tls_connection *conn, if (client_cert == NULL) return -1; +#ifdef ANDROID + if (os_strncmp("keystore://", client_cert, 11) == 0) { + BIO *bio = BIO_from_keystore(&client_cert[11]); + X509 *x509 = NULL; + int ret = -1; + if (bio) { + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (x509) { + if (SSL_use_certificate(conn->ssl, x509) == 1) + ret = 0; + X509_free(x509); + } + return ret; + } +#endif /* ANDROID */ + #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) { + ERR_clear_error(); 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"); } + + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_file failed"); #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); #endif /* OPENSSL_NO_STDIO */ @@ -1330,6 +1879,7 @@ static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert) if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_ASN1) != 1 && + SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 && SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_PEM) != 1) { tls_show_errors(MSG_INFO, __func__, @@ -1581,6 +2131,8 @@ static int tls_connection_engine_ca_cert(void *_ssl_ctx, 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); + conn->ca_cert_verify = 1; + return 0; #else /* OPENSSL_NO_ENGINE */ @@ -1644,10 +2196,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "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, @@ -1657,10 +2205,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "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, @@ -1670,9 +2214,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "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, @@ -1694,10 +2235,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "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, @@ -1706,10 +2243,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "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", @@ -1735,15 +2268,15 @@ static int tls_connection_private_key(void *_ssl_ctx, } if (!ok) { - wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key"); + tls_show_errors(MSG_INFO, __func__, + "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"); @@ -1789,7 +2322,7 @@ static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, 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"); @@ -1951,6 +2484,11 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, struct tls_keys *keys) { +#ifdef CONFIG_FIPS + wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS " + "mode"); + return -1; +#else /* CONFIG_FIPS */ SSL *ssl; if (conn == NULL || keys == NULL) @@ -1968,6 +2506,7 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, keys->server_random_len = SSL3_RANDOM_SIZE; return 0; +#endif /* CONFIG_FIPS */ } @@ -1975,34 +2514,49 @@ 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 OPENSSL_VERSION_NUMBER >= 0x10001000L + SSL *ssl; + if (conn == NULL) + return -1; + if (server_random_first) + return -1; + ssl = conn->ssl; + if (SSL_export_keying_material(ssl, out, out_len, label, + os_strlen(label), NULL, 0, 0) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF"); + return 0; + } +#endif return -1; } -u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) +static struct wpabuf * +openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, + int server) { int res; - u8 *out_data; + struct wpabuf *out_data; - if (appl_data) - *appl_data = NULL; + conn->server = !!server; /* * Give TLS handshake data from the server (if available) to OpenSSL * for processing. */ if (in_data && - BIO_write(conn->ssl_in, in_data, in_len) < 0) { + 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 */ - res = SSL_connect(conn->ssl); + 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) @@ -2020,7 +2574,7 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, /* 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 = os_malloc(res == 0 ? 1 : 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); @@ -2028,10 +2582,10 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); } - *out_len = 0; return NULL; } - res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); + 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"); @@ -2039,169 +2593,169 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); } - *out_len = 0; + wpabuf_free(out_data); return NULL; } - *out_len = res; - - if (SSL_is_init_finished(conn->ssl) && appl_data) { - *appl_data = os_malloc(in_len); - if (*appl_data) { - res = SSL_read(conn->ssl, *appl_data, in_len); - 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"); - } - os_free(*appl_data); - *appl_data = NULL; - } else { - *appl_data_len = res; - wpa_hexdump_key(MSG_MSGDUMP, "SSL: Application" - " Data in Finish message", - *appl_data, *appl_data_len); - } - } - } + wpabuf_put(out_data, res); return out_data; } -u8 * tls_connection_server_handshake(void *ssl_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) +static struct wpabuf * +openssl_get_appl_data(struct tls_connection *conn, size_t max_len) { + struct wpabuf *appl_data; int res; - u8 *out_data; - /* - * Give TLS handshake data from the client (if available) to OpenSSL - * for processing. - */ - if (in_data && - BIO_write(conn->ssl_in, in_data, in_len) < 0) { - tls_show_errors(MSG_INFO, __func__, - "Handshake failed - BIO_write"); + appl_data = wpabuf_alloc(max_len + 100); + if (appl_data == NULL) return NULL; - } - /* Initiate TLS handshake or continue the existing handshake */ - res = SSL_accept(conn->ssl); - if (res != 1) { + 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) - wpa_printf(MSG_DEBUG, "SSL: SSL_accept - want " - "more data"); - else if (err == SSL_ERROR_WANT_WRITE) - wpa_printf(MSG_DEBUG, "SSL: SSL_accept - want to " - "write"); - else { - tls_show_errors(MSG_INFO, __func__, "SSL_accept"); - return NULL; - } - } - - /* Get the TLS handshake data to be sent to the client */ - res = BIO_ctrl_pending(conn->ssl_out); - wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); - out_data = os_malloc(res == 0 ? 1 : res); - if (out_data == NULL) { - wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " - "handshake output (%d bytes)", res); - if (BIO_reset(conn->ssl_out) < 0) { + 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__, - "BIO_reset failed"); + "Failed to read possible " + "Application Data"); } - *out_len = 0; + wpabuf_free(appl_data); return NULL; } - res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); - if (res < 0) { - tls_show_errors(MSG_INFO, __func__, - "Handshake failed - BIO_read"); - if (BIO_reset(conn->ssl_out) < 0) { - tls_show_errors(MSG_INFO, __func__, - "BIO_reset failed"); - } - *out_len = 0; + + 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; - } - *out_len = res; + + 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; } -int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +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 -1; + 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 res; + return NULL; } - res = SSL_write(conn->ssl, in_data, in_len); + 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 res; + return NULL; } /* Read encrypted data to be sent to the server */ - res = BIO_read(conn->ssl_out, out_data, out_len); + 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"); - return res; + wpabuf_free(buf); + return NULL; } + wpabuf_put(buf, res); - return res; + return buf; } -int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +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, in_data, in_len); + 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 res; + return NULL; } if (BIO_reset(conn->ssl_out) < 0) { tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); - return res; + return NULL; } /* Read decrypted data for further processing */ - res = SSL_read(conn->ssl, out_data, out_len); + /* + * 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"); - return res; + wpabuf_free(buf); + return NULL; } + wpabuf_put(buf, res); - return res; + return buf; } @@ -2292,7 +2846,7 @@ int tls_connection_enable_workaround(void *ssl_ctx, } -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) +#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. */ @@ -2315,7 +2869,7 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, return 0; } -#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) @@ -2342,6 +2896,222 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) } +#ifdef HAVE_OCSP + +static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + BIO *out; + size_t rlen; + char *txt; + int res; + + if (wpa_debug_level > MSG_DEBUG) + return; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + OCSP_RESPONSE_print(out, rsp, 0); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return; + } + + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP Response\n%s", txt); + } + os_free(txt); + BIO_free(out); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static int ocsp_resp_cb(SSL *s, void *arg) +{ + struct tls_connection *conn = arg; + const unsigned char *p; + int len, status, reason; + OCSP_RESPONSE *rsp; + OCSP_BASICRESP *basic; + OCSP_CERTID *id; + ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update; + X509_STORE *store; + STACK_OF(X509) *certs = NULL; + + len = SSL_get_tlsext_status_ocsp_resp(s, &p); + if (!p) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); + return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; + } + + wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len); + + rsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if (!rsp) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response"); + return 0; + } + + ocsp_debug_print_resp(rsp); + + status = OCSP_response_status(rsp); + if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)", + status, OCSP_response_status_str(status)); + return 0; + } + + basic = OCSP_response_get1_basic(rsp); + if (!basic) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse"); + return 0; + } + + store = SSL_CTX_get_cert_store(s->ctx); + if (conn->peer_issuer) { + wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer"); + X509_print_fp(stdout, conn->peer_issuer); + + if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: Could not add issuer to certificate store\n"); + } + certs = sk_X509_new_null(); + if (certs) { + X509 *cert; + cert = X509_dup(conn->peer_issuer); + if (cert && !sk_X509_push(certs, cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer to OCSP responder trust store\n"); + X509_free(cert); + sk_X509_free(certs); + certs = NULL; + } + if (conn->peer_issuer_issuer) { + cert = X509_dup(conn->peer_issuer_issuer); + if (cert && !sk_X509_push(certs, cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer to OCSP responder trust store\n"); + X509_free(cert); + } + } + } + } + + status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER); + sk_X509_pop_free(certs, X509_free); + if (status <= 0) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: OCSP response failed verification"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded"); + + if (!conn->peer_cert) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + if (!conn->peer_issuer) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update)) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", + (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : + " (OCSP not required)"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; + } + + if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: OCSP status times invalid"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s", + OCSP_cert_status_str(status)); + + if (status == V_OCSP_CERTSTATUS_GOOD) + return 1; + if (status == V_OCSP_CERTSTATUS_REVOKED) + return 0; + if (conn->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required"); + return 0; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue"); + return 1; +} + + +static int ocsp_status_cb(SSL *s, void *arg) +{ + char *tmp; + char *resp; + size_t len; + + if (tls_global->ocsp_stapling_response == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured"); + return SSL_TLSEXT_ERR_OK; + } + + resp = os_readfile(tls_global->ocsp_stapling_response, &len); + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file"); + /* TODO: Build OCSPResponse with responseStatus = internalError + */ + return SSL_TLSEXT_ERR_OK; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response"); + tmp = OPENSSL_malloc(len); + if (tmp == NULL) { + os_free(resp); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + os_memcpy(tmp, resp, len); + os_free(resp); + SSL_set_tlsext_status_ocsp_resp(s, tmp, len); + + return SSL_TLSEXT_ERR_OK; +} + +#endif /* HAVE_OCSP */ + + int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { @@ -2366,7 +3136,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } if (tls_connection_set_subject_match(conn, params->subject_match, - params->altsubject_match)) + params->altsubject_match, + params->suffix_match)) return -1; if (params->engine && params->ca_cert_id) { @@ -2407,6 +3178,26 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } +#ifdef SSL_OP_NO_TICKET + if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); +#ifdef SSL_clear_options + else + SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ +#endif /* SSL_OP_NO_TICKET */ + +#ifdef HAVE_OCSP + if (params->flags & TLS_CONN_REQUEST_OCSP) { + SSL_CTX *ssl_ctx = tls_ctx; + SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp); + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); + SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn); + } +#endif /* HAVE_OCSP */ + + conn->flags = params->flags; + tls_get_errors(tls_ctx); return 0; @@ -2440,6 +3231,26 @@ int tls_global_set_params(void *tls_ctx, return -1; } +#ifdef SSL_OP_NO_TICKET + if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); +#ifdef SSL_CTX_clear_options + else + SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ +#endif /* SSL_OP_NO_TICKET */ + +#ifdef HAVE_OCSP + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb); + SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx); + os_free(tls_global->ocsp_stapling_response); + if (params->ocsp_stapling_response) + tls_global->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + else + tls_global->ocsp_stapling_response = NULL; +#endif /* HAVE_OCSP */ + return 0; } @@ -2449,6 +3260,7 @@ int tls_connection_get_keyblock_size(void *tls_ctx, { const EVP_CIPHER *c; const EVP_MD *h; + int md_size; if (conn == NULL || conn->ssl == NULL || conn->ssl->enc_read_ctx == NULL || @@ -2462,9 +3274,20 @@ int tls_connection_get_keyblock_size(void *tls_ctx, #else h = conn->ssl->read_hash; #endif + if (h) + md_size = EVP_MD_size(h); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + else if (conn->ssl->s3) + md_size = conn->ssl->s3->tmp.new_mac_secret_size; +#endif + else + return -1; + wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " + "IV_len=%d", EVP_CIPHER_key_length(c), md_size, + EVP_CIPHER_iv_length(c)); return 2 * (EVP_CIPHER_key_length(c) + - EVP_MD_size(h) + + md_size + EVP_CIPHER_iv_length(c)); } @@ -2475,38 +3298,7 @@ unsigned int tls_capabilities(void *tls_ctx) } -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) -{ - return -1; -} - - -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) +#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. */ @@ -2619,7 +3411,7 @@ static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg) } #endif /* SSL_OP_NO_TICKET */ #endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ int tls_connection_set_session_ticket_cb(void *tls_ctx, @@ -2627,7 +3419,7 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, tls_session_ticket_cb cb, void *ctx) { -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; @@ -2665,7 +3457,7 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, } return 0; -#else /* EAP_FAST || EAP_FAST_DYNAMIC */ +#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ return -1; -#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ } diff --git a/contrib/hostapd/src/crypto/tls_schannel.c b/contrib/hostapd/src/crypto/tls_schannel.c index 87e74353dc..2c2daa8a80 100644 --- a/contrib/hostapd/src/crypto/tls_schannel.c +++ b/contrib/hostapd/src/crypto/tls_schannel.c @@ -1,15 +1,9 @@ /* - * WPA Supplicant / SSL/TLS interface functions for Microsoft Schannel - * Copyright (c) 2005, Jouni Malinen + * SSL/TLS interface functions for Microsoft Schannel + * Copyright (c) 2005-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ /* @@ -215,9 +209,8 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, } -static u8 * tls_conn_hs_clienthello(struct tls_global *global, - struct tls_connection *conn, - size_t *out_len) +static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global, + struct tls_connection *conn) { DWORD sspi_flags, sspi_flags_out; SecBufferDesc outbuf; @@ -260,15 +253,14 @@ static u8 * tls_conn_hs_clienthello(struct tls_global *global, } if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { - u8 *buf; + struct wpabuf *buf; wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", outbufs[0].pvBuffer, outbufs[0].cbBuffer); conn->start = 0; - *out_len = outbufs[0].cbBuffer; - buf = os_malloc(*out_len); + buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, + outbufs[0].cbBuffer); if (buf == NULL) return NULL; - os_memcpy(buf, outbufs[0].pvBuffer, *out_len); global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); return buf; } @@ -316,28 +308,27 @@ static int tls_get_eap(struct tls_global *global, struct tls_connection *conn) } -u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) +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 = ssl_ctx; + 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; - u8 *out_buf = NULL; + struct wpabuf *out_buf = NULL; if (appl_data) *appl_data = NULL; - if (conn->start) { - return tls_conn_hs_clienthello(global, conn, out_len); - } + if (conn->start) + return tls_conn_hs_clienthello(global, conn); wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process", - in_len); + (int) wpabuf_len(in_data)); sspi_flags = ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | @@ -346,8 +337,8 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, ISC_REQ_MANUAL_CRED_VALIDATION; /* Input buffer for Schannel */ - inbufs[0].pvBuffer = (u8 *) in_data; - inbufs[0].cbBuffer = in_len; + 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 */ @@ -392,11 +383,8 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { wpa_hexdump(MSG_MSGDUMP, "SChannel - output", outbufs[0].pvBuffer, outbufs[0].cbBuffer); - *out_len = outbufs[0].cbBuffer; - out_buf = os_malloc(*out_len); - if (out_buf) - os_memcpy(out_buf, outbufs[0].pvBuffer, - *out_len); + 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) @@ -420,19 +408,16 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, /* Need to return something to get final TLS ACK. */ if (out_buf == NULL) - out_buf = os_malloc(1); + 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_len = outbufs[1].cbBuffer; - appl_data = os_malloc(*appl_data_len); - if (appl_data) - os_memcpy(appl_data, - outbufs[1].pvBuffer, - *appl_data_len); + *appl_data = wpabuf_alloc_copy( + outbufs[1].pvBuffer, + outbufs[1].cbBuffer); } global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); inbufs[1].pvBuffer = NULL; @@ -470,26 +455,26 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, } -u8 * tls_connection_server_handshake(void *ssl_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) { return NULL; } -int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { - struct tls_global *global = ssl_ctx; + struct tls_global *global = tls_ctx; SECURITY_STATUS status; SecBufferDesc buf; SecBuffer bufs[4]; SecPkgContext_StreamSizes sizes; int i; - size_t total_len; + struct wpabuf *out; status = global->sspi->QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, @@ -497,34 +482,27 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, if (status != SEC_E_OK) { wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed", __func__); - return -1; + return NULL; } wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u", __func__, (unsigned int) sizes.cbHeader, (unsigned int) sizes.cbTrailer); - total_len = sizes.cbHeader + in_len + sizes.cbTrailer; - - if (out_len < total_len) { - wpa_printf(MSG_DEBUG, "%s: too short out_data (out_len=%lu " - "in_len=%lu total_len=%lu)", __func__, - (unsigned long) out_len, (unsigned long) in_len, - (unsigned long) total_len); - return -1; - } + out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) + + sizes.cbTrailer); os_memset(&bufs, 0, sizeof(bufs)); - bufs[0].pvBuffer = out_data; + bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader); bufs[0].cbBuffer = sizes.cbHeader; bufs[0].BufferType = SECBUFFER_STREAM_HEADER; - os_memcpy(out_data + sizes.cbHeader, in_data, in_len); - bufs[1].pvBuffer = out_data + sizes.cbHeader; - bufs[1].cbBuffer = in_len; + 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 = out_data + sizes.cbHeader + in_len; + bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer); bufs[2].cbBuffer = sizes.cbTrailer; bufs[2].BufferType = SECBUFFER_STREAM_TRAILER; @@ -543,7 +521,7 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, (int) bufs[2].cbBuffer, (int) bufs[2].BufferType); wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: " "out_data=%p bufs %p %p %p", - out_data, bufs[0].pvBuffer, bufs[1].pvBuffer, + wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer, bufs[2].pvBuffer); for (i = 0; i < 3; i++) { @@ -556,39 +534,37 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, if (status == SEC_E_OK) { wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); - wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Encrypted data from " - "EncryptMessage", out_data, total_len); - return total_len; + 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); - return -1; + wpabuf_free(out); + return NULL; } -int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { - struct tls_global *global = ssl_ctx; + struct tls_global *global = tls_ctx; SECURITY_STATUS status; SecBufferDesc buf; SecBuffer bufs[4]; int i; + struct wpabuf *out, *tmp; - if (out_len < in_len) { - wpa_printf(MSG_DEBUG, "%s: out_len=%lu < in_len=%lu", __func__, - (unsigned long) out_len, (unsigned long) in_len); - return -1; - } - - wpa_hexdump(MSG_MSGDUMP, "Schannel: Encrypted data to DecryptMessage", - in_data, in_len); + wpa_hexdump_buf(MSG_MSGDUMP, + "Schannel: Encrypted data to DecryptMessage", in_data); os_memset(&bufs, 0, sizeof(bufs)); - os_memcpy(out_data, in_data, in_len); - bufs[0].pvBuffer = out_data; - bufs[0].cbBuffer = in_len; + 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; @@ -611,7 +587,7 @@ int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, (int) bufs[3].cbBuffer, (int) bufs[3].BufferType); wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: " "out_data=%p bufs %p %p %p %p", - out_data, bufs[0].pvBuffer, bufs[1].pvBuffer, + wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer, bufs[2].pvBuffer, bufs[3].pvBuffer); switch (status) { @@ -628,23 +604,21 @@ int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, if (i == 4) { wpa_printf(MSG_DEBUG, "%s: No output data from " "DecryptMessage", __func__); - return -1; + wpabuf_free(tmp); + return NULL; } wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from " "DecryptMessage", bufs[i].pvBuffer, bufs[i].cbBuffer); - if (bufs[i].cbBuffer > out_len) { - wpa_printf(MSG_DEBUG, "%s: Too long output data", - __func__); - return -1; - } - os_memmove(out_data, bufs[i].pvBuffer, bufs[i].cbBuffer); - return 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); - return -1; + wpabuf_free(tmp); + return NULL; } @@ -756,34 +730,3 @@ 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; -} - - -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) -{ - return -1; -} - - -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/contrib/hostapd/src/drivers/Apple80211.h b/contrib/hostapd/src/drivers/Apple80211.h deleted file mode 100644 index 2a612e7308..0000000000 --- a/contrib/hostapd/src/drivers/Apple80211.h +++ /dev/null @@ -1,156 +0,0 @@ -#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/contrib/hostapd/src/drivers/MobileApple80211.c b/contrib/hostapd/src/drivers/MobileApple80211.c deleted file mode 100644 index ce004fe4c9..0000000000 --- a/contrib/hostapd/src/drivers/MobileApple80211.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "includes.h" -#include - -#include "common.h" - -#include -#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/contrib/hostapd/src/drivers/MobileApple80211.h b/contrib/hostapd/src/drivers/MobileApple80211.h deleted file mode 100644 index 64d439d660..0000000000 --- a/contrib/hostapd/src/drivers/MobileApple80211.h +++ /dev/null @@ -1,43 +0,0 @@ -#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/contrib/hostapd/src/drivers/android_drv.h b/contrib/hostapd/src/drivers/android_drv.h new file mode 100644 index 0000000000..31d94408a9 --- /dev/null +++ b/contrib/hostapd/src/drivers/android_drv.h @@ -0,0 +1,56 @@ +/* + * Android driver interface + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ANDROID_DRV_H +#define ANDROID_DRV_H + +#define WPA_EVENT_DRIVER_STATE "CTRL-EVENT-DRIVER-STATE " + +#define MAX_SSID_LEN 32 + +#define MAX_DRV_CMD_SIZE 248 +#define DRV_NUMBER_SEQUENTIAL_ERRORS 4 + +#define WEXT_PNOSETUP_HEADER "PNOSETUP " +#define WEXT_PNOSETUP_HEADER_SIZE 9 +#define WEXT_PNO_TLV_PREFIX 'S' +#define WEXT_PNO_TLV_VERSION '1' +#define WEXT_PNO_TLV_SUBVERSION '2' +#define WEXT_PNO_TLV_RESERVED '0' +#define WEXT_PNO_VERSION_SIZE 4 +#define WEXT_PNO_AMOUNT 16 +#define WEXT_PNO_SSID_SECTION 'S' +/* SSID header size is SSID section type above + SSID length */ +#define WEXT_PNO_SSID_HEADER_SIZE 2 +#define WEXT_PNO_SCAN_INTERVAL_SECTION 'T' +#define WEXT_PNO_SCAN_INTERVAL_LENGTH 2 +#define WEXT_PNO_SCAN_INTERVAL 30 +/* Scan interval size is scan interval section type + scan interval length + * above */ +#define WEXT_PNO_SCAN_INTERVAL_SIZE (1 + WEXT_PNO_SCAN_INTERVAL_LENGTH) +#define WEXT_PNO_REPEAT_SECTION 'R' +#define WEXT_PNO_REPEAT_LENGTH 1 +#define WEXT_PNO_REPEAT 4 +/* Repeat section size is Repeat section type + Repeat value length above */ +#define WEXT_PNO_REPEAT_SIZE (1 + WEXT_PNO_REPEAT_LENGTH) +#define WEXT_PNO_MAX_REPEAT_SECTION 'M' +#define WEXT_PNO_MAX_REPEAT_LENGTH 1 +#define WEXT_PNO_MAX_REPEAT 3 +/* Max Repeat section size is Max Repeat section type + Max Repeat value length + * above */ +#define WEXT_PNO_MAX_REPEAT_SIZE (1 + WEXT_PNO_MAX_REPEAT_LENGTH) +/* This corresponds to the size of all sections expect SSIDs */ +#define WEXT_PNO_NONSSID_SECTIONS_SIZE \ +(WEXT_PNO_SCAN_INTERVAL_SIZE + WEXT_PNO_REPEAT_SIZE + WEXT_PNO_MAX_REPEAT_SIZE) +/* PNO Max command size is total of header, version, ssid and other sections + + * Null termination */ +#define WEXT_PNO_MAX_COMMAND_SIZE \ + (WEXT_PNOSETUP_HEADER_SIZE + WEXT_PNO_VERSION_SIZE \ + + WEXT_PNO_AMOUNT * (WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN) \ + + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) + +#endif /* ANDROID_DRV_H */ diff --git a/contrib/hostapd/src/drivers/driver.h b/contrib/hostapd/src/drivers/driver.h index c2975d2c12..7ad857616c 100644 --- a/contrib/hostapd/src/drivers/driver.h +++ b/contrib/hostapd/src/drivers/driver.h @@ -1,88 +1,186 @@ /* - * WPA Supplicant - driver interface definition - * Copyright (c) 2003-2008, Jouni Malinen + * Driver interface definition + * Copyright (c) 2003-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * - * 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 3 +#define WPA_SUPPLICANT_DRIVER_VERSION 4 + +#include "common/defs.h" +#include "utils/list.h" + +#define HOSTAPD_CHAN_DISABLED 0x00000001 +#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 +#define HOSTAPD_CHAN_NO_IBSS 0x00000004 +#define HOSTAPD_CHAN_RADAR 0x00000008 +#define HOSTAPD_CHAN_HT40PLUS 0x00000010 +#define HOSTAPD_CHAN_HT40MINUS 0x00000020 +#define HOSTAPD_CHAN_HT40 0x00000040 +#define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080 + +#define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000 +#define HOSTAPD_CHAN_DFS_USABLE 0x00000100 +#define HOSTAPD_CHAN_DFS_UNAVAILABLE 0x00000200 +#define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300 +#define HOSTAPD_CHAN_DFS_MASK 0x00000300 + +#define HOSTAPD_CHAN_VHT_10_70 0x00000800 +#define HOSTAPD_CHAN_VHT_30_50 0x00001000 +#define HOSTAPD_CHAN_VHT_50_30 0x00002000 +#define HOSTAPD_CHAN_VHT_70_10 0x00004000 + +enum reg_change_initiator { + REGDOM_SET_BY_CORE, + REGDOM_SET_BY_USER, + REGDOM_SET_BY_DRIVER, + REGDOM_SET_BY_COUNTRY_IE, + REGDOM_BEACON_HINT, +}; + +/** + * struct hostapd_channel_data - Channel information + */ +struct hostapd_channel_data { + /** + * chan - Channel number (IEEE 802.11) + */ + short chan; + + /** + * freq - Frequency in MHz + */ + int freq; + + /** + * flag - Channel flags (HOSTAPD_CHAN_*) + */ + int flag; + + /** + * max_tx_power - Regulatory transmit power limit in dBm + */ + u8 max_tx_power; + + /* + * survey_list - Linked list of surveys + */ + struct dl_list survey_list; + + /** + * min_nf - Minimum observed noise floor, in dBm, based on all + * surveyed channel data + */ + s8 min_nf; + +#ifdef CONFIG_ACS + /** + * interference_factor - Computed interference factor on this + * channel (used internally in src/ap/acs.c; driver wrappers do not + * need to set this) + */ + long double interference_factor; +#endif /* CONFIG_ACS */ +}; + +#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) +#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1) + +/** + * 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; + + /** + * vht_capab - VHT (IEEE 802.11ac) capabilities + */ + u32 vht_capab; + + /** + * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters + */ + u8 vht_mcs_set[8]; -#include "defs.h" + unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ +}; -#define AUTH_ALG_OPEN_SYSTEM 0x01 -#define AUTH_ALG_SHARED_KEY 0x02 -#define AUTH_ALG_LEAP 0x04 #define IEEE80211_MODE_INFRA 0 #define IEEE80211_MODE_IBSS 1 +#define IEEE80211_MODE_AP 2 #define IEEE80211_CAP_ESS 0x0001 #define IEEE80211_CAP_IBSS 0x0002 #define IEEE80211_CAP_PRIVACY 0x0010 -#define SSID_MAX_WPA_IE_LEN 40 -/** - * struct wpa_scan_result - Scan results (old structure) - * @bssid: BSSID - * @ssid: SSID - * @ssid_len: length of the ssid - * @wpa_ie: WPA IE - * @wpa_ie_len: length of the wpa_ie - * @rsn_ie: RSN IE - * @rsn_ie_len: length of the RSN IE - * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) - * @caps: capability information field in host byte order - * @qual: signal quality - * @noise: noise level - * @level: signal level - * @maxrate: maximum supported rate - * @mdie_present: Whether MDIE was included in Beacon/ProbeRsp frame - * @mdie: Mobility domain identifier IE (IEEE 802.11r MDIE) (starting from - * IE type field) - * @tsf: Timestamp - * - * 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. - * - * This structure is the old data structure used for scan results. It is - * obsoleted by the new struct wpa_scan_res structure and the old version is - * only included for backwards compatibility with existing driver wrapper - * implementations. New implementations are encouraged to implement for struct - * wpa_scan_res. The old structure will be removed at some point. - */ -struct wpa_scan_result { - u8 bssid[ETH_ALEN]; - u8 ssid[32]; - size_t ssid_len; - u8 wpa_ie[SSID_MAX_WPA_IE_LEN]; - size_t wpa_ie_len; - u8 rsn_ie[SSID_MAX_WPA_IE_LEN]; - size_t rsn_ie_len; - int freq; - u16 caps; - int qual; - int noise; - int level; - int maxrate; - int mdie_present; - u8 mdie[5]; - u64 tsf; -}; +/* DMG (60 GHz) IEEE 802.11ad */ +/* type - bits 0..1 */ +#define IEEE80211_CAP_DMG_MASK 0x0003 +#define IEEE80211_CAP_DMG_IBSS 0x0001 /* Tx by: STA */ +#define IEEE80211_CAP_DMG_PBSS 0x0002 /* Tx by: PCP */ +#define IEEE80211_CAP_DMG_AP 0x0003 /* Tx by: AP */ +#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) @@ -91,7 +189,10 @@ struct wpa_scan_result { * @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 @@ -103,6 +204,7 @@ struct wpa_scan_result { * 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; @@ -111,18 +213,28 @@ struct wpa_scan_res { int noise; int level; u64 tsf; + unsigned int age; size_t ie_len; - /* followed by ie_len octets of IEs */ + 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 + * @fetch_time: Time when the results were fetched from the driver */ struct wpa_scan_results { struct wpa_scan_res **res; size_t num; + struct os_reltime fetch_time; }; /** @@ -142,6 +254,144 @@ struct wpa_interface_info { const char *drv_name; }; +#define WPAS_MAX_SCAN_SSIDS 16 + +/** + * 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; + + /** + * filter_rssi - Filter by RSSI + * + * The driver may filter scan results in firmware to reduce host + * wakeups and thereby save power. Specify the RSSI threshold in s32 + * dBm. + */ + s32 filter_rssi; + + /** + * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes + * + * When set, the driver is expected to remove rates 1, 2, 5.5, and 11 + * Mbps from the support rates element(s) in the Probe Request frames + * and not to transmit the frames at any of those rates. + */ + u8 p2p_probe; + + /** + * only_new_results - Request driver to report only new results + * + * This is used to request the driver to report only BSSes that have + * been detected after this scan request has been started, i.e., to + * flush old cached BSS entries. + */ + int only_new_results; + + /* + * NOTE: Whenever adding new parameters here, please make sure + * wpa_scan_clone_params() and wpa_scan_free_params() get updated with + * matching changes. + */ +}; + +/** + * 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; + + /** + * p2p - Whether this connection is a P2P group + */ + int p2p; + + const u8 *sae_data; + size_t sae_data_len; + +}; + +enum wps_mode { + WPS_MODE_NONE /* no WPS provisioning being used */, + WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */, + WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection + */ +}; + /** * struct wpa_driver_associate_params - Association parameters * Data for struct wpa_driver_ops::associate(). @@ -157,6 +407,10 @@ struct wpa_driver_associate_params { * ssid - The selected SSID */ const u8 *ssid; + + /** + * ssid_len - Length of the SSID (1..32) + */ size_t ssid_len; /** @@ -166,6 +420,13 @@ struct wpa_driver_associate_params { */ int freq; + /** + * bg_scan_period - Background scan period in seconds, 0 to disable + * background scan, or -1 to indicate no change to default driver + * configuration + */ + int bg_scan_period; + /** * wpa_ie - WPA information element for (Re)Association Request * WPA information element to be included in (Re)Association @@ -185,20 +446,41 @@ struct wpa_driver_associate_params { * 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; - /* The selected pairwise/group cipher and key management - * suites. These are usually ignored if @wpa_ie is used. */ - wpa_cipher pairwise_suite; - wpa_cipher group_suite; - wpa_key_mgmt key_mgmt_suite; + /** + * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2 + */ + unsigned int wpa_proto; + + /** + * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*) + * + * This is usually ignored if @wpa_ie is used. + */ + unsigned int pairwise_suite; + + /** + * group_suite - Selected group cipher suite (WPA_CIPHER_*) + * + * This is usually ignored if @wpa_ie is used. + */ + unsigned int group_suite; + + /** + * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*) + * + * This is usually ignored if @wpa_ie is used. + */ + unsigned int key_mgmt_suite; /** * auth_alg - Allowed authentication algorithms - * Bit field of AUTH_ALG_* + * Bit field of WPA_AUTH_ALG_* */ int auth_alg; @@ -225,11 +507,7 @@ struct wpa_driver_associate_params { /** * mgmt_frame_protection - IEEE 802.11w management frame protection */ - enum { - NO_MGMT_FRAME_PROTECTION, - MGMT_FRAME_PROTECTION_OPTIONAL, - MGMT_FRAME_PROTECTION_REQUIRED - } mgmt_frame_protection; + enum mfp_options mgmt_frame_protection; /** * ft_ies - IEEE 802.11r / FT information elements @@ -288,106 +566,692 @@ struct wpa_driver_associate_params { * be prepared to handle %NULL value as an error. */ const u8 *psk; -}; -/** - * 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; + /** + * 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; -#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; + /** + * 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; -#define WPA_DRIVER_AUTH_OPEN 0x00000001 -#define WPA_DRIVER_AUTH_SHARED 0x00000002 -#define WPA_DRIVER_AUTH_LEAP 0x00000004 - unsigned int auth; + /** + * wps - WPS mode + * + * If the driver needs to do special configuration for WPS association, + * this variable provides more information on what type of association + * is being requested. Most drivers should not need ot use this. + */ + enum wps_mode wps; -/* Driver generated WPA/RSN IE */ -#define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 -#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 - unsigned int flags; -}; + /** + * p2p - Whether this connection is a P2P group + */ + int p2p; + /** + * uapsd - UAPSD parameters for the network + * -1 = do not change defaults + * AP mode: 1 = enabled, 0 = disabled + * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE + */ + int uapsd; -#define WPA_CHAN_W_SCAN 0x00000001 -#define WPA_CHAN_W_ACTIVE_SCAN 0x00000002 -#define WPA_CHAN_W_IBSS 0x00000004 + /** + * fixed_bssid - Whether to force this BSSID in IBSS mode + * 1 = Fix this BSSID and prevent merges. + * 0 = Do not fix BSSID. + */ + int fixed_bssid; -struct wpa_channel_data { - short chan; /* channel number (IEEE 802.11) */ - short freq; /* frequency in MHz */ - int flag; /* flag for user space use (WPA_CHAN_*) */ -}; + /** + * disable_ht - Disable HT (IEEE 802.11n) for this connection + */ + int disable_ht; -#define WPA_RATE_ERP 0x00000001 -#define WPA_RATE_BASIC 0x00000002 -#define WPA_RATE_PREAMBLE2 0x00000004 -#define WPA_RATE_SUPPORTED 0x00000010 -#define WPA_RATE_OFDM 0x00000020 -#define WPA_RATE_CCK 0x00000040 -#define WPA_RATE_MANDATORY 0x00000100 - -struct wpa_rate_data { - int rate; /* rate in 100 kbps */ - int flags; /* WPA_RATE_ flags */ -}; + /** + * HT Capabilities over-rides. Only bits set in the mask will be used, + * and not all values are used by the kernel anyway. Currently, MCS, + * MPDU and MSDU fields are used. + */ + const u8 *htcaps; /* struct ieee80211_ht_capabilities * */ + const u8 *htcaps_mask; /* struct ieee80211_ht_capabilities * */ -typedef enum { - WPA_MODE_IEEE80211B, - WPA_MODE_IEEE80211G, - WPA_MODE_IEEE80211A, - NUM_WPA_MODES -} wpa_hw_mode; +#ifdef CONFIG_VHT_OVERRIDES + /** + * disable_vht - Disable VHT for this connection + */ + int disable_vht; -struct wpa_hw_modes { - wpa_hw_mode mode; - int num_channels; - struct wpa_channel_data *channels; - int num_rates; - struct wpa_rate_data *rates; + /** + * VHT capability overrides. + */ + const struct ieee80211_vht_capabilities *vhtcaps; + const struct ieee80211_vht_capabilities *vhtcaps_mask; +#endif /* CONFIG_VHT_OVERRIDES */ }; - -struct ieee80211_rx_status { - int channel; - int ssi; +enum hide_ssid { + NO_SSID_HIDING, + HIDDEN_SSID_ZERO_LEN, + HIDDEN_SSID_ZERO_CONTENTS }; +struct wpa_driver_ap_params { + /** + * head - Beacon head from IEEE 802.11 header to IEs before TIM IE + */ + u8 *head; -/** - * 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; + /** + * head_len - Length of the head buffer in octets + */ + size_t head_len; /** - * get_bssid - Get the current BSSID - * @priv: private driver interface data - * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes) - * + * tail - Beacon tail following TIM IE + */ + u8 *tail; + + /** + * tail_len - Length of the tail buffer in octets + */ + size_t tail_len; + + /** + * dtim_period - DTIM period + */ + int dtim_period; + + /** + * beacon_int - Beacon interval + */ + int beacon_int; + + /** + * basic_rates: -1 terminated array of basic rates in 100 kbps + * + * This parameter can be used to set a specific basic rate set for the + * BSS. If %NULL, default basic rate set is used. + */ + int *basic_rates; + + /** + * proberesp - Probe Response template + * + * This is used by drivers that reply to Probe Requests internally in + * AP mode and require the full Probe Response template. + */ + u8 *proberesp; + + /** + * proberesp_len - Length of the proberesp buffer in octets + */ + size_t proberesp_len; + + /** + * ssid - The SSID to use in Beacon/Probe Response frames + */ + const u8 *ssid; + + /** + * ssid_len - Length of the SSID (1..32) + */ + size_t ssid_len; + + /** + * hide_ssid - Whether to hide the SSID + */ + enum hide_ssid hide_ssid; + + /** + * pairwise_ciphers - WPA_CIPHER_* bitfield + */ + unsigned int pairwise_ciphers; + + /** + * group_cipher - WPA_CIPHER_* + */ + unsigned int group_cipher; + + /** + * key_mgmt_suites - WPA_KEY_MGMT_* bitfield + */ + unsigned int key_mgmt_suites; + + /** + * auth_algs - WPA_AUTH_ALG_* bitfield + */ + unsigned int auth_algs; + + /** + * wpa_version - WPA_PROTO_* bitfield + */ + unsigned int wpa_version; + + /** + * privacy - Whether privacy is used in the BSS + */ + int privacy; + + /** + * beacon_ies - WPS/P2P IE(s) for Beacon frames + * + * This is used to add IEs like WPS IE and P2P IE by drivers that do + * not use the full Beacon template. + */ + const struct wpabuf *beacon_ies; + + /** + * proberesp_ies - P2P/WPS IE(s) for Probe Response frames + * + * This is used to add IEs like WPS IE and P2P IE by drivers that + * reply to Probe Request frames internally. + */ + const struct wpabuf *proberesp_ies; + + /** + * assocresp_ies - WPS IE(s) for (Re)Association Response frames + * + * This is used to add IEs like WPS IE by drivers that reply to + * (Re)Association Request frames internally. + */ + const struct wpabuf *assocresp_ies; + + /** + * isolate - Whether to isolate frames between associated stations + * + * If this is non-zero, the AP is requested to disable forwarding of + * frames between associated stations. + */ + int isolate; + + /** + * cts_protect - Whether CTS protection is enabled + */ + int cts_protect; + + /** + * preamble - Whether short preamble is enabled + */ + int preamble; + + /** + * short_slot_time - Whether short slot time is enabled + * + * 0 = short slot time disable, 1 = short slot time enabled, -1 = do + * not set (e.g., when 802.11g mode is not in use) + */ + int short_slot_time; + + /** + * ht_opmode - HT operation mode or -1 if HT not in use + */ + int ht_opmode; + + /** + * interworking - Whether Interworking is enabled + */ + int interworking; + + /** + * hessid - Homogeneous ESS identifier or %NULL if not set + */ + const u8 *hessid; + + /** + * access_network_type - Access Network Type (0..15) + * + * This is used for filtering Probe Request frames when Interworking is + * enabled. + */ + u8 access_network_type; + + /** + * ap_max_inactivity - Timeout in seconds to detect STA's inactivity + * + * This is used by driver which advertises this capability. + */ + int ap_max_inactivity; + + /** + * disable_dgaf - Whether group-addressed frames are disabled + */ + int disable_dgaf; +}; + +/** + * 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 +#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080 + 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 +#define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010 +#define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020 +#define WPA_DRIVER_CAPA_ENC_GCMP_256 0x00000040 +#define WPA_DRIVER_CAPA_ENC_CCMP_256 0x00000080 +#define WPA_DRIVER_CAPA_ENC_BIP 0x00000100 +#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128 0x00000200 +#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256 0x00000400 +#define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256 0x00000800 + 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 +/* unused: 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 +/* unused: 0x00000100 */ +/* Driver supports concurrent P2P operations */ +#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200 +/* + * Driver uses the initial interface as a dedicated management interface, i.e., + * it cannot be used for P2P group operations or non-P2P purposes. + */ +#define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400 +/* This interface is P2P capable (P2P GO or P2P Client) */ +#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 +/* unused: 0x00001000 */ +/* + * Driver uses the initial interface for P2P management interface and non-P2P + * purposes (e.g., connect to infra AP), but this interface cannot be used for + * P2P group operations. + */ +#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000 +/* + * Driver is known to use sane error codes, i.e., when it indicates that + * something (e.g., association) fails, there was indeed a failure and the + * operation does not end up getting completed successfully later. + */ +#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000 +/* Driver supports off-channel TX */ +#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000 +/* Driver indicates TX status events for EAPOL Data frames */ +#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000 +/* Driver indicates TX status events for Deauth/Disassoc frames */ +#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000 +/* Driver supports roaming (BSS selection) in firmware */ +#define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000 +/* Driver supports operating as a TDLS peer */ +#define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000 +/* Driver requires external TDLS setup/teardown/discovery */ +#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000 +/* Driver indicates support for Probe Response offloading in AP mode */ +#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000 +/* Driver supports U-APSD in AP mode */ +#define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000 +/* Driver supports inactivity timer in AP mode */ +#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000 +/* Driver expects user space implementation of MLME in AP mode */ +#define WPA_DRIVER_FLAGS_AP_MLME 0x01000000 +/* Driver supports SAE with user space SME */ +#define WPA_DRIVER_FLAGS_SAE 0x02000000 +/* Driver makes use of OBSS scan mechanism in wpa_supplicant */ +#define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000 +/* Driver supports IBSS (Ad-hoc) mode */ +#define WPA_DRIVER_FLAGS_IBSS 0x08000000 +/* Driver supports radar detection */ +#define WPA_DRIVER_FLAGS_RADAR 0x10000000 +/* Driver supports a dedicated interface for P2P Device */ +#define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000 +/* Driver supports QoS Mapping */ +#define WPA_DRIVER_FLAGS_QOS_MAPPING 0x40000000 +/* Driver supports CSA in AP mode */ +#define WPA_DRIVER_FLAGS_AP_CSA 0x80000000 + unsigned int flags; + + int max_scan_ssids; + int max_sched_scan_ssids; + int sched_scan_supported; + int max_match_sets; + + /** + * max_remain_on_chan - Maximum remain-on-channel duration in msec + */ + unsigned int max_remain_on_chan; + + /** + * max_stations - Maximum number of associated stations the driver + * supports in AP mode + */ + unsigned int max_stations; + + /** + * probe_resp_offloads - Bitmap of supported protocols by the driver + * for Probe Response offloading. + */ +/* Driver Probe Response offloading support for WPS ver. 1 */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001 +/* Driver Probe Response offloading support for WPS ver. 2 */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002 +/* Driver Probe Response offloading support for P2P */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004 +/* Driver Probe Response offloading support for IEEE 802.11u (Interworking) */ +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008 + unsigned int probe_resp_offloads; + + unsigned int max_acl_mac_addrs; + + /** + * Number of supported concurrent channels + */ + unsigned int num_multichan_concurrent; + + /** + * extended_capa - extended capabilities in driver/device + * + * Must be allocated and freed by driver and the pointers must be + * valid for the lifetime of the driver, i.e., freed in deinit() + */ + const u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; +}; + + +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; + const struct ieee80211_vht_capabilities *vht_capabilities; + u32 flags; /* bitmask of WPA_STA_* flags */ + int set; /* Set STA parameters instead of add */ + u8 qosinfo; + const u8 *ext_capab; + size_t ext_capab_len; + const u8 *supp_channels; + size_t supp_channels_len; + const u8 *supp_oper_classes; + size_t supp_oper_classes_len; +}; + +struct hostapd_freq_params { + int mode; + int freq; + int channel; + /* for HT */ + int ht_enabled; + int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, + * secondary channel below primary, 1 = HT40 + * enabled, secondary channel above primary */ + + /* for VHT */ + int vht_enabled; + + /* valid for both HT and VHT, center_freq2 is non-zero + * only for bandwidth 80 and an 80+80 channel */ + int center_freq1, center_freq2; + int bandwidth; +}; + +struct mac_address { + u8 addr[ETH_ALEN]; +}; + +struct hostapd_acl_params { + u8 acl_policy; + unsigned int num_mac_acl; + struct mac_address mac_acl[0]; +}; + +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, + + /** + * WPA_IF_P2P_GO - P2P Group Owner + */ + WPA_IF_P2P_GO, + + /** + * WPA_IF_P2P_CLIENT - P2P Client + */ + WPA_IF_P2P_CLIENT, + + /** + * WPA_IF_P2P_GROUP - P2P Group interface (will become either + * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known) + */ + WPA_IF_P2P_GROUP, + + /** + * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the + * abstracted P2P Device function in the driver + */ + WPA_IF_P2P_DEVICE +}; + +struct wpa_init_params { + void *global_priv; + 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; + enum mfp_options ieee80211w; +}; + +#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) +#define WPA_STA_TDLS_PEER BIT(4) + +enum tdls_oper { + TDLS_DISCOVERY_REQ, + TDLS_SETUP, + TDLS_TEARDOWN, + TDLS_ENABLE_LINK, + TDLS_DISABLE_LINK, + TDLS_ENABLE, + TDLS_DISABLE +}; + +enum wnm_oper { + WNM_SLEEP_ENTER_CONFIRM, + WNM_SLEEP_ENTER_FAIL, + WNM_SLEEP_EXIT_CONFIRM, + WNM_SLEEP_EXIT_FAIL, + WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */ + WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */ + WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for + * a STA */ + WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE + * for a STA */ + WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */ + WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE + * for a STA */ + WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */ +}; + +/* enum chan_width - Channel width definitions */ +enum chan_width { + CHAN_WIDTH_20_NOHT, + CHAN_WIDTH_20, + CHAN_WIDTH_40, + CHAN_WIDTH_80, + CHAN_WIDTH_80P80, + CHAN_WIDTH_160, + CHAN_WIDTH_UNKNOWN +}; + +/** + * struct wpa_signal_info - Information about channel signal quality + */ +struct wpa_signal_info { + u32 frequency; + int above_threshold; + int current_signal; + int avg_signal; + int current_noise; + int current_txrate; + enum chan_width chanwidth; + int center_frq1; + int center_frq2; +}; + +/** + * struct beacon_data - Beacon data + * @head: Head portion of Beacon frame (before TIM IE) + * @tail: Tail portion of Beacon frame (after TIM IE) + * @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL + * @proberesp_ies: Extra information element(s) to add into Probe Response + * frames or %NULL + * @assocresp_ies: Extra information element(s) to add into (Re)Association + * Response frames or %NULL + * @probe_resp: Probe Response frame template + * @head_len: Length of @head + * @tail_len: Length of @tail + * @beacon_ies_len: Length of beacon_ies in octets + * @proberesp_ies_len: Length of proberesp_ies in octets + * @proberesp_ies_len: Length of proberesp_ies in octets + * @probe_resp_len: Length of probe response template (@probe_resp) + */ +struct beacon_data { + u8 *head, *tail; + u8 *beacon_ies; + u8 *proberesp_ies; + u8 *assocresp_ies; + u8 *probe_resp; + + size_t head_len, tail_len; + size_t beacon_ies_len; + size_t proberesp_ies_len; + size_t assocresp_ies_len; + size_t probe_resp_len; +}; + +/** + * struct csa_settings - Settings for channel switch command + * @cs_count: Count in Beacon frames (TBTT) to perform the switch + * @block_tx: 1 - block transmission for CSA period + * @freq_params: Next channel frequency parameter + * @beacon_csa: Beacon/probe resp/asooc resp info for CSA period + * @beacon_after: Next beacon/probe resp/asooc resp info + * @counter_offset_beacon: Offset to the count field in beacon's tail + * @counter_offset_presp: Offset to the count field in probe resp. + */ +struct csa_settings { + u8 cs_count; + u8 block_tx; + + struct hostapd_freq_params freq_params; + struct beacon_data beacon_csa; + struct beacon_data beacon_after; + + u16 counter_offset_beacon; + u16 counter_offset_presp; +}; + +/** + * 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. @@ -413,38 +1277,22 @@ struct wpa_driver_ops { */ int (*get_ssid)(void *priv, u8 *ssid); - /** - * set_wpa - Enable/disable WPA support (OBSOLETE) - * @priv: private driver interface data - * @enabled: 1 = enable, 0 = disable - * - * Returns: 0 on success, -1 on failure - * - * Note: This function is included for backwards compatibility. This is - * called only just after init and just before deinit, so these - * functions can be used to implement same functionality and the driver - * interface need not define this function. - * - * Configure the kernel driver to enable/disable WPA support. This may - * be empty function, if WPA support is always enabled. Common - * configuration items are WPA IE (clearing it when WPA support is - * disabled), Privacy flag configuration for capability field (note: - * this the value need to set in associate handler to allow plaintext - * mode to be used) when trying to associate with, roaming mode (can - * allow wpa_supplicant to control roaming if ap_scan=1 is used; - * however, drivers can also implement roaming if desired, especially - * ap_scan=2 mode is used for this). - */ - int (*set_wpa)(void *priv, int enabled); - /** * 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_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK, + * %WPA_ALG_GCMP, %WPA_ALG_GCMP_256, %WPA_ALG_CCMP_256, + * %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256, + * %WPA_ALG_BIP_CMAC_256); * %WPA_ALG_NONE clears the key. - * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for - * broadcast/default keys + * @addr: Address of the peer STA (BSSID of the current AP when setting + * pairwise key in station mode), ff:ff:ff:ff:ff:ff for + * broadcast keys, %NULL for default keys that are used both for + * broadcast and unicast; when clearing keys, %NULL is used to + * indicate that both the broadcast-only and default key of the + * specified key index is to be cleared * @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 @@ -452,13 +1300,13 @@ struct wpa_driver_ops { * @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) + * keys and set to zero for unicast keys); %NULL if not set * @seq_len: length of the seq, depends on the algorithm: - * TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets + * TKIP: 6 octets, CCMP/GCMP: 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) + * TKIP: 32, CCMP/GCMP: 16, IGTK: 16) * * Returns: 0 on success, -1 on failure * @@ -475,13 +1323,14 @@ struct wpa_driver_ops { * 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 + * will trigger 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)(void *priv, wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, const u8 *seq, size_t seq_len, + 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); /** @@ -543,76 +1392,16 @@ struct wpa_driver_ops { int (*set_countermeasures)(void *priv, int enabled); /** - * set_drop_unencrypted - Enable/disable unencrypted frame filtering + * deauthenticate - Request driver to deauthenticate * @priv: private driver interface data - * @enabled: 1 = unencrypted Tx/Rx frames will be dropped, 0 = disabled - * - * Returns: 0 on success, -1 on failure - * - * Configure the driver to drop all non-EAPOL frames (both receive and - * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must - * still be allowed for key negotiation. - */ - int (*set_drop_unencrypted)(void *priv, int enabled); - - /** - * scan - Request the driver to initiate scan - * @priv: private driver interface data - * @ssid: specific SSID to scan for (ProbeReq) or %NULL to scan for - * all SSIDs (either active scan with broadcast SSID or passive - * scan - * @ssid_len: length of the SSID - * - * 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_results(). - */ - int (*scan)(void *priv, const u8 *ssid, size_t ssid_len); - - /** - * get_scan_results - Fetch the latest scan results (old version) - * @priv: private driver interface data - * @results: pointer to buffer for scan results - * @max_size: maximum number of entries (buffer size) - * - * Returns: Number of scan result entries used on success, -1 on - * failure - * - * If scan results include more than max_size BSSes, max_size will be - * returned and the remaining entries will not be included in the - * buffer. - * - * This function is depracated. New driver wrapper implementations - * should implement support for get_scan_results2(). - */ - int (*get_scan_results)(void *priv, - struct wpa_scan_result *results, - size_t max_size); - - /** - * deauthenticate - Request driver to deauthenticate - * @priv: private driver interface data - * @addr: peer address (BSSID of the AP) - * @reason_code: 16-bit reason code to be sent in the deauthentication - * frame + * @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 @@ -623,27 +1412,6 @@ struct wpa_driver_ops { int (*associate)(void *priv, struct wpa_driver_associate_params *params); - /** - * set_auth_alg - Set IEEE 802.11 authentication algorithm - * @priv: private driver interface data - * @auth_alg: bit field of AUTH_ALG_* - * - * If the driver supports more than one authentication algorithm at the - * same time, it should configure all supported algorithms. If not, one - * algorithm needs to be selected arbitrarily. Open System - * authentication should be ok for most cases and it is recommended to - * be used if other options are not supported. Static WEP configuration - * may also use Shared Key authentication and LEAP requires its own - * algorithm number. For LEAP, user can make sure that only one - * algorithm is used at a time by configuring LEAP as the only - * supported EAP method. This information is also available in - * associate() params, so set_auth_alg may not be needed in case of - * most drivers. - * - * Returns: 0 on success, -1 on failure - */ - int (*set_auth_alg)(void *priv, int auth_alg); - /** * add_pmkid - Add PMKSA cache entry to the driver * @priv: private driver interface data @@ -764,9 +1532,9 @@ struct wpa_driver_ops { * 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 by calling - * wpa_supplicant_rx_eapol(). The driver interface is required to - * implement get_mac_addr() handler if send_eapol() is used. + * 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); @@ -811,91 +1579,21 @@ struct wpa_driver_ops { * 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. - */ - struct wpa_hw_modes * (*get_hw_feature_data)(void *priv, - u16 *num_modes, - u16 *flags); - - /** - * set_channel - Set channel - * @priv: Private driver interface data - * @phymode: WPA_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, wpa_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); + struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, + u16 *num_modes, + u16 *flags); /** * 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 + * @noack: Do not wait for this frame to be acked (disable retries) * 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); + int (*send_mlme)(void *priv, const u8 *data, size_t data_len, + int noack); /** * update_ft_ies - Update FT (IEEE 802.11r) IEs @@ -940,29 +1638,6 @@ struct wpa_driver_ops { */ struct wpa_scan_results * (*get_scan_results2)(void *priv); - /** - * set_probe_req_ie - Set information element(s) for Probe Request - * @priv: private driver interface data - * @ies: Information elements to append or %NULL to remove extra IEs - * @ies_len: Length of the IE buffer in octets - * Returns: 0 on success, -1 on failure - */ - int (*set_probe_req_ie)(void *priv, const u8 *ies, size_t ies_len); - - /** - * set_mode - Request driver to set the operating mode - * @priv: private driver interface data - * @mode: Operation mode (infra/ibss) IEEE80211_MODE_* - * - * This handler will be called before any key configuration and call to - * associate() handler in order to allow the operation mode to be - * configured as early as possible. This information is also available - * in associate() params and as such, some driver wrappers may not need - * to implement set_mode() handler. - * Returns: 0 on success, -1 on failure - */ - int (*set_mode)(void *priv, int mode); - /** * set_country - Set country * @priv: Private driver interface data @@ -974,6 +1649,14 @@ struct wpa_driver_ops { */ int (*set_country)(void *priv, const char *alpha2); + /** + * get_country - Get country + * @priv: Private driver interface data + * @alpha2: Buffer for returning country code (at least 3 octets) + * Returns: 0 on success, -1 on failure + */ + int (*get_country)(void *priv, char *alpha2); + /** * global_init - Global driver initialization * Returns: Pointer to private data (global), %NULL on failure @@ -1017,138 +1700,1499 @@ struct wpa_driver_ops { * failure */ struct wpa_interface_info * (*get_interfaces)(void *global_priv); -}; -/* Function to check whether a driver is for wired connections */ -static inline int IS_WIRED(const struct wpa_driver_ops *drv) -{ - return os_strcmp(drv->name, "wired") == 0 || - os_strcmp(drv->name, "roboswitch") == 0; -} + /** + * 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); -/** - * enum wpa_event_type - Event type for wpa_supplicant_event() calls - */ -typedef enum wpa_event_type { /** - * EVENT_ASSOC - Association completed + * authenticate - Request driver to authenticate + * @priv: private driver interface data + * @params: authentication parameters + * Returns: 0 on success, -1 on failure * - * 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. + * 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. */ - EVENT_ASSOC, + int (*authenticate)(void *priv, + struct wpa_driver_auth_params *params); /** - * EVENT_DISASSOC - Association lost + * set_ap - Set Beacon and Probe Response information for AP mode + * @priv: Private driver interface data + * @params: Parameters to use in AP mode + * + * This function is used to configure Beacon template and/or extra IEs + * to add for Beacon and Probe Response frames 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. Depending on the + * driver architectue, this can be done either by using the full + * template or the set of additional IEs (e.g., WPS and P2P IE). + * Similarly, Probe Response processing depends on the driver design. + * If the driver (or firmware) takes care of replying to Probe Request + * frames, the extra IEs provided here needs to be added to the Probe + * Response frames. * - * 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. + * Returns: 0 on success, -1 on failure */ - EVENT_DISASSOC, + int (*set_ap)(void *priv, struct wpa_driver_ap_params *params); /** - * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected + * set_acl - Set ACL in AP mode + * @priv: Private driver interface data + * @params: Parameters to configure ACL + * Returns: 0 on success, -1 on failure * - * 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. + * This is used only for the drivers which support MAC address ACL. */ - EVENT_MICHAEL_MIC_FAILURE, + int (*set_acl)(void *priv, struct hostapd_acl_params *params); /** - * EVENT_SCAN_RESULTS - Scan results available + * 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 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. + * This function is used instead of init() or init2() when the driver + * wrapper is used with hostapd. */ - EVENT_SCAN_RESULTS, + void * (*hapd_init)(struct hostapd_data *hapd, + struct wpa_init_params *params); /** - * EVENT_ASSOCINFO - Report optional extra information for association + * 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 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. + * 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_ap() to set WPA/RSN IE + * for Beacon frames. + * + * DEPRECATED - use set_ap() instead */ - EVENT_ASSOCINFO, + int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params); /** - * EVENT_INTERFACE_STATUS - Report interface status changes + * 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 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. + * 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_ap(). + * + * DEPRECATED - use set_ap() instead + */ + 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, GCMP, 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_ap(). + * + * DEPRECATED - use set_ap() instead + */ + int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); + + /** + * read_sta_data - Fetch station data + * @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 + * @flags: WPA_STA_* flags for the destination station + * + * 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, u32 flags); + + /** + * 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_ap() 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 + * + * DEPRECATED - use set_ap() instead + */ + 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. + * + * With TDLS, this function is also used to add or set (params->set 1) + * TDLS peer entries. + */ + 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_tx_queue_params - Set TX queue parameters + * @priv: Private driver interface data + * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK) + * @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); + + /** + * 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) + * @bridge: Bridge interface to use or %NULL if no bridge configured + * @use_existing: Whether to allow existing interface to be used + * 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, + const char *bridge, int use_existing); + + /** + * 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_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) + * @assocresp: WPS IE(s) for (Re)Association 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_ap() + * and does not process Probe Request frames. If the driver takes care + * of (Re)Association frame processing, the assocresp buffer includes + * WPS IE(s) that need to be added to (Re)Association Response frames + * whenever a (Re)Association Request frame indicated use of WPS. + * + * This will also be used to add P2P IE(s) into Beacon/Probe Response + * frames when operating as a GO. The driver is responsible for adding + * timing related attributes (e.g., NoA) in addition to the IEs + * included here by appending them after these buffers. This call is + * also used to provide Probe Response IEs for P2P Listen state + * operations for drivers that generate the Probe Response frames + * internally. + * + * DEPRECATED - use set_ap() instead + */ + int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp); + + /** + * 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 + * @bridge_ifname: Bridge interface to use for the WDS station or %NULL + * to indicate that bridge is not to be used + * @ifname_wds: Buffer to return the interface name for the new WDS + * station or %NULL to indicate name is not returned. + * Returns: 0 on success, -1 on failure + */ + int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val, + const char *bridge_ifname, char *ifname_wds); + + /** + * send_action - Transmit an Action frame + * @priv: Private driver interface data + * @freq: Frequency (in MHz) of the channel + * @wait: Time to wait off-channel for a response (in ms), or zero + * @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 + @ @no_cck: Whether CCK rates must not be used to transmit this frame + * 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 the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will + * be transmitted on the given channel and the device will wait for a + * response on that channel for the given wait time. + * + * If the flag is not set, the wait time will be ignored. In this case, + * if a remain-on-channel duration is in progress, the frame must be + * transmitted on that channel; alternatively the frame may be sent on + * the current operational channel (if in associated state in station + * mode or while operating as an AP.) + */ + int (*send_action)(void *priv, unsigned int freq, unsigned int wait, + const u8 *dst, const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, int no_cck); + + /** + * send_action_cancel_wait - Cancel action frame TX wait + * @priv: Private driver interface data + * + * This command cancels the wait time associated with sending an action + * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is + * set in the driver flags. + */ + void (*send_action_cancel_wait)(void *priv); + + /** + * 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_MGMT 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); + + /** + * 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. If the interface was not dynamically added, + * change the driver mode to station mode to allow normal station + * operations like scanning to be completed. + */ + int (*deinit_ap)(void *priv); + + /** + * deinit_p2p_cli - Deinitialize P2P client 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 P2P client mode. If the + * interface was not dynamically added, change the interface type back + * to station mode. + */ + int (*deinit_p2p_cli)(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); + + /** + * shared_freq - Get operating frequency of shared interface(s) + * @priv: Private driver interface data + * Returns: Operating frequency in MHz, 0 if no shared operation in + * use, or -1 on failure + * + * This command can be used to request the current operating frequency + * of any virtual interface that shares the same radio to provide + * information for channel selection for other virtual interfaces. + */ + int (*shared_freq)(void *priv); + + /** + * get_noa - Get current Notice of Absence attribute payload + * @priv: Private driver interface data + * @buf: Buffer for returning NoA + * @buf_len: Buffer length in octets + * Returns: Number of octets used in buf, 0 to indicate no NoA is being + * advertized, or -1 on failure + * + * This function is used to fetch the current Notice of Absence + * attribute value from GO. + */ + int (*get_noa)(void *priv, u8 *buf, size_t buf_len); + + /** + * set_noa - Set Notice of Absence parameters for GO (testing) + * @priv: Private driver interface data + * @count: Count + * @start: Start time in ms from next TBTT + * @duration: Duration in ms + * Returns: 0 on success or -1 on failure + * + * This function is used to set Notice of Absence parameters for GO. It + * is used only for testing. To disable NoA, all parameters are set to + * 0. + */ + int (*set_noa)(void *priv, u8 count, int start, int duration); + + /** + * set_p2p_powersave - Set P2P power save options + * @priv: Private driver interface data + * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change + * @opp_ps: 0 = disable, 1 = enable, -1 = no change + * @ctwindow: 0.. = change (msec), -1 = no change + * Returns: 0 on success or -1 on failure + */ + int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps, + int ctwindow); + + /** + * ampdu - Enable/disable aggregation + * @priv: Private driver interface data + * @ampdu: 1/0 = enable/disable A-MPDU aggregation + * Returns: 0 on success or -1 on failure + */ + int (*ampdu)(void *priv, int ampdu); + + /** + * get_radio_name - Get physical radio name for the device + * @priv: Private driver interface data + * Returns: Radio name or %NULL if not known + * + * The returned data must not be modified by the caller. It is assumed + * that any interface that has the same radio name as another is + * sharing the same physical radio. This information can be used to + * share scan results etc. information between the virtual interfaces + * to speed up various operations. + */ + const char * (*get_radio_name)(void *priv); + + /** + * send_tdls_mgmt - for sending TDLS management packets + * @priv: private driver interface data + * @dst: Destination (peer) MAC address + * @action_code: TDLS action code for the mssage + * @dialog_token: Dialog Token to use in the message (if needed) + * @status_code: Status Code or Reason Code to use (if needed) + * @buf: TDLS IEs to add to the message + * @len: Length of buf in octets + * Returns: 0 on success, negative (<0) on failure + * + * This optional function can be used to send packet to driver which is + * responsible for receiving and sending all TDLS packets. + */ + int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *buf, size_t len); + + /** + * tdls_oper - Ask the driver to perform high-level TDLS operations + * @priv: Private driver interface data + * @oper: TDLS high-level operation. See %enum tdls_oper + * @peer: Destination (peer) MAC address + * Returns: 0 on success, negative (<0) on failure + * + * This optional function can be used to send high-level TDLS commands + * to the driver. + */ + int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer); + + /** + * wnm_oper - Notify driver of the WNM frame reception + * @priv: Private driver interface data + * @oper: WNM operation. See %enum wnm_oper + * @peer: Destination (peer) MAC address + * @buf: Buffer for the driver to fill in (for getting IE) + * @buf_len: Return the len of buf + * Returns: 0 on success, negative (<0) on failure + */ + int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len); + + /** + * set_qos_map - Set QoS Map + * @priv: Private driver interface data + * @qos_map_set: QoS Map + * @qos_map_set_len: Length of QoS Map + */ + int (*set_qos_map)(void *priv, const u8 *qos_map_set, + u8 qos_map_set_len); + + /** + * signal_poll - Get current connection information + * @priv: Private driver interface data + * @signal_info: Connection info structure + */ + int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info); + + /** + * set_authmode - Set authentication algorithm(s) for static WEP + * @priv: Private driver interface data + * @authmode: 1=Open System, 2=Shared Key, 3=both + * Returns: 0 on success, -1 on failure + * + * This function can be used to set authentication algorithms for AP + * mode when static WEP is used. If the driver uses user space MLME/SME + * implementation, there is no need to implement this function. + * + * DEPRECATED - use set_ap() instead + */ + int (*set_authmode)(void *priv, int authmode); + +#ifdef ANDROID + /** + * driver_cmd - Execute driver-specific command + * @priv: Private driver interface data + * @cmd: Command to execute + * @buf: Return buffer + * @buf_len: Buffer length + * Returns: 0 on success, -1 on failure + */ + int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len); +#endif /* ANDROID */ + + /** + * set_rekey_info - Set rekey information + * @priv: Private driver interface data + * @kek: Current KEK + * @kck: Current KCK + * @replay_ctr: Current EAPOL-Key Replay Counter + * + * This optional function can be used to provide information for the + * driver/firmware to process EAPOL-Key frames in Group Key Handshake + * while the host (including wpa_supplicant) is sleeping. + */ + void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck, + const u8 *replay_ctr); + + /** + * sta_assoc - Station association indication + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for association frame + * @addr: MAC address of the station to associate + * @reassoc: flag to indicate re-association + * @status: association response status code + * @ie: assoc response ie buffer + * @len: ie buffer length + * Returns: 0 on success, -1 on failure + * + * This function indicates the driver to send (Re)Association + * Response frame to the station. + */ + int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr, + int reassoc, u16 status, const u8 *ie, size_t len); + + /** + * sta_auth - Station authentication indication + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for authentication frame + * @addr: MAC address of the station to associate + * @seq: authentication sequence number + * @status: authentication response status code + * @ie: authentication frame ie buffer + * @len: ie buffer length + * + * This function indicates the driver to send Authentication frame + * to the station. + */ + int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr, + u16 seq, u16 status, const u8 *ie, size_t len); + + /** + * add_tspec - Add traffic stream + * @priv: Private driver interface data + * @addr: MAC address of the station to associate + * @tspec_ie: tspec ie buffer + * @tspec_ielen: tspec ie length + * Returns: 0 on success, -1 on failure + * + * This function adds the traffic steam for the station + * and fills the medium_time in tspec_ie. + */ + int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie, + size_t tspec_ielen); + + /** + * add_sta_node - Add a station node in the driver + * @priv: Private driver interface data + * @addr: MAC address of the station to add + * @auth_alg: authentication algorithm used by the station + * Returns: 0 on success, -1 on failure + * + * This function adds the station node in the driver, when + * the station gets added by FT-over-DS. + */ + int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg); + + /** + * sched_scan - Request the driver to initiate scheduled scan + * @priv: Private driver interface data + * @params: Scan parameters + * @interval: Interval between scan cycles in milliseconds + * Returns: 0 on success, -1 on failure + * + * This operation should be used for scheduled scan offload to + * the hardware. Every time scan results are available, the + * driver should report scan results event for wpa_supplicant + * which will eventually request the results with + * wpa_driver_get_scan_results2(). This operation is optional + * and if not provided or if it returns -1, we fall back to + * normal host-scheduled scans. + */ + int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params, + u32 interval); + + /** + * stop_sched_scan - Request the driver to stop a scheduled scan + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This should cause the scheduled scan to be stopped and + * results should stop being sent. Must be supported if + * sched_scan is supported. + */ + int (*stop_sched_scan)(void *priv); + + /** + * poll_client - Probe (null data or such) the given station + * @priv: Private driver interface data + * @own_addr: MAC address of sending interface + * @addr: MAC address of the station to probe + * @qos: Indicates whether station is QoS station + * + * This function is used to verify whether an associated station is + * still present. This function does not need to be implemented if the + * driver provides such inactivity polling mechanism. + */ + void (*poll_client)(void *priv, const u8 *own_addr, + const u8 *addr, int qos); + + /** + * radio_disable - Disable/enable radio + * @priv: Private driver interface data + * @disabled: 1=disable 0=enable radio + * Returns: 0 on success, -1 on failure + * + * This optional command is for testing purposes. It can be used to + * disable the radio on a testbed device to simulate out-of-radio-range + * conditions. + */ + int (*radio_disable)(void *priv, int disabled); + + /** + * switch_channel - Announce channel switch and migrate the GO to the + * given frequency + * @priv: Private driver interface data + * @settings: Settings for CSA period and new channel + * Returns: 0 on success, -1 on failure + * + * This function is used to move the GO to the legacy STA channel to + * avoid frequency conflict in single channel concurrency. + */ + int (*switch_channel)(void *priv, struct csa_settings *settings); + + /** + * start_dfs_cac - Listen for radar interference on the channel + * @priv: Private driver interface data + * @freq: Channel parameters + * Returns: 0 on success, -1 on failure + */ + int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq); + + /** + * stop_ap - Removes beacon from AP + * @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. Unlike deinit_ap, it does not change to station + * mode. + */ + int (*stop_ap)(void *priv); + + /** + * get_survey - Retrieve survey data + * @priv: Private driver interface data + * @freq: If set, survey data for the specified frequency is only + * being requested. If not set, all survey data is requested. + * Returns: 0 on success, -1 on failure + * + * Use this to retrieve: + * + * - the observed channel noise floor + * - the amount of time we have spent on the channel + * - the amount of time during which we have spent on the channel that + * the radio has determined the medium is busy and we cannot + * transmit + * - the amount of time we have spent receiving data + * - the amount of time we have spent transmitting data + * + * This data can be used for spectrum heuristics. One example is + * Automatic Channel Selection (ACS). The channel survey data is + * kept on a linked list on the channel data, one entry is added + * for each survey. The min_nf of the channel is updated for each + * survey. + */ + int (*get_survey)(void *priv, unsigned int freq); + + /** + * status - Get driver interface status information + * @priv: Private driver interface data + * @buf: Buffer for printing tou the status information + * @buflen: Maximum length of the buffer + * Returns: Length of written status information or -1 on failure + */ + int (*status)(void *priv, char *buf, size_t buflen); +}; + + +/** + * 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_TDLS - Request TDLS operation + * + * This event can be used to request a TDLS operation to be performed. + */ + EVENT_TDLS, + + /** + * 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 the association response 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_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, + + /** + * EVENT_INTERFACE_ENABLED - Notify that interface was enabled + * + * This event is used to indicate that the interface was enabled after + * having been previously disabled, e.g., due to rfkill. + */ + EVENT_INTERFACE_ENABLED, + + /** + * EVENT_INTERFACE_DISABLED - Notify that interface was disabled + * + * This event is used to indicate that the interface was disabled, + * e.g., due to rfkill. + */ + EVENT_INTERFACE_DISABLED, + + /** + * EVENT_CHANNEL_LIST_CHANGED - Channel list changed + * + * This event is used to indicate that the channel list has changed, + * e.g., because of a regulatory domain change triggered by scan + * results including an AP advertising a country code. + */ + EVENT_CHANNEL_LIST_CHANGED, + + /** + * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable + * + * This event is used to indicate that the driver cannot maintain this + * interface in its operation mode anymore. The most likely use for + * this is to indicate that AP mode operation is not available due to + * operating channel would need to be changed to a DFS channel when + * the driver does not support radar detection and another virtual + * interfaces caused the operating channel to change. Other similar + * resource conflicts could also trigger this for station mode + * interfaces. + */ + EVENT_INTERFACE_UNAVAILABLE, + + /** + * EVENT_BEST_CHANNEL + * + * Driver generates this event whenever it detects a better channel + * (e.g., based on RSSI or channel use). This information can be used + * to improve channel selection for a new AP/P2P group. + */ + EVENT_BEST_CHANNEL, + + /** + * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received + * + * This event should be called when a Deauthentication frame is dropped + * due to it not being protected (MFP/IEEE 802.11w). + * union wpa_event_data::unprot_deauth is required to provide more + * details of the frame. + */ + EVENT_UNPROT_DEAUTH, + + /** + * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received + * + * This event should be called when a Disassociation frame is dropped + * due to it not being protected (MFP/IEEE 802.11w). + * union wpa_event_data::unprot_disassoc is required to provide more + * details of the frame. + */ + EVENT_UNPROT_DISASSOC, + + /** + * EVENT_STATION_LOW_ACK + * + * Driver generates this event whenever it detected that a particular + * station was lost. Detection can be through massive transmission + * failures for example. + */ + EVENT_STATION_LOW_ACK, + + /** + * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore + */ + EVENT_IBSS_PEER_LOST, + + /** + * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey + * + * This event carries the new replay counter to notify wpa_supplicant + * of the current EAPOL-Key Replay Counter in case the driver/firmware + * completed Group Key Handshake while the host (including + * wpa_supplicant was sleeping). + */ + EVENT_DRIVER_GTK_REKEY, + + /** + * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped + */ + EVENT_SCHED_SCAN_STOPPED, + + /** + * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll + * + * This event indicates that the station responded to the poll + * initiated with @poll_client. + */ + EVENT_DRIVER_CLIENT_POLL_OK, + + /** + * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status + */ + EVENT_EAPOL_TX_STATUS, + + /** + * EVENT_CH_SWITCH - AP or GO decided to switch channels + * + * Described in wpa_event_data.ch_switch + * */ + EVENT_CH_SWITCH, + + /** + * EVENT_WNM - Request WNM operation + * + * This event can be used to request a WNM operation to be performed. + */ + EVENT_WNM, + + /** + * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode + * + * This event indicates that the driver reported a connection failure + * with the specified client (for example, max client reached, etc.) in + * AP mode. + */ + EVENT_CONNECT_FAILED_REASON, + + /** + * EVENT_RADAR_DETECTED - Notify of radar detection + * + * A radar has been detected on the supplied frequency, hostapd should + * react accordingly (e.g., change channel). + */ + EVENT_DFS_RADAR_DETECTED, + + /** + * EVENT_CAC_FINISHED - Notify that channel availability check has been completed + * + * After a successful CAC, the channel can be marked clear and used. + */ + EVENT_DFS_CAC_FINISHED, + + /** + * EVENT_CAC_ABORTED - Notify that channel availability check has been aborted + * + * The CAC was not successful, and the channel remains in the previous + * state. This may happen due to a radar beeing detected or other + * external influences. + */ + EVENT_DFS_CAC_ABORTED, + + /** + * EVENT_DFS_CAC_NOP_FINISHED - Notify that non-occupancy period is over + * + * The channel which was previously unavailable is now available again. */ - EVENT_INTERFACE_STATUS, + EVENT_DFS_NOP_FINISHED, /** - * 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. + * EVENT_SURVEY - Received survey data * - * Driver will be notified about successful pre-authentication with - * struct wpa_driver_ops::add_pmkid() calls. + * This event gets triggered when a driver query is issued for survey + * data and the requested data becomes available. The returned data is + * stored in struct survey_results. The results provide at most one + * survey entry for each frequency and at minimum will provide one + * survey entry for one frequency. The survey data can be os_malloc()'d + * and then os_free()'d, so the event callback must only copy data. */ - EVENT_PMKID_CANDIDATE, + EVENT_SURVEY, /** - * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request) + * EVENT_SCAN_STARTED - Scan started * - * 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. + * This indicates that driver has started a scan operation either based + * on a request from wpa_supplicant/hostapd or from another application. + * EVENT_SCAN_RESULTS is used to indicate when the scan has been + * completed (either successfully or by getting cancelled). */ - EVENT_STKSTART, + EVENT_SCAN_STARTED, /** - * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs + * EVENT_AVOID_FREQUENCIES - Received avoid frequency range * - * 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. + * This event indicates a set of frequency ranges that should be avoided + * to reduce issues due to interference or internal co-existence + * information in the driver. */ - EVENT_FT_RESPONSE -} wpa_event_type; + EVENT_AVOID_FREQUENCIES +}; + + +/** + * struct freq_survey - Channel survey info + * + * @ifidx: Interface index in which this survey was observed + * @freq: Center of frequency of the surveyed channel + * @nf: Channel noise floor in dBm + * @channel_time: Amount of time in ms the radio spent on the channel + * @channel_time_busy: Amount of time in ms the radio detected some signal + * that indicated to the radio the channel was not clear + * @channel_time_rx: Amount of time the radio spent receiving data + * @channel_time_tx: Amount of time the radio spent transmitting data + * @filled: bitmask indicating which fields have been reported, see + * SURVEY_HAS_* defines. + * @list: Internal list pointers + */ +struct freq_survey { + u32 ifidx; + unsigned int freq; + s8 nf; + u64 channel_time; + u64 channel_time_busy; + u64 channel_time_rx; + u64 channel_time_tx; + unsigned int filled; + struct dl_list list; +}; + +#define SURVEY_HAS_NF BIT(0) +#define SURVEY_HAS_CHAN_TIME BIT(1) +#define SURVEY_HAS_CHAN_TIME_BUSY BIT(2) +#define SURVEY_HAS_CHAN_TIME_RX BIT(3) +#define SURVEY_HAS_CHAN_TIME_TX BIT(4) /** @@ -1164,6 +3208,11 @@ union wpa_event_data { * calls. */ struct assoc_info { + /** + * reassoc - Flag to indicate association or reassociation + */ + int reassoc; + /** * req_ies - (Re)Association Request IEs * @@ -1175,7 +3224,7 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *req_ies; + const u8 *req_ies; /** * req_ies_len - Length of req_ies in bytes @@ -1193,7 +3242,7 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *resp_ies; + const u8 *resp_ies; /** * resp_ies_len - Length of resp_ies in bytes @@ -1216,18 +3265,91 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *beacon_ies; + 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; + + /** + * ie - Optional IE(s) in Disassociation frame + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; + + /** + * locally_generated - Whether the frame was locally generated + */ + int locally_generated; + } 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; + + /** + * ie - Optional IE(s) in Deauthentication frame + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; + + /** + * locally_generated - Whether the frame was locally generated + */ + int locally_generated; + } deauth_info; + /** * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE */ struct michael_mic_failure { int unicast; + const u8 *src; } michael_mic_failure; /** @@ -1259,6 +3381,36 @@ union wpa_event_data { u8 peer[ETH_ALEN]; } stkstart; + /** + * struct tdls - Data for EVENT_TDLS + */ + struct tdls { + u8 peer[ETH_ALEN]; + enum { + TDLS_REQUEST_SETUP, + TDLS_REQUEST_TEARDOWN + } oper; + u16 reason_code; /* for teardown */ + } tdls; + + /** + * struct wnm - Data for EVENT_WNM + */ + struct wnm { + u8 addr[ETH_ALEN]; + enum { + WNM_OPER_SLEEP, + } oper; + enum { + WNM_SLEEP_ENTER, + WNM_SLEEP_EXIT + } sleep_action; + int sleep_intval; + u16 reason_code; + u8 *buf; + u16 buf_len; + } wnm; + /** * struct ft_ies - FT information elements (EVENT_FT_RESPONSE) * @@ -1273,7 +3425,364 @@ union wpa_event_data { 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]; + u8 bssid[ETH_ALEN]; + u16 auth_type; + u16 auth_transaction; + u16 status_code; + const u8 *ies; + size_t ies_len; + } auth; + + /** + * struct assoc_reject - Data for EVENT_ASSOC_REJECT events + */ + struct assoc_reject { + /** + * bssid - BSSID of the AP that rejected association + */ + const u8 *bssid; + + /** + * 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; + + /** + * 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 *bssid; + const u8 *addr; + int wds; + } rx_from_unknown; + + /** + * struct rx_mgmt - Data for EVENT_RX_MGMT events + */ + struct rx_mgmt { + const u8 *frame; + size_t frame_len; + u32 datarate; + + /** + * freq - Frequency (in MHz) on which the frame was received + */ + int freq; + + /** + * ssi_signal - Signal strength in dBm (or 0 if not available) + */ + int ssi_signal; + } rx_mgmt; + + /** + * 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; + + /** + * da - Destination address of the received Probe Request frame + * or %NULL if not available + */ + const u8 *da; + + /** + * bssid - BSSID of the received Probe Request frame or %NULL + * if not available + */ + const u8 *bssid; + + /** + * ie - IEs from the Probe Request body + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; + + /** + * signal - signal strength in dBm (or 0 if not available) + */ + int ssi_signal; + } 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; + + /** + * signal_change - Data for EVENT_SIGNAL_CHANGE events + */ + struct wpa_signal_info signal_change; + + /** + * struct best_channel - Data for EVENT_BEST_CHANNEL events + * @freq_24: Best 2.4 GHz band channel frequency in MHz + * @freq_5: Best 5 GHz band channel frequency in MHz + * @freq_overall: Best channel frequency in MHz + * + * 0 can be used to indicate no preference in either band. + */ + struct best_channel { + int freq_24; + int freq_5; + int freq_overall; + } best_chan; + + struct unprot_deauth { + const u8 *sa; + const u8 *da; + u16 reason_code; + } unprot_deauth; + + struct unprot_disassoc { + const u8 *sa; + const u8 *da; + u16 reason_code; + } unprot_disassoc; + + /** + * struct low_ack - Data for EVENT_STATION_LOW_ACK events + * @addr: station address + */ + struct low_ack { + u8 addr[ETH_ALEN]; + } low_ack; + + /** + * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST + */ + struct ibss_peer_lost { + u8 peer[ETH_ALEN]; + } ibss_peer_lost; + + /** + * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY + */ + struct driver_gtk_rekey { + const u8 *bssid; + const u8 *replay_ctr; + } driver_gtk_rekey; + + /** + * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events + * @addr: station address + */ + struct client_poll { + u8 addr[ETH_ALEN]; + } client_poll; + + /** + * struct eapol_tx_status + * @dst: Original destination + * @data: Data starting with IEEE 802.1X header (!) + * @data_len: Length of data + * @ack: Indicates ack or lost frame + * + * This corresponds to hapd_send_eapol if the frame sent + * there isn't just reported as EVENT_TX_STATUS. + */ + struct eapol_tx_status { + const u8 *dst; + const u8 *data; + int data_len; + int ack; + } eapol_tx_status; + + /** + * struct ch_switch + * @freq: Frequency of new channel in MHz + * @ht_enabled: Whether this is an HT channel + * @ch_offset: Secondary channel offset + * @ch_width: Channel width + * @cf1: Center frequency 1 + * @cf2: Center frequency 2 + */ + struct ch_switch { + int freq; + int ht_enabled; + int ch_offset; + enum chan_width ch_width; + int cf1; + int cf2; + } ch_switch; + + /** + * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON + * @addr: Remote client address + * @code: Reason code for connection failure + */ + struct connect_failed_reason { + u8 addr[ETH_ALEN]; + enum { + MAX_CLIENT_REACHED, + BLOCKED_CLIENT + } code; + } connect_failed_reason; + + /** + * struct dfs_event - Data for radar detected events + * @freq: Frequency of the channel in MHz + */ + struct dfs_event { + int freq; + int ht_enabled; + int chan_offset; + enum chan_width chan_width; + int cf1; + int cf2; + } dfs_event; + + /** + * survey_results - Survey result data for EVENT_SURVEY + * @freq_filter: Requested frequency survey filter, 0 if request + * was for all survey data + * @survey_list: Linked list of survey data + */ + struct survey_results { + unsigned int freq_filter; + struct dl_list survey_list; /* struct freq_survey */ + } survey_results; + + /** + * channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED + * @initiator: Initiator of the regulatory change + */ + struct channel_list_changed { + enum reg_change_initiator initiator; + } channel_list_changed; + + /** + * freq_range - List of frequency ranges + * + * This is used as the data with EVENT_AVOID_FREQUENCIES. + */ + struct wpa_freq_range_list freq_range; }; /** @@ -1286,40 +3795,53 @@ union wpa_event_data { * Driver wrapper code should call this function whenever an event is received * from the driver. */ -void wpa_supplicant_event(void *ctx, wpa_event_type event, +void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data); -/** - * 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. + +/* + * The following inline functions are provided for convenience to simplify + * event indication for some of the common events. */ -void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, - const u8 *buf, size_t len); - -void wpa_supplicant_sta_rx(void *ctx, const u8 *buf, size_t len, - struct ieee80211_rx_status *rx_status); -void wpa_supplicant_sta_free_hw_features(struct wpa_hw_modes *hw_features, - size_t num_hw_features); - -const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie); -#define WPA_IE_VENDOR_TYPE 0x0050f201 -#define WPS_IE_VENDOR_TYPE 0x0050f204 -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); -int wpa_scan_get_max_rate(const struct wpa_scan_res *res); + +static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie, + size_t ielen, int reassoc) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.assoc_info.reassoc = reassoc; + 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); +} + +/* driver_common.c */ void wpa_scan_results_free(struct wpa_scan_results *res); -void wpa_scan_sort_results(struct wpa_scan_results *res); + +/* Convert wpa_event_type to a string for logging */ +const char * event_to_string(enum wpa_event_type event); + +/* NULL terminated array of linked in driver wrappers */ +extern struct wpa_driver_ops *wpa_drivers[]; #endif /* DRIVER_H */ diff --git a/contrib/hostapd/src/drivers/driver_atheros.c b/contrib/hostapd/src/drivers/driver_atheros.c new file mode 100644 index 0000000000..23a4e2b95e --- /dev/null +++ b/contrib/hostapd/src/drivers/driver_atheros.c @@ -0,0 +1,2193 @@ +/* + * hostapd / Driver interaction with Atheros driver + * Copyright (c) 2004, Sam Leffler + * Copyright (c) 2004, Video54 Technologies + * Copyright (c) 2005-2007, Jouni Malinen + * Copyright (c) 2009, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include +#include + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "l2_packet/l2_packet.h" +#include "p2p/p2p.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 "ieee80211_external.h" + + +#ifdef CONFIG_WPS +#include +#endif /* CONFIG_WPS */ + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW 0x0019 +#endif + +#include "linux_wext.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 atheros_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 */ + struct wpabuf *wpa_ie; + struct wpabuf *wps_beacon_ie; + struct wpabuf *wps_probe_resp_ie; + u8 own_addr[ETH_ALEN]; +}; + +static int atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code); +static int atheros_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_SET_MEDENYENTRY: + return "SET_MEDENYENTRY"; + 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 atheros_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 atheros_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 +atheros_configure_wpa(struct atheros_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<wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<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); +#ifdef CONFIG_IEEE80211W + if (params->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + v |= BIT(7); + if (params->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) + v |= BIT(6); + } +#endif /* CONFIG_IEEE80211W */ + + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v); + 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 +atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + struct atheros_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 atheros_set_privacy(drv, 0); + } + if (!params->wpa && !params->ieee802_1x) { + wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (params->wpa && atheros_configure_wpa(drv, params) != 0) { + wpa_printf(MSG_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!"); + return -1; + } + + return 0; +} + +static int +atheros_set_privacy(void *priv, int enabled) +{ + struct atheros_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); +} + +static int +atheros_set_sta_authorized(void *priv, const u8 *addr, int authorized) +{ + struct atheros_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 +atheros_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 atheros_set_sta_authorized(priv, addr, 1); + if (!(flags_and & WPA_STA_AUTHORIZED)) + return atheros_set_sta_authorized(priv, addr, 0); + return 0; +} + +static int +atheros_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct atheros_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 +atheros_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 atheros_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + int ret; + + if (alg == WPA_ALG_NONE) + return atheros_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; +#ifdef CONFIG_IEEE80211W + case WPA_ALG_IGTK: + cipher = IEEE80211_CIPHER_AES_CMAC; + break; +#endif /* CONFIG_IEEE80211W */ + 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 || is_broadcast_ether_addr(addr)) { + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + if (set_tx) + 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 +atheros_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct atheros_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 +atheros_flush(void *priv) +{ + u8 allsta[IEEE80211_ADDR_LEN]; + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return atheros_sta_deauth(priv, NULL, allsta, + IEEE80211_REASON_AUTH_LEAVE); +} + + +static int +atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct atheros_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 +atheros_sta_clear_stats(void *priv, const u8 *addr) +{ + struct atheros_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 +atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) +{ + struct atheros_driver_data *drv = priv; + u8 buf[512]; + struct ieee80211req_getset_appiebuf *app_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) ie_len); + wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len); + + wpabuf_free(drv->wpa_ie); + drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len); + + app_ie = (struct ieee80211req_getset_appiebuf *) buf; + os_memcpy(&(app_ie->app_buf[0]), ie, ie_len); + app_ie->app_buflen = ie_len; + + app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON; + + /* append WPS IE for Beacon */ + if (drv->wps_beacon_ie != NULL) { + os_memcpy(&(app_ie->app_buf[ie_len]), + wpabuf_head(drv->wps_beacon_ie), + wpabuf_len(drv->wps_beacon_ie)); + app_ie->app_buflen = ie_len + wpabuf_len(drv->wps_beacon_ie); + } + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(Beacon)", + app_ie->app_buf, app_ie->app_buflen); + set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + app_ie->app_buflen); + + /* append WPS IE for Probe Response */ + app_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_RESP; + if (drv->wps_probe_resp_ie != NULL) { + os_memcpy(&(app_ie->app_buf[ie_len]), + wpabuf_head(drv->wps_probe_resp_ie), + wpabuf_len(drv->wps_probe_resp_ie)); + app_ie->app_buflen = ie_len + + wpabuf_len(drv->wps_probe_resp_ie); + } else + app_ie->app_buflen = ie_len; + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF(ProbeResp)", + app_ie->app_buf, app_ie->app_buflen); + set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, app_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + app_ie->app_buflen); + return 0; +} + +static int +atheros_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + struct atheros_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 +atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + struct atheros_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 atheros_raw_recv_wps(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_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.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; + 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 */ + +#ifdef CONFIG_IEEE80211R +static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u16 fc; + u16 stype; + int ielen; + const u8 *iebuf; + + /* Do 11R processing for ASSOC/AUTH/FT ACTION frames */ + if (len < IEEE80211_HDRLEN) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) + return; + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, + (int) len); + + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", + __func__); + return; + } + switch (stype) { + case WLAN_FC_STYPE_ASSOC_REQ: + if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.assoc_req)) + break; + ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); + iebuf = mgmt->u.assoc_req.variable; + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.reassoc_req)) + break; + ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); + iebuf = mgmt->u.reassoc_req.variable; + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1); + break; + case WLAN_FC_STYPE_ACTION: + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + break; + case WLAN_FC_STYPE_AUTH: + if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth)) + break; + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); + os_memcpy(event.auth.bssid, mgmt->bssid, 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); + event.auth.auth_transaction = + le_to_host16(mgmt->u.auth.auth_transaction); + event.auth.ies = mgmt->u.auth.variable; + event.auth.ies_len = len - IEEE80211_HDRLEN - + sizeof(mgmt->u.auth); + wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event); + break; + default: + break; + } +} +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_HS20 +static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + u16 fc; + union wpa_event_data event; + + /* Send the Action frame for HS20 processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.action.category) + + sizeof(mgmt->u.action.u.public_action)) + 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_ACTION || + mgmt->u.action.category != WLAN_ACTION_PUBLIC) + return; + + wpa_printf(MSG_DEBUG, "%s:Received Public Action frame", __func__); + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = (const u8 *) mgmt; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); +} + +#endif /* CONFIG_HS20 */ + + +static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, + u8 qos_map_set_len) +{ +#ifdef CONFIG_ATHEROS_QOS_MAP + struct atheros_driver_data *drv = ctx; + struct ieee80211req_athdbg req; + struct ieee80211_qos_map *qos_map = &req.data.qos_map; + struct iwreq iwr; + int i, up_start; + + if (qos_map_set_len < 16 || qos_map_set_len > 58 || + qos_map_set_len & 1) { + wpa_printf(MSG_ERROR, "Invalid QoS Map"); + return -1; + } else { + memset(&req, 0, sizeof(struct ieee80211req_athdbg)); + req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name)); + iwr.u.data.pointer = (void *) &req; + iwr.u.data.length = sizeof(struct ieee80211req_athdbg); + } + + qos_map->valid = 1; + qos_map->num_dscp_except = (qos_map_set_len - 16) / 2; + if (qos_map->num_dscp_except) { + for (i = 0; i < qos_map->num_dscp_except; i++) { + qos_map->dscp_exception[i].dscp = qos_map_set[i * 2]; + qos_map->dscp_exception[i].up = qos_map_set[i * 2 + 1]; + } + } + + up_start = qos_map_set_len - 16; + for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) { + qos_map->up[i].low = qos_map_set[up_start + (i * 2)]; + qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1]; + } + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_DBGREQ]"); + wpa_printf(MSG_DEBUG, "%s: %s: Failed to set QoS Map", + __func__, drv->iface); + return -1; + } +#endif /* CONFIG_ATHEROS_QOS_MAP */ + + return 0; +} + +#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) +static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct atheros_driver_data *drv = ctx; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u16 fc; + u16 stype; + + /* Do 11R processing for WNM ACTION frames */ + if (len < IEEE80211_HDRLEN) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) + return; + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, + (int) len); + + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", + __func__); + return; + } + + switch (stype) { + case WLAN_FC_STYPE_ACTION: + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + break; + default: + break; + } +} +#endif /* CONFIG_WNM */ + +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) +static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ +#ifdef CONFIG_WPS + atheros_raw_recv_wps(ctx, src_addr, buf, len); +#endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211R + atheros_raw_recv_11r(ctx, src_addr, buf, len); +#endif /* CONFIG_IEEE80211R */ +#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) + atheros_raw_recv_11v(ctx, src_addr, buf, len); +#endif /* CONFIG_WNM */ +#ifdef CONFIG_HS20 + atheros_raw_recv_hs20(ctx, src_addr, buf, len); +#endif /* CONFIG_HS20 */ +} +#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ + +static int atheros_receive_pkt(struct atheros_driver_data *drv) +{ + int ret = 0; + struct ieee80211req_set_filter filt; + + wpa_printf(MSG_DEBUG, "%s Enter", __func__); + filt.app_filterype = 0; +#ifdef CONFIG_WPS + filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ; +#endif /* CONFIG_WPS */ +#ifdef CONFIG_IEEE80211R + filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ | + IEEE80211_FILTER_TYPE_AUTH | + IEEE80211_FILTER_TYPE_ACTION); +#endif +#ifdef CONFIG_WNM + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_WNM */ +#ifdef CONFIG_HS20 + filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; +#endif /* CONFIG_HS20 */ + if (filt.app_filterype) { + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + } + +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) + drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, + atheros_raw_receive, drv, 1); + if (drv->sock_raw == NULL) + return -1; +#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ + return ret; +} + +static int atheros_reset_appfilter(struct atheros_driver_data *drv) +{ + struct ieee80211req_set_filter filt; + filt.app_filterype = 0; + return set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); +} + +#ifdef CONFIG_WPS +static int +atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +{ + struct atheros_driver_data *drv = priv; + u8 buf[512]; + struct ieee80211req_getset_appiebuf *beac_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu frametype=%u", __func__, + (unsigned long) len, frametype); + wpa_hexdump(MSG_DEBUG, "atheros: IE", ie, len); + + beac_ie = (struct ieee80211req_getset_appiebuf *) buf; + beac_ie->app_frmtype = frametype; + beac_ie->app_buflen = len; + os_memcpy(&(beac_ie->app_buf[0]), ie, len); + + /* append the WPA/RSN IE if it is set already */ + if (((frametype == IEEE80211_APPIE_FRAME_BEACON) || + (frametype == IEEE80211_APPIE_FRAME_PROBE_RESP)) && + (drv->wpa_ie != NULL)) { + wpa_hexdump_buf(MSG_DEBUG, "atheros: Append WPA/RSN IE", + drv->wpa_ie); + os_memcpy(&(beac_ie->app_buf[len]), wpabuf_head(drv->wpa_ie), + wpabuf_len(drv->wpa_ie)); + beac_ie->app_buflen += wpabuf_len(drv->wpa_ie); + } + + wpa_hexdump(MSG_DEBUG, "atheros: SET_APPIEBUF", + beac_ie->app_buf, beac_ie->app_buflen); + return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + beac_ie->app_buflen); +} + +static int +atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) +{ + struct atheros_driver_data *drv = priv; + + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - beacon", beacon); + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - proberesp", + proberesp); + wpa_hexdump_buf(MSG_DEBUG, "atheros: set_ap_wps_ie - assocresp", + assocresp); + wpabuf_free(drv->wps_beacon_ie); + drv->wps_beacon_ie = beacon ? wpabuf_dup(beacon) : NULL; + wpabuf_free(drv->wps_probe_resp_ie); + drv->wps_probe_resp_ie = proberesp ? wpabuf_dup(proberesp) : NULL; + + atheros_set_wps_ie(priv, assocresp ? wpabuf_head(assocresp) : NULL, + assocresp ? wpabuf_len(assocresp) : 0, + IEEE80211_APPIE_FRAME_ASSOC_RESP); + if (atheros_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, + beacon ? wpabuf_len(beacon) : 0, + IEEE80211_APPIE_FRAME_BEACON)) + return -1; + return atheros_set_wps_ie(priv, + proberesp ? wpabuf_head(proberesp) : NULL, + proberesp ? wpabuf_len(proberesp): 0, + IEEE80211_APPIE_FRAME_PROBE_RESP); +} +#else /* CONFIG_WPS */ +#define atheros_set_ap_wps_ie NULL +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_IEEE80211R +static int +atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq, + u16 status_code, const u8 *ie, size_t len) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d", + __func__, ether_sprintf(addr), status_code); + + mlme.im_op = IEEE80211_MLME_AUTH; + mlme.im_reason = status_code; + mlme.im_seq = seq; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + mlme.im_optie_len = len; + if (len) { + if (len < IEEE80211_MAX_OPT_IE) { + os_memcpy(mlme.im_optie, ie, len); + } else { + wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " + "opt_ie STA (addr " MACSTR " reason %d, " + "ie_len %d)", + __func__, MAC2STR(addr), status_code, + (int) len); + return -1; + } + } + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), status_code); + } + return ret; +} + +static int +atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr, + int reassoc, u16 status_code, const u8 *ie, size_t len) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d reassoc %d", + __func__, ether_sprintf(addr), status_code, reassoc); + + if (reassoc) + mlme.im_op = IEEE80211_MLME_REASSOC; + else + mlme.im_op = IEEE80211_MLME_ASSOC; + mlme.im_reason = status_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + mlme.im_optie_len = len; + if (len) { + if (len < IEEE80211_MAX_OPT_IE) { + os_memcpy(mlme.im_optie, ie, len); + } else { + wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " + "opt_ie STA (addr " MACSTR " reason %d, " + "ie_len %d)", + __func__, MAC2STR(addr), status_code, + (int) len); + return -1; + } + } + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to assoc STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), status_code); + } + return ret; +} +#endif /* CONFIG_IEEE80211R */ + +static void +atheros_new_sta(struct atheros_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, "atheros req WPA IE", + ie.wpa_ie, IEEE80211_MAX_OPT_IE); + wpa_hexdump(MSG_MSGDUMP, "atheros req RSN IE", + ie.rsn_ie, IEEE80211_MAX_OPT_IE); +#ifdef ATH_WPS_IE + wpa_hexdump(MSG_MSGDUMP, "atheros req WPS IE", + ie.wps_ie, IEEE80211_MAX_OPT_IE); +#endif /* ATH_WPS_IE */ + iebuf = ie.wpa_ie; + /* atheros 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) { + /* atheros-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]; + +#ifdef ATH_WPS_IE + /* if WPS IE is present, preference is given to WPS */ + if (ie.wps_ie && + (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) { + iebuf = ie.wps_ie; + ielen = ie.wps_ie[1]; + } +#endif /* ATH_WPS_IE */ + + if (ielen == 0) + iebuf = NULL; + else + ielen += 2; + +no_ie: + drv_event_assoc(hapd, addr, iebuf, ielen, 0); + + 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 +atheros_wireless_event_wireless_custom(struct atheros_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); +#endif /* CONFIG_WPS */ +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) +#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */ + } 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 " | zero padding | frame + */ + int len = atoi(custom + 16); + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " + "length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); + } else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) { + /* Format: "Manage.assoc_req " | zero padding | + * frame */ + int len = atoi(custom + 17); + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" + "assoc_req/auth event length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); + } else if (strncmp(custom, "Manage.action ", 14) == 0) { + /* Format: "Manage.assoc_req " | zero padding | + * frame */ + int len = atoi(custom + 14); + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" + "assoc_req/auth event length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); + } else if (strncmp(custom, "Manage.auth ", 12) == 0) { + /* Format: "Manage.auth " | zero padding | frame + */ + int len = atoi(custom + 12); + if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/" + "assoc_req/auth event length %d", len); + return; + } + atheros_raw_receive(drv, NULL, + (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); +#endif /* CONFIG_WPS or CONFIG_IEEE80211R */ + } +} + +/* +* Handle size of data problem. WEXT only allows data of 256 bytes for custom +* events, and p2p data can be much bigger. So the athr driver sends a small +* event telling me to collect the big data with an ioctl. +* On the first event, send all pending events to supplicant. +*/ +static void fetch_pending_big_events(struct atheros_driver_data *drv) +{ + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u8 tbuf[IW_PRIV_SIZE_MASK]; /* max size is 2047 bytes */ + u16 fc, stype; + struct iwreq iwr; + size_t data_len; + u32 freq, frame_type; + + while (1) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.data.pointer = (void *) tbuf; + iwr.u.data.length = sizeof(tbuf); + iwr.u.data.flags = IEEE80211_IOC_P2P_FETCH_FRAME; + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) + < 0) { + if (errno == ENOSPC) { + wpa_printf(MSG_DEBUG, "%s:%d exit", + __func__, __LINE__); + return; + } + wpa_printf(MSG_DEBUG, "athr: %s: P2P_BIG_PARAM[" + "P2P_FETCH_FRAME] failed: %s", + __func__, strerror(errno)); + return; + } + data_len = iwr.u.data.length; + wpa_hexdump(MSG_DEBUG, "athr: P2P_FETCH_FRAME data", + (u8 *) tbuf, data_len); + if (data_len < sizeof(freq) + sizeof(frame_type) + 24) { + wpa_printf(MSG_DEBUG, "athr: frame too short"); + continue; + } + os_memcpy(&freq, tbuf, sizeof(freq)); + os_memcpy(&frame_type, &tbuf[sizeof(freq)], + sizeof(frame_type)); + mgmt = (void *) &tbuf[sizeof(freq) + sizeof(frame_type)]; + data_len -= sizeof(freq) + sizeof(frame_type); + + if (frame_type == IEEE80211_EV_RX_MGMT) { + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + wpa_printf(MSG_DEBUG, "athr: EV_RX_MGMT stype=%u " + "freq=%u len=%u", stype, freq, (int) data_len); + + if (stype == WLAN_FC_STYPE_ACTION) { + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = (const u8 *) mgmt; + event.rx_mgmt.frame_len = data_len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, + &event); + continue; + } + } else { + wpa_printf(MSG_DEBUG, "athr: %s unknown type %d", + __func__, frame_type); + continue; + } + } +} + +static void +atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv, + int opcode, char *buf, int len) +{ + switch (opcode) { + case IEEE80211_EV_RX_MGMT: + wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT"); + fetch_pending_big_events(drv); + break; + default: + break; + } +} + +static void +atheros_wireless_event_wireless(struct atheros_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: + atheros_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'; + + if (iwe->u.data.flags != 0) { + atheros_wireless_event_atheros_custom( + drv, (int) iwe->u.data.flags, + buf, len); + } else { + atheros_wireless_event_wireless_custom( + drv, buf, buf + iwe->u.data.length); + } + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void +atheros_wireless_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, u8 *buf, size_t len) +{ + struct atheros_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) { + atheros_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static int +atheros_get_we_version(struct atheros_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; + } + + os_free(range); + return 0; +} + + +static int +atheros_wireless_event_init(struct atheros_driver_data *drv) +{ + struct netlink_config *cfg; + + atheros_get_we_version(drv); + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return -1; + cfg->ctx = drv; + cfg->newlink_cb = atheros_wireless_event_rtm_newlink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + return -1; + } + + return 0; +} + + +static int +atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr, u32 flags) +{ + struct atheros_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 atheros_driver_data *drv = ctx; + drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + +static void * +atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) +{ + struct atheros_driver_data *drv; + struct ifreq ifr; + struct iwreq iwr; + char brname[IFNAMSIZ]; + + drv = os_zalloc(sizeof(struct atheros_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for atheros 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; + os_memcpy(drv->own_addr, params->own_addr, ETH_ALEN); + 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); + atheros_set_privacy(drv, 0); /* default to no privacy */ + + if (atheros_receive_pkt(drv)) + goto bad; + + if (atheros_wireless_event_init(drv)) + goto bad; + + return drv; +bad: + atheros_reset_appfilter(drv); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); + 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 +atheros_deinit(void *priv) +{ + struct atheros_driver_data *drv = priv; + + atheros_reset_appfilter(drv); + 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); + wpabuf_free(drv->wpa_ie); + wpabuf_free(drv->wps_beacon_ie); + wpabuf_free(drv->wps_probe_resp_ie); + free(drv); +} + +static int +atheros_set_ssid(void *priv, const u8 *buf, int len) +{ + struct atheros_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 +atheros_get_ssid(void *priv, u8 *buf, int len) +{ + struct atheros_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 > IW_ESSID_MAX_SIZE) ? + IW_ESSID_MAX_SIZE : len; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + return ret; +} + +static int +atheros_set_countermeasures(void *priv, int enabled) +{ + struct atheros_driver_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); +} + +static int +atheros_commit(void *priv) +{ + struct atheros_driver_data *drv = priv; + return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); +} + +static int atheros_set_authmode(void *priv, int auth_algs) +{ + int authmode; + + if ((auth_algs & WPA_AUTH_ALG_OPEN) && + (auth_algs & WPA_AUTH_ALG_SHARED)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_algs & WPA_AUTH_ALG_OPEN) + authmode = IEEE80211_AUTH_OPEN; + else if (auth_algs & WPA_AUTH_ALG_SHARED) + authmode = IEEE80211_AUTH_SHARED; + else + return -1; + + return set80211param(priv, IEEE80211_PARAM_AUTHMODE, authmode); +} + +static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) +{ + /* + * TODO: Use this to replace set_authmode, set_privacy, set_ieee8021x, + * set_generic_elem, and hapd_set_ssid. + */ + + wpa_printf(MSG_DEBUG, "atheros: set_ap - pairwise_ciphers=0x%x " + "group_cipher=0x%x key_mgmt_suites=0x%x auth_algs=0x%x " + "wpa_version=0x%x privacy=%d interworking=%d", + params->pairwise_ciphers, params->group_cipher, + params->key_mgmt_suites, params->auth_algs, + params->wpa_version, params->privacy, params->interworking); + wpa_hexdump_ascii(MSG_DEBUG, "atheros: SSID", + params->ssid, params->ssid_len); + if (params->hessid) + wpa_printf(MSG_DEBUG, "atheros: HESSID " MACSTR, + MAC2STR(params->hessid)); + wpa_hexdump_buf(MSG_DEBUG, "atheros: beacon_ies", + params->beacon_ies); + wpa_hexdump_buf(MSG_DEBUG, "atheros: proberesp_ies", + params->proberesp_ies); + wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies", + params->assocresp_ies); + + return 0; +} + + +#ifdef CONFIG_IEEE80211R + +static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, + int noack) +{ + struct atheros_driver_data *drv = priv; + u8 buf[1510]; + const struct ieee80211_mgmt *mgmt; + struct ieee80211req_mgmtbuf *mgmt_frm; + + mgmt = (const struct ieee80211_mgmt *) frm; + wpa_printf(MSG_DEBUG, "%s frmlen = %lu " MACSTR, __func__, + (unsigned long) data_len, MAC2STR(mgmt->da)); + mgmt_frm = (struct ieee80211req_mgmtbuf *) buf; + memcpy(mgmt_frm->macaddr, (u8 *)mgmt->da, IEEE80211_ADDR_LEN); + mgmt_frm->buflen = data_len; + if (&mgmt_frm->buf[0] + data_len > buf + sizeof(buf)) { + wpa_printf(MSG_INFO, "atheros: Too long frame for " + "atheros_send_mgmt (%u)", (unsigned int) data_len); + return -1; + } + os_memcpy(&mgmt_frm->buf[0], frm, data_len); + return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm, + sizeof(struct ieee80211req_mgmtbuf) + data_len); +} + + +static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie, + size_t tspec_ielen) +{ + struct atheros_driver_data *drv = priv; + int retv; + struct ieee80211req_res req; + struct ieee80211req_res_addts *addts = &req.u.addts; + + wpa_printf(MSG_DEBUG, "%s", __func__); + req.type = IEEE80211_RESREQ_ADDTS; + os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); + os_memcpy(addts->tspecie, tspec_ie, tspec_ielen); + retv = set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, + sizeof(struct ieee80211req_res)); + if (retv < 0) { + wpa_printf(MSG_DEBUG, "%s IEEE80211_IOCTL_RES_REQ FAILED " + "retv = %d", __func__, retv); + return -1; + } + os_memcpy(tspec_ie, addts->tspecie, tspec_ielen); + return addts->status; +} + + +static int atheros_add_sta_node(void *priv, const u8 *addr, u16 auth_alg) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211req_res req; + struct ieee80211req_res_addnode *addnode = &req.u.addnode; + + wpa_printf(MSG_DEBUG, "%s", __func__); + req.type = IEEE80211_RESREQ_ADDNODE; + os_memcpy(&req.macaddr[0], addr, IEEE80211_ADDR_LEN); + addnode->auth_alg = auth_alg; + return set80211priv(drv, IEEE80211_IOCTL_RES_REQ, &req, + sizeof(struct ieee80211req_res)); +} + +#endif /* CONFIG_IEEE80211R */ + + +/* Use only to set a big param, get will not work. */ +static int +set80211big(struct atheros_driver_data *drv, int op, const void *data, int len) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.data.pointer = (void *) data; + iwr.u.data.length = len; + iwr.u.data.flags = op; + wpa_printf(MSG_DEBUG, "%s: op=0x%x=%d (%s) len=0x%x", + __func__, op, op, athr_get_param_name(op), len); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_P2P_BIG_PARAM, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: op=0x%x (%s) subop=0x%x=%d " + "value=0x%x,0x%x failed: %d (%s)", + __func__, op, athr_get_ioctl_name(op), iwr.u.mode, + iwr.u.mode, iwr.u.data.length, + iwr.u.data.flags, errno, strerror(errno)); + return -1; + } + return 0; +} + + +static int atheros_send_action(void *priv, unsigned int freq, + unsigned int wait, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, int no_cck) +{ + struct atheros_driver_data *drv = priv; + struct ieee80211_p2p_send_action *act; + int res; + + act = os_zalloc(sizeof(*act) + data_len); + if (act == NULL) + return -1; + act->freq = freq; + os_memcpy(act->dst_addr, dst, ETH_ALEN); + os_memcpy(act->src_addr, src, ETH_ALEN); + os_memcpy(act->bssid, bssid, ETH_ALEN); + os_memcpy(act + 1, data, data_len); + wpa_printf(MSG_DEBUG, "%s: freq=%d, wait=%u, dst=" MACSTR ", src=" + MACSTR ", bssid=" MACSTR, + __func__, act->freq, wait, MAC2STR(act->dst_addr), + MAC2STR(act->src_addr), MAC2STR(act->bssid)); + wpa_hexdump(MSG_MSGDUMP, "athr: act", (u8 *) act, sizeof(*act)); + wpa_hexdump(MSG_MSGDUMP, "athr: data", data, data_len); + + res = set80211big(drv, IEEE80211_IOC_P2P_SEND_ACTION, + act, sizeof(*act) + data_len); + os_free(act); + return res; +} + + +#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) +static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer, + u8 *ie, u16 *len, enum wnm_oper oper) +{ +#define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */ + u8 buf[IEEE80211_APPIE_MAX]; + struct ieee80211req_getset_appiebuf *tfs_ie; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR, + drv->iface, oper, MAC2STR(peer)); + + switch (oper) { + case WNM_SLEEP_TFS_REQ_IE_SET: + if (*len > IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf)) { + wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large"); + return -1; + } + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len; + + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = *len; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + /* copy the ie */ + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + case WNM_SLEEP_TFS_RESP_IE_ADD: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + + *len = tfs_ie->app_buflen; + os_memcpy(ie, &(tfs_ie->app_buf[0]), *len); + wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0], + *len); + break; + case WNM_SLEEP_TFS_RESP_IE_NONE: + *len = 0; + break; + case WNM_SLEEP_TFS_IE_DEL: + tfs_ie = (struct ieee80211req_getset_appiebuf *) buf; + tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM; + tfs_ie->app_buflen = IEEE80211_APPIE_MAX - + sizeof(struct ieee80211req_getset_appiebuf); + /* Command header for driver */ + os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN); + val = oper; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2); + val = 0; + os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2); + + if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie, + IEEE80211_APPIE_MAX)) { + wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: " + "%s", __func__, strerror(errno)); + return -1; + } + break; + default: + wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper); + break; + } + + return 0; +} + + +static int atheros_wnm_sleep(struct atheros_driver_data *drv, + const u8 *peer, enum wnm_oper oper) +{ + u8 *data, *pos; + size_t dlen; + int ret; + u16 val; + + wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR, + oper, MAC2STR(peer)); + + dlen = ETH_ALEN + 2 + 2; + data = os_malloc(dlen); + if (data == NULL) + return -1; + + /* Command header for driver */ + pos = data; + os_memcpy(pos, peer, ETH_ALEN); + pos += ETH_ALEN; + + val = oper; + os_memcpy(pos, &val, 2); + pos += 2; + + val = 0; + os_memcpy(pos, &val, 2); + + ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM); + + os_free(data); + + return ret; +} + + +static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len) +{ + struct atheros_driver_data *drv = priv; + + switch (oper) { + case WNM_SLEEP_ENTER_CONFIRM: + case WNM_SLEEP_ENTER_FAIL: + case WNM_SLEEP_EXIT_CONFIRM: + case WNM_SLEEP_EXIT_FAIL: + return atheros_wnm_sleep(drv, peer, oper); + case WNM_SLEEP_TFS_REQ_IE_SET: + case WNM_SLEEP_TFS_RESP_IE_ADD: + case WNM_SLEEP_TFS_RESP_IE_NONE: + case WNM_SLEEP_TFS_IE_DEL: + return athr_wnm_tfs(drv, peer, buf, buf_len, oper); + default: + wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d", + oper); + return -1; + } +} +#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ + + +const struct wpa_driver_ops wpa_driver_atheros_ops = { + .name = "atheros", + .hapd_init = atheros_init, + .hapd_deinit = atheros_deinit, + .set_ieee8021x = atheros_set_ieee8021x, + .set_privacy = atheros_set_privacy, + .set_key = atheros_set_key, + .get_seqnum = atheros_get_seqnum, + .flush = atheros_flush, + .set_generic_elem = atheros_set_opt_ie, + .sta_set_flags = atheros_sta_set_flags, + .read_sta_data = atheros_read_sta_driver_data, + .hapd_send_eapol = atheros_send_eapol, + .sta_disassoc = atheros_sta_disassoc, + .sta_deauth = atheros_sta_deauth, + .hapd_set_ssid = atheros_set_ssid, + .hapd_get_ssid = atheros_get_ssid, + .set_countermeasures = atheros_set_countermeasures, + .sta_clear_stats = atheros_sta_clear_stats, + .commit = atheros_commit, + .set_ap_wps_ie = atheros_set_ap_wps_ie, + .set_authmode = atheros_set_authmode, + .set_ap = atheros_set_ap, +#ifdef CONFIG_IEEE80211R + .sta_assoc = atheros_sta_assoc, + .sta_auth = atheros_sta_auth, + .send_mlme = atheros_send_mgmt, + .add_tspec = atheros_add_tspec, + .add_sta_node = atheros_add_sta_node, +#endif /* CONFIG_IEEE80211R */ + .send_action = atheros_send_action, +#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM) + .wnm_oper = atheros_wnm_oper, +#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */ + .set_qos_map = atheros_set_qos_map, +}; diff --git a/contrib/hostapd/src/drivers/driver_atmel.c b/contrib/hostapd/src/drivers/driver_atmel.c deleted file mode 100644 index 0a7a66ddb5..0000000000 --- a/contrib/hostapd/src/drivers/driver_atmel.c +++ /dev/null @@ -1,506 +0,0 @@ -/* - * WPA Supplicant - Driver interaction with Atmel Wireless LAN drivers - * Copyright (c) 2000-2005, ATMEL Corporation - * Copyright (c) 2004-2007, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -/****************************************************************************** - 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 - -#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(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) -{ - 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_set_drop_unencrypted(void *priv, - int enabled) -{ - /* FIX */ - printf("wpa_driver_atmel_set_drop_unencrypted - 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(¶m, 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, ¶m, 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(¶m, 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, ¶m, 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(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, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_atmel_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); -} - - -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; - } - - return drv; -} - - -static void wpa_driver_atmel_deinit(void *priv) -{ - struct wpa_driver_atmel_data *drv = priv; - 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_wpa = wpa_driver_atmel_set_wpa, - .set_key = wpa_driver_atmel_set_key, - .init = wpa_driver_atmel_init, - .deinit = wpa_driver_atmel_deinit, - .set_countermeasures = wpa_driver_atmel_set_countermeasures, - .set_drop_unencrypted = wpa_driver_atmel_set_drop_unencrypted, - .scan = 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/contrib/hostapd/src/drivers/driver_broadcom.c b/contrib/hostapd/src/drivers/driver_broadcom.c deleted file mode 100644 index 3600dae110..0000000000 --- a/contrib/hostapd/src/drivers/driver_broadcom.c +++ /dev/null @@ -1,604 +0,0 @@ -/* - * WPA Supplicant - driver interaction with old Broadcom wl.o driver - * Copyright (c) 2004, Nikki Chumkov - * Copyright (c) 2004, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * 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 - -#include "common.h" - -#if 0 -#include -#include /* the L2 protocols */ -#else -#include -#include /* The L2 protocols */ -#endif -#include -#include - -/* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys - * WRT54G GPL tarball. */ -#include - -#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(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) -{ - 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; - - 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) { - data.assoc_info.resp_ies = os_malloc(left); - if (data.assoc_info.resp_ies == NULL) - return; - os_memcpy(data.assoc_info.resp_ies, - buf + WL_WPA_HEADER_LEN, left); - data.assoc_info.resp_ies_len = left; - wpa_hexdump(MSG_MSGDUMP, "BROADCOM: copying %d bytes " - "into resp_ies", - data.assoc_info.resp_ies, left); - } - /* data.assoc_info.req_ies = NULL; */ - /* data.assoc_info.req_ies_len = 0; */ - - wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); - 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; - } - os_free(data.assoc_info.resp_ies); -} - -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; - - return drv; -} - -static void wpa_driver_broadcom_deinit(void *priv) -{ - struct wpa_driver_broadcom_data *drv = priv; - 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, const u8 *ssid, - size_t ssid_len) -{ - struct wpa_driver_broadcom_data *drv = priv; - wlc_ssid_t wst = { 0, "" }; - - 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 int -wpa_driver_broadcom_get_scan_results(void *priv, - struct wpa_scan_result *results, - size_t max_size) -{ - struct wpa_driver_broadcom_data *drv = priv; - char *buf; - wl_scan_results_t *wsr; - wl_bss_info_t *wbi; - size_t ap_num; - - buf = os_malloc(WLC_IOCTL_MAXLEN); - if (buf == NULL) - return -1; - - 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 -1; - } - - os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); - - for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { - int left; - struct bss_ie_hdr *ie; - - os_memcpy(results[ap_num].bssid, &wbi->BSSID, ETH_ALEN); - os_memcpy(results[ap_num].ssid, wbi->SSID, wbi->SSID_len); - results[ap_num].ssid_len = wbi->SSID_len; - results[ap_num].freq = frequency_list[wbi->channel - 1]; - /* get ie's */ - wpa_hexdump(MSG_MSGDUMP, "BROADCOM: AP IEs", - (u8 *) wbi + sizeof(*wbi), wbi->ie_length); - ie = (struct bss_ie_hdr *) ((u8 *) wbi + sizeof(*wbi)); - for (left = wbi->ie_length; left > 0; - left -= (ie->len + 2), ie = (struct bss_ie_hdr *) - ((u8 *) ie + 2 + ie->len)) { - wpa_printf(MSG_MSGDUMP, "BROADCOM: IE: id:%x, len:%d", - ie->elem_id, ie->len); - if (ie->len >= 3) - wpa_printf(MSG_MSGDUMP, - "BROADCOM: oui:%02x%02x%02x", - ie->oui[0], ie->oui[1], ie->oui[2]); - if (ie->elem_id != 0xdd || - ie->len < 6 || - os_memcmp(ie->oui, WPA_OUI, 3) != 0) - continue; - os_memcpy(results[ap_num].wpa_ie, ie, ie->len + 2); - results[ap_num].wpa_ie_len = ie->len + 2; - break; - } - - 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 ap_num; -} - -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, 0, 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; - - 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 0; -} - -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_wpa = wpa_driver_broadcom_set_wpa, - .set_key = wpa_driver_broadcom_set_key, - .init = wpa_driver_broadcom_init, - .deinit = wpa_driver_broadcom_deinit, - .set_countermeasures = wpa_driver_broadcom_set_countermeasures, - .set_drop_unencrypted = wpa_driver_broadcom_set_drop_unencrypted, - .scan = wpa_driver_broadcom_scan, - .get_scan_results = wpa_driver_broadcom_get_scan_results, - .deauthenticate = wpa_driver_broadcom_deauthenticate, - .disassociate = wpa_driver_broadcom_disassociate, - .associate = wpa_driver_broadcom_associate, -}; diff --git a/contrib/hostapd/src/drivers/driver_bsd.c b/contrib/hostapd/src/drivers/driver_bsd.c index 218d913869..ca64d5c3d0 100644 --- a/contrib/hostapd/src/drivers/driver_bsd.c +++ b/contrib/hostapd/src/drivers/driver_bsd.c @@ -1,211 +1,968 @@ /* * WPA Supplicant - driver interaction with BSD net80211 layer * Copyright (c) 2004, Sam Leffler + * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include +#include #include "common.h" #include "driver.h" #include "eloop.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_common.h" #include +#include #ifdef __NetBSD__ #include -#define COMPAT_FREEBSD_NET80211 #else #include #endif +#include +#ifdef __DragonFly__ +#include +#include +#else /* __DragonFly__ */ +#ifdef __GLIBC__ +#include +#endif /* __GLIBC__ */ #include -#include #include +#include +#endif /* __DragonFly__ || __GLIBC__ */ +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +#endif +#if __NetBSD__ +#include +#endif + +#include "l2_packet/l2_packet.h" + +struct bsd_driver_data { + struct hostapd_data *hapd; /* back pointer */ -struct wpa_driver_bsd_data { 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; - 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 */ + 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 */ + enum ieee80211_opmode opmode; /* operation mode */ + char *event_buf; + size_t event_buf_len; }; +/* Generic functions for hostapd and wpa_supplicant */ + static int -set80211var(struct wpa_driver_bsd_data *drv, int op, const void *arg, int arg_len) +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, IFNAMSIZ); + os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name)); ireq.i_type = op; - ireq.i_len = arg_len; + ireq.i_val = val; ireq.i_data = (void *) arg; + ireq.i_len = arg_len; if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { - fprintf(stderr, "ioctl[SIOCS80211, op %u, len %u]: %s\n", - op, arg_len, strerror(errno)); + 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) { + 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(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; +#ifdef IEEE80211_KEY_NOREPLAY + struct bsd_driver_data *drv = priv; +#endif /* IEEE80211_KEY_NOREPLAY */ + + 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 || is_broadcast_ether_addr(addr)) + 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 (is_broadcast_ether_addr(addr)) { + 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; +#ifndef HOSTAPD +#ifdef IEEE80211_KEY_NOREPLAY + /* + * Ignore replay failures in IBSS and AHDEMO mode. + */ + if (drv->opmode == IEEE80211_M_IBSS || + drv->opmode == IEEE80211_M_AHDEMO) + wk.ik_flags |= IEEE80211_KEY_NOREPLAY; +#endif /* IEEE80211_KEY_NOREPLAY */ +#endif /* HOSTAPD */ + wk.ik_keylen = key_len; + if (seq) { +#ifdef WORDS_BIGENDIAN + /* + * wk.ik_keyrsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 *keyrsc = (u8 *) &wk.ik_keyrsc; + for (i = 0; i < seq_len; i++) + keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i]; +#else /* WORDS_BIGENDIAN */ + os_memcpy(&wk.ik_keyrsc, seq, seq_len); +#endif /* WORDS_BIGENDIAN */ + } + 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<wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<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 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, 0); +} + +static int +bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr, u32 flags) +{ + 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, struct hostapd_freq_params *freq) +{ + struct bsd_driver_data *drv = priv; +#ifdef SIOCS80211CHANNEL + struct ieee80211chanreq creq; +#endif /* SIOCS80211CHANNEL */ + u32 mode; + int channel = freq->channel; + + if (channel < 14) { + mode = +#ifdef CONFIG_IEEE80211N + freq->ht_enabled ? IFM_IEEE80211_11NG : +#endif /* CONFIG_IEEE80211N */ + IFM_IEEE80211_11G; + } else if (channel == 14) { + mode = IFM_IEEE80211_11B; + } else { + mode = +#ifdef CONFIG_IEEE80211N + freq->ht_enabled ? IFM_IEEE80211_11NA : +#endif /* CONFIG_IEEE80211N */ + 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 = (u_int16_t)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; +} + +static size_t +rtbuf_len(void) +{ + size_t len; + + int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0}; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { + wpa_printf(MSG_WARNING, "%s failed: %s\n", __func__, + strerror(errno)); + len = 2048; + } + + return len; +} + +#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; } - return 0; + +#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; + 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, drv->event_buf, drv->event_buf_len); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_ERROR, "%s read() failed: %s\n", + __func__, strerror(errno)); + return; + } + + rtm = (struct rt_msghdr *) drv->event_buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", + 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 int -get80211var(struct wpa_driver_bsd_data *drv, int op, void *arg, int arg_len) +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { - struct ieee80211req ireq; + struct bsd_driver_data *drv = ctx; + drv_event_eapol_rx(drv->hapd, src_addr, buf, len); +} - os_memset(&ireq, 0, sizeof(ireq)); - os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); - ireq.i_type = op; - ireq.i_len = arg_len; - ireq.i_data = arg; +static void * +bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) +{ + struct bsd_driver_data *drv; - if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) { - fprintf(stderr, "ioctl[SIOCG80211, op %u, len %u]: %s\n", - op, arg_len, strerror(errno)); - return -1; + drv = os_zalloc(sizeof(struct bsd_driver_data)); + if (drv == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate memory for bsd driver data"); + return NULL; } - return ireq.i_len; -} -static int -set80211param(struct wpa_driver_bsd_data *drv, int op, int arg) -{ - struct ieee80211req ireq; + drv->event_buf_len = rtbuf_len(); - os_memset(&ireq, 0, sizeof(ireq)); - os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); - ireq.i_type = op; - ireq.i_val = arg; + drv->event_buf = os_malloc(drv->event_buf_len); + if (drv->event_buf == NULL) { + wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); + goto bad; + } - if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { - fprintf(stderr, "ioctl[SIOCS80211, op %u, arg 0x%x]: %s\n", - op, arg, strerror(errno)); - return -1; + drv->hapd = hapd; + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; } - return 0; -} + os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); -static int -get80211param(struct wpa_driver_bsd_data *drv, int op) -{ - struct ieee80211req ireq; + 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; - os_memset(&ireq, 0, sizeof(ireq)); - os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); - ireq.i_type = op; + /* mark down during setup */ + if (bsd_ctrl_iface(drv, 0) < 0) + goto bad; - if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) { - fprintf(stderr, "ioctl[SIOCG80211, op %u]: %s\n", - op, strerror(errno)); - return -1; + drv->route = socket(PF_ROUTE, SOCK_RAW, 0); + if (drv->route < 0) { + perror("socket(PF_ROUTE,SOCK_RAW)"); + goto bad; } - return ireq.i_val; + 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); + os_free(drv->event_buf); + if (drv != NULL) + os_free(drv); + return NULL; } -static int -getifflags(struct wpa_driver_bsd_data *drv, int *flags) + +static void +bsd_deinit(void *priv) { - struct ifreq ifr; + struct bsd_driver_data *drv = priv; - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - if (ioctl(drv->sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { - perror("SIOCGIFFLAGS"); - return errno; + if (drv->route >= 0) { + eloop_unregister_read_sock(drv->route); + close(drv->route); } - *flags = ifr.ifr_flags & 0xffff; - return 0; + 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->event_buf); + os_free(drv); } + static int -setifflags(struct wpa_driver_bsd_data *drv, int flags) +bsd_commit(void *priv) { - struct ifreq ifr; - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - ifr.ifr_flags = flags & 0xffff; - if (ioctl(drv->sock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { - perror("SIOCSIFFLAGS"); - return errno; - } - return 0; + return bsd_ctrl_iface(priv, 1); } + static int -wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) +bsd_set_sta_authorized(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) { - struct wpa_driver_bsd_data *drv = priv; + int authorized = -1; - return get80211var(drv, IEEE80211_IOC_BSSID, - bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0; + /* 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); } +#else /* HOSTAPD */ -#if 0 static int -wpa_driver_bsd_set_bssid(void *priv, const char *bssid) +get80211param(struct bsd_driver_data *drv, int op) { - struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req ireq; - return set80211var(drv, IEEE80211_IOC_BSSID, - bssid, IEEE80211_ADDR_LEN); + if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0) + return -1; + return ireq.i_val; } -#endif static int -wpa_driver_bsd_get_ssid(void *priv, u8 *ssid) +wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) { - struct wpa_driver_bsd_data *drv = priv; + struct bsd_driver_data *drv = priv; +#ifdef SIOCG80211BSSID + struct ieee80211_bssid bs; - return get80211var(drv, IEEE80211_IOC_SSID, - ssid, IEEE80211_NWID_LEN); + 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_set_ssid(void *priv, const u8 *ssid, - size_t ssid_len) +wpa_driver_bsd_get_ssid(void *priv, u8 *ssid) { - struct wpa_driver_bsd_data *drv = priv; - - return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len); + struct bsd_driver_data *drv = priv; + return bsd_get_ssid(drv, ssid, 0); } static int -wpa_driver_bsd_set_wpa_ie(struct wpa_driver_bsd_data *drv, - const u8 *wpa_ie, size_t wpa_ie_len) +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) { - struct wpa_driver_bsd_data *drv = priv; int ret = 0; wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", __FUNCTION__, wpa, privacy); - if (!wpa && wpa_driver_bsd_set_wpa_ie(drv, NULL, 0) < 0) + if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0) ret = -1; - if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0) + if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0) ret = -1; - if (set80211param(drv, IEEE80211_IOC_WPA, wpa) < 0) + if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0) ret = -1; return ret; @@ -219,177 +976,112 @@ wpa_driver_bsd_set_wpa(void *priv, int enabled) return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); } -static int -wpa_driver_bsd_del_key(struct wpa_driver_bsd_data *drv, int key_idx, - const unsigned char *addr) -{ - struct ieee80211req_del_key wk; - - os_memset(&wk, 0, sizeof(wk)); - if (addr != NULL && - bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) != 0) { - struct ether_addr ea; - - os_memcpy(&ea, addr, IEEE80211_ADDR_LEN); - wpa_printf(MSG_DEBUG, "%s: addr=%s keyidx=%d", - __func__, ether_ntoa(&ea), key_idx); - os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); - wk.idk_keyix = (uint8_t) IEEE80211_KEYIX_NONE; - } else { - wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __func__, key_idx); - wk.idk_keyix = key_idx; - } - return set80211var(drv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); -} - -static int -wpa_driver_bsd_set_key(void *priv, 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 wpa_driver_bsd_data *drv = priv; - struct ieee80211req_key wk; - struct ether_addr ea; - char *alg_name; - u_int8_t cipher; - - if (alg == WPA_ALG_NONE) - return wpa_driver_bsd_del_key(drv, key_idx, addr); - - switch (alg) { - case WPA_ALG_WEP: - 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", - __func__, alg); - return -1; - } - - os_memcpy(&ea, addr, IEEE80211_ADDR_LEN); - wpa_printf(MSG_DEBUG, - "%s: alg=%s addr=%s key_idx=%d set_tx=%d seq_len=%zu key_len=%zu", - __func__, alg_name, ether_ntoa(&ea), key_idx, set_tx, - seq_len, key_len); - - if (seq_len > sizeof(u_int64_t)) { - wpa_printf(MSG_DEBUG, "%s: seq_len %zu too big", - __func__, seq_len); - return -2; - } - if (key_len > sizeof(wk.ik_keydata)) { - wpa_printf(MSG_DEBUG, "%s: key length %zu too big", - __func__, key_len); - return -3; - } - - os_memset(&wk, 0, sizeof(wk)); - wk.ik_type = cipher; - wk.ik_flags = IEEE80211_KEY_RECV; - if (set_tx) - wk.ik_flags |= IEEE80211_KEY_XMIT; - 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 (bcmp(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(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); -} - static int wpa_driver_bsd_set_countermeasures(void *priv, int enabled) { - struct wpa_driver_bsd_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return set80211param(drv, IEEE80211_IOC_COUNTERMEASURES, enabled); + return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled); } static int wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled) { - struct wpa_driver_bsd_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return set80211param(drv, IEEE80211_IOC_DROPUNENCRYPTED, enabled); + return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled); } static int wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) { - struct wpa_driver_bsd_data *drv = priv; - struct ieee80211req_mlme mlme; - - wpa_printf(MSG_DEBUG, "%s", __func__); - os_memset(&mlme, 0, sizeof(mlme)); - mlme.im_op = IEEE80211_MLME_DEAUTH; - mlme.im_reason = reason_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); + 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) +wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) { - struct wpa_driver_bsd_data *drv = priv; - struct ieee80211req_mlme mlme; + int authmode; - wpa_printf(MSG_DEBUG, "%s", __func__); - os_memset(&mlme, 0, sizeof(mlme)); - mlme.im_op = IEEE80211_MLME_DISASSOC; - mlme.im_reason = reason_code; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); + 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 wpa_driver_bsd_data *drv = priv; + struct bsd_driver_data *drv = priv; struct ieee80211req_mlme mlme; + u32 mode; int privacy; + int ret = 0; wpa_printf(MSG_DEBUG, "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u" , __func__ - , params->ssid_len, params->ssid - , params->wpa_ie_len + , (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) { + 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 && + privacy = !(params->pairwise_suite == WPA_CIPHER_NONE && + params->group_suite == WPA_CIPHER_NONE && + params->key_mgmt_suite == WPA_KEY_MGMT_NONE && params->wpa_ie_len == 0); wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy); @@ -410,75 +1102,102 @@ wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0) return -1; - return 0; + return ret; } static int -wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) +wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params) { - struct wpa_driver_bsd_data *drv = priv; - int authmode; - - if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) && - (auth_alg & AUTH_ALG_SHARED_KEY)) - authmode = IEEE80211_AUTH_AUTO; - else if (auth_alg & AUTH_ALG_SHARED_KEY) - authmode = IEEE80211_AUTH_SHARED; - else - authmode = IEEE80211_AUTH_OPEN; + 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; + } - return set80211param(drv, IEEE80211_IOC_AUTHMODE, authmode); -} + 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; + } -static int -wpa_driver_bsd_scan(void *priv, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_bsd_data *drv = priv; - int flags; + 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 (getifflags(drv, &flags) != 0 || setifflags(drv, flags | IFF_UP) != 0) + 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 (wpa_driver_bsd_set_ssid(drv, ssid, ssid_len) < 0) + 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 */ } -#include -#if __FreeBSD__ -#include -#endif -#if __NetBSD__ -#include -#endif - static void wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) { - struct wpa_driver_bsd_data *drv = sock_ctx; - char buf[2048]; + struct bsd_driver_data *drv = sock_ctx; 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)); + n = read(sock, drv->event_buf, drv->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) - perror("read(PF_ROUTE)"); + wpa_printf(MSG_ERROR, "%s read() failed: %s\n", + __func__, strerror(errno)); return; } - rtm = (struct rt_msghdr *) buf; + rtm = (struct rt_msghdr *) drv->event_buf; if (rtm->rtm_version != RTM_VERSION) { - wpa_printf(MSG_DEBUG, "Routing message version %d not " - "understood\n", rtm->rtm_version); + wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", + rtm->rtm_version); return; } os_memset(&event, 0, sizeof(event)); @@ -487,8 +1206,8 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) ifan = (struct if_announcemsghdr *) rtm; if (ifan->ifan_index != drv->ifindex) break; - strlcpy(event.interface_status.ifname, drv->ifname, - sizeof(event.interface_status.ifname)); + 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; @@ -508,14 +1227,31 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) 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; @@ -539,8 +1275,8 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) if (ifm->ifm_index != drv->ifindex) break; if ((rtm->rtm_flags & RTF_UP) == 0) { - strlcpy(event.interface_status.ifname, drv->ifname, - sizeof(event.interface_status.ifname)); + 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); @@ -550,135 +1286,199 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) } } -/* Compare function for sorting scan results. Return >0 if @b is consider - * better. */ -static int -wpa_scan_result_compar(const void *a, const void *b) +static void +wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, + struct ieee80211req_scan_result *sr) { - const struct wpa_scan_result *wa = a; - const struct wpa_scan_result *wb = b; + struct wpa_scan_res *result, **tmp; + size_t extra_len; + u8 *pos; - /* WPA/WPA2 support preferred */ - if ((wb->wpa_ie_len || wb->rsn_ie_len) && - !(wa->wpa_ie_len || wa->rsn_ie_len)) - return 1; - if (!(wb->wpa_ie_len || wb->rsn_ie_len) && - (wa->wpa_ie_len || wa->rsn_ie_len)) - return -1; + extra_len = 2 + sr->isr_ssid_len; + extra_len += 2 + sr->isr_nrates; + extra_len += 3; /* ERP IE */ + extra_len += sr->isr_ie_len; - /* privacy support preferred */ - if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) && - (wb->caps & IEEE80211_CAPINFO_PRIVACY) == 0) - return 1; - if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) == 0 && - (wb->caps & IEEE80211_CAPINFO_PRIVACY)) - return -1; + 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; + /* + * the rssi value reported by the kernel is in 0.5dB steps relative to + * the reported noise floor. see ieee80211_node.h for details. + */ + result->level = sr->isr_rssi / 2 + sr->isr_noise; + + pos = (u8 *)(result + 1); - /* best/max rate preferred if signal level close enough XXX */ - if (wa->maxrate != wb->maxrate && abs(wb->level - wa->level) < 5) - return wb->maxrate - wa->maxrate; + *pos++ = WLAN_EID_SSID; + *pos++ = sr->isr_ssid_len; + os_memcpy(pos, sr + 1, sr->isr_ssid_len); + pos += sr->isr_ssid_len; - /* use freq for channel preference */ + /* + * 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; - /* all things being equal, use signal level */ - return wb->level - wa->level; + result->ie_len = pos - (u8 *)(result + 1); + + tmp = os_realloc_array(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 int -getmaxrate(uint8_t rates[15], uint8_t nrates) +struct wpa_scan_results * +wpa_driver_bsd_get_scan_results2(void *priv) { - int i, maxrate = -1; + 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; - for (i = 0; i < nrates; i++) { - int rate = rates[i] & IEEE80211_RATE_VAL; - if (rate > maxrate) - rate = maxrate; + 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; } - return maxrate; -} -/* unalligned little endian access */ -#define LE_READ_4(p) \ - ((u_int32_t) \ - ((((const u_int8_t *)(p))[0] ) | \ - (((const u_int8_t *)(p))[1] << 8) | \ - (((const u_int8_t *)(p))[2] << 16) | \ - (((const u_int8_t *)(p))[3] << 24))) + wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)", + len, (unsigned long)res->num); + + return res; +} -static int __inline -iswpaoui(const u_int8_t *frm) +static int wpa_driver_bsd_capa(struct bsd_driver_data *drv) { - return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); +#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<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 int -wpa_driver_bsd_get_scan_results(void *priv, - struct wpa_scan_result *results, - size_t max_size) +static enum ieee80211_opmode +get80211opmode(struct bsd_driver_data *drv) { -#define min(a,b) ((a)>(b)?(b):(a)) - struct wpa_driver_bsd_data *drv = priv; - uint8_t buf[24*1024]; - uint8_t *cp, *vp; - struct ieee80211req_scan_result *sr; - struct wpa_scan_result *wsr; - int len, ielen; + struct ifmediareq ifmr; - os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); - len = get80211var(drv, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf)); - if (len < 0) - return -1; - cp = buf; - wsr = results; - while (len >= sizeof(struct ieee80211req_scan_result)) { - sr = (struct ieee80211req_scan_result *) cp; - os_memcpy(wsr->bssid, sr->isr_bssid, IEEE80211_ADDR_LEN); - wsr->ssid_len = sr->isr_ssid_len; - wsr->freq = sr->isr_freq; - wsr->noise = sr->isr_noise; - wsr->qual = sr->isr_rssi; - wsr->level = 0; /* XXX? */ - wsr->caps = sr->isr_capinfo; - wsr->maxrate = getmaxrate(sr->isr_rates, sr->isr_nrates); - vp = (u_int8_t *)(sr+1); - os_memcpy(wsr->ssid, vp, sr->isr_ssid_len); - if (sr->isr_ie_len > 0) { - vp += sr->isr_ssid_len; - ielen = sr->isr_ie_len; - while (ielen > 0) { - switch (vp[0]) { - case IEEE80211_ELEMID_VENDOR: - if (!iswpaoui(vp)) - break; - wsr->wpa_ie_len = - min(2+vp[1], SSID_MAX_WPA_IE_LEN); - os_memcpy(wsr->wpa_ie, vp, - wsr->wpa_ie_len); - break; - case IEEE80211_ELEMID_RSN: - wsr->rsn_ie_len = - min(2+vp[1], SSID_MAX_WPA_IE_LEN); - os_memcpy(wsr->rsn_ie, vp, - wsr->rsn_ie_len); - break; - } - ielen -= 2+vp[1]; - vp += 2+vp[1]; - } + if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) { + if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) { + if (ifmr.ifm_current & IFM_FLAG0) + return IEEE80211_M_AHDEMO; + else + return IEEE80211_M_IBSS; } - - cp += sr->isr_len, len -= sr->isr_len; - wsr++; + if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) + return IEEE80211_M_HOSTAP; + if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) + return IEEE80211_M_MONITOR; +#ifdef IEEE80211_M_MBSS + if (ifmr.ifm_current & IFM_IEEE80211_MBSS) + return IEEE80211_M_MBSS; +#endif /* IEEE80211_M_MBSS */ } - qsort(results, wsr - results, sizeof(struct wpa_scan_result), - wpa_scan_result_compar); - - wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%d BSSes)", - len, wsr - results); - - return wsr - results; -#undef min + return IEEE80211_M_STA; } static void * @@ -686,11 +1486,20 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) { #define GETPARAM(drv, param, v) \ (((v) = get80211param(drv, param)) != -1) - struct wpa_driver_bsd_data *drv; + struct bsd_driver_data *drv; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; + + drv->event_buf_len = rtbuf_len(); + + drv->event_buf = os_malloc(drv->event_buf_len); + if (drv->event_buf == NULL) { + wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__); + goto fail1; + } + /* * NB: We require the interface name be mappable to an index. * This implies we do not support having wpa_supplicant @@ -706,6 +1515,12 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) drv->sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->sock < 0) goto fail1; + + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + /* Down interface during setup. */ + if (bsd_ctrl_iface(drv, 0) < 0) + goto fail; + drv->route = socket(PF_ROUTE, SOCK_RAW, 0); if (drv->route < 0) goto fail; @@ -713,7 +1528,6 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) wpa_driver_bsd_event_receive, ctx, drv); drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", @@ -730,22 +1544,17 @@ wpa_driver_bsd_init(void *ctx, const char *ifname) __func__, strerror(errno)); goto fail; } - if (set80211param(drv, IEEE80211_IOC_ROAMING, IEEE80211_ROAMING_MANUAL) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " - "roaming: %s", __func__, strerror(errno)); - goto fail; - } - if (set80211param(drv, IEEE80211_IOC_WPA, 1+2) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support %s", - __func__, strerror(errno)); + if (wpa_driver_bsd_capa(drv)) goto fail; - } + + drv->opmode = get80211opmode(drv); return drv; fail: close(drv->sock); fail1: + os_free(drv->event_buf); os_free(drv); return NULL; #undef GETPARAM @@ -754,41 +1563,69 @@ fail1: static void wpa_driver_bsd_deinit(void *priv) { - struct wpa_driver_bsd_data *drv = priv; - int flags; + struct bsd_driver_data *drv = priv; + wpa_driver_bsd_set_wpa(drv, 0); eloop_unregister_read_sock(drv->route); /* NB: mark interface down */ - if (getifflags(drv, &flags) == 0) - (void) setifflags(drv, flags &~ IFF_UP); + 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->event_buf); 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 (Atheros, etc.)", + .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, + .sta_set_flags = bsd_set_sta_authorized, + .commit = bsd_commit, +#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_wpa = wpa_driver_bsd_set_wpa, - .set_key = wpa_driver_bsd_set_key, .set_countermeasures = wpa_driver_bsd_set_countermeasures, - .set_drop_unencrypted = wpa_driver_bsd_set_drop_unencrypted, - .scan = wpa_driver_bsd_scan, - .get_scan_results = wpa_driver_bsd_get_scan_results, + .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, - .set_auth_alg = wpa_driver_bsd_set_auth_alg, + .get_capa = wpa_driver_bsd_get_capa, +#endif /* HOSTAPD */ + .set_freq = bsd_set_freq, + .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, + .set_generic_elem = bsd_set_opt_ie, }; diff --git a/contrib/hostapd/src/drivers/driver_common.c b/contrib/hostapd/src/drivers/driver_common.c new file mode 100644 index 0000000000..3058cd57dd --- /dev/null +++ b/contrib/hostapd/src/drivers/driver_common.c @@ -0,0 +1,86 @@ +/* + * Common driver-related functions + * Copyright (c) 2003-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "utils/common.h" +#include "driver.h" + +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); +} + + +const char * event_to_string(enum wpa_event_type event) +{ +#define E2S(n) case EVENT_ ## n: return #n + switch (event) { + E2S(ASSOC); + E2S(DISASSOC); + E2S(MICHAEL_MIC_FAILURE); + E2S(SCAN_RESULTS); + E2S(ASSOCINFO); + E2S(INTERFACE_STATUS); + E2S(PMKID_CANDIDATE); + E2S(STKSTART); + E2S(TDLS); + E2S(FT_RESPONSE); + E2S(IBSS_RSN_START); + E2S(AUTH); + E2S(DEAUTH); + E2S(ASSOC_REJECT); + E2S(AUTH_TIMED_OUT); + E2S(ASSOC_TIMED_OUT); + E2S(FT_RRB_RX); + E2S(WPS_BUTTON_PUSHED); + E2S(TX_STATUS); + E2S(RX_FROM_UNKNOWN); + E2S(RX_MGMT); + E2S(REMAIN_ON_CHANNEL); + E2S(CANCEL_REMAIN_ON_CHANNEL); + E2S(MLME_RX); + E2S(RX_PROBE_REQ); + E2S(NEW_STA); + E2S(EAPOL_RX); + E2S(SIGNAL_CHANGE); + E2S(INTERFACE_ENABLED); + E2S(INTERFACE_DISABLED); + E2S(CHANNEL_LIST_CHANGED); + E2S(INTERFACE_UNAVAILABLE); + E2S(BEST_CHANNEL); + E2S(UNPROT_DEAUTH); + E2S(UNPROT_DISASSOC); + E2S(STATION_LOW_ACK); + E2S(IBSS_PEER_LOST); + E2S(DRIVER_GTK_REKEY); + E2S(SCHED_SCAN_STOPPED); + E2S(DRIVER_CLIENT_POLL_OK); + E2S(EAPOL_TX_STATUS); + E2S(CH_SWITCH); + E2S(WNM); + E2S(CONNECT_FAILED_REASON); + E2S(DFS_RADAR_DETECTED); + E2S(DFS_CAC_FINISHED); + E2S(DFS_CAC_ABORTED); + E2S(DFS_NOP_FINISHED); + E2S(SURVEY); + E2S(SCAN_STARTED); + E2S(AVOID_FREQUENCIES); + } + + return "UNKNOWN"; +#undef E2S +} diff --git a/contrib/hostapd/src/drivers/driver_hostap.c b/contrib/hostapd/src/drivers/driver_hostap.c index 84ef3bdedc..16f5563af9 100644 --- a/contrib/hostapd/src/drivers/driver_hostap.c +++ b/contrib/hostapd/src/drivers/driver_hostap.c @@ -1,21 +1,15 @@ /* - * WPA Supplicant - driver interaction with Linux Host AP driver + * Driver interaction with Linux Host AP driver * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include -#include "wireless_copy.h" +#include "linux_wext.h" #include "common.h" #include "driver.h" #include "driver_wext.h" @@ -23,491 +17,1177 @@ #include "driver_hostap.h" -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 */ +#include +#include + +#include "priv_netlink.h" +#include "netlink.h" +#include "linux_ioctl.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.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(struct wpa_driver_hostap_data *drv, - struct prism2_hostapd_param *param, - int len, int show_err) +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 iwreq iwr; + struct ieee80211_hdr *hdr; + u16 fc, ethertype; + u8 *pos, *sa; + size_t left; + union wpa_event_data event; - 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 (len < sizeof(struct ieee80211_hdr)) + return; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); - if (ioctl(drv->sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { - int ret = errno; - if (show_err) - perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); - return ret; + if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) { + printf("Not ToDS data frame (fc=0x%04x)\n", fc); + return; } - return 0; + sa = hdr->addr2; + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); + event.rx_from_unknown.addr = sa; + 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 int wpa_driver_hostap_set_wpa_ie(struct wpa_driver_hostap_data *drv, - const u8 *wpa_ie, size_t wpa_ie_len) +static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf, + size_t len, int ok) { - 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); + 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); +} - param = os_zalloc(blen); - if (param == NULL) + +static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) +{ + struct ieee80211_hdr *hdr; + u16 fc, extra_len, type, stype; + 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; + } 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; + } - 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); + if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) { + printf("Could not register read socket\n"); + return -1; + } - os_free(param); + 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, int noack) +{ + 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 prism2param(struct wpa_driver_hostap_data *drv, int param, - int value) +static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr, + u32 flags) { - struct iwreq iwr; - int *i, ret = 0; + struct hostap_driver_data *drv = priv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - i = (int *) iwr.u.name; - *i++ = param; - *i++ = value; + 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; + } - if (ioctl(drv->sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { - perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); - ret = -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, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " + "failed: %d (%s)", + (unsigned long) len, errno, strerror(errno)); } - return ret; + free(hdr); + + return res; } -static int wpa_driver_hostap_set_wpa(void *priv, int enabled) +static int hostap_sta_set_flags(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) { - struct wpa_driver_hostap_data *drv = priv; - int ret = 0; + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + 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(¶m, 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, ¶m, sizeof(param)); +} - 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 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 void show_set_key_error(struct prism2_hostapd_param *param) +static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, + int len) { - 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; + 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(void *priv, wpa_alg alg, - const u8 *addr, int key_idx, - int set_tx, const u8 *seq, size_t seq_len, +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 hostap_driver_data *drv = priv; struct prism2_hostapd_param *param; u8 *buf; size_t blen; int ret = 0; - char *alg_name; + 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: - alg_name = "none"; + os_strlcpy((char *) param->u.crypt.alg, "NONE", + HOSTAP_CRYPT_ALG_NAME_LEN); break; case WPA_ALG_WEP: - alg_name = "WEP"; + os_strlcpy((char *) param->u.crypt.alg, "WEP", + HOSTAP_CRYPT_ALG_NAME_LEN); break; case WPA_ALG_TKIP: - alg_name = "TKIP"; + os_strlcpy((char *) param->u.crypt.alg, "TKIP", + HOSTAP_CRYPT_ALG_NAME_LEN); break; case WPA_ALG_CCMP: - alg_name = "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); - 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); + return ret; +} - if (seq_len > 8) - return -2; - blen = sizeof(*param) + key_len; +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_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 + param->cmd = PRISM2_GET_ENCRYPTION; if (addr == NULL) - os_memset(param->sta_addr, 0xff, ETH_ALEN); + 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); + memcpy(param->sta_addr, addr, ETH_ALEN); + param->u.crypt.idx = idx; - if (hostapd_ioctl(drv, param, blen, 1)) { - wpa_printf(MSG_WARNING, "Failed to set encryption."); - show_set_key_error(param); + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to get encryption.\n"); ret = -1; + } else { + memcpy(seq, param->u.crypt.seq, 8); } - os_free(buf); + free(buf); return ret; } -static int wpa_driver_hostap_set_countermeasures(void *priv, int enabled) +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 wpa_driver_hostap_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return prism2param(drv, PRISM2_PARAM_TKIP_COUNTERMEASURES, enabled); + 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 wpa_driver_hostap_set_drop_unencrypted(void *priv, int enabled) +static int hostap_set_privacy(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_DROP_UNENCRYPTED, enabled); + struct hostap_drvier_data *drv = priv; + + return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, + enabled); } -static int wpa_driver_hostap_reset(struct wpa_driver_hostap_data *drv, - int type) +static int hostap_set_ssid(void *priv, const u8 *buf, int len) { + struct hostap_driver_data *drv = priv; 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; + 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->sock, PRISM2_IOCTL_RESET, &iwr) < 0) { - perror("ioctl[PRISM2_IOCTL_RESET]"); - ret = -1; + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; } - return ret; + + return 0; } -static int wpa_driver_hostap_mlme(struct wpa_driver_hostap_data *drv, - const u8 *addr, int cmd, int reason_code) +static int hostap_flush(void *priv) { + struct hostap_driver_data *drv = priv; 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(¶m, 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, ¶m, sizeof(param), 1); - if (ret == 0) { - os_sleep(0, 100000); - ret = wpa_driver_hostap_reset(drv, 2); - } - return ret; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_FLUSH; + return hostapd_ioctl(drv, ¶m, sizeof(param)); } -static int wpa_driver_hostap_deauthenticate(void *priv, const u8 *addr, - int reason_code) +static int hostap_read_sta_data(void *priv, + struct hostap_sta_driver_data *data, + const u8 *addr) { - 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); + 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 wpa_driver_hostap_disassociate(void *priv, const u8 *addr, - int reason_code) +static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params) { - 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); + 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(¶m, 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, ¶m, sizeof(param)); } -static int -wpa_driver_hostap_associate(void *priv, - struct wpa_driver_associate_params *params) +static int hostap_sta_remove(void *priv, const u8 *addr) { - struct wpa_driver_hostap_data *drv = priv; - int ret = 0; - int allow_unencrypted_eapol; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - 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; + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED); + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_REMOVE_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + printf("Could not remove station from kernel driver.\n"); + return -1; } + return 0; +} - 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. */ +static int hostap_get_inact_sec(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_GET_INFO_STA; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + return -1; } - return ret; + return param.u.get_info_sta.inactive_sec; } -static int wpa_driver_hostap_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int hostap_sta_clear_stats(void *priv, const u8 *addr) { - struct wpa_driver_hostap_data *drv = priv; + struct hostap_driver_data *drv = priv; struct prism2_hostapd_param param; - int ret; - 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, ssid, ssid_len); + memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS; + memcpy(param.sta_addr, addr, ETH_ALEN); + if (hostapd_ioctl(drv, ¶m, sizeof(param))) { + return -1; } - if (ssid_len > 32) - ssid_len = 32; + return 0; +} - os_memset(¶m, 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, ¶m, 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); +static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen, elem_len; - return ret; + 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(¶m->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 wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg) +static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) { - struct wpa_driver_hostap_data *drv = priv; - int algs = 0; + 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); +} - if (auth_alg & AUTH_ALG_OPEN_SYSTEM) - algs |= 1; - if (auth_alg & AUTH_ALG_SHARED_KEY) - algs |= 2; - if (auth_alg & 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 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 int wpa_driver_hostap_get_bssid(void *priv, u8 *bssid) +static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, + char *data, int len) { - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); + 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 int wpa_driver_hostap_get_ssid(void *priv, u8 *ssid) +static void hostapd_wireless_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) { - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); + 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 struct wpa_scan_results * wpa_driver_hostap_get_scan_results(void *priv) +static int hostap_get_we_version(struct hostap_driver_data *drv) { - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); + 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 wpa_driver_hostap_set_operstate(void *priv, int state) +static int hostap_wireless_event_init(struct hostap_driver_data *drv) { - struct wpa_driver_hostap_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); + 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 * wpa_driver_hostap_init(void *ctx, const char *ifname) +static void * hostap_init(struct hostapd_data *hapd, + struct wpa_init_params *params) { - struct wpa_driver_hostap_data *drv; + struct hostap_driver_data *drv; - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) + drv = os_zalloc(sizeof(struct hostap_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for hostapd driver data\n"); return NULL; - drv->wext = wpa_driver_wext_init(ctx, ifname); - if (drv->wext == NULL) { - os_free(drv); + } + + 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; } - 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); + 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 (os_strncmp(ifname, "wlan", 4) == 0) { + 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; + + if (is_broadcast_ether_addr(addr)) { /* - * Host AP driver may use both wlan# and wifi# interface in - * wireless events. + * 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. */ - char ifname2[IFNAMSIZ + 1]; - os_strlcpy(ifname2, ifname, sizeof(ifname2)); - os_memcpy(ifname2, "wifi", 4); - wpa_driver_wext_alternative_ifindex(drv->wext, ifname2); + return 0; } - return drv; + 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), 0); +} + + +static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.freq.m = freq->channel; + iwr.u.freq.e = 0; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + return -1; + } + + return 0; +} + + +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), 0); +} + + +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; } -static void wpa_driver_hostap_deinit(void *priv) +static void wpa_driver_hostap_poll_client(void *priv, const u8 *own_addr, + const u8 *addr, int qos) { - struct wpa_driver_hostap_data *drv = priv; - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); + struct ieee80211_hdr hdr; + + os_memset(&hdr, 0, sizeof(hdr)); + + /* + * WLAN_FC_STYPE_NULLFUNC would be more appropriate, + * but it is apparently not retried so TX Exc events + * are not received for it. + * This is the reason the driver overrides the default + * handling. + */ + hdr.frame_control = IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_DATA); + + hdr.frame_control |= + host_to_le16(WLAN_FC_FROMDS); + os_memcpy(hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + + hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0); } const struct wpa_driver_ops wpa_driver_hostap_ops = { .name = "hostap", .desc = "Host AP driver (Intersil Prism2/2.5/3)", - .get_bssid = wpa_driver_hostap_get_bssid, - .get_ssid = wpa_driver_hostap_get_ssid, - .set_wpa = wpa_driver_hostap_set_wpa, .set_key = wpa_driver_hostap_set_key, - .set_countermeasures = wpa_driver_hostap_set_countermeasures, - .set_drop_unencrypted = wpa_driver_hostap_set_drop_unencrypted, - .scan = 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, - .set_auth_alg = wpa_driver_hostap_set_auth_alg, - .init = wpa_driver_hostap_init, - .deinit = wpa_driver_hostap_deinit, - .set_operstate = wpa_driver_hostap_set_operstate, + .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, + .set_freq = hostap_set_freq, + .poll_client = wpa_driver_hostap_poll_client, }; diff --git a/contrib/hostapd/src/drivers/driver_hostap.h b/contrib/hostapd/src/drivers/driver_hostap.h index a2508ed924..a9d3e76cbe 100644 --- a/contrib/hostapd/src/drivers/driver_hostap.h +++ b/contrib/hostapd/src/drivers/driver_hostap.h @@ -1,24 +1,40 @@ /* - * WPA Supplicant - driver interaction with Linux Host AP driver - * Copyright (c) 2003-2005, Jouni Malinen + * Driver interaction with Linux Host AP driver + * Copyright (c) 2002-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 */ @@ -63,6 +79,47 @@ enum { 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, @@ -140,8 +197,8 @@ struct prism2_hostapd_param { } u; }; -#define HOSTAP_CRYPT_FLAG_SET_TX_KEY 0x01 -#define HOSTAP_CRYPT_FLAG_PERMANENT 0x02 +#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 diff --git a/contrib/hostapd/src/drivers/driver_ipw.c b/contrib/hostapd/src/drivers/driver_ipw.c deleted file mode 100644 index 3c19cccf46..0000000000 --- a/contrib/hostapd/src/drivers/driver_ipw.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Linux ipw2100/2200 drivers - * Copyright (c) 2005 Zhu Yi - * Copyright (c) 2004 Lubomir Gelo - * Copyright (c) 2003-2004, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * 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 - -#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 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(¶m, 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, ¶m, 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(¶m, 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, ¶m, 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(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) -{ - 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 (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 & AUTH_ALG_OPEN_SYSTEM) - algs |= 1; - if (auth_alg & AUTH_ALG_SHARED_KEY) - algs |= 2; - if (auth_alg & 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, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_ipw_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); -} - - -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; - } - - return drv; -} - - -static void wpa_driver_ipw_deinit(void *priv) -{ - struct wpa_driver_ipw_data *drv = priv; - 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_wpa = wpa_driver_ipw_set_wpa, - .set_key = wpa_driver_ipw_set_key, - .set_countermeasures = wpa_driver_ipw_set_countermeasures, - .set_drop_unencrypted = wpa_driver_ipw_set_drop_unencrypted, - .scan = 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, - .set_auth_alg = wpa_driver_ipw_set_auth_alg, - .init = wpa_driver_ipw_init, - .deinit = wpa_driver_ipw_deinit, - .set_operstate = wpa_driver_ipw_set_operstate, -}; diff --git a/contrib/hostapd/src/drivers/driver_madwifi.c b/contrib/hostapd/src/drivers/driver_madwifi.c index 7521037234..1635c1fbb5 100644 --- a/contrib/hostapd/src/drivers/driver_madwifi.c +++ b/contrib/hostapd/src/drivers/driver_madwifi.c @@ -1,20 +1,15 @@ /* - * WPA Supplicant - driver interaction with MADWIFI 802.11 driver + * hostapd - driver interaction with MADWIFI 802.11 driver * Copyright (c) 2004, Sam Leffler - * Copyright (c) 2004-2005, Jouni Malinen + * Copyright (c) 2004, Video54 Technologies + * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * Please note that madwifi 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. + * This driver wrapper is only for hostapd AP mode functionality. Station + * (wpa_supplicant) operations with madwifi are supported by the driver_wext.c + * wrapper. */ #include "includes.h" @@ -24,8 +19,8 @@ #include "driver.h" #include "driver_wext.h" #include "eloop.h" -#include "ieee802_11_defs.h" -#include "wireless_copy.h" +#include "common/ieee802_11_defs.h" +#include "linux_wext.h" /* * Avoid conflicts with wpa_supplicant definitions by undefining a definition. @@ -42,33 +37,78 @@ #include #include +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME +#include + +#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 */ -struct wpa_driver_madwifi_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; +#define WPA_KEY_RSC_LEN 8 + +#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 wpa_driver_madwifi_data *drv, int op, void *data, int len, - int show_err) +set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) { 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) { + 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. */ - os_memcpy(iwr.u.name, data, len); + memcpy(iwr.u.name, data, len); } else { /* * Argument data too big for inline transfer; setup a @@ -79,523 +119,1191 @@ set80211priv(struct wpa_driver_madwifi_data *drv, int op, void *data, int len, iwr.u.data.length = len; } - if (ioctl(drv->sock, op, &iwr) < 0) { - if (show_err) { + if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { #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]", - }; + 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; - 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]", - }; + 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 && op <= last && - idx < (int) (sizeof(opnames) / sizeof(opnames[0])) - && opnames[idx]) - perror(opnames[idx]); - else - perror("ioctl[unknown???]"); - } + int idx = op - first; + if (first <= op && + idx < (int) ARRAY_SIZE(opnames) && + 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) +set80211param(struct madwifi_driver_data *drv, int op, int arg) { struct iwreq iwr; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.mode = op; - os_memcpy(iwr.u.name+sizeof(u32), &arg, sizeof(arg)); + 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]"); + 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 -wpa_driver_madwifi_set_wpa_ie(struct wpa_driver_madwifi_data *drv, - const u8 *wpa_ie, size_t wpa_ie_len) +madwifi_configure_wpa(struct madwifi_driver_data *drv, + struct wpa_bss_params *params) { - struct iwreq iwr; + int v; - 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; + 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<wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<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; + } - if (ioctl(drv->sock, IEEE80211_IOCTL_SETOPTIE, &iwr) < 0) { - perror("ioctl[IEEE80211_IOCTL_SETOPTIE]"); + 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 -wpa_driver_madwifi_del_key(struct wpa_driver_madwifi_data *drv, int key_idx, - const u8 *addr) +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) { + wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (params->wpa && madwifi_configure_wpa(drv, params) != 0) { + wpa_printf(MSG_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + wpa_printf(MSG_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: 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); + 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; + } - return set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk), 1); + 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(void *priv, wpa_alg alg, +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 madwifi_driver_data *drv = priv; struct ieee80211req_key wk; - char *alg_name; u_int8_t cipher; + int ret; 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(drv->wext, alg, addr, - key_idx, set_tx, - seq, seq_len, - key, key_len); - } - alg_name = "WEP"; + 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; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; + else if (alg == WPA_ALG_TKIP) cipher = IEEE80211_CIPHER_TKIP; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; + else if (alg == WPA_ALG_CCMP) cipher = IEEE80211_CIPHER_AES_CCM; - break; - default: - wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d", - __FUNCTION__, alg); + else { + printf("%s: unknown/unsupported algorithm %d\n", + __func__, 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); + printf("%s: key length %lu too big\n", __func__, + (unsigned long) key_len); return -3; } - os_memset(&wk, 0, sizeof(wk)); + 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_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL || is_broadcast_ether_addr(addr)) { + 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 -#define WPA_KEY_RSC_LEN 8 { - size_t i; + /* + * 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]; - 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); + 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 */ - os_memcpy(&wk.ik_keyrsc, seq, seq_len); + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); #endif /* WORDS_BIGENDIAN */ - os_memcpy(wk.ik_keydata, key, key_len); - - return set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk), 1); + return 0; } -static int -wpa_driver_madwifi_set_countermeasures(void *priv, int enabled) + +static int +madwifi_flush(void *priv) { - struct wpa_driver_madwifi_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled, 1); +#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 -wpa_driver_madwifi_set_drop_unencrypted(void *priv, int enabled) +madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) { - struct wpa_driver_madwifi_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); - return set80211param(drv, IEEE80211_PARAM_DROPUNENCRYPTED, enabled, 1); + 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 -wpa_driver_madwifi_deauthenticate(void *priv, const u8 *addr, int reason_code) +madwifi_sta_clear_stats(void *priv, const u8 *addr) { - struct wpa_driver_madwifi_data *drv = priv; +#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", __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); + 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 -wpa_driver_madwifi_disassociate(void *priv, const u8 *addr, int reason_code) +madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) { - struct wpa_driver_madwifi_data *drv = priv; + struct madwifi_driver_data *drv = priv; struct ieee80211req_mlme mlme; + int ret; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - mlme.im_op = IEEE80211_MLME_DISASSOC; + 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; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); + 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 -wpa_driver_madwifi_associate(void *priv, - struct wpa_driver_associate_params *params) +madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) { - struct wpa_driver_madwifi_data *drv = priv; + struct madwifi_driver_data *drv = priv; struct ieee80211req_mlme mlme; - int ret = 0, privacy = 1; + int ret; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); - /* - * 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; + 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); + } - 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; + return ret; +} - if (set80211param(drv, IEEE80211_PARAM_PRIVACY, privacy, 1) < 0) - ret = -1; +#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.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; + 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 */ - if (params->wpa_ie_len && - set80211param(drv, IEEE80211_PARAM_WPA, - params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1, 1) < 0) - ret = -1; +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; - 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; - } - } + 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 -wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg) +madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) { - struct wpa_driver_madwifi_data *drv = priv; - int authmode; + struct madwifi_driver_data *drv = priv; + u8 buf[256]; + struct ieee80211req_getset_appiebuf *beac_ie; - if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) && - (auth_alg & AUTH_ALG_SHARED_KEY)) - authmode = IEEE80211_AUTH_AUTO; - else if (auth_alg & AUTH_ALG_SHARED_KEY) - authmode = IEEE80211_AUTH_SHARED; - else - authmode = IEEE80211_AUTH_OPEN; + 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 set80211param(drv, IEEE80211_PARAM_AUTHMODE, authmode, 1); + return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, + sizeof(struct ieee80211req_getset_appiebuf) + len); } static int -wpa_driver_madwifi_scan(void *priv, const u8 *ssid, size_t ssid_len) +madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) +{ + 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 int madwifi_set_freq(void *priv, struct hostapd_freq_params *freq) { - struct wpa_driver_madwifi_data *drv = priv; + struct madwifi_driver_data *drv = priv; struct iwreq iwr; - int ret = 0; 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; + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.freq.m = freq->channel; + iwr.u.freq.e = 0; - if (ioctl(drv->sock, SIOCSIWSCAN, &iwr) < 0) { - perror("ioctl[SIOCSIWSCAN]"); - ret = -1; + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + return -1; } + return 0; +} + +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; + /* - * 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). + * Fetch negotiated WPA/RSN parameters from the system. */ - 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); + 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 */ - return ret; + ielen = iebuf[1]; + if (ielen == 0) + iebuf = NULL; + else + ielen += 2; + +no_ie: + drv_event_assoc(hapd, addr, iebuf, ielen, 0); + + 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 int wpa_driver_madwifi_get_bssid(void *priv, u8 *bssid) +static void +madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, + char *custom) { - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); + 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 int wpa_driver_madwifi_get_ssid(void *priv, u8 *ssid) +static void +madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, + char *data, int len) { - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); + 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 struct wpa_scan_results * -wpa_driver_madwifi_get_scan_results(void *priv) +static void +madwifi_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len) { - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); + 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 wpa_driver_madwifi_set_operstate(void *priv, int state) +static int +madwifi_get_we_version(struct madwifi_driver_data *drv) { - struct wpa_driver_madwifi_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); + 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 wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies, - size_t ies_len) +static int +madwifi_wireless_event_init(struct madwifi_driver_data *drv) { - struct ieee80211req_getset_appiebuf *probe_req_ie; - int ret; + struct netlink_config *cfg; - probe_req_ie = os_malloc(sizeof(*probe_req_ie) + ies_len); - if (probe_req_ie == NULL) + 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; + } - 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); + return 0; +} - ret = set80211priv(priv, IEEE80211_IOCTL_SET_APPIEBUF, probe_req_ie, - sizeof(struct ieee80211req_getset_appiebuf) + - ies_len, 1); - os_free(probe_req_ie); +static int +madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr, u32 flags) +{ + struct madwifi_driver_data *drv = priv; + unsigned char buf[3000]; + unsigned char *bp = buf; + struct l2_ethhdr *eth; + size_t len; + int status; - return ret; + /* + * 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 * wpa_driver_madwifi_init(void *ctx, const char *ifname) +static void * +madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) { - struct wpa_driver_madwifi_data *drv; + struct madwifi_driver_data *drv; + struct ifreq ifr; + struct iwreq iwr; + char brname[IFNAMSIZ]; - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) + drv = os_zalloc(sizeof(struct madwifi_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for madwifi driver data\n"); 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; + 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)); - if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " - "roaming", __FUNCTION__); - goto fail3; + 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); - if (set80211param(drv, IEEE80211_PARAM_WPA, 3, 1) < 0) { - wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support", - __FUNCTION__); - goto fail3; + 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; } - return drv; + /* mark down during setup */ + linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + madwifi_set_privacy(drv, 0); /* default to no privacy */ -fail3: - close(drv->sock); -fail2: - wpa_driver_wext_deinit(drv->wext); -fail: - os_free(drv); + 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 wpa_driver_madwifi_deinit(void *priv) +static void +madwifi_deinit(void *priv) { - struct wpa_driver_madwifi_data *drv = 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); +} - 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__); +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; - wpa_driver_wext_deinit(drv->wext); + 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; - close(drv->sock); - os_free(drv); + 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_madwifi_ops = { .name = "madwifi", .desc = "MADWIFI 802.11 support (Atheros, etc.)", - .get_bssid = wpa_driver_madwifi_get_bssid, - .get_ssid = wpa_driver_madwifi_get_ssid, .set_key = wpa_driver_madwifi_set_key, - .init = wpa_driver_madwifi_init, - .deinit = wpa_driver_madwifi_deinit, - .set_countermeasures = wpa_driver_madwifi_set_countermeasures, - .set_drop_unencrypted = wpa_driver_madwifi_set_drop_unencrypted, - .scan = 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_auth_alg = wpa_driver_madwifi_set_auth_alg, - .set_operstate = wpa_driver_madwifi_set_operstate, - .set_probe_req_ie = wpa_driver_madwifi_set_probe_req_ie, + .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, + .set_freq = madwifi_set_freq, }; diff --git a/contrib/hostapd/src/drivers/driver_ndis.c b/contrib/hostapd/src/drivers/driver_ndis.c index a90e277ca0..4953af6a16 100644 --- a/contrib/hostapd/src/drivers/driver_ndis.c +++ b/contrib/hostapd/src/drivers/driver_ndis.c @@ -2,14 +2,8 @@ * WPA Supplicant - Windows/NDIS driver interface * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifdef __CYGWIN__ @@ -40,7 +34,7 @@ int close(int fd); #include "common.h" #include "driver.h" #include "eloop.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" #include "driver_ndis.h" int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv); @@ -353,6 +347,47 @@ typedef struct 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 @@ -690,33 +725,42 @@ static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr, } -static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr, - int reason_code) +static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) { - struct wpa_driver_ndis_data *drv = priv; - return wpa_driver_ndis_disconnect(drv); + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } -static int wpa_driver_ndis_set_wpa(void *priv, int enabled) +static int wpa_driver_ndis_scan_native80211( + struct wpa_driver_ndis_data *drv, + struct wpa_driver_scan_params *params) { - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return 0; -} - + DOT11_SCAN_REQUEST_V2 req; + int res; -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); + 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, const u8 *ssid, size_t ssid_len) +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"); @@ -734,6 +778,25 @@ static int wpa_driver_ndis_scan(void *priv, const u8 *ssid, size_t ssid_len) } +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) { @@ -787,7 +850,7 @@ static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv) os_free(b); return NULL; } - results->res = os_zalloc(count * sizeof(struct wpa_scan_res *)); + results->res = os_calloc(count, sizeof(struct wpa_scan_res *)); if (results->res == NULL) { os_free(results); os_free(b); @@ -912,7 +975,8 @@ static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv, } -static int wpa_driver_ndis_set_key(void *priv, wpa_alg alg, const u8 *addr, +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) @@ -923,8 +987,7 @@ static int wpa_driver_ndis_set_key(void *priv, wpa_alg alg, const u8 *addr, int res, pairwise; u8 bssid[ETH_ALEN]; - if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", - ETH_ALEN) == 0) { + if (addr == NULL || is_broadcast_ether_addr(addr)) { /* Group Key */ pairwise = 0; if (wpa_driver_ndis_get_bssid(drv, bssid) < 0) @@ -988,6 +1051,7 @@ wpa_driver_ndis_associate(void *priv, { struct wpa_driver_ndis_data *drv = priv; u32 auth_mode, encr, priv_mode, mode; + u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; drv->mode = params->mode; @@ -1010,17 +1074,17 @@ wpa_driver_ndis_associate(void *priv, /* Try to continue anyway */ } - if (params->key_mgmt_suite == KEY_MGMT_NONE || - params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) { + if (params->key_mgmt_suite == WPA_KEY_MGMT_NONE || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_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, WPA_ALG_WEP, bcast, 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]); @@ -1028,8 +1092,8 @@ wpa_driver_ndis_associate(void *priv, } if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { - if (params->auth_alg & AUTH_ALG_SHARED_KEY) { - if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + 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; @@ -1038,46 +1102,75 @@ wpa_driver_ndis_associate(void *priv, 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) + if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK) auth_mode = Ndis802_11AuthModeWPA2PSK; else auth_mode = Ndis802_11AuthModeWPA2; #ifdef CONFIG_WPS - } else if (params->key_mgmt_suite == KEY_MGMT_WPS) { + } else if (params->key_mgmt_suite == WPA_KEY_MGMT_WPS) { auth_mode = Ndis802_11AuthModeOpen; priv_mode = Ndis802_11PrivFilterAcceptAll; + if (params->wps == WPS_MODE_PRIVACY) { + u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 }; + /* + * Some NDIS drivers refuse to associate in open mode + * configuration due to Privacy field mismatch, so use + * a workaround to make the configuration look like + * matching one for WPS provisioning. + */ + wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a " + "workaround to allow driver to associate " + "for WPS"); + wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP, + bcast, 0, 1, + NULL, 0, dummy_key, + sizeof(dummy_key)); + } #endif /* CONFIG_WPS */ } else { priv_mode = Ndis802_11PrivFilter8021xWEP; - if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) + if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE) auth_mode = Ndis802_11AuthModeWPANone; - else if (params->key_mgmt_suite == KEY_MGMT_PSK) + else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK) auth_mode = Ndis802_11AuthModeWPAPSK; else auth_mode = Ndis802_11AuthModeWPA; } switch (params->pairwise_suite) { - case CIPHER_CCMP: + case WPA_CIPHER_CCMP: encr = Ndis802_11Encryption3Enabled; break; - case CIPHER_TKIP: + case WPA_CIPHER_TKIP: encr = Ndis802_11Encryption2Enabled; break; - case CIPHER_WEP40: - case CIPHER_WEP104: + case WPA_CIPHER_WEP40: + case WPA_CIPHER_WEP104: encr = Ndis802_11Encryption1Enabled; break; - case CIPHER_NONE: - if (params->group_suite == CIPHER_CCMP) + case WPA_CIPHER_NONE: +#ifdef CONFIG_WPS + if (params->wps == WPS_MODE_PRIVACY) { + encr = Ndis802_11Encryption1Enabled; + break; + } +#endif /* CONFIG_WPS */ + if (params->group_suite == WPA_CIPHER_CCMP) encr = Ndis802_11Encryption3Enabled; - else if (params->group_suite == CIPHER_TKIP) + else if (params->group_suite == WPA_CIPHER_TKIP) encr = Ndis802_11Encryption2Enabled; else encr = Ndis802_11EncryptionDisabled; break; default: +#ifdef CONFIG_WPS + if (params->wps == WPS_MODE_PRIVACY) { + encr = Ndis802_11Encryption1Enabled; + break; + } +#endif /* CONFIG_WPS */ encr = Ndis802_11EncryptionDisabled; + break; }; if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER, @@ -2017,14 +2110,8 @@ static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) 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'; - } - + drv->adapter_desc = dup_binstr(desc, dlen); os_free(b); - if (drv->adapter_desc == NULL) return -1; @@ -2191,14 +2278,8 @@ static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) } 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'; - } - + drv->adapter_desc = dup_binstr(desc[i], dlen); os_free(names); - if (drv->adapter_desc == NULL) return -1; @@ -2801,16 +2882,31 @@ static void * wpa_driver_ndis_init(void *ctx, const char *ifname) 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 */ - if (!drv->has_capability && drv->capa.enc == 0) { + 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); } } @@ -3091,49 +3187,32 @@ wpa_driver_ndis_get_interfaces(void *global_priv) } -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_wpa, - wpa_driver_ndis_set_key, - wpa_driver_ndis_init, - wpa_driver_ndis_deinit, - NULL /* set_param */, - NULL /* set_countermeasures */, - NULL /* set_drop_unencrypted */, - wpa_driver_ndis_scan, - NULL /* get_scan_results */, - wpa_driver_ndis_deauthenticate, - wpa_driver_ndis_disassociate, - wpa_driver_ndis_associate, - NULL /* set_auth_alg */, - 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_probe_req_ie */, - NULL /* set_mode */, - NULL /* set_country */, - NULL /* global_init */, - NULL /* global_deinit */, - NULL /* init2 */, - wpa_driver_ndis_get_interfaces -}; +static const char *ndis_drv_name = "ndis"; +static const char *ndis_drv_desc = "Windows NDIS driver"; + +struct wpa_driver_ops wpa_driver_ndis_ops; + +void driver_ndis_init_ops(void) +{ + os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops)); + wpa_driver_ndis_ops.name = ndis_drv_name; + wpa_driver_ndis_ops.desc = ndis_drv_desc; + wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid; + wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid; + wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key; + wpa_driver_ndis_ops.init = wpa_driver_ndis_init; + wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit; + wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate; + wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate; + wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid; + wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid; + wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid; + wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa; + wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll; + wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname; + wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr; + wpa_driver_ndis_ops.get_scan_results2 = + wpa_driver_ndis_get_scan_results; + wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces; + wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan; +} diff --git a/contrib/hostapd/src/drivers/driver_ndis.h b/contrib/hostapd/src/drivers/driver_ndis.h index cdce4bac85..89d136d3b4 100644 --- a/contrib/hostapd/src/drivers/driver_ndis.h +++ b/contrib/hostapd/src/drivers/driver_ndis.h @@ -2,14 +2,8 @@ * WPA Supplicant - Windows/NDIS driver interface * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DRIVER_NDIS_H @@ -52,6 +46,7 @@ struct wpa_driver_ndis_data { struct ndis_pmkid_entry *pmkid; char *adapter_desc; int wired; + int native80211; int mode; int wzc_disabled; int oid_bssid_set; diff --git a/contrib/hostapd/src/drivers/driver_ndis_.c b/contrib/hostapd/src/drivers/driver_ndis_.c index 4bee9aa543..4d23001964 100644 --- a/contrib/hostapd/src/drivers/driver_ndis_.c +++ b/contrib/hostapd/src/drivers/driver_ndis_.c @@ -2,14 +2,8 @@ * WPA Supplicant - Windows/NDIS driver interface - event processing * Copyright (c) 2004-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/hostapd/src/drivers/driver_ndiswrapper.c b/contrib/hostapd/src/drivers/driver_ndiswrapper.c deleted file mode 100644 index b5c534a74d..0000000000 --- a/contrib/hostapd/src/drivers/driver_ndiswrapper.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Linux ndiswrapper - * Copyright (c) 2004-2006, Giridhar Pemmasani - * Copyright (c) 2004-2006, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * 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 - -#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 -{ - 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; - wpa_cipher pairwise_suite; - wpa_cipher group_suite; - 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 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(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) -{ - 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(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; - - 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, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); -} - - -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; - } - - return drv; -} - - -static void wpa_ndiswrapper_deinit(void *priv) -{ - struct wpa_driver_ndiswrapper_data *drv = priv; - 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_wpa = wpa_ndiswrapper_set_wpa, - .set_key = wpa_ndiswrapper_set_key, - .set_countermeasures = wpa_ndiswrapper_set_countermeasures, - .set_drop_unencrypted = wpa_ndiswrapper_set_drop_unencrypted, - .deauthenticate = wpa_ndiswrapper_deauthenticate, - .disassociate = wpa_ndiswrapper_disassociate, - .associate = wpa_ndiswrapper_associate, - .set_auth_alg = wpa_ndiswrapper_set_auth_alg, - - .get_bssid = wpa_ndiswrapper_get_bssid, - .get_ssid = wpa_ndiswrapper_get_ssid, - .scan = 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/contrib/hostapd/src/drivers/driver_nl80211.c b/contrib/hostapd/src/drivers/driver_nl80211.c index 9ab6d17279..5323e99c08 100644 --- a/contrib/hostapd/src/drivers/driver_nl80211.c +++ b/contrib/hostapd/src/drivers/driver_nl80211.c @@ -1,36 +1,181 @@ /* - * WPA Supplicant - driver interaction with Linux nl80211/cfg80211 - * Copyright (c) 2003-2008, Jouni Malinen + * Driver interaction with Linux nl80211/cfg80211 + * Copyright (c) 2002-2014, Jouni Malinen + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg + * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include -#include +#include +#include +#include +#include #include #include #include -#include "nl80211_copy.h" -#ifdef CONFIG_CLIENT_MLME +#include #include -#include -#include "radiotap.h" -#include "radiotap_iter.h" -#endif /* CONFIG_CLIENT_MLME */ +#include +#include +#include "nl80211_copy.h" -#include "wireless_copy.h" #include "common.h" -#include "driver.h" #include "eloop.h" -#include "ieee802_11_defs.h" +#include "utils/list.h" +#include "common/qca-vendor.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "l2_packet/l2_packet.h" +#include "netlink.h" +#include "linux_ioctl.h" +#include "radiotap.h" +#include "radiotap_iter.h" +#include "rfkill.h" +#include "driver.h" + +#ifndef SO_WIFI_STATUS +# if defined(__sparc__) +# define SO_WIFI_STATUS 0x0025 +# elif defined(__parisc__) +# define SO_WIFI_STATUS 0x4022 +# else +# define SO_WIFI_STATUS 41 +# endif + +# define SCM_WIFI_STATUS SO_WIFI_STATUS +#endif + +#ifndef SO_EE_ORIGIN_TXSTATUS +#define SO_EE_ORIGIN_TXSTATUS 4 +#endif + +#ifndef PACKET_TX_TIMESTAMP +#define PACKET_TX_TIMESTAMP 16 +#endif + +#ifdef ANDROID +#include "android_drv.h" +#endif /* ANDROID */ +#ifdef CONFIG_LIBNL20 +/* libnl 2.0 compatibility code */ +#define nl_handle nl_sock +#define nl80211_handle_alloc nl_socket_alloc_cb +#define nl80211_handle_destroy nl_socket_free +#else +/* + * libnl 1.1 has a bug, it tries to allocate socket numbers densely + * but when you free a socket again it will mess up its bitmap and + * and use the wrong number the next time it needs a socket ID. + * Therefore, we wrap the handle alloc/destroy and add our own pid + * accounting. + */ +static uint32_t port_bitmap[32] = { 0 }; + +static struct nl_handle *nl80211_handle_alloc(void *cb) +{ + struct nl_handle *handle; + uint32_t pid = getpid() & 0x3FFFFF; + int i; + + handle = nl_handle_alloc_cb(cb); + + for (i = 0; i < 1024; i++) { + if (port_bitmap[i / 32] & (1 << (i % 32))) + continue; + port_bitmap[i / 32] |= 1 << (i % 32); + pid += i << 22; + break; + } + + nl_socket_set_local_port(handle, pid); + + return handle; +} + +static void nl80211_handle_destroy(struct nl_handle *handle) +{ + uint32_t port = nl_socket_get_local_port(handle); + + port >>= 22; + port_bitmap[port / 32] &= ~(1 << (port % 32)); + + nl_handle_destroy(handle); +} +#endif /* CONFIG_LIBNL20 */ + + +#ifdef ANDROID +/* system/core/libnl_2 does not include nl_socket_set_nonblocking() */ +static int android_nl_socket_set_nonblocking(struct nl_handle *handle) +{ + return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK); +} +#undef nl_socket_set_nonblocking +#define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h) +#endif /* ANDROID */ + + +static struct nl_handle * nl_create_handle(struct nl_cb *cb, const char *dbg) +{ + struct nl_handle *handle; + + handle = nl80211_handle_alloc(cb); + if (handle == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks (%s)", dbg); + return NULL; + } + + if (genl_connect(handle)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " + "netlink (%s)", dbg); + nl80211_handle_destroy(handle); + return NULL; + } + + return handle; +} + + +static void nl_destroy_handles(struct nl_handle **handle) +{ + if (*handle == NULL) + return; + nl80211_handle_destroy(*handle); + *handle = NULL; +} + + +#if __WORDSIZE == 64 +#define ELOOP_SOCKET_INVALID (intptr_t) 0x8888888888888889ULL +#else +#define ELOOP_SOCKET_INVALID (intptr_t) 0x88888889ULL +#endif + +static void nl80211_register_eloop_read(struct nl_handle **handle, + eloop_sock_handler handler, + void *eloop_data) +{ + nl_socket_set_nonblocking(*handle); + eloop_register_read_sock(nl_socket_get_fd(*handle), handler, + eloop_data, *handle); + *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); +} + + +static void nl80211_destroy_eloop_handle(struct nl_handle **handle) +{ + *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); + eloop_unregister_read_sock(nl_socket_get_fd(*handle)); + nl_destroy_handles(handle); +} + #ifndef IFF_LOWER_UP #define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ @@ -46,51 +191,383 @@ #define IF_OPER_UP 6 #endif +struct nl80211_global { + struct dl_list interfaces; + int if_add_ifindex; + u64 if_add_wdevid; + int if_add_wdevid_set; + struct netlink_data *netlink; + struct nl_cb *nl_cb; + struct nl_handle *nl; + int nl80211_id; + int ioctl_sock; /* socket for ioctl() use */ + + struct nl_handle *nl_event; +}; + +struct nl80211_wiphy_data { + struct dl_list list; + struct dl_list bsss; + struct dl_list drvs; + + struct nl_handle *nl_beacons; + struct nl_cb *nl_cb; + + int wiphy_idx; +}; + +static void nl80211_global_deinit(void *priv); + +struct i802_bss { + struct wpa_driver_nl80211_data *drv; + struct i802_bss *next; + int ifindex; + u64 wdev_id; + char ifname[IFNAMSIZ + 1]; + char brname[IFNAMSIZ]; + unsigned int beacon_set:1; + unsigned int added_if_into_bridge:1; + unsigned int added_bridge:1; + unsigned int in_deinit:1; + unsigned int wdev_id_set:1; + unsigned int added_if:1; + + u8 addr[ETH_ALEN]; + + int freq; + int if_dynamic; + + void *ctx; + struct nl_handle *nl_preq, *nl_mgmt; + struct nl_cb *nl_cb; + + struct nl80211_wiphy_data *wiphy_data; + struct dl_list wiphy_list; +}; struct wpa_driver_nl80211_data { + struct nl80211_global *global; + struct dl_list list; + struct dl_list wiphy_list; + char phyname[32]; void *ctx; - int wext_event_sock; - int ioctl_sock; - char ifname[IFNAMSIZ + 1]; int ifindex; int if_removed; - u8 *assoc_req_ies; - size_t assoc_req_ies_len; - u8 *assoc_resp_ies; - size_t assoc_resp_ies_len; + int if_disabled; + int ignore_if_down_event; + struct rfkill_data *rfkill; struct wpa_driver_capa capa; + u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; 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; + enum scan_states { + NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED, + SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED, + SCHED_SCAN_RESULTS + } scan_state; - struct nl_handle *nl_handle; - struct nl_cache *nl_cache; struct nl_cb *nl_cb; - struct genl_family *nl80211; -#ifdef CONFIG_CLIENT_MLME - int monitor_sock; /* socket for monitor */ + u8 auth_bssid[ETH_ALEN]; + u8 auth_attempt_bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u8 prev_bssid[ETH_ALEN]; + int associated; + u8 ssid[32]; + size_t ssid_len; + enum nl80211_iftype nlmode; + enum nl80211_iftype ap_scan_as_station; + unsigned int assoc_freq; + + int monitor_sock; int monitor_ifidx; -#endif /* CONFIG_CLIENT_MLME */ + int monitor_refcount; + + unsigned int disabled_11b_rates:1; + unsigned int pending_remain_on_chan:1; + unsigned int in_interface_list:1; + unsigned int device_ap_sme:1; + unsigned int poll_command_supported:1; + unsigned int data_tx_status:1; + unsigned int scan_for_auth:1; + unsigned int retry_auth:1; + unsigned int use_monitor:1; + unsigned int ignore_next_local_disconnect:1; + unsigned int allow_p2p_device:1; + unsigned int hostapd:1; + unsigned int start_mode_ap:1; + unsigned int start_iface_up:1; + + u64 remain_on_chan_cookie; + u64 send_action_cookie; + + unsigned int last_mgmt_freq; + + struct wpa_driver_scan_filter *filter_ssids; + size_t num_filter_ssids; + + struct i802_bss *first_bss; + + int eapol_tx_sock; + + int eapol_sock; /* socket for EAPOL frames */ + + int default_if_indices[16]; + int *if_indices; + int num_if_indices; + + /* From failed authentication command */ + int auth_freq; + u8 auth_bssid_[ETH_ALEN]; + u8 auth_ssid[32]; + size_t auth_ssid_len; + int auth_alg; + u8 *auth_ie; + size_t auth_ie_len; + u8 auth_wep_key[4][16]; + size_t auth_wep_key_len[4]; + int auth_wep_tx_keyidx; + int auth_local_state_change; + int auth_p2p; }; +static void wpa_driver_nl80211_deinit(struct i802_bss *bss); 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_flush_pmkid(void *priv); -static int wpa_driver_nl80211_get_range(void *priv); -static void -wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); +static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, + enum nl80211_iftype nlmode); +static int +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, + const u8 *set_addr, int first); +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); +static int nl80211_send_frame_cmd(struct i802_bss *bss, + unsigned int freq, unsigned int wait, + const u8 *buf, size_t buf_len, u64 *cookie, + int no_cck, int no_ack, int offchanok); +static int nl80211_register_frame(struct i802_bss *bss, + struct nl_handle *hl_handle, + u16 type, const u8 *match, size_t match_len); +static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, + int report); +#ifdef ANDROID +static int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params); +static int android_pno_stop(struct i802_bss *bss); +extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, + size_t buf_len); +#endif /* ANDROID */ +#ifdef ANDROID_P2P +int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration); +int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len); +int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow); +int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp); +#endif /* ANDROID_P2P */ + +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(struct i802_bss *bss, + enum wpa_driver_if_type type, + const char *ifname); + +static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, + struct hostapd_freq_params *freq); +static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, + int ifindex, int disabled); + +static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv); +static int wpa_driver_nl80211_authenticate_retry( + struct wpa_driver_nl80211_data *drv); + +static int i802_set_iface_flags(struct i802_bss *bss, int up); + + +static const char * nl80211_command_to_string(enum nl80211_commands cmd) +{ +#define C2S(x) case x: return #x; + switch (cmd) { + C2S(NL80211_CMD_UNSPEC) + C2S(NL80211_CMD_GET_WIPHY) + C2S(NL80211_CMD_SET_WIPHY) + C2S(NL80211_CMD_NEW_WIPHY) + C2S(NL80211_CMD_DEL_WIPHY) + C2S(NL80211_CMD_GET_INTERFACE) + C2S(NL80211_CMD_SET_INTERFACE) + C2S(NL80211_CMD_NEW_INTERFACE) + C2S(NL80211_CMD_DEL_INTERFACE) + C2S(NL80211_CMD_GET_KEY) + C2S(NL80211_CMD_SET_KEY) + C2S(NL80211_CMD_NEW_KEY) + C2S(NL80211_CMD_DEL_KEY) + C2S(NL80211_CMD_GET_BEACON) + C2S(NL80211_CMD_SET_BEACON) + C2S(NL80211_CMD_START_AP) + C2S(NL80211_CMD_STOP_AP) + C2S(NL80211_CMD_GET_STATION) + C2S(NL80211_CMD_SET_STATION) + C2S(NL80211_CMD_NEW_STATION) + C2S(NL80211_CMD_DEL_STATION) + C2S(NL80211_CMD_GET_MPATH) + C2S(NL80211_CMD_SET_MPATH) + C2S(NL80211_CMD_NEW_MPATH) + C2S(NL80211_CMD_DEL_MPATH) + C2S(NL80211_CMD_SET_BSS) + C2S(NL80211_CMD_SET_REG) + C2S(NL80211_CMD_REQ_SET_REG) + C2S(NL80211_CMD_GET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MGMT_EXTRA_IE) + C2S(NL80211_CMD_GET_REG) + C2S(NL80211_CMD_GET_SCAN) + C2S(NL80211_CMD_TRIGGER_SCAN) + C2S(NL80211_CMD_NEW_SCAN_RESULTS) + C2S(NL80211_CMD_SCAN_ABORTED) + C2S(NL80211_CMD_REG_CHANGE) + C2S(NL80211_CMD_AUTHENTICATE) + C2S(NL80211_CMD_ASSOCIATE) + C2S(NL80211_CMD_DEAUTHENTICATE) + C2S(NL80211_CMD_DISASSOCIATE) + C2S(NL80211_CMD_MICHAEL_MIC_FAILURE) + C2S(NL80211_CMD_REG_BEACON_HINT) + C2S(NL80211_CMD_JOIN_IBSS) + C2S(NL80211_CMD_LEAVE_IBSS) + C2S(NL80211_CMD_TESTMODE) + C2S(NL80211_CMD_CONNECT) + C2S(NL80211_CMD_ROAM) + C2S(NL80211_CMD_DISCONNECT) + C2S(NL80211_CMD_SET_WIPHY_NETNS) + C2S(NL80211_CMD_GET_SURVEY) + C2S(NL80211_CMD_NEW_SURVEY_RESULTS) + C2S(NL80211_CMD_SET_PMKSA) + C2S(NL80211_CMD_DEL_PMKSA) + C2S(NL80211_CMD_FLUSH_PMKSA) + C2S(NL80211_CMD_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_SET_TX_BITRATE_MASK) + C2S(NL80211_CMD_REGISTER_FRAME) + C2S(NL80211_CMD_FRAME) + C2S(NL80211_CMD_FRAME_TX_STATUS) + C2S(NL80211_CMD_SET_POWER_SAVE) + C2S(NL80211_CMD_GET_POWER_SAVE) + C2S(NL80211_CMD_SET_CQM) + C2S(NL80211_CMD_NOTIFY_CQM) + C2S(NL80211_CMD_SET_CHANNEL) + C2S(NL80211_CMD_SET_WDS_PEER) + C2S(NL80211_CMD_FRAME_WAIT_CANCEL) + C2S(NL80211_CMD_JOIN_MESH) + C2S(NL80211_CMD_LEAVE_MESH) + C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE) + C2S(NL80211_CMD_UNPROT_DISASSOCIATE) + C2S(NL80211_CMD_NEW_PEER_CANDIDATE) + C2S(NL80211_CMD_GET_WOWLAN) + C2S(NL80211_CMD_SET_WOWLAN) + C2S(NL80211_CMD_START_SCHED_SCAN) + C2S(NL80211_CMD_STOP_SCHED_SCAN) + C2S(NL80211_CMD_SCHED_SCAN_RESULTS) + C2S(NL80211_CMD_SCHED_SCAN_STOPPED) + C2S(NL80211_CMD_SET_REKEY_OFFLOAD) + C2S(NL80211_CMD_PMKSA_CANDIDATE) + C2S(NL80211_CMD_TDLS_OPER) + C2S(NL80211_CMD_TDLS_MGMT) + C2S(NL80211_CMD_UNEXPECTED_FRAME) + C2S(NL80211_CMD_PROBE_CLIENT) + C2S(NL80211_CMD_REGISTER_BEACONS) + C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME) + C2S(NL80211_CMD_SET_NOACK_MAP) + C2S(NL80211_CMD_CH_SWITCH_NOTIFY) + C2S(NL80211_CMD_START_P2P_DEVICE) + C2S(NL80211_CMD_STOP_P2P_DEVICE) + C2S(NL80211_CMD_CONN_FAILED) + C2S(NL80211_CMD_SET_MCAST_RATE) + C2S(NL80211_CMD_SET_MAC_ACL) + C2S(NL80211_CMD_RADAR_DETECT) + C2S(NL80211_CMD_GET_PROTOCOL_FEATURES) + C2S(NL80211_CMD_UPDATE_FT_IES) + C2S(NL80211_CMD_FT_EVENT) + C2S(NL80211_CMD_CRIT_PROTOCOL_START) + C2S(NL80211_CMD_CRIT_PROTOCOL_STOP) + C2S(NL80211_CMD_GET_COALESCE) + C2S(NL80211_CMD_SET_COALESCE) + C2S(NL80211_CMD_CHANNEL_SWITCH) + C2S(NL80211_CMD_VENDOR) + C2S(NL80211_CMD_SET_QOS_MAP) + default: + return "NL80211_CMD_UNKNOWN"; + } +#undef C2S +} + + +/* Converts nl80211_chan_width to a common format */ +static enum chan_width convert2width(int width) +{ + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return CHAN_WIDTH_20_NOHT; + case NL80211_CHAN_WIDTH_20: + return CHAN_WIDTH_20; + case NL80211_CHAN_WIDTH_40: + return CHAN_WIDTH_40; + case NL80211_CHAN_WIDTH_80: + return CHAN_WIDTH_80; + case NL80211_CHAN_WIDTH_80P80: + return CHAN_WIDTH_80P80; + case NL80211_CHAN_WIDTH_160: + return CHAN_WIDTH_160; + } + return CHAN_WIDTH_UNKNOWN; +} + + +static int is_ap_interface(enum nl80211_iftype nlmode) +{ + return (nlmode == NL80211_IFTYPE_AP || + nlmode == NL80211_IFTYPE_P2P_GO); +} + + +static int is_sta_interface(enum nl80211_iftype nlmode) +{ + return (nlmode == NL80211_IFTYPE_STATION || + nlmode == NL80211_IFTYPE_P2P_CLIENT); +} + + +static int is_p2p_net_interface(enum nl80211_iftype nlmode) +{ + return (nlmode == NL80211_IFTYPE_P2P_CLIENT || + nlmode == NL80211_IFTYPE_P2P_GO); +} + + +static void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) +{ + if (drv->associated) + os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); + drv->associated = 0; + os_memset(drv->bssid, 0, ETH_ALEN); +} + + +struct nl80211_bss_info_arg { + struct wpa_driver_nl80211_data *drv; + struct wpa_scan_results *res; + unsigned int assoc_freq; + u8 assoc_bssid[ETH_ALEN]; +}; + +static int bss_info_handler(struct nl_msg *msg, void *arg); /* nl80211 code */ @@ -116,19 +593,26 @@ static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, return NL_SKIP; } -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) + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + + +static int send_and_recv(struct nl80211_global *global, + 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); + cb = nl_cb_clone(global->nl_cb); if (!cb) goto out; - err = nl_send_auto_complete(drv->nl_handle, msg); + err = nl_send_auto_complete(nl_handle, msg); if (err < 0) goto out; @@ -142,8 +626,14 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, valid_data); - while (err > 0) - nl_recvmsgs(drv->nl_handle, cb); + while (err > 0) { + int res = nl_recvmsgs(nl_handle, cb); + if (res) { + wpa_printf(MSG_INFO, + "nl80211: %s->nl_recvmsgs failed: %d", + __func__, res); + } + } out: nl_cb_put(cb); nlmsg_free(msg); @@ -151,12 +641,45 @@ static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, } +static int send_and_recv_msgs_global(struct nl80211_global *global, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + return send_and_recv(global, global->nl, msg, valid_handler, + valid_data); +} + + +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->global, drv->global->nl, msg, + valid_handler, valid_data); +} + + struct family_data { const char *group; int id; }; +static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss) +{ + if (bss->wdev_id_set) + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + else + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + return 0; + +nla_put_failure: + return -1; +} + + static int family_handler(struct nl_msg *msg, void *arg) { struct family_data *res = arg; @@ -188,7 +711,7 @@ static int family_handler(struct nl_msg *msg, void *arg) } -static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, +static int nl_get_multicast_id(struct nl80211_global *global, const char *family, const char *group) { struct nl_msg *msg; @@ -198,11 +721,11 @@ static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), + genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "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); + ret = send_and_recv_msgs_global(global, msg, family_handler, &res); msg = NULL; if (ret == 0) ret = res.id; @@ -213,557 +736,387 @@ nla_put_failure: } -static int wpa_driver_nl80211_send_oper_ifla( - struct wpa_driver_nl80211_data *drv, - int linkmode, int operstate) +static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, int flags, uint8_t cmd) { - 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 = drv->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 = (struct rtattr *) - ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); - 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, "WEXT: Operstate: linkmode=%d, operstate=%d", - linkmode, operstate); - - ret = send(drv->wext_event_sock, &req, req.hdr.nlmsg_len, 0); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " - "%s (assume operstate is not supported)", - strerror(errno)); - } - - return ret < 0 ? -1 : 0; + return genlmsg_put(msg, 0, 0, drv->global->nl80211_id, + 0, flags, cmd, 0); } -static int wpa_driver_nl80211_set_auth_param( - struct wpa_driver_nl80211_data *drv, int idx, u32 value) +struct wiphy_idx_data { + int wiphy_idx; + enum nl80211_iftype nlmode; + u8 *macaddr; +}; + + +static int netdev_info_handler(struct nl_msg *msg, void *arg) { - struct iwreq iwr; - int ret = 0; + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wiphy_idx_data *info = arg; - 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; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); - 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; - } + if (tb[NL80211_ATTR_WIPHY]) + info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); - return ret; + if (tb[NL80211_ATTR_IFTYPE]) + info->nlmode = nla_get_u32(tb[NL80211_ATTR_IFTYPE]); + + if (tb[NL80211_ATTR_MAC] && info->macaddr) + os_memcpy(info->macaddr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + + return NL_SKIP; } -static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) +static int nl80211_get_wiphy_index(struct i802_bss *bss) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; + struct nl_msg *msg; + struct wiphy_idx_data data = { + .wiphy_idx = -1, + .macaddr = NULL, + }; - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + msg = nlmsg_alloc(); + if (!msg) + return NL80211_IFTYPE_UNSPECIFIED; - if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { - perror("ioctl[SIOCGIWAP]"); - ret = -1; - } - os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); - return ret; + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) + return data.wiphy_idx; + msg = NULL; +nla_put_failure: + nlmsg_free(msg); + return -1; } -static int wpa_driver_nl80211_set_bssid(void *priv, const u8 *bssid) +static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; + struct nl_msg *msg; + struct wiphy_idx_data data = { + .nlmode = NL80211_IFTYPE_UNSPECIFIED, + .macaddr = NULL, + }; - 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); + msg = nlmsg_alloc(); + if (!msg) + return -1; - if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { - perror("ioctl[SIOCSIWAP]"); - ret = -1; - } + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); - return ret; + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) + return data.nlmode; + msg = NULL; +nla_put_failure: + nlmsg_free(msg); + return NL80211_IFTYPE_UNSPECIFIED; } -static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) +static int nl80211_get_macaddr(struct i802_bss *bss) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; + struct nl_msg *msg; + struct wiphy_idx_data data = { + .macaddr = bss->addr, + }; + + msg = nlmsg_alloc(); + if (!msg) + return NL80211_IFTYPE_UNSPECIFIED; - 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; + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; - 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 send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data); - return ret; +nla_put_failure: + nlmsg_free(msg); + return NL80211_IFTYPE_UNSPECIFIED; } -static int wpa_driver_nl80211_set_ssid(void *priv, const u8 *ssid, - size_t ssid_len) +static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv, + struct nl80211_wiphy_data *w) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; - char buf[33]; + struct nl_msg *msg; + int ret = -1; - if (ssid_len > 32) + msg = nlmsg_alloc(); + if (!msg) 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; - } + nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS); + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx); + ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register beacons 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_set_freq(void *priv, int freq) +static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; + struct nl80211_wiphy_data *w = eloop_ctx; + int res; - 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; + wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available"); - if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - perror("ioctl[SIOCSIWFREQ]"); - ret = -1; + res = nl_recvmsgs(handle, w->nl_cb); + if (res) { + wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", + __func__, res); } - - return ret; } -static void -wpa_driver_nl80211_event_wireless_custom(void *ctx, char *custom) +static int process_beacon_event(struct nl_msg *msg, void *arg) { - union wpa_event_data data; - - wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", - custom); + struct nl80211_wiphy_data *w = arg; + struct wpa_driver_nl80211_data *drv; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + union wpa_event_data event; - 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; - - spos = custom + 17; - - bytes = strspn(spos, "0123456789abcdefABCDEF"); - if (!bytes || (bytes & 1)) - return; - bytes /= 2; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); - data.assoc_info.req_ies = os_malloc(bytes); - if (data.assoc_info.req_ies == NULL) - return; - - data.assoc_info.req_ies_len = bytes; - hexstr2bin(spos, data.assoc_info.req_ies, bytes); + if (gnlh->cmd != NL80211_CMD_FRAME) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)", + gnlh->cmd); + return NL_SKIP; + } - spos += bytes * 2; + if (!tb[NL80211_ATTR_FRAME]) + return NL_SKIP; - data.assoc_info.resp_ies = NULL; - data.assoc_info.resp_ies_len = 0; + dl_list_for_each(drv, &w->drvs, struct wpa_driver_nl80211_data, + wiphy_list) { + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = nla_data(tb[NL80211_ATTR_FRAME]); + event.rx_mgmt.frame_len = nla_len(tb[NL80211_ATTR_FRAME]); + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + } - if (os_strncmp(spos, " RespIEs=", 9) == 0) { - spos += 9; + return NL_SKIP; +} - bytes = strspn(spos, "0123456789abcdefABCDEF"); - if (!bytes || (bytes & 1)) - goto done; - bytes /= 2; - data.assoc_info.resp_ies = os_malloc(bytes); - if (data.assoc_info.resp_ies == NULL) - goto done; +static struct nl80211_wiphy_data * +nl80211_get_wiphy_data_ap(struct i802_bss *bss) +{ + static DEFINE_DL_LIST(nl80211_wiphys); + struct nl80211_wiphy_data *w; + int wiphy_idx, found = 0; + struct i802_bss *tmp_bss; - data.assoc_info.resp_ies_len = bytes; - hexstr2bin(spos, data.assoc_info.resp_ies, bytes); - } + if (bss->wiphy_data != NULL) + return bss->wiphy_data; - wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + wiphy_idx = nl80211_get_wiphy_index(bss); - done: - os_free(data.assoc_info.resp_ies); - os_free(data.assoc_info.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 */ + dl_list_for_each(w, &nl80211_wiphys, struct nl80211_wiphy_data, list) { + if (w->wiphy_idx == wiphy_idx) + goto add; } -} + /* alloc new one */ + w = os_zalloc(sizeof(*w)); + if (w == NULL) + return NULL; + w->wiphy_idx = wiphy_idx; + dl_list_init(&w->bsss); + dl_list_init(&w->drvs); -static int wpa_driver_nl80211_event_wireless_michaelmicfailure( - void *ctx, const char *ev, size_t len) -{ - const struct iw_michaelmicfailure *mic; - union wpa_event_data data; + w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!w->nl_cb) { + os_free(w); + return NULL; + } + nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event, + w); + + w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb, + "wiphy beacons"); + if (w->nl_beacons == NULL) { + os_free(w); + return NULL; + } - if (len < sizeof(*mic)) - return -1; + if (nl80211_register_beacons(bss->drv, w)) { + nl_destroy_handles(&w->nl_beacons); + os_free(w); + return NULL; + } - mic = (const struct iw_michaelmicfailure *) ev; + nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w); - wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " - "flags=0x%x src_addr=" MACSTR, mic->flags, - MAC2STR(mic->src_addr.sa_data)); + dl_list_add(&nl80211_wiphys, &w->list); - 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); +add: + /* drv entry for this bss already there? */ + dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) { + if (tmp_bss->drv == bss->drv) { + found = 1; + break; + } + } + /* if not add it */ + if (!found) + dl_list_add(&w->drvs, &bss->drv->wiphy_list); - return 0; + dl_list_add(&w->bsss, &bss->wiphy_list); + bss->wiphy_data = w; + return w; } -static int wpa_driver_nl80211_event_wireless_pmkidcand( - struct wpa_driver_nl80211_data *drv, const char *ev, size_t len) +static void nl80211_put_wiphy_data_ap(struct i802_bss *bss) { - const struct iw_pmkid_cand *cand; - union wpa_event_data data; - const u8 *addr; + struct nl80211_wiphy_data *w = bss->wiphy_data; + struct i802_bss *tmp_bss; + int found = 0; - if (len < sizeof(*cand)) - return -1; + if (w == NULL) + return; + bss->wiphy_data = NULL; + dl_list_del(&bss->wiphy_list); - cand = (const struct iw_pmkid_cand *) ev; - addr = (const u8 *) cand->bssid.sa_data; + /* still any for this drv present? */ + dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) { + if (tmp_bss->drv == bss->drv) { + found = 1; + break; + } + } + /* if not remove it */ + if (!found) + dl_list_del(&bss->drv->wiphy_list); - wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " - "flags=0x%x index=%d bssid=" MACSTR, cand->flags, - cand->index, MAC2STR(addr)); + if (!dl_list_empty(&w->bsss)) + return; - 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); + nl80211_destroy_eloop_handle(&w->nl_beacons); - return 0; + nl_cb_put(w->nl_cb); + dl_list_del(&w->list); + os_free(w); } -static int wpa_driver_nl80211_event_wireless_assocreqie( - struct wpa_driver_nl80211_data *drv, const char *ev, int len) +static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) { - if (len < 0) + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!drv->associated) 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; - + os_memcpy(bssid, drv->bssid, ETH_ALEN); return 0; } -static int wpa_driver_nl80211_event_wireless_assocrespie( - struct wpa_driver_nl80211_data *drv, const char *ev, int len) +static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) { - 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; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!drv->associated) return -1; - } - os_memcpy(drv->assoc_resp_ies, ev, len); - drv->assoc_resp_ies_len = len; - - return 0; + os_memcpy(ssid, drv->ssid, drv->ssid_len); + return drv->ssid_len; } -static void wpa_driver_nl80211_event_assoc_ies(struct wpa_driver_nl80211_data *drv) +static void wpa_driver_nl80211_event_newlink( + struct wpa_driver_nl80211_data *drv, char *ifname) { - union wpa_event_data data; - - if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) - return; + union wpa_event_data event; - os_memset(&data, 0, sizeof(data)); - if (drv->assoc_req_ies) { - data.assoc_info.req_ies = drv->assoc_req_ies; - drv->assoc_req_ies = NULL; - 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; - drv->assoc_resp_ies = NULL; - data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; + if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { + if (if_nametoindex(drv->first_bss->ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK", + drv->first_bss->ifname); + return; + } + if (!drv->if_removed) + return; + wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event", + drv->first_bss->ifname); + drv->if_removed = 0; } - wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); - - os_free(data.assoc_info.req_ies); - os_free(data.assoc_info.resp_ies); + os_memset(&event, 0, sizeof(event)); + os_strlcpy(event.interface_status.ifname, ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_ADDED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } -static void wpa_driver_nl80211_event_wireless(struct wpa_driver_nl80211_data *drv, - void *ctx, char *data, int len) +static void wpa_driver_nl80211_event_dellink( + struct wpa_driver_nl80211_data *drv, char *ifname) { - struct iw_event iwe_buf, *iwe = &iwe_buf; - char *pos, *end, *custom, *buf; + union wpa_event_data event; - 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) + if (os_strcmp(drv->first_bss->ifname, ifname) == 0) { + if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s", + ifname); 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(ctx, EVENT_DISASSOC, - NULL); - - } else { - wpa_driver_nl80211_event_assoc_ies(drv); - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); - } - break; - case IWEVMICHAELMICFAILURE: - wpa_driver_nl80211_event_wireless_michaelmicfailure( - ctx, custom, iwe->u.data.length); - break; - 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'; - wpa_driver_nl80211_event_wireless_custom(ctx, buf); - os_free(buf); - break; - case IWEVASSOCREQIE: - wpa_driver_nl80211_event_wireless_assocreqie( - drv, custom, iwe->u.data.length); - break; - case IWEVASSOCRESPIE: - wpa_driver_nl80211_event_wireless_assocrespie( - drv, custom, iwe->u.data.length); - break; - case IWEVPMKIDCAND: - wpa_driver_nl80211_event_wireless_pmkidcand( - drv, custom, iwe->u.data.length); - break; } - - pos += iwe->len; + wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1", + ifname); + drv->if_removed = 1; + } else { + wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed", + ifname); } -} - - -static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, - void *ctx, 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(ctx, EVENT_INTERFACE_STATUS, &event); + os_strlcpy(event.interface_status.ifname, ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, - struct nlmsghdr *h) + u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, _nlmsg_len, rta_len; + int attrlen, rta_len; struct rtattr *attr; - ifi = NLMSG_DATA(h); - - _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - _nlmsg_len; - if (attrlen < 0) - return 0; - - attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + 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) + if (os_strcmp(((char *) attr) + rta_len, + drv->first_bss->ifname) == 0) return 1; else break; @@ -776,16 +1129,15 @@ static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, - int ifindex, struct nlmsghdr *h) + int ifindex, u8 *buf, size_t len) { if (drv->ifindex == ifindex) return 1; - if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, h)) { - drv->ifindex = if_nametoindex(drv->ifname); + if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " "interface"); - wpa_driver_nl80211_finish_drv_init(drv); + wpa_driver_nl80211_finish_drv_init(drv, NULL, 0); return 1; } @@ -793,32 +1145,125 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, } -static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv, - void *ctx, struct nlmsghdr *h, - size_t len) +static struct wpa_driver_nl80211_data * +nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, _nlmsg_len, rta_len; - struct rtattr * attr; + struct wpa_driver_nl80211_data *drv; + dl_list_for_each(drv, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) || + have_ifidx(drv, idx)) + return drv; + } + return NULL; +} - if (len < sizeof(*ifi)) - return; - ifi = NLMSG_DATA(h); +static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) +{ + struct nl80211_global *global = ctx; + struct wpa_driver_nl80211_data *drv; + int attrlen; + struct rtattr *attr; + u32 brid = 0; + char namebuf[IFNAMSIZ]; + char ifname[IFNAMSIZ + 1]; + char extra[100], *pos, *end; - if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, h)) { - wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_NEWLINK 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, + extra[0] = '\0'; + pos = extra; + end = pos + sizeof(extra); + ifname[0] = '\0'; + + attrlen = len; + attr = (struct rtattr *) buf; + while (RTA_OK(attr, attrlen)) { + switch (attr->rta_type) { + case IFLA_IFNAME: + if (RTA_PAYLOAD(attr) >= IFNAMSIZ) + break; + os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr)); + ifname[RTA_PAYLOAD(attr)] = '\0'; + break; + case IFLA_MASTER: + brid = nla_get_u32((struct nlattr *) attr); + pos += os_snprintf(pos, end - pos, " master=%u", brid); + break; + case IFLA_WIRELESS: + pos += os_snprintf(pos, end - pos, " wext"); + break; + case IFLA_OPERSTATE: + pos += os_snprintf(pos, end - pos, " operstate=%u", + nla_get_u32((struct nlattr *) attr)); + break; + case IFLA_LINKMODE: + pos += os_snprintf(pos, end - pos, " linkmode=%u", + nla_get_u32((struct nlattr *) attr)); + break; + } + attr = RTA_NEXT(attr, attrlen); + } + extra[sizeof(extra) - 1] = '\0'; + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_flags=0x%x (%s%s%s%s)", + ifi->ifi_index, ifname, extra, 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]" : ""); + + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + if (if_indextoname(ifi->ifi_index, namebuf) && + linux_iface_up(drv->global->ioctl_sock, + drv->first_bss->ifname) > 0) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " + "event since interface %s is up", namebuf); + return; + } + wpa_printf(MSG_DEBUG, "nl80211: Interface down"); + if (drv->ignore_if_down_event) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " + "event generated by mode change"); + drv->ignore_if_down_event = 0; + } else { + drv->if_disabled = 1; + wpa_supplicant_event(drv->ctx, + EVENT_INTERFACE_DISABLED, NULL); + } + } + + if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + if (if_indextoname(ifi->ifi_index, namebuf) && + linux_iface_up(drv->global->ioctl_sock, + drv->first_bss->ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " + "event since interface %s is down", + namebuf); + } else if (if_nametoindex(drv->first_bss->ifname) == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " + "event since interface %s does not exist", + drv->first_bss->ifname); + } else if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " + "event since interface %s is marked " + "removed", drv->first_bss->ifname); + } else { + wpa_printf(MSG_DEBUG, "nl80211: Interface up"); + drv->if_disabled = 0; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, + NULL); + } + } + /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_nl80211_set_operstate() @@ -827,1940 +1272,10502 @@ static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data */ if (drv->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && - !(ifi->ifi_flags & IFF_RUNNING)) - wpa_driver_nl80211_send_oper_ifla(drv, -1, IF_OPER_UP); + !(ifi->ifi_flags & IFF_RUNNING)) { + wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate"); + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, + -1, IF_OPER_UP); + } - _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + if (ifname[0]) + wpa_driver_nl80211_event_newlink(drv, ifname); + + if (ifi->ifi_family == AF_BRIDGE && brid) { + /* device has been added to bridge */ + if_indextoname(brid, namebuf); + wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", + brid, namebuf); + add_ifidx(drv, brid); + } +} + + +static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) +{ + struct nl80211_global *global = ctx; + struct wpa_driver_nl80211_data *drv; + int attrlen; + struct rtattr *attr; + u32 brid = 0; + char ifname[IFNAMSIZ + 1]; - attrlen = h->nlmsg_len - _nlmsg_len; - if (attrlen < 0) + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_DELLINK event for foreign ifindex %d", + ifi->ifi_index); return; + } - attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + ifname[0] = '\0'; - rta_len = RTA_ALIGN(sizeof(struct rtattr)); + attrlen = len; + attr = (struct rtattr *) buf; while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_WIRELESS) { - wpa_driver_nl80211_event_wireless( - drv, ctx, ((char *) attr) + rta_len, - attr->rta_len - rta_len); - } else if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_nl80211_event_link( - drv, ctx, - ((char *) attr) + rta_len, - attr->rta_len - rta_len, 0); + switch (attr->rta_type) { + case IFLA_IFNAME: + if (RTA_PAYLOAD(attr) >= IFNAMSIZ) + break; + os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr)); + ifname[RTA_PAYLOAD(attr)] = '\0'; + break; + case IFLA_MASTER: + brid = nla_get_u32((struct nlattr *) attr); + break; } attr = RTA_NEXT(attr, attrlen); } + + if (ifname[0]) + wpa_driver_nl80211_event_dellink(drv, ifname); + + 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); + } } -static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv, - void *ctx, struct nlmsghdr *h, - size_t len) +static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, + const u8 *frame, size_t len) { - struct ifinfomsg *ifi; - int attrlen, _nlmsg_len, rta_len; - struct rtattr * attr; + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; - if (len < sizeof(*ifi)) + wpa_printf(MSG_DEBUG, "nl80211: Authenticate 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(drv->auth_attempt_bssid, 0, 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.auth_transaction = + le_to_host16(mgmt->u.auth.auth_transaction); + 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); + } - ifi = NLMSG_DATA(h); + wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event); +} - _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - attrlen = h->nlmsg_len - _nlmsg_len; - if (attrlen < 0) - return; +static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + int ret; + struct nl80211_bss_info_arg arg; + + os_memset(&arg, 0, sizeof(arg)); + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; - attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - rta_len = RTA_ALIGN(sizeof(struct rtattr)); - while (RTA_OK(attr, attrlen)) { - if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_nl80211_event_link( - drv, ctx, - ((char *) attr) + rta_len, - attr->rta_len - rta_len, 1); - } - attr = RTA_NEXT(attr, attrlen); + arg.drv = drv; + ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); + msg = NULL; + if (ret == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the " + "associated BSS from scan results: %u MHz", + arg.assoc_freq); + if (arg.assoc_freq) + drv->assoc_freq = arg.assoc_freq; + return drv->assoc_freq; } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " + "(%s)", ret, strerror(-ret)); +nla_put_failure: + nlmsg_free(msg); + return drv->assoc_freq; } -static void wpa_driver_nl80211_event_receive_wext(int sock, void *eloop_ctx, - void *sock_ctx) +static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, + const u8 *frame, size_t len) { - char buf[8192]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - int max_events = 10; + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 status; -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) - perror("recvfrom(netlink)"); + wpa_printf(MSG_DEBUG, "nl80211: Associate event"); + 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; } - 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, "Malformed netlink message: " - "len=%d left=%d plen=%d", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx, - h, plen); - break; - case RTM_DELLINK: - wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx, - h, plen); - break; + status = le_to_host16(mgmt->u.assoc_resp.status_code); + if (status != WLAN_STATUS_SUCCESS) { + os_memset(&event, 0, sizeof(event)); + event.assoc_reject.bssid = mgmt->bssid; + 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; - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); + return; } - if (left > 0) { - wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " - "message", left); - } + drv->associated = 1; + os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); + os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN); - 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; + 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; -static int no_seq_check(struct nl_msg *msg, void *arg) -{ - return NL_OK; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } -static int process_event(struct nl_msg *msg, void *arg) +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) { - 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 event; - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); + 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; + } - if (tb[NL80211_ATTR_IFINDEX]) { - int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - if (ifindex != drv->ifindex) { - wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" - " for foreign interface (ifindex %d)", - gnlh->cmd, ifindex); - return NL_SKIP; + if (cmd == NL80211_CMD_CONNECT) + wpa_printf(MSG_DEBUG, "nl80211: Connect event"); + else if (cmd == NL80211_CMD_ROAM) + wpa_printf(MSG_DEBUG, "nl80211: Roam event"); + + os_memset(&event, 0, sizeof(event)); + if (cmd == NL80211_CMD_CONNECT && + nla_get_u16(status) != WLAN_STATUS_SUCCESS) { + if (addr) + event.assoc_reject.bssid = nla_data(addr); + 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; } - 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); - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); - 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); - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); - break; - default: - wpa_printf(MSG_DEBUG, "nl0211: Ignored unknown event (cmd=%d)", - gnlh->cmd); - break; + drv->associated = 1; + if (addr) { + os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); + os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); } - return NL_SKIP; + 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); + } + + event.assoc_info.freq = nl80211_get_assoc_freq(drv); + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } -static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, - void *sock_ctx) +static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, + struct nlattr *reason, struct nlattr *addr, + struct nlattr *by_ap) { - struct nl_cb *cb; - struct wpa_driver_nl80211_data *drv = eloop_ctx; - - wpa_printf(MSG_DEBUG, "nl80211: Event message available"); + union wpa_event_data data; + unsigned int locally_generated = by_ap == NULL; - cb = nl_cb_clone(drv->nl_cb); - if (!cb) + 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"); 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, cb); - nl_cb_put(cb); + } + + if (drv->ignore_next_local_disconnect) { + drv->ignore_next_local_disconnect = 0; + if (locally_generated) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " + "event triggered during reassociation"); + return; + } + wpa_printf(MSG_WARNING, "nl80211: Was expecting local " + "disconnect but got another disconnect " + "event first"); + } + + wpa_printf(MSG_DEBUG, "nl80211: Disconnect event"); + nl80211_mark_disconnected(drv); + os_memset(&data, 0, sizeof(data)); + if (reason) + data.deauth_info.reason_code = nla_get_u16(reason); + data.deauth_info.locally_generated = by_ap == NULL; + wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data); } -static int wpa_driver_nl80211_get_ifflags_ifname(struct wpa_driver_nl80211_data *drv, - const char *ifname, int *flags) +static int calculate_chan_offset(int width, int freq, int cf1, int cf2) { - struct ifreq ifr; + int freq1 = 0; - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { - perror("ioctl[SIOCGIFFLAGS]"); - return -1; + switch (convert2width(width)) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + return 0; + case CHAN_WIDTH_40: + freq1 = cf1 - 10; + break; + case CHAN_WIDTH_80: + freq1 = cf1 - 30; + break; + case CHAN_WIDTH_160: + freq1 = cf1 - 70; + break; + case CHAN_WIDTH_UNKNOWN: + case CHAN_WIDTH_80P80: + /* FIXME: implement this */ + return 0; } - *flags = ifr.ifr_flags & 0xffff; - return 0; + + return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1; } -/** - * wpa_driver_nl80211_get_ifflags - Get interface flags (SIOCGIFFLAGS) - * @drv: driver_nl80211 private data - * @flags: Pointer to returned flags value - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_get_ifflags(struct wpa_driver_nl80211_data *drv, - int *flags) +static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, + struct nlattr *ifindex, struct nlattr *freq, + struct nlattr *type, struct nlattr *bw, + struct nlattr *cf1, struct nlattr *cf2) { - return wpa_driver_nl80211_get_ifflags_ifname(drv, drv->ifname, flags); -} + struct i802_bss *bss; + union wpa_event_data data; + int ht_enabled = 1; + int chan_offset = 0; + int ifidx; + wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); -static int wpa_driver_nl80211_set_ifflags_ifname( - struct wpa_driver_nl80211_data *drv, - const char *ifname, int flags) -{ - struct ifreq ifr; + if (!freq) + return; - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_flags = flags & 0xffff; - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - perror("SIOCSIFFLAGS"); + ifidx = nla_get_u32(ifindex); + for (bss = drv->first_bss; bss; bss = bss->next) + if (bss->ifindex == ifidx) + break; + + if (bss == NULL) { + wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring", + ifidx); + return; + } + + if (type) { + switch (nla_get_u32(type)) { + case NL80211_CHAN_NO_HT: + ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + chan_offset = -1; + break; + } + } else if (bw && cf1) { + /* This can happen for example with VHT80 ch switch */ + chan_offset = calculate_chan_offset(nla_get_u32(bw), + nla_get_u32(freq), + nla_get_u32(cf1), + cf2 ? nla_get_u32(cf2) : 0); + } else { + wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail"); + } + + os_memset(&data, 0, sizeof(data)); + data.ch_switch.freq = nla_get_u32(freq); + data.ch_switch.ht_enabled = ht_enabled; + data.ch_switch.ch_offset = chan_offset; + if (bw) + data.ch_switch.ch_width = convert2width(nla_get_u32(bw)); + if (cf1) + data.ch_switch.cf1 = nla_get_u32(cf1); + if (cf2) + data.ch_switch.cf2 = nla_get_u32(cf2); + + bss->freq = data.ch_switch.freq; + + wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data); +} + + +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_mgmt(struct wpa_driver_nl80211_data *drv, + struct nlattr *freq, struct nlattr *sig, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 fc, stype; + int ssi_signal = 0; + int rx_freq = 0; + + wpa_printf(MSG_MSGDUMP, "nl80211: Frame event"); + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24) { + wpa_printf(MSG_DEBUG, "nl80211: Too short management frame"); + return; + } + + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + if (sig) + ssi_signal = (s32) nla_get_u32(sig); + + os_memset(&event, 0, sizeof(event)); + if (freq) { + event.rx_mgmt.freq = nla_get_u32(freq); + rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq; + } + wpa_printf(MSG_DEBUG, + "nl80211: RX frame freq=%d ssi_signal=%d stype=%u len=%u", + rx_freq, ssi_signal, stype, (unsigned int) len); + event.rx_mgmt.frame = frame; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); +} + + +static void mlme_event_mgmt_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; + + wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event"); + if (!is_ap_interface(drv->nlmode)) { + u64 cookie_val; + + if (!cookie) + return; + + cookie_val = nla_get_u64(cookie); + wpa_printf(MSG_DEBUG, "nl80211: Action TX status:" + " cookie=0%llx%s (ack=%d)", + (long long unsigned int) cookie_val, + cookie_val == drv->send_action_cookie ? + " (match)" : " (unknown)", ack != NULL); + 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; + + if (type == EVENT_DEAUTH) + wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event"); + else + wpa_printf(MSG_DEBUG, "nl80211: Disassociate event"); + + mgmt = (const struct ieee80211_mgmt *) frame; + if (len >= 24) { + bssid = mgmt->bssid; + + if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + !drv->associated && + os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) { + /* + * Avoid issues with some roaming cases where + * disconnection event for the old AP may show up after + * we have started connection with the new AP. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, + MAC2STR(bssid), + MAC2STR(drv->auth_attempt_bssid)); + return; + } + + 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; + } + } + + nl80211_mark_disconnected(drv); + 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.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); + event.disassoc_info.addr = bssid; + event.disassoc_info.reason_code = reason_code; + if (frame + len > mgmt->u.disassoc.variable) { + event.disassoc_info.ie = mgmt->u.disassoc.variable; + event.disassoc_info.ie_len = frame + len - + mgmt->u.disassoc.variable; + } + } else { + event.deauth_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); + event.deauth_info.addr = bssid; + event.deauth_info.reason_code = reason_code; + if (frame + len > mgmt->u.deauth.variable) { + event.deauth_info.ie = mgmt->u.deauth.variable; + event.deauth_info.ie_len = frame + len - + mgmt->u.deauth.variable; + } + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + +static void mlme_event_unprot_disconnect(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; + u16 reason_code = 0; + + if (type == EVENT_UNPROT_DEAUTH) + wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event"); + else + wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event"); + + if (len < 24) + return; + + mgmt = (const struct ieee80211_mgmt *) frame; + + 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_UNPROT_DISASSOC) { + event.unprot_disassoc.sa = mgmt->sa; + event.unprot_disassoc.da = mgmt->da; + event.unprot_disassoc.reason_code = reason_code; + } else { + event.unprot_deauth.sa = mgmt->sa; + event.unprot_deauth.da = mgmt->da; + event.unprot_deauth.reason_code = reason_code; + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + +static void mlme_event(struct i802_bss *bss, + enum nl80211_commands cmd, struct nlattr *frame, + struct nlattr *addr, struct nlattr *timed_out, + struct nlattr *freq, struct nlattr *ack, + struct nlattr *cookie, struct nlattr *sig) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + const u8 *data; + size_t len; + + if (timed_out && addr) { + mlme_timeout_event(drv, cmd, addr); + return; + } + + if (frame == NULL) { + wpa_printf(MSG_DEBUG, + "nl80211: MLME event %d (%s) without frame data", + cmd, nl80211_command_to_string(cmd)); + return; + } + + data = nla_data(frame); + len = nla_len(frame); + if (len < 4 + 2 * ETH_ALEN) { + wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" + MACSTR ") - too short", + cmd, nl80211_command_to_string(cmd), bss->ifname, + MAC2STR(bss->addr)); + return; + } + wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR + ") A1=" MACSTR " A2=" MACSTR, cmd, + nl80211_command_to_string(cmd), bss->ifname, + MAC2STR(bss->addr), MAC2STR(data + 4), + MAC2STR(data + 4 + ETH_ALEN)); + if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && + os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 && + os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) { + wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event " + "for foreign address", bss->ifname); + return; + } + 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_FRAME: + mlme_event_mgmt(drv, freq, sig, nla_data(frame), + nla_len(frame)); + break; + case NL80211_CMD_FRAME_TX_STATUS: + mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame), + nla_len(frame), ack); + break; + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_UNPROT_DISASSOCIATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC, + nla_data(frame), nla_len(frame)); + break; + default: + break; + } +} + + +static void mlme_event_michael_mic_failure(struct i802_bss *bss, + 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(bss->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 */ + + if (cancel_event) + drv->pending_remain_on_chan = 0; + + 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 mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + union wpa_event_data data; + + os_memset(&data, 0, sizeof(data)); + + if (tb[NL80211_ATTR_IE]) { + data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]); + data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]); + } + + if (tb[NL80211_ATTR_IE_RIC]) { + data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]); + data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]); + } + + if (tb[NL80211_ATTR_MAC]) + os_memcpy(data.ft_ies.target_ap, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR, + MAC2STR(data.ft_ies.target_ap)); + + wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &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; + + if (drv->scan_for_auth) { + drv->scan_for_auth = 0; + wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " + "cfg80211 BSS entry"); + wpa_driver_nl80211_authenticate_retry(drv); + return; + } + + 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); + wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'", + wpa_ssid_txt(s->ssid, s->ssid_len)); + info->num_ssids++; + if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) + break; + } + } + if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { + char msg[200], *pos, *end; + int res; + + pos = msg; + end = pos + sizeof(msg); + *pos = '\0'; + + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) + { + freqs[num_freqs] = nla_get_u32(nl); + res = os_snprintf(pos, end - pos, " %d", + freqs[num_freqs]); + if (res > 0 && end - pos > res) + pos += res; + num_freqs++; + if (num_freqs == MAX_REPORT_FREQS - 1) + break; + } + info->freqs = freqs; + info->num_freqs = num_freqs; + wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", + msg); + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); +} + + +static int get_link_signal(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1]; + static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = { + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 }, + }; + struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1]; + static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, + [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG }, + [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, + }; + struct wpa_signal_info *sig_change = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_STA_INFO] || + nla_parse_nested(sinfo, NL80211_STA_INFO_MAX, + tb[NL80211_ATTR_STA_INFO], policy)) + return NL_SKIP; + if (!sinfo[NL80211_STA_INFO_SIGNAL]) + return NL_SKIP; + + sig_change->current_signal = + (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]); + + if (sinfo[NL80211_STA_INFO_SIGNAL_AVG]) + sig_change->avg_signal = + (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]); + else + sig_change->avg_signal = 0; + + if (sinfo[NL80211_STA_INFO_TX_BITRATE]) { + if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX, + sinfo[NL80211_STA_INFO_TX_BITRATE], + rate_policy)) { + sig_change->current_txrate = 0; + } else { + if (rinfo[NL80211_RATE_INFO_BITRATE]) { + sig_change->current_txrate = + nla_get_u16(rinfo[ + NL80211_RATE_INFO_BITRATE]) * 100; + } + } + } + + return NL_SKIP; +} + + +static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig) +{ + struct nl_msg *msg; + + sig->current_signal = -9999; + sig->current_txrate = 0; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); + + return send_and_recv_msgs(drv, msg, get_link_signal, sig); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int get_link_noise(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_signal_info *sig_change = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: survey data missing!"); + return NL_SKIP; + } + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested " + "attributes!"); + return NL_SKIP; + } + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; + + if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + sig_change->frequency) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + sig_change->current_noise = + (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + + return NL_SKIP; +} + + +static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig_change) +{ + struct nl_msg *msg; + + sig_change->current_noise = 9999; + sig_change->frequency = drv->assoc_freq; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_link_noise, sig_change); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_scan_results *scan_results = arg; + struct wpa_scan_res *scan_res; + size_t i; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: Survey data missing"); + return NL_SKIP; + } + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested " + "attributes"); + return NL_SKIP; + } + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; + + for (i = 0; i < scan_results->num; ++i) { + scan_res = scan_results->res[i]; + if (!scan_res) + continue; + if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + scan_res->freq) + continue; + if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) + continue; + scan_res->noise = (s8) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; + } + + return NL_SKIP; +} + + +static int nl80211_get_noise_for_scan_results( + struct wpa_driver_nl80211_data *drv, + struct wpa_scan_results *scan_res) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, + scan_res); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +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 }, + [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, + }; + struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; + enum nl80211_cqm_rssi_threshold_event event; + union wpa_event_data ed; + struct wpa_signal_info sig; + int res; + + 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; + } + + os_memset(&ed, 0, sizeof(ed)); + + if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) { + if (!tb[NL80211_ATTR_MAC]) + return; + os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); + return; + } + + if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) + return; + event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); + + 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; + + res = nl80211_get_link_signal(drv, &sig); + if (res == 0) { + ed.signal_change.current_signal = sig.current_signal; + ed.signal_change.current_txrate = sig.current_txrate; + wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", + sig.current_signal, sig.current_txrate); + } + + res = nl80211_get_link_noise(drv, &sig); + if (res == 0) { + ed.signal_change.current_noise = sig.current_noise; + wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", + sig.current_noise); + } + + wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); +} + + +static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u8 *addr; + union wpa_event_data data; + + if (tb[NL80211_ATTR_MAC] == NULL) + return; + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); + + if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { + u8 *ies = NULL; + size_t ies_len = 0; + if (tb[NL80211_ATTR_IE]) { + ies = nla_data(tb[NL80211_ATTR_IE]); + ies_len = nla_len(tb[NL80211_ATTR_IE]); + } + wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len); + drv_event_assoc(drv->ctx, addr, ies, ies_len, 0); + return; + } + + if (drv->nlmode != NL80211_IFTYPE_ADHOC) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data); +} + + +static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u8 *addr; + union wpa_event_data data; + + if (tb[NL80211_ATTR_MAC] == NULL) + return; + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR, + MAC2STR(addr)); + + if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { + drv_event_disassoc(drv->ctx, addr); + return; + } + + if (drv->nlmode != NL80211_IFTYPE_ADHOC) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data); +} + + +static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA]; + static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = { + [NL80211_REKEY_DATA_KEK] = { + .minlen = NL80211_KEK_LEN, + .maxlen = NL80211_KEK_LEN, + }, + [NL80211_REKEY_DATA_KCK] = { + .minlen = NL80211_KCK_LEN, + .maxlen = NL80211_KCK_LEN, + }, + [NL80211_REKEY_DATA_REPLAY_CTR] = { + .minlen = NL80211_REPLAY_CTR_LEN, + .maxlen = NL80211_REPLAY_CTR_LEN, + }, + }; + union wpa_event_data data; + + if (!tb[NL80211_ATTR_MAC]) + return; + if (!tb[NL80211_ATTR_REKEY_DATA]) + return; + if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA, + tb[NL80211_ATTR_REKEY_DATA], rekey_policy)) + return; + if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]) + return; + + os_memset(&data, 0, sizeof(data)); + data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR, + MAC2STR(data.driver_gtk_rekey.bssid)); + data.driver_gtk_rekey.replay_ctr = + nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]); + wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter", + data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN); + wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data); +} + + +static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE]; + static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = { + [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 }, + [NL80211_PMKSA_CANDIDATE_BSSID] = { + .minlen = ETH_ALEN, + .maxlen = ETH_ALEN, + }, + [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG }, + }; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event"); + + if (!tb[NL80211_ATTR_PMKSA_CANDIDATE]) + return; + if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE, + tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy)) + return; + if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] || + !cand[NL80211_PMKSA_CANDIDATE_BSSID]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, + nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN); + data.pmkid_candidate.index = + nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]); + data.pmkid_candidate.preauth = + cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); +} + + +static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.client_poll.addr, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); +} + + +static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) { + case NL80211_TDLS_SETUP: + wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer " + MACSTR, MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_SETUP; + break; + case NL80211_TDLS_TEARDOWN: + wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer " + MACSTR, MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_TEARDOWN; + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione " + "event"); + return; + } + if (tb[NL80211_ATTR_REASON_CODE]) { + data.tdls.reason_code = + nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); + } + + wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data); +} + + +static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL); +} + + +static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + u32 reason; + + wpa_printf(MSG_DEBUG, "nl80211: Connect failed event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.connect_failed_reason.addr, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]); + switch (reason) { + case NL80211_CONN_FAIL_MAX_CLIENTS: + wpa_printf(MSG_DEBUG, "nl80211: Max client reached"); + data.connect_failed_reason.code = MAX_CLIENT_REACHED; + break; + case NL80211_CONN_FAIL_BLOCKED_CLIENT: + wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR + " tried to connect", + MAC2STR(data.connect_failed_reason.addr)); + data.connect_failed_reason.code = BLOCKED_CLIENT; + break; + default: + wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason " + "%u", reason); + return; + } + + wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data); +} + + +static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + enum nl80211_radar_event event_type; + + if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT]) + return; + + os_memset(&data, 0, sizeof(data)); + data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); + + /* Check HT params */ + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + data.dfs_event.ht_enabled = 1; + data.dfs_event.chan_offset = 0; + + switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { + case NL80211_CHAN_NO_HT: + data.dfs_event.ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + data.dfs_event.chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + data.dfs_event.chan_offset = -1; + break; + } + } + + /* Get VHT params */ + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) + data.dfs_event.chan_width = + convert2width(nla_get_u32( + tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_CENTER_FREQ1]) + data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + + wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz", + data.dfs_event.freq, data.dfs_event.ht_enabled, + data.dfs_event.chan_offset, data.dfs_event.chan_width, + data.dfs_event.cf1, data.dfs_event.cf2); + + switch (event_type) { + case NL80211_RADAR_DETECTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); + break; + case NL80211_RADAR_CAC_FINISHED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); + break; + case NL80211_RADAR_CAC_ABORTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); + break; + case NL80211_RADAR_NOP_FINISHED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d " + "received", event_type); + break; + } +} + + +static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, + int wds) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + union wpa_event_data event; + + if (!tb[NL80211_ATTR_MAC]) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.bssid = bss->addr; + event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]); + event.rx_from_unknown.wds = wds; + + wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); +} + + +static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, + const u8 *data, size_t len) +{ + u32 i, count; + union wpa_event_data event; + struct wpa_freq_range *range = NULL; + const struct qca_avoid_freq_list *freq_range; + + freq_range = (const struct qca_avoid_freq_list *) data; + if (len < sizeof(freq_range->count)) + return; + + count = freq_range->count; + if (len < sizeof(freq_range->count) + + count * sizeof(struct qca_avoid_freq_range)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)", + (unsigned int) len); + return; + } + + if (count > 0) { + range = os_calloc(count, sizeof(struct wpa_freq_range)); + if (range == NULL) + return; + } + + os_memset(&event, 0, sizeof(event)); + for (i = 0; i < count; i++) { + unsigned int idx = event.freq_range.num; + range[idx].min = freq_range->range[i].start_freq; + range[idx].max = freq_range->range[i].end_freq; + wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u", + range[idx].min, range[idx].max); + if (range[idx].min > range[idx].max) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range"); + continue; + } + event.freq_range.num++; + } + event.freq_range.range = range; + + wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event); + + os_free(range); +} + + +static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, + u32 subcmd, u8 *data, size_t len) +{ + switch (subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: + qca_nl80211_avoid_freq(drv, data, len); + break; + default: + wpa_printf(MSG_DEBUG, + "nl80211: Ignore unsupported QCA vendor event %u", + subcmd); + break; + } +} + + +static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u32 vendor_id, subcmd, wiphy = 0; + int wiphy_idx; + u8 *data = NULL; + size_t len = 0; + + if (!tb[NL80211_ATTR_VENDOR_ID] || + !tb[NL80211_ATTR_VENDOR_SUBCMD]) + return; + + vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]); + subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]); + + if (tb[NL80211_ATTR_WIPHY]) + wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + + wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u", + wiphy, vendor_id, subcmd); + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]); + len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]); + wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len); + } + + wiphy_idx = nl80211_get_wiphy_index(drv->first_bss); + if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)", + wiphy, wiphy_idx); + return; + } + + switch (vendor_id) { + case OUI_QCA: + nl80211_vendor_event_qca(drv, subcmd, data, len); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event"); + break; + } +} + + +static void do_process_drv_event(struct i802_bss *bss, int cmd, + struct nlattr **tb) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", + cmd, nl80211_command_to_string(cmd), bss->ifname); + + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && + (cmd == NL80211_CMD_NEW_SCAN_RESULTS || + cmd == NL80211_CMD_SCAN_ABORTED)) { + wpa_driver_nl80211_set_mode(drv->first_bss, + drv->ap_scan_as_station); + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; + } + + switch (cmd) { + case NL80211_CMD_TRIGGER_SCAN: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger"); + drv->scan_state = SCAN_STARTED; + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); + break; + case NL80211_CMD_START_SCHED_SCAN: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started"); + drv->scan_state = SCHED_SCAN_STARTED; + break; + case NL80211_CMD_SCHED_SCAN_STOPPED: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); + drv->scan_state = SCHED_SCAN_STOPPED; + wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); + break; + case NL80211_CMD_NEW_SCAN_RESULTS: + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: New scan results available"); + drv->scan_state = SCAN_COMPLETED; + 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_SCHED_SCAN_RESULTS: + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: New sched scan results available"); + drv->scan_state = SCHED_SCAN_RESULTS; + send_scan_event(drv, 0, tb); + break; + case NL80211_CMD_SCAN_ABORTED: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); + drv->scan_state = 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_FRAME_TX_STATUS: + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + case NL80211_CMD_UNPROT_DISASSOCIATE: + mlme_event(bss, 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], + tb[NL80211_ATTR_RX_SIGNAL_DBM]); + break; + case NL80211_CMD_CONNECT: + case NL80211_CMD_ROAM: + mlme_event_connect(drv, cmd, + tb[NL80211_ATTR_STATUS_CODE], + tb[NL80211_ATTR_MAC], + tb[NL80211_ATTR_REQ_IE], + tb[NL80211_ATTR_RESP_IE]); + break; + case NL80211_CMD_CH_SWITCH_NOTIFY: + mlme_event_ch_switch(drv, + tb[NL80211_ATTR_IFINDEX], + tb[NL80211_ATTR_WIPHY_FREQ], + tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], + tb[NL80211_ATTR_CHANNEL_WIDTH], + tb[NL80211_ATTR_CENTER_FREQ1], + tb[NL80211_ATTR_CENTER_FREQ2]); + break; + case NL80211_CMD_DISCONNECT: + mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], + tb[NL80211_ATTR_MAC], + tb[NL80211_ATTR_DISCONNECTED_BY_AP]); + break; + case NL80211_CMD_MICHAEL_MIC_FAILURE: + mlme_event_michael_mic_failure(bss, 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; + case NL80211_CMD_REG_CHANGE: + wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); + if (tb[NL80211_ATTR_REG_INITIATOR] == NULL) + break; + os_memset(&data, 0, sizeof(data)); + switch (nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])) { + case NL80211_REGDOM_SET_BY_CORE: + data.channel_list_changed.initiator = + REGDOM_SET_BY_CORE; + break; + case NL80211_REGDOM_SET_BY_USER: + data.channel_list_changed.initiator = + REGDOM_SET_BY_USER; + break; + case NL80211_REGDOM_SET_BY_DRIVER: + data.channel_list_changed.initiator = + REGDOM_SET_BY_DRIVER; + break; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + data.channel_list_changed.initiator = + REGDOM_SET_BY_COUNTRY_IE; + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unknown reg change initiator %d received", + nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR])); + break; + } + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + &data); + break; + case NL80211_CMD_REG_BEACON_HINT: + wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); + os_memset(&data, 0, sizeof(data)); + data.channel_list_changed.initiator = REGDOM_BEACON_HINT; + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + &data); + break; + case NL80211_CMD_NEW_STATION: + nl80211_new_station_event(drv, tb); + break; + case NL80211_CMD_DEL_STATION: + nl80211_del_station_event(drv, tb); + break; + case NL80211_CMD_SET_REKEY_OFFLOAD: + nl80211_rekey_offload_event(drv, tb); + break; + case NL80211_CMD_PMKSA_CANDIDATE: + nl80211_pmksa_candidate_event(drv, tb); + break; + case NL80211_CMD_PROBE_CLIENT: + nl80211_client_probe_event(drv, tb); + break; + case NL80211_CMD_TDLS_OPER: + nl80211_tdls_oper_event(drv, tb); + break; + case NL80211_CMD_CONN_FAILED: + nl80211_connect_failed_event(drv, tb); + break; + case NL80211_CMD_FT_EVENT: + mlme_event_ft_event(drv, tb); + break; + case NL80211_CMD_RADAR_DETECT: + nl80211_radar_event(drv, tb); + break; + case NL80211_CMD_STOP_AP: + nl80211_stop_ap(drv, tb); + break; + case NL80211_CMD_VENDOR: + nl80211_vendor_event(drv, tb); + break; + default: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " + "(cmd=%d)", cmd); + break; + } +} + + +static int process_drv_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]; + struct i802_bss *bss; + int ifidx = -1; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX]) { + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + + for (bss = drv->first_bss; bss; bss = bss->next) + if (ifidx == -1 || ifidx == bss->ifindex) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + wpa_printf(MSG_DEBUG, + "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d)", + gnlh->cmd, ifidx); + } else if (tb[NL80211_ATTR_WDEV]) { + u64 wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wpa_printf(MSG_DEBUG, "nl80211: Process event on P2P device"); + for (bss = drv->first_bss; bss; bss = bss->next) { + if (bss->wdev_id_set && wdev_id == bss->wdev_id) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + } + wpa_printf(MSG_DEBUG, + "nl80211: Ignored event (cmd=%d) for foreign interface (wdev 0x%llx)", + gnlh->cmd, (long long unsigned int) wdev_id); + } + + return NL_SKIP; +} + + +static int process_global_event(struct nl_msg *msg, void *arg) +{ + struct nl80211_global *global = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct wpa_driver_nl80211_data *drv, *tmp; + int ifidx = -1; + struct i802_bss *bss; + u64 wdev_id = 0; + int wdev_id_set = 0; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX]) + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + else if (tb[NL80211_ATTR_WDEV]) { + wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wdev_id_set = 1; + } + + dl_list_for_each_safe(drv, tmp, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + for (bss = drv->first_bss; bss; bss = bss->next) { + if ((ifidx == -1 && !wdev_id_set) || + ifidx == bss->ifindex || + (wdev_id_set && bss->wdev_id_set && + wdev_id == bss->wdev_id)) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + } + } + + return NL_SKIP; +} + + +static int process_bss_event(struct nl_msg *msg, void *arg) +{ + struct i802_bss *bss = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s", + gnlh->cmd, nl80211_command_to_string(gnlh->cmd), + bss->ifname); + + switch (gnlh->cmd) { + case NL80211_CMD_FRAME: + case NL80211_CMD_FRAME_TX_STATUS: + mlme_event(bss, 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], + tb[NL80211_ATTR_RX_SIGNAL_DBM]); + break; + case NL80211_CMD_UNEXPECTED_FRAME: + nl80211_spurious_frame(bss, tb, 0); + break; + case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: + nl80211_spurious_frame(bss, tb, 1); + 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 *handle) +{ + struct nl_cb *cb = eloop_ctx; + int res; + + wpa_printf(MSG_MSGDUMP, "nl80211: Event message available"); + + res = nl_recvmsgs(handle, cb); + if (res) { + wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", + __func__, res); + } +} + + +/** + * 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'; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG); + + 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: + nlmsg_free(msg); + return -EINVAL; +} + + +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]) { + wpa_printf(MSG_DEBUG, "nl80211: No country information available"); + return NL_SKIP; + } + os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3); + return NL_SKIP; +} + + +static int wpa_driver_nl80211_get_country(void *priv, char *alpha2) +{ + 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; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + alpha2[0] = '\0'; + ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2); + if (!alpha2[0]) + ret = -1; + + return ret; +} + + +static int protocol_feature_handler(struct nl_msg *msg, void *arg) +{ + u32 *feat = 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_PROTOCOL_FEATURES]) + *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]); + + return NL_SKIP; +} + + +static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv) +{ + u32 feat = 0; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES); + if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0) + return feat; + + msg = NULL; +nla_put_failure: + nlmsg_free(msg); + return 0; +} + + +struct wiphy_info_data { + struct wpa_driver_nl80211_data *drv; + struct wpa_driver_capa *capa; + + unsigned int num_multichan_concurrent; + + unsigned int error:1; + unsigned int device_ap_sme:1; + unsigned int poll_command_supported:1; + unsigned int data_tx_status:1; + unsigned int monitor_supported:1; + unsigned int auth_supported:1; + unsigned int connect_supported:1; + unsigned int p2p_go_supported:1; + unsigned int p2p_client_supported:1; + unsigned int p2p_concurrent:1; + unsigned int channel_switch_supported:1; + unsigned int set_qos_map_supported:1; +}; + + +static unsigned int probe_resp_offload_support(int supp_protocols) +{ + unsigned int prot = 0; + + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING; + + return prot; +} + + +static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_mode; + int i; + + if (tb == NULL) + return; + + nla_for_each_nested(nl_mode, tb, i) { + switch (nla_type(nl_mode)) { + case NL80211_IFTYPE_AP: + info->capa->flags |= WPA_DRIVER_FLAGS_AP; + break; + case NL80211_IFTYPE_ADHOC: + info->capa->flags |= WPA_DRIVER_FLAGS_IBSS; + break; + case NL80211_IFTYPE_P2P_DEVICE: + info->capa->flags |= + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; + break; + case NL80211_IFTYPE_P2P_GO: + info->p2p_go_supported = 1; + break; + case NL80211_IFTYPE_P2P_CLIENT: + info->p2p_client_supported = 1; + break; + case NL80211_IFTYPE_MONITOR: + info->monitor_supported = 1; + break; + } + } +} + + +static int wiphy_info_iface_comb_process(struct wiphy_info_data *info, + struct nlattr *nl_combi) +{ + struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; + struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; + struct nlattr *nl_limit, *nl_mode; + int err, rem_limit, rem_mode; + int combination_has_p2p = 0, combination_has_mgd = 0; + static struct nla_policy + iface_combination_policy[NUM_NL80211_IFACE_COMB] = { + [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, + [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, + [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 }, + }, + iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { + [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, + [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, + }; + + err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, + nl_combi, iface_combination_policy); + if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || + !tb_comb[NL80211_IFACE_COMB_MAXNUM] || + !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) + return 0; /* broken combination */ + + if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) + info->capa->flags |= WPA_DRIVER_FLAGS_RADAR; + + nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS], + rem_limit) { + err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT, + nl_limit, iface_limit_policy); + if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES]) + return 0; /* broken combination */ + + nla_for_each_nested(nl_mode, + tb_limit[NL80211_IFACE_LIMIT_TYPES], + rem_mode) { + int ift = nla_type(nl_mode); + if (ift == NL80211_IFTYPE_P2P_GO || + ift == NL80211_IFTYPE_P2P_CLIENT) + combination_has_p2p = 1; + if (ift == NL80211_IFTYPE_STATION) + combination_has_mgd = 1; + } + if (combination_has_p2p && combination_has_mgd) + break; + } + + if (combination_has_p2p && combination_has_mgd) { + info->p2p_concurrent = 1; + info->num_multichan_concurrent = + nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]); + return 1; + } + + return 0; +} + + +static void wiphy_info_iface_comb(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_combi; + int rem_combi; + + if (tb == NULL) + return; + + nla_for_each_nested(nl_combi, tb, rem_combi) { + if (wiphy_info_iface_comb_process(info, nl_combi) > 0) + break; + } +} + + +static void wiphy_info_supp_cmds(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_cmd; + int i; + + if (tb == NULL) + return; + + nla_for_each_nested(nl_cmd, tb, i) { + switch (nla_get_u32(nl_cmd)) { + case NL80211_CMD_AUTHENTICATE: + info->auth_supported = 1; + break; + case NL80211_CMD_CONNECT: + info->connect_supported = 1; + break; + case NL80211_CMD_START_SCHED_SCAN: + info->capa->sched_scan_supported = 1; + break; + case NL80211_CMD_PROBE_CLIENT: + info->poll_command_supported = 1; + break; + case NL80211_CMD_CHANNEL_SWITCH: + info->channel_switch_supported = 1; + break; + case NL80211_CMD_SET_QOS_MAP: + info->set_qos_map_supported = 1; + break; + } + } +} + + +static void wiphy_info_cipher_suites(struct wiphy_info_data *info, + struct nlattr *tb) +{ + int i, num; + u32 *ciphers; + + if (tb == NULL) + return; + + num = nla_len(tb) / sizeof(u32); + ciphers = nla_data(tb); + for (i = 0; i < num; i++) { + u32 c = ciphers[i]; + + wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d", + c >> 24, (c >> 16) & 0xff, + (c >> 8) & 0xff, c & 0xff); + switch (c) { + case WLAN_CIPHER_SUITE_CCMP_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256; + break; + case WLAN_CIPHER_SUITE_GCMP_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256; + break; + case WLAN_CIPHER_SUITE_CCMP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP; + break; + case WLAN_CIPHER_SUITE_GCMP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP; + break; + case WLAN_CIPHER_SUITE_TKIP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP; + break; + case WLAN_CIPHER_SUITE_WEP104: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104; + break; + case WLAN_CIPHER_SUITE_WEP40: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256; + break; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256; + break; + } + } +} + + +static void wiphy_info_max_roc(struct wpa_driver_capa *capa, + struct nlattr *tb) +{ + if (tb) + capa->max_remain_on_chan = nla_get_u32(tb); +} + + +static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls, + struct nlattr *ext_setup) +{ + if (tdls == NULL) + return; + + wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; + + if (ext_setup) { + wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; + } +} + + +static void wiphy_info_feature_flags(struct wiphy_info_data *info, + struct nlattr *tb) +{ + u32 flags; + struct wpa_driver_capa *capa = info->capa; + + if (tb == NULL) + return; + + flags = nla_get_u32(tb); + + if (flags & NL80211_FEATURE_SK_TX_STATUS) + info->data_tx_status = 1; + + if (flags & NL80211_FEATURE_INACTIVITY_TIMER) + capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER; + + if (flags & NL80211_FEATURE_SAE) + capa->flags |= WPA_DRIVER_FLAGS_SAE; + + if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) + capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; +} + + +static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa, + struct nlattr *tb) +{ + u32 protocols; + + if (tb == NULL) + return; + + protocols = nla_get_u32(tb); + wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP " + "mode"); + capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD; + capa->probe_resp_offloads = probe_resp_offload_support(protocols); +} + + +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; + struct wpa_driver_capa *capa = info->capa; + struct wpa_driver_nl80211_data *drv = info->drv; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_WIPHY_NAME]) + os_strlcpy(drv->phyname, + nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), + sizeof(drv->phyname)); + if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) + capa->max_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); + + if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) + capa->max_sched_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); + + if (tb[NL80211_ATTR_MAX_MATCH_SETS]) + capa->max_match_sets = + nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); + + if (tb[NL80211_ATTR_MAC_ACL_MAX]) + capa->max_acl_mac_addrs = + nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]); + + wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]); + wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]); + wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]); + wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]); + + if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " + "off-channel TX"); + capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; + } + + if (tb[NL80211_ATTR_ROAM_SUPPORT]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); + capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; + } + + wiphy_info_max_roc(capa, + tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); + + if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD]) + capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD; + + wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT], + tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]); + + if (tb[NL80211_ATTR_DEVICE_AP_SME]) + info->device_ap_sme = 1; + + wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]); + wiphy_info_probe_resp_offload(capa, + tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); + + if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] && + drv->extended_capa == NULL) { + drv->extended_capa = + os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); + if (drv->extended_capa) { + os_memcpy(drv->extended_capa, + nla_data(tb[NL80211_ATTR_EXT_CAPA]), + nla_len(tb[NL80211_ATTR_EXT_CAPA])); + drv->extended_capa_len = + nla_len(tb[NL80211_ATTR_EXT_CAPA]); + } + drv->extended_capa_mask = + os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); + if (drv->extended_capa_mask) { + os_memcpy(drv->extended_capa_mask, + nla_data(tb[NL80211_ATTR_EXT_CAPA]), + nla_len(tb[NL80211_ATTR_EXT_CAPA])); + } else { + os_free(drv->extended_capa); + drv->extended_capa = NULL; + drv->extended_capa_len = 0; + } + } + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + struct nlattr *nl; + int rem; + + nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) { + struct nl80211_vendor_cmd_info *vinfo; + if (nla_len(nl) != sizeof(*vinfo)) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); + continue; + } + vinfo = nla_data(nl); + wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", + vinfo->vendor_id, vinfo->subcmd); + } + } + + if (tb[NL80211_ATTR_VENDOR_EVENTS]) { + struct nlattr *nl; + int rem; + + nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) { + struct nl80211_vendor_cmd_info *vinfo; + if (nla_len(nl) != sizeof(*vinfo)) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); + continue; + } + vinfo = nla_data(nl); + wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u", + vinfo->vendor_id, vinfo->subcmd); + } + } + + return NL_SKIP; +} + + +static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, + struct wiphy_info_data *info) +{ + u32 feat; + struct nl_msg *msg; + + os_memset(info, 0, sizeof(*info)); + info->capa = &drv->capa; + info->drv = drv; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY); + else + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); + + NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); + if (nl80211_set_iface_id(msg, drv->first_bss) < 0) + goto nla_put_failure; + + if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info)) + return -1; + + 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"); + info->error = 1; + } + + if (info->p2p_go_supported && info->p2p_client_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; + if (info->p2p_concurrent) { + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface (driver advertised support)"); + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + } + if (info->num_multichan_concurrent > 1) { + wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel " + "concurrent (driver advertised support)"); + drv->capa.num_multichan_concurrent = + info->num_multichan_concurrent; + } + + /* default to 5000 since early versions of mac80211 don't set it */ + if (!drv->capa.max_remain_on_chan) + drv->capa.max_remain_on_chan = 5000; + + if (info->channel_switch_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; + + return 0; +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; + + if (info.error) + return -1; + + drv->has_capability = 1; + 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.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + + drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES; + drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; + drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + + if (!info.device_ap_sme) { + drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; + + /* + * No AP SME is currently assumed to also indicate no AP MLME + * in the driver/firmware. + */ + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME; + } + + drv->device_ap_sme = info.device_ap_sme; + drv->poll_command_supported = info.poll_command_supported; + drv->data_tx_status = info.data_tx_status; + if (info.set_qos_map_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING; + + /* + * If poll command and tx status are supported, mac80211 is new enough + * to have everything we need to not need monitor interfaces. + */ + drv->use_monitor = !info.poll_command_supported || !info.data_tx_status; + + if (drv->device_ap_sme && drv->use_monitor) { + /* + * Non-mac80211 drivers may not support monitor interface. + * Make sure we do not get stuck with incorrect capability here + * by explicitly testing this. + */ + if (!info.monitor_supported) { + wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor " + "with device_ap_sme since no monitor mode " + "support detected"); + drv->use_monitor = 0; + } + } + + /* + * If we aren't going to use monitor interfaces, but the + * driver doesn't support data TX status, we won't get TX + * status for EAPOL frames. + */ + if (!drv->use_monitor && !info.data_tx_status) + drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + + return 0; +} + + +#ifdef ANDROID +static int android_genl_ctrl_resolve(struct nl_handle *handle, + const char *name) +{ + /* + * Android ICS has very minimal genl_ctrl_resolve() implementation, so + * need to work around that. + */ + struct nl_cache *cache = NULL; + struct genl_family *nl80211 = NULL; + int id = -1; + + if (genl_ctrl_alloc_cache(handle, &cache) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache"); + goto fail; + } + + nl80211 = genl_ctrl_search_by_name(cache, name); + if (nl80211 == NULL) + goto fail; + + id = genl_family_get_id(nl80211); + +fail: + if (nl80211) + genl_family_put(nl80211); + if (cache) + nl_cache_free(cache); + + return id; +} +#define genl_ctrl_resolve android_genl_ctrl_resolve +#endif /* ANDROID */ + + +static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) +{ + int ret; + + global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (global->nl_cb == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks"); + return -1; + } + + global->nl = nl_create_handle(global->nl_cb, "nl"); + if (global->nl == NULL) + goto err; + + global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211"); + if (global->nl80211_id < 0) { + wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " + "found"); + goto err; + } + + global->nl_event = nl_create_handle(global->nl_cb, "event"); + if (global->nl_event == NULL) + goto err; + + ret = nl_get_multicast_id(global, "nl80211", "scan"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " + "membership for scan events: %d (%s)", + ret, strerror(-ret)); + goto err; + } + + ret = nl_get_multicast_id(global, "nl80211", "mlme"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " + "membership for mlme events: %d (%s)", + ret, strerror(-ret)); + goto err; + } + + ret = nl_get_multicast_id(global, "nl80211", "regulatory"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " + "membership for regulatory events: %d (%s)", + ret, strerror(-ret)); + /* Continue without regulatory events */ + } + + ret = nl_get_multicast_id(global, "nl80211", "vendor"); + if (ret >= 0) + ret = nl_socket_add_membership(global->nl_event, ret); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast " + "membership for vendor events: %d (%s)", + ret, strerror(-ret)); + /* Continue without vendor events */ + } + + nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_global_event, global); + + nl80211_register_eloop_read(&global->nl_event, + wpa_driver_nl80211_event_receive, + global->nl_cb); + + return 0; + +err: + nl_destroy_handles(&global->nl_event); + nl_destroy_handles(&global->nl); + nl_cb_put(global->nl_cb); + global->nl_cb = NULL; + return -1; +} + + +static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) +{ + drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!drv->nl_cb) { + wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct"); + return -1; + } + + nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_drv_event, drv); + + return 0; +} + + +static void wpa_driver_nl80211_rfkill_blocked(void *ctx) +{ + wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ +} + + +static void wpa_driver_nl80211_rfkill_unblocked(void *ctx) +{ + struct wpa_driver_nl80211_data *drv = ctx; + wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked"); + if (i802_set_iface_flags(drv->first_bss, 1)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP " + "after rfkill unblock"); + return; + } + /* rtnetlink ifup handler will report interface as enabled */ +} + + +static void wpa_driver_nl80211_handle_eapol_tx_status(int sock, + void *eloop_ctx, + void *handle) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + u8 data[2048]; + struct msghdr msg; + struct iovec entry; + u8 control[512]; + struct cmsghdr *cmsg; + int res, found_ee = 0, found_wifi = 0, acked = 0; + union wpa_event_data event; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &entry; + msg.msg_iovlen = 1; + entry.iov_base = data; + entry.iov_len = sizeof(data); + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + res = recvmsg(sock, &msg, MSG_ERRQUEUE); + /* if error or not fitting 802.3 header, return */ + if (res < 14) + return; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_WIFI_STATUS) { + int *ack; + + found_wifi = 1; + ack = (void *)CMSG_DATA(cmsg); + acked = *ack; + } + + if (cmsg->cmsg_level == SOL_PACKET && + cmsg->cmsg_type == PACKET_TX_TIMESTAMP) { + struct sock_extended_err *err = + (struct sock_extended_err *)CMSG_DATA(cmsg); + + if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS) + found_ee = 1; + } + } + + if (!found_ee || !found_wifi) + return; + + memset(&event, 0, sizeof(event)); + event.eapol_tx_status.dst = data; + event.eapol_tx_status.data = data + 14; + event.eapol_tx_status.data_len = res - 14; + event.eapol_tx_status.ack = acked; + wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event); +} + + +static int nl80211_init_bss(struct i802_bss *bss) +{ + bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!bss->nl_cb) + return -1; + + nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_bss_event, bss); + + return 0; +} + + +static void nl80211_destroy_bss(struct i802_bss *bss) +{ + nl_cb_put(bss->nl_cb); + bss->nl_cb = NULL; +} + + +static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, + void *global_priv, int hostapd, + const u8 *set_addr) +{ + struct wpa_driver_nl80211_data *drv; + struct rfkill_config *rcfg; + struct i802_bss *bss; + + if (global_priv == NULL) + return NULL; + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->global = global_priv; + drv->ctx = ctx; + drv->hostapd = !!hostapd; + drv->eapol_sock = -1; + drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); + drv->if_indices = drv->default_if_indices; + + drv->first_bss = os_zalloc(sizeof(*drv->first_bss)); + if (!drv->first_bss) { + os_free(drv); + return NULL; + } + bss = drv->first_bss; + bss->drv = drv; + bss->ctx = ctx; + + os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname)); + drv->monitor_ifidx = -1; + drv->monitor_sock = -1; + drv->eapol_tx_sock = -1; + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; + + if (wpa_driver_nl80211_init_nl(drv)) { + os_free(drv); + return NULL; + } + + if (nl80211_init_bss(bss)) + goto failed; + + rcfg = os_zalloc(sizeof(*rcfg)); + if (rcfg == NULL) + goto failed; + rcfg->ctx = drv; + os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); + rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (drv->rfkill == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available"); + os_free(rcfg); + } + + if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0) + drv->start_iface_up = 1; + + if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1)) + goto failed; + + drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (drv->eapol_tx_sock < 0) + goto failed; + + if (drv->data_tx_status) { + int enabled = 1; + + if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS, + &enabled, sizeof(enabled)) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: wifi status sockopt failed\n"); + drv->data_tx_status = 0; + if (!drv->use_monitor) + drv->capa.flags &= + ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + } else { + eloop_register_read_sock(drv->eapol_tx_sock, + wpa_driver_nl80211_handle_eapol_tx_status, + drv, NULL); + } + } + + if (drv->global) { + dl_list_add(&drv->global->interfaces, &drv->list); + drv->in_interface_list = 1; + } + + return bss; + +failed: + wpa_driver_nl80211_deinit(bss); + return NULL; +} + + +/** + * 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 + * @global_priv: private driver global data from global_init() + * Returns: Pointer to private data, %NULL on failure + */ +static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, + void *global_priv) +{ + return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL); +} + + +static int nl80211_register_frame(struct i802_bss *bss, + struct nl_handle *nl_handle, + u16 type, const u8 *match, size_t match_len) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -1; + char buf[30]; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + buf[0] = '\0'; + wpa_snprintf_hex(buf, sizeof(buf), match, match_len); + wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p match=%s", + type, nl_handle, buf); + + nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); + NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); + + ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register frame command " + "failed (type=%u): ret=%d (%s)", + type, ret, strerror(-ret)); + wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", + match, match_len); + goto nla_put_failure; + } + ret = 0; +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int nl80211_alloc_mgmt_handle(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (bss->nl_mgmt) { + wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting " + "already on! (nl_mgmt=%p)", bss->nl_mgmt); + return -1; + } + + bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt"); + if (bss->nl_mgmt == NULL) + return -1; + + return 0; +} + + +static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss) +{ + nl80211_register_eloop_read(&bss->nl_mgmt, + wpa_driver_nl80211_event_receive, + bss->nl_cb); +} + + +static int nl80211_register_action_frame(struct i802_bss *bss, + const u8 *match, size_t match_len) +{ + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4); + return nl80211_register_frame(bss, bss->nl_mgmt, + type, match, match_len); +} + + +static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = 0; + + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP " + "handle %p", bss->nl_mgmt); + + if (drv->nlmode == NL80211_IFTYPE_ADHOC) { + u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4); + + /* register for any AUTH message */ + nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0); + } + +#ifdef CONFIG_INTERWORKING + /* QoS Map Configure */ + if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0) + ret = -1; +#endif /* CONFIG_INTERWORKING */ +#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) + /* GAS Initial Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0) + ret = -1; + /* GAS Initial Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0) + ret = -1; + /* GAS Comeback Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0) + ret = -1; + /* GAS Comeback Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0) + ret = -1; + /* Protected GAS Initial Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0a", 2) < 0) + ret = -1; + /* Protected GAS Initial Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0b", 2) < 0) + ret = -1; + /* Protected GAS Comeback Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0c", 2) < 0) + ret = -1; + /* Protected GAS Comeback Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0) + ret = -1; +#endif /* CONFIG_P2P || CONFIG_INTERWORKING */ +#ifdef CONFIG_P2P + /* P2P Public Action */ + if (nl80211_register_action_frame(bss, + (u8 *) "\x04\x09\x50\x6f\x9a\x09", + 6) < 0) + ret = -1; + /* P2P Action */ + if (nl80211_register_action_frame(bss, + (u8 *) "\x7f\x50\x6f\x9a\x09", + 5) < 0) + ret = -1; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_IEEE80211W + /* SA Query Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_TDLS + if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) { + /* TDLS Discovery Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) < + 0) + ret = -1; + } +#endif /* CONFIG_TDLS */ + + /* FT Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) + ret = -1; + else + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT | + WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + + /* WNM - BSS Transition Management Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0) + ret = -1; + /* WNM-Sleep Mode Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) + ret = -1; + + nl80211_mgmt_handle_register_eloop(bss); + + return ret; +} + + +static int nl80211_register_spurious_class3(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_UNEXPECTED_FRAME); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 " + "failed: ret=%d (%s)", + ret, strerror(-ret)); + goto nla_put_failure; + } + ret = 0; +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) +{ + static const int stypes[] = { + WLAN_FC_STYPE_AUTH, + WLAN_FC_STYPE_ASSOC_REQ, + WLAN_FC_STYPE_REASSOC_REQ, + WLAN_FC_STYPE_DISASSOC, + WLAN_FC_STYPE_DEAUTH, + WLAN_FC_STYPE_ACTION, + WLAN_FC_STYPE_PROBE_REQ, +/* Beacon doesn't work as mac80211 doesn't currently allow + * it, but it wouldn't really be the right thing anyway as + * it isn't per interface ... maybe just dump the scan + * results periodically for OLBC? + */ +// WLAN_FC_STYPE_BEACON, + }; + unsigned int i; + + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " + "handle %p", bss->nl_mgmt); + + for (i = 0; i < ARRAY_SIZE(stypes); i++) { + if (nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (stypes[i] << 4), + NULL, 0) < 0) { + goto out_err; + } + } + + if (nl80211_register_spurious_class3(bss)) + goto out_err; + + if (nl80211_get_wiphy_data_ap(bss) == NULL) + goto out_err; + + nl80211_mgmt_handle_register_eloop(bss); + return 0; + +out_err: + nl_destroy_handles(&bss->nl_mgmt); + return -1; +} + + +static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss) +{ + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP " + "handle %p (device SME)", bss->nl_mgmt); + + if (nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_ACTION << 4), + NULL, 0) < 0) + goto out_err; + + nl80211_mgmt_handle_register_eloop(bss); + return 0; + +out_err: + nl_destroy_handles(&bss->nl_mgmt); + return -1; +} + + +static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason) +{ + if (bss->nl_mgmt == NULL) + return; + wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p " + "(%s)", bss->nl_mgmt, reason); + nl80211_destroy_eloop_handle(&bss->nl_mgmt); + + nl80211_put_wiphy_data_ap(bss); +} + + +static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); +} + + +static void nl80211_del_p2pdev(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + + wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s", + bss->ifname, (long long unsigned int) bss->wdev_id, + strerror(-ret)); + +nla_put_failure: + nlmsg_free(msg); +} + + +static int nl80211_set_p2pdev(struct i802_bss *bss, int start) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + if (start) + nl80211_cmd(drv, msg, 0, NL80211_CMD_START_P2P_DEVICE); + else + nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_P2P_DEVICE); + + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + + wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s", + start ? "Start" : "Stop", + bss->ifname, (long long unsigned int) bss->wdev_id, + strerror(-ret)); + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int i802_set_iface_flags(struct i802_bss *bss, int up) +{ + enum nl80211_iftype nlmode; + + nlmode = nl80211_get_ifmode(bss); + if (nlmode != NL80211_IFTYPE_P2P_DEVICE) { + return linux_set_iface_flags(bss->drv->global->ioctl_sock, + bss->ifname, up); + } + + /* P2P Device has start/stop which is equivalent */ + return nl80211_set_p2pdev(bss, up); +} + + +static int +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, + const u8 *set_addr, int first) +{ + struct i802_bss *bss = drv->first_bss; + int send_rfkill_event = 0; + enum nl80211_iftype nlmode; + + drv->ifindex = if_nametoindex(bss->ifname); + bss->ifindex = drv->ifindex; + bss->wdev_id = drv->global->if_add_wdevid; + bss->wdev_id_set = drv->global->if_add_wdevid_set; + + bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex; + bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set; + drv->global->if_add_wdevid_set = 0; + + if (wpa_driver_nl80211_capa(drv)) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", + bss->ifname, drv->phyname); + + if (set_addr && + (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) || + linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + set_addr))) + return -1; + + if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP) + drv->start_mode_ap = 1; + + if (drv->hostapd) + nlmode = NL80211_IFTYPE_AP; + else if (bss->if_dynamic) + nlmode = nl80211_get_ifmode(bss); + else + nlmode = NL80211_IFTYPE_STATION; + + if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode"); + return -1; + } + + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { + int ret = nl80211_set_p2pdev(bss, 1); + if (ret < 0) + wpa_printf(MSG_ERROR, "nl80211: Could not start P2P device"); + nl80211_get_macaddr(bss); + return ret; + } + + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) { + if (rfkill_is_blocked(drv->rfkill)) { + wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable " + "interface '%s' due to rfkill", + bss->ifname); + drv->if_disabled = 1; + send_rfkill_event = 1; + } else { + wpa_printf(MSG_ERROR, "nl80211: Could not set " + "interface '%s' UP", bss->ifname); + return -1; + } + } + + if (!drv->hostapd) + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, + 1, IF_OPER_DORMANT); + + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + bss->addr)) + return -1; + + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill, + drv, drv->ctx); + } + + 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; + + wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)", + drv->ifindex); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +/** + * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface + * @bss: 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(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + bss->in_deinit = 1; + if (drv->data_tx_status) + eloop_unregister_read_sock(drv->eapol_tx_sock); + if (drv->eapol_tx_sock >= 0) + close(drv->eapol_tx_sock); + + if (bss->nl_preq) + wpa_driver_nl80211_probe_req_report(bss, 0); + if (bss->added_if_into_bridge) { + if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, + bss->ifname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "interface %s from bridge %s: %s", + bss->ifname, bss->brname, strerror(errno)); + } + if (bss->added_bridge) { + if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "bridge %s: %s", + bss->brname, strerror(errno)); + } + + nl80211_remove_monitor_interface(drv); + + if (is_ap_interface(drv->nlmode)) + wpa_driver_nl80211_del_beacon(drv); + + 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); + + if (drv->disabled_11b_rates) + nl80211_disable_11b_rates(drv, drv->ifindex, 0); + + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0, + IF_OPER_UP); + rfkill_deinit(drv->rfkill); + + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + + if (!drv->start_iface_up) + (void) i802_set_iface_flags(bss, 0); + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) { + if (!drv->hostapd || !drv->start_mode_ap) + wpa_driver_nl80211_set_mode(bss, + NL80211_IFTYPE_STATION); + nl80211_mgmt_unsubscribe(bss, "deinit"); + } else { + nl80211_mgmt_unsubscribe(bss, "deinit"); + nl80211_del_p2pdev(bss); + } + nl_cb_put(drv->nl_cb); + + nl80211_destroy_bss(drv->first_bss); + + os_free(drv->filter_ssids); + + os_free(drv->auth_ie); + + if (drv->in_interface_list) + dl_list_del(&drv->list); + + os_free(drv->extended_capa); + os_free(drv->extended_capa_mask); + os_free(drv->first_bss); + 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 != NL80211_IFTYPE_UNSPECIFIED) { + wpa_driver_nl80211_set_mode(drv->first_bss, + drv->ap_scan_as_station); + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; + } + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static struct nl_msg * +nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd, + struct wpa_driver_scan_params *params, u64 *wdev_id) +{ + struct nl_msg *msg; + size_t i; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + nl80211_cmd(drv, msg, 0, cmd); + + if (!wdev_id) + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + else + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, *wdev_id); + + if (params->num_ssids) { + struct nlattr *ssids; + + ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); + if (ssids == NULL) + goto fail; + 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); + if (nla_put(msg, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid) < 0) + goto fail; + } + nla_nest_end(msg, ssids); + } + + if (params->extra_ies) { + wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", + params->extra_ies, params->extra_ies_len); + if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len, + params->extra_ies) < 0) + goto fail; + } + + if (params->freqs) { + struct nlattr *freqs; + freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); + if (freqs == NULL) + goto fail; + for (i = 0; params->freqs[i]; i++) { + wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " + "MHz", params->freqs[i]); + if (nla_put_u32(msg, i + 1, params->freqs[i]) < 0) + goto fail; + } + nla_nest_end(msg, freqs); + } + + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + + if (params->only_new_results) { + wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH"); + NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, + NL80211_SCAN_FLAG_FLUSH); + } + + return msg; + +fail: +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + + +/** + * wpa_driver_nl80211_scan - Request the driver to initiate scan + * @bss: 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(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1, timeout; + struct nl_msg *msg = NULL; + + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); + drv->scan_for_auth = 0; + + msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params, + bss->wdev_id_set ? &bss->wdev_id : NULL); + if (!msg) + return -1; + + if (params->p2p_probe) { + struct nlattr *rates; + + wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); + + rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES); + if (rates == NULL) + goto nla_put_failure; + + /* + * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates + * by masking out everything else apart from the OFDM rates 6, + * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz + * rates are left enabled. + */ + NLA_PUT(msg, NL80211_BAND_2GHZ, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c"); + nla_nest_end(msg, rates); + + NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); + } + + 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)); + if (drv->hostapd && is_ap_interface(drv->nlmode)) { + /* + * mac80211 does not allow scan requests in AP mode, so + * try to do this in station mode. + */ + if (wpa_driver_nl80211_set_mode( + bss, NL80211_IFTYPE_STATION)) + goto nla_put_failure; + + if (wpa_driver_nl80211_scan(bss, params)) { + wpa_driver_nl80211_set_mode(bss, drv->nlmode); + goto nla_put_failure; + } + + /* Restore AP mode when processing scan results */ + drv->ap_scan_as_station = drv->nlmode; + ret = 0; + } else + goto nla_put_failure; + } + + drv->scan_state = SCAN_REQUESTED; + /* 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(msg); + return ret; +} + + +/** + * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * @interval: Interval between scan cycles in milliseconds + * Returns: 0 on success, -1 on failure or if not supported + */ +static int wpa_driver_nl80211_sched_scan(void *priv, + struct wpa_driver_scan_params *params, + u32 interval) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + struct nl_msg *msg; + size_t i; + + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request"); + +#ifdef ANDROID + if (!drv->capa.sched_scan_supported) + return android_pno_start(bss, params); +#endif /* ANDROID */ + + msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params, + bss->wdev_id_set ? &bss->wdev_id : NULL); + if (!msg) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval); + + if ((drv->num_filter_ssids && + (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || + params->filter_rssi) { + struct nlattr *match_sets; + match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH); + if (match_sets == NULL) + goto nla_put_failure; + + for (i = 0; i < drv->num_filter_ssids; i++) { + struct nlattr *match_set_ssid; + wpa_hexdump_ascii(MSG_MSGDUMP, + "nl80211: Sched scan filter SSID", + drv->filter_ssids[i].ssid, + drv->filter_ssids[i].ssid_len); + + match_set_ssid = nla_nest_start(msg, i + 1); + if (match_set_ssid == NULL) + goto nla_put_failure; + NLA_PUT(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + drv->filter_ssids[i].ssid_len, + drv->filter_ssids[i].ssid); + if (params->filter_rssi) + NLA_PUT_U32(msg, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + params->filter_rssi); + + nla_nest_end(msg, match_set_ssid); + } + + /* + * Due to backward compatibility code, newer kernels treat this + * matchset (with only an RSSI filter) as the default for all + * other matchsets, unless it's the only one, in which case the + * matchset will actually allow all SSIDs above the RSSI. + */ + if (params->filter_rssi) { + struct nlattr *match_set_rssi; + match_set_rssi = nla_nest_start(msg, 0); + if (match_set_rssi == NULL) + goto nla_put_failure; + NLA_PUT_U32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + params->filter_rssi); + wpa_printf(MSG_MSGDUMP, + "nl80211: Sched scan RSSI filter %d dBm", + params->filter_rssi); + nla_nest_end(msg, match_set_rssi); + } + + nla_nest_end(msg, match_sets); + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + + /* TODO: if we get an error here, we should fall back to normal scan */ + + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: " + "ret=%d (%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + + wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " + "scan interval %d msec", ret, interval); + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * Returns: 0 on success, -1 on failure or if not supported + */ +static int wpa_driver_nl80211_stop_sched_scan(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = 0; + struct nl_msg *msg; + +#ifdef ANDROID + if (!drv->capa.sched_scan_supported) + return android_pno_stop(bss); +#endif /* ANDROID */ + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN); + + 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: Sched scan stop failed: " + "ret=%d (%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + + wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop sent (ret=%d)", ret); + +nla_put_failure: + nlmsg_free(msg); + 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; +} + + +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; + size_t i; + + 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_STATUS]) { + enum nl80211_bss_status status; + status = nla_get_u32(bss[NL80211_BSS_STATUS]); + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_FREQUENCY]) { + _arg->assoc_freq = + nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", + _arg->assoc_freq); + } + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_BSSID]) { + os_memcpy(_arg->assoc_bssid, + nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: Associated with " + MACSTR, MAC2STR(_arg->assoc_bssid)); + } + } + if (!res) + 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_QUAL_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; + } + } + + /* + * cfg80211 maintains separate BSS table entries for APs if the same + * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does + * not use frequency as a separate key in the BSS table, so filter out + * duplicated entries. Prefer associated BSS entry in such a case in + * order to get the correct frequency into the BSS table. Similarly, + * prefer newer entries over older. + */ + for (i = 0; i < res->num; i++) { + const u8 *s1, *s2; + if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) + continue; + + s1 = nl80211_get_ie((u8 *) (res->res[i] + 1), + res->res[i]->ie_len, WLAN_EID_SSID); + s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); + if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || + os_memcmp(s1, s2, 2 + s1[1]) != 0) + continue; + + /* Same BSSID,SSID was already included in scan results */ + wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " + "for " MACSTR, MAC2STR(r->bssid)); + + if (((r->flags & WPA_SCAN_ASSOCIATED) && + !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) || + r->age < res->res[i]->age) { + os_free(res->res[i]); + res->res[i] = r; + } else + os_free(r); + return NL_SKIP; + } + + tmp = os_realloc_array(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 (is_sta_interface(drv->nlmode) && + 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 (is_sta_interface(drv->nlmode) && + !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 (is_sta_interface(drv->nlmode) && + 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 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; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + if (nl80211_set_iface_id(msg, drv->first_bss) < 0) + goto nla_put_failure; + + 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, "nl80211: Received scan results (%lu " + "BSSes)", (unsigned long) res->num); + nl80211_get_noise_for_scan_results(drv, res); + 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 u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len) +{ + switch (alg) { + case WPA_ALG_WEP: + if (key_len == 5) + return WLAN_CIPHER_SUITE_WEP40; + return WLAN_CIPHER_SUITE_WEP104; + case WPA_ALG_TKIP: + return WLAN_CIPHER_SUITE_TKIP; + case WPA_ALG_CCMP: + return WLAN_CIPHER_SUITE_CCMP; + case WPA_ALG_GCMP: + return WLAN_CIPHER_SUITE_GCMP; + case WPA_ALG_CCMP_256: + return WLAN_CIPHER_SUITE_CCMP_256; + case WPA_ALG_GCMP_256: + return WLAN_CIPHER_SUITE_GCMP_256; + case WPA_ALG_IGTK: + return WLAN_CIPHER_SUITE_AES_CMAC; + case WPA_ALG_BIP_GMAC_128: + return WLAN_CIPHER_SUITE_BIP_GMAC_128; + case WPA_ALG_BIP_GMAC_256: + return WLAN_CIPHER_SUITE_BIP_GMAC_256; + case WPA_ALG_BIP_CMAC_256: + return WLAN_CIPHER_SUITE_BIP_CMAC_256; + case WPA_ALG_SMS4: + return WLAN_CIPHER_SUITE_SMS4; + case WPA_ALG_KRK: + return WLAN_CIPHER_SUITE_KRK; + case WPA_ALG_NONE: + case WPA_ALG_PMK: + wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d", + alg); + return 0; + } + + wpa_printf(MSG_ERROR, "nl80211: Unsupported encryption algorithm %d", + alg); + return 0; +} + + +static u32 wpa_cipher_to_cipher_suite(unsigned int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP_256: + return WLAN_CIPHER_SUITE_CCMP_256; + case WPA_CIPHER_GCMP_256: + return WLAN_CIPHER_SUITE_GCMP_256; + case WPA_CIPHER_CCMP: + return WLAN_CIPHER_SUITE_CCMP; + case WPA_CIPHER_GCMP: + return WLAN_CIPHER_SUITE_GCMP; + case WPA_CIPHER_TKIP: + return WLAN_CIPHER_SUITE_TKIP; + case WPA_CIPHER_WEP104: + return WLAN_CIPHER_SUITE_WEP104; + case WPA_CIPHER_WEP40: + return WLAN_CIPHER_SUITE_WEP40; + } + + return 0; +} + + +static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[], + int max_suites) +{ + int num_suites = 0; + + if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256) + suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256; + if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256) + suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256; + if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; + if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP; + if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP) + suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; + if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; + if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; + + return num_suites; +} + + +static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, + 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_nl80211_data *drv = bss->drv; + int ifindex; + struct nl_msg *msg; + int ret; + int tdls = 0; + + /* Ignore for P2P Device */ + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + return 0; + + ifindex = if_nametoindex(ifname); + wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d " + "set_tx=%d seq_len=%lu key_len=%lu", + __func__, ifindex, ifname, alg, addr, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); +#ifdef CONFIG_TDLS + if (key_idx == -1) { + key_idx = 0; + tdls = 1; + } +#endif /* CONFIG_TDLS */ + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + if (alg == WPA_ALG_NONE) { + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY); + } else { + nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY); + NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + wpa_alg_to_cipher_suite(alg, key_len)); + } + + if (seq && seq_len) + NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq); + + if (addr && !is_broadcast_ether_addr(addr)) { + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + if (alg != WPA_ALG_WEP && key_idx && !set_tx) { + wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK"); + NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, + NL80211_KEYTYPE_GROUP); + } + } else if (addr && is_broadcast_ether_addr(addr)) { + struct nlattr *types; + + wpa_printf(MSG_DEBUG, " broadcast key"); + + types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); + if (!types) + goto nla_put_failure; + NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST); + nla_nest_end(msg, types); + } + 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 || tdls) + return ret; + if (is_ap_interface(drv->nlmode) && addr && + !is_broadcast_ether_addr(addr)) + return ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY); + 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); + if (addr && is_broadcast_ether_addr(addr)) { + struct nlattr *types; + + types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); + if (!types) + goto nla_put_failure; + NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST); + nla_nest_end(msg, types); + } else if (addr) { + struct nlattr *types; + + types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); + if (!types) + goto nla_put_failure; + NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST); + nla_nest_end(msg, types); + } + + 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: + nlmsg_free(msg); + 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); + + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, + wpa_alg_to_cipher_suite(alg, key_len)); + + 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 (params->wps == WPS_MODE_PRIVACY) + privacy = 1; + if (params->pairwise_suite && + params->pairwise_suite != WPA_CIPHER_NONE) + privacy = 1; + + 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; + + nl80211_cmd(drv, msg, 0, cmd); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); + if (addr) + 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_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed: reason=%u ret=%d (%s)", + reason_code, 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, + int reason_code) +{ + int ret; + + wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code); + nl80211_mark_disconnected(drv); + /* Disconnect command doesn't need BSSID - it uses cached value */ + ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, + reason_code, 0); + /* + * For locally generated disconnect, supplicant already generates a + * DEAUTH event, so ignore the event from NL80211. + */ + drv->ignore_next_local_disconnect = ret == 0; + + return ret; +} + + +static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss, + const u8 *addr, int reason_code) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) + return wpa_driver_nl80211_disconnect(drv, reason_code); + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", + __func__, MAC2STR(addr), reason_code); + nl80211_mark_disconnected(drv); + if (drv->nlmode == NL80211_IFTYPE_ADHOC) + return nl80211_leave_ibss(drv); + return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, + reason_code, 0); +} + + +static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_auth_params *params) +{ + int i; + + drv->auth_freq = params->freq; + drv->auth_alg = params->auth_alg; + drv->auth_wep_tx_keyidx = params->wep_tx_keyidx; + drv->auth_local_state_change = params->local_state_change; + drv->auth_p2p = params->p2p; + + if (params->bssid) + os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN); + else + os_memset(drv->auth_bssid_, 0, ETH_ALEN); + + if (params->ssid) { + os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len); + drv->auth_ssid_len = params->ssid_len; + } else + drv->auth_ssid_len = 0; + + + os_free(drv->auth_ie); + drv->auth_ie = NULL; + drv->auth_ie_len = 0; + if (params->ie) { + drv->auth_ie = os_malloc(params->ie_len); + if (drv->auth_ie) { + os_memcpy(drv->auth_ie, params->ie, params->ie_len); + drv->auth_ie_len = params->ie_len; + } + } + + for (i = 0; i < 4; i++) { + if (params->wep_key[i] && params->wep_key_len[i] && + params->wep_key_len[i] <= 16) { + os_memcpy(drv->auth_wep_key[i], params->wep_key[i], + params->wep_key_len[i]); + drv->auth_wep_key_len[i] = params->wep_key_len[i]; + } else + drv->auth_wep_key_len[i] = 0; + } +} + + +static int wpa_driver_nl80211_authenticate( + struct i802_bss *bss, struct wpa_driver_auth_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1, i; + struct nl_msg *msg; + enum nl80211_auth_type type; + enum nl80211_iftype nlmode; + int count = 0; + int is_retry; + + is_retry = drv->retry_auth; + drv->retry_auth = 0; + + nl80211_mark_disconnected(drv); + os_memset(drv->auth_bssid, 0, ETH_ALEN); + if (params->bssid) + os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN); + else + os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); + /* FIX: IBSS mode */ + nlmode = params->p2p ? + NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + if (drv->nlmode != nlmode && + wpa_driver_nl80211_set_mode(bss, nlmode) < 0) + return -1; + +retry: + msg = nlmsg_alloc(); + if (!msg) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)", + drv->ifindex); + + nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE); + + for (i = 0; i < 4; i++) { + if (!params->wep_key[i]) + continue; + wpa_driver_nl80211_set_key(bss->ifname, bss, 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->sae_data) { + wpa_hexdump(MSG_DEBUG, " * SAE data", params->sae_data, + params->sae_data_len); + NLA_PUT(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len, + params->sae_data); + } + 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 if (params->auth_alg & WPA_AUTH_ALG_SAE) + type = NL80211_AUTHTYPE_SAE; + 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_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed (auth): 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; + } + + if (ret == -ENOENT && params->freq && !is_retry) { + /* + * cfg80211 has likely expired the BSS entry even + * though it was previously available in our internal + * BSS table. To recover quickly, start a single + * channel scan on the specified channel. + */ + struct wpa_driver_scan_params scan; + int freqs[2]; + + os_memset(&scan, 0, sizeof(scan)); + scan.num_ssids = 1; + if (params->ssid) { + scan.ssids[0].ssid = params->ssid; + scan.ssids[0].ssid_len = params->ssid_len; + } + freqs[0] = params->freq; + freqs[1] = 0; + scan.freqs = freqs; + wpa_printf(MSG_DEBUG, "nl80211: Trigger single " + "channel scan to refresh cfg80211 BSS " + "entry"); + ret = wpa_driver_nl80211_scan(bss, &scan); + if (ret == 0) { + nl80211_copy_auth_params(drv, params); + drv->scan_for_auth = 1; + } + } else if (is_retry) { + /* + * Need to indicate this with an event since the return + * value from the retry is not delivered to core code. + */ + union wpa_event_data event; + wpa_printf(MSG_DEBUG, "nl80211: Authentication retry " + "failed"); + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.timeout_event.addr, drv->auth_bssid_, + ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT, + &event); + } + + goto nla_put_failure; + } + ret = 0; + wpa_printf(MSG_DEBUG, "nl80211: Authentication request send " + "successfully"); + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_authenticate_retry( + struct wpa_driver_nl80211_data *drv) +{ + struct wpa_driver_auth_params params; + struct i802_bss *bss = drv->first_bss; + int i; + + wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again"); + + os_memset(¶ms, 0, sizeof(params)); + params.freq = drv->auth_freq; + params.auth_alg = drv->auth_alg; + params.wep_tx_keyidx = drv->auth_wep_tx_keyidx; + params.local_state_change = drv->auth_local_state_change; + params.p2p = drv->auth_p2p; + + if (!is_zero_ether_addr(drv->auth_bssid_)) + params.bssid = drv->auth_bssid_; + + if (drv->auth_ssid_len) { + params.ssid = drv->auth_ssid; + params.ssid_len = drv->auth_ssid_len; + } + + params.ie = drv->auth_ie; + params.ie_len = drv->auth_ie_len; + + for (i = 0; i < 4; i++) { + if (drv->auth_wep_key_len[i]) { + params.wep_key[i] = drv->auth_wep_key[i]; + params.wep_key_len[i] = drv->auth_wep_key_len[i]; + } + } + + drv->retry_auth = 1; + return wpa_driver_nl80211_authenticate(bss, ¶ms); +} + + +struct phy_info_arg { + u16 *num_modes; + struct hostapd_hw_modes *modes; + int last_mode, last_chan_idx; +}; + +static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, + struct nlattr *ampdu_factor, + struct nlattr *ampdu_density, + struct nlattr *mcs_set) +{ + if (capa) + mode->ht_capab = nla_get_u16(capa); + + if (ampdu_factor) + mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03; + + if (ampdu_density) + mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2; + + if (mcs_set && nla_len(mcs_set) >= 16) { + u8 *mcs; + mcs = nla_data(mcs_set); + os_memcpy(mode->mcs_set, mcs, 16); + } +} + + +static void phy_info_vht_capa(struct hostapd_hw_modes *mode, + struct nlattr *capa, + struct nlattr *mcs_set) +{ + if (capa) + mode->vht_capab = nla_get_u32(capa); + + if (mcs_set && nla_len(mcs_set) >= 8) { + u8 *mcs; + mcs = nla_data(mcs_set); + os_memcpy(mode->vht_mcs_set, mcs, 8); + } +} + + +static void phy_info_freq(struct hostapd_hw_modes *mode, + struct hostapd_channel_data *chan, + struct nlattr *tb_freq[]) +{ + u8 channel; + chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + chan->flag = 0; + if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES) + chan->chan = channel; + + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + chan->flag |= HOSTAPD_CHAN_DISABLED; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR]) + chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN | HOSTAPD_CHAN_NO_IBSS; + if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) + chan->flag |= HOSTAPD_CHAN_RADAR; + + if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { + enum nl80211_dfs_state state = + nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); + + switch (state) { + case NL80211_DFS_USABLE: + chan->flag |= HOSTAPD_CHAN_DFS_USABLE; + break; + case NL80211_DFS_AVAILABLE: + chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE; + break; + case NL80211_DFS_UNAVAILABLE: + chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE; + break; + } + } +} + + +static int phy_info_freqs(struct phy_info_arg *phy_info, + struct hostapd_hw_modes *mode, struct nlattr *tb) +{ + 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_NO_IR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 }, + }; + int new_channels = 0; + struct hostapd_channel_data *channel; + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + struct nlattr *nl_freq; + int rem_freq, idx; + + if (tb == NULL) + return NL_OK; + + nla_for_each_nested(nl_freq, tb, 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; + new_channels++; + } + + channel = os_realloc_array(mode->channels, + mode->num_channels + new_channels, + sizeof(struct hostapd_channel_data)); + if (!channel) + return NL_SKIP; + + mode->channels = channel; + mode->num_channels += new_channels; + + idx = phy_info->last_chan_idx; + + nla_for_each_nested(nl_freq, tb, 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; + phy_info_freq(mode, &mode->channels[idx], tb_freq); + idx++; + } + phy_info->last_chan_idx = idx; + + return NL_OK; +} + + +static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb) +{ + 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 *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; + struct nlattr *nl_rate; + int rem_rate, idx; + + if (tb == NULL) + return NL_OK; + + nla_for_each_nested(nl_rate, tb, 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_calloc(mode->num_rates, sizeof(int)); + if (!mode->rates) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_rate, tb, 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]); + idx++; + } + + return NL_OK; +} + + +static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) +{ + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + struct hostapd_hw_modes *mode; + int ret; + + if (phy_info->last_mode != nl_band->nla_type) { + mode = os_realloc_array(phy_info->modes, + *phy_info->num_modes + 1, + sizeof(*mode)); + if (!mode) + return NL_SKIP; + phy_info->modes = mode; + + mode = &phy_info->modes[*(phy_info->num_modes)]; + os_memset(mode, 0, sizeof(*mode)); + mode->mode = NUM_HOSTAPD_MODES; + mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN | + HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN; + + /* + * Unsupported VHT MCS stream is defined as value 3, so the VHT + * MCS RX/TX map must be initialized with 0xffff to mark all 8 + * possible streams as unsupported. This will be overridden if + * driver advertises VHT support. + */ + mode->vht_mcs_set[0] = 0xff; + mode->vht_mcs_set[1] = 0xff; + mode->vht_mcs_set[4] = 0xff; + mode->vht_mcs_set[5] = 0xff; + + *(phy_info->num_modes) += 1; + phy_info->last_mode = nl_band->nla_type; + phy_info->last_chan_idx = 0; + } else + mode = &phy_info->modes[*(phy_info->num_modes) - 1]; + + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), + nla_len(nl_band), NULL); + + phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA], + tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR], + tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY], + tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); + phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA], + tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); + ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]); + if (ret != NL_OK) + return ret; + ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); + if (ret != NL_OK) + return ret; + + return NL_OK; +} + + +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 *nl_band; + int rem_band; + + 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) + { + int res = phy_info_band(phy_info, nl_band); + if (res != NL_OK) + return res; + } + + return NL_SKIP; +} + + +static struct hostapd_hw_modes * +wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes, + u16 *num_modes) +{ + u16 m; + struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; + int i, mode11g_idx = -1; + + /* heuristic to set up modes */ + for (m = 0; m < *num_modes; m++) { + if (!modes[m].num_channels) + continue; + if (modes[m].channels[0].freq < 4000) { + modes[m].mode = HOSTAPD_MODE_IEEE80211B; + for (i = 0; i < modes[m].num_rates; i++) { + if (modes[m].rates[i] > 200) { + modes[m].mode = HOSTAPD_MODE_IEEE80211G; + break; + } + } + } else if (modes[m].channels[0].freq > 50000) + modes[m].mode = HOSTAPD_MODE_IEEE80211AD; + else + modes[m].mode = HOSTAPD_MODE_IEEE80211A; + } + + /* 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_array(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 void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (chan->freq - 10 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_HT40; + } +} + + +static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (!(chan->flag & HOSTAPD_CHAN_HT40)) + continue; + if (chan->freq - 30 >= start && chan->freq - 10 <= end) + chan->flag |= HOSTAPD_CHAN_HT40MINUS; + if (chan->freq + 10 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_HT40PLUS; + } +} + + +static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp, + struct phy_info_arg *results) +{ + u16 m; + + for (m = 0; m < *results->num_modes; m++) { + int c; + struct hostapd_hw_modes *mode = &results->modes[m]; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if ((u32) chan->freq - 10 >= start && + (u32) chan->freq + 10 <= end) + chan->max_tx_power = max_eirp; + } + } +} + + +static void nl80211_reg_rule_ht40(u32 start, u32 end, + struct phy_info_arg *results) +{ + u16 m; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode(&results->modes[m], start, end); + } +} + + +static void nl80211_reg_rule_sec(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_bw; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) + return; + + start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + if (max_bw < 20) + return; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode_sec(&results->modes[m], start, end); + } +} + + +static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (chan->freq - 10 >= start && chan->freq + 70 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_10_70; + + if (chan->freq - 30 >= start && chan->freq + 50 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_30_50; + + if (chan->freq - 50 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_50_30; + + if (chan->freq - 70 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_70_10; + } +} + + +static void nl80211_reg_rule_vht(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_bw; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) + return; + + start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + if (max_bw < 80) + return; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + /* TODO: use a real VHT support indication */ + if (!results->modes[m].vht_capab) + continue; + + nl80211_set_vht_mode(&results->modes[m], start, end); + } +} + + +static const char * dfs_domain_name(enum nl80211_dfs_regions region) +{ + switch (region) { + case NL80211_DFS_UNSET: + return "DFS-UNSET"; + case NL80211_DFS_FCC: + return "DFS-FCC"; + case NL80211_DFS_ETSI: + return "DFS-ETSI"; + case NL80211_DFS_JP: + return "DFS-JP"; + default: + return "DFS-invalid"; + } +} + + +static int nl80211_get_reg(struct nl_msg *msg, void *arg) +{ + struct phy_info_arg *results = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *nl_rule; + struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1]; + int rem_rule; + static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, + }; + + 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; + } + + if (tb_msg[NL80211_ATTR_DFS_REGION]) { + enum nl80211_dfs_regions dfs_domain; + dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]); + wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)", + (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), + dfs_domain_name(dfs_domain)); + } else { + wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", + (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + u32 start, end, max_eirp = 0, max_bw = 0; + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL) + continue; + start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) + max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100; + if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) + max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm", + start, end, max_bw, max_eirp); + if (max_bw >= 40) + nl80211_reg_rule_ht40(start, end, results); + if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) + nl80211_reg_rule_max_eirp(start, end, max_eirp, + results); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_sec(tb_rule, results); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_vht(tb_rule, results); + } + + return NL_SKIP; +} + + +static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, + struct phy_info_arg *results) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + return send_and_recv_msgs(drv, msg, nl80211_get_reg, results); +} + + +static struct hostapd_hw_modes * +wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + u32 feat; + 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, + .last_mode = -1, + }; + + *num_modes = 0; + *flags = 0; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY); + else + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); + + NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { + nl80211_set_regulatory_flags(drv, &result); + return wpa_driver_nl80211_postprocess_modes(result.modes, + num_modes); + } + msg = NULL; + nla_put_failure: + nlmsg_free(msg); + return NULL; +} + + +static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt, int noack) +{ + __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, + }; + int res; + u16 txflags = 0; + + if (encrypt) + rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; + + if (drv->monitor_sock < 0) { + wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available " + "for %s", __func__); + return -1; + } + + if (noack) + txflags |= IEEE80211_RADIOTAP_F_TX_NOACK; + WPA_PUT_LE16(&rtap_hdr[12], txflags); + + res = sendmsg(drv->monitor_sock, &msg, 0); + if (res < 0) { + wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno)); + return -1; + } + return 0; +} + + +static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, + const void *data, size_t len, + int encrypt, int noack, + unsigned int freq, int no_cck, + int offchanok, unsigned int wait_time) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + u64 cookie; + int res; + + if (freq == 0) { + wpa_printf(MSG_DEBUG, "nl80211: send_frame - Use bss->freq=%u", + bss->freq); + freq = bss->freq; + } + + if (drv->use_monitor) { + wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_mntr", + freq, bss->freq); + return wpa_driver_nl80211_send_mntr(drv, data, len, + encrypt, noack); + } + + wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd"); + res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len, + &cookie, no_cck, noack, offchanok); + if (res == 0 && !noack) { + 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_ACTION) { + wpa_printf(MSG_MSGDUMP, + "nl80211: Update send_action_cookie from 0x%llx to 0x%llx", + (long long unsigned int) + drv->send_action_cookie, + (long long unsigned int) cookie); + drv->send_action_cookie = cookie; + } + } + + return res; +} + + +static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data, + size_t data_len, int noack, + unsigned int freq, int no_cck, + int offchanok, + unsigned int wait_time) +{ + 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); + wpa_printf(MSG_DEBUG, "nl80211: send_mlme - noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u fc=0x%x nlmode=%d", + noack, freq, no_cck, offchanok, wait_time, fc, drv->nlmode); + + if ((is_sta_interface(drv->nlmode) || + drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) { + /* + * The use of last_mgmt_freq is a bit of a hack, + * but it works due to the single-threaded nature + * of wpa_supplicant. + */ + if (freq == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d", + drv->last_mgmt_freq); + freq = drv->last_mgmt_freq; + } + return nl80211_send_frame_cmd(bss, freq, 0, + data, data_len, NULL, 1, noack, + 1); + } + + if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) { + if (freq == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d", + bss->freq); + freq = bss->freq; + } + return nl80211_send_frame_cmd(bss, freq, + (int) freq == bss->freq ? 0 : + wait_time, + data, data_len, + &drv->send_action_cookie, + no_cck, noack, offchanok); + } + + 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; + } + + wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame"); + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, + noack, freq, no_cck, offchanok, + wait_time); +} + + +static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, + int slot, int ht_opmode, int ap_isolate, + int *basic_rates) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); + + 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); + if (ht_opmode >= 0) + NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode); + if (ap_isolate >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate); + + if (basic_rates) { + u8 rates[NL80211_MAX_SUPP_RATES]; + u8 rates_len = 0; + int i; + + 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: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int wpa_driver_nl80211_set_acl(void *priv, + struct hostapd_acl_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *acl; + unsigned int i; + int ret = 0; + + if (!(drv->capa.max_acl_mac_addrs)) + return -ENOTSUP; + + if (params->num_mac_acl > drv->capa.max_acl_mac_addrs) + return -ENOTSUP; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)", + params->acl_policy ? "Accept" : "Deny", params->num_mac_acl); + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_MAC_ACL); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + NLA_PUT_U32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ? + NL80211_ACL_POLICY_DENY_UNLESS_LISTED : + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED); + + acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS); + if (acl == NULL) + goto nla_put_failure; + + for (i = 0; i < params->num_mac_acl; i++) + NLA_PUT(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr); + + nla_nest_end(msg, acl); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)", + ret, strerror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + + return ret; +} + + +static int wpa_driver_nl80211_set_ap(void *priv, + struct wpa_driver_ap_params *params) +{ + 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); + int num_suites; + u32 suites[10], suite; + u32 ver; + + 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; + + nl80211_cmd(drv, msg, 0, cmd); + wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head", + params->head, params->head_len); + NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head); + wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail", + params->tail, params->tail_len); + NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail); + wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int); + NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int); + wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period); + NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period); + wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid", + params->ssid, params->ssid_len); + NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid); + if (params->proberesp && params->proberesp_len) { + wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)", + params->proberesp, params->proberesp_len); + NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len, + params->proberesp); + } + switch (params->hide_ssid) { + case NO_SSID_HIDING: + wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use"); + NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_NOT_IN_USE); + break; + case HIDDEN_SSID_ZERO_LEN: + wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len"); + NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_ZERO_LEN); + break; + case HIDDEN_SSID_ZERO_CONTENTS: + wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents"); + NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_ZERO_CONTENTS); + break; + } + wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy); + if (params->privacy) + NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); + wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs); + if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) == + (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) { + /* Leave out the attribute */ + } else if (params->auth_algs & WPA_AUTH_ALG_SHARED) + NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, + NL80211_AUTHTYPE_SHARED_KEY); + else + NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, + NL80211_AUTHTYPE_OPEN_SYSTEM); + + wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version); + ver = 0; + if (params->wpa_version & WPA_PROTO_WPA) + ver |= NL80211_WPA_VERSION_1; + if (params->wpa_version & WPA_PROTO_RSN) + ver |= NL80211_WPA_VERSION_2; + if (ver) + NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); + + wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x", + params->key_mgmt_suites); + num_suites = 0; + if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X) + suites[num_suites++] = WLAN_AKM_SUITE_8021X; + if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK) + suites[num_suites++] = WLAN_AKM_SUITE_PSK; + if (num_suites) { + NLA_PUT(msg, NL80211_ATTR_AKM_SUITES, + num_suites * sizeof(u32), suites); + } + + if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X && + params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT); + + wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x", + params->pairwise_ciphers); + num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers, + suites, ARRAY_SIZE(suites)); + if (num_suites) { + NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + num_suites * sizeof(u32), suites); + } + + wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x", + params->group_cipher); + suite = wpa_cipher_to_cipher_suite(params->group_cipher); + if (suite) + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite); + + if (params->beacon_ies) { + wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies", + params->beacon_ies); + NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies), + wpabuf_head(params->beacon_ies)); + } + if (params->proberesp_ies) { + wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies", + params->proberesp_ies); + NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, + wpabuf_len(params->proberesp_ies), + wpabuf_head(params->proberesp_ies)); + } + if (params->assocresp_ies) { + wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies", + params->assocresp_ies); + NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP, + wpabuf_len(params->assocresp_ies), + wpabuf_head(params->assocresp_ies)); + } + + if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) { + wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d", + params->ap_max_inactivity); + NLA_PUT_U16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT, + params->ap_max_inactivity); + } + + 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; + nl80211_set_bss(bss, params->cts_protect, params->preamble, + params->short_slot_time, params->ht_opmode, + params->isolate, params->basic_rates); + } + return ret; + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_put_freq_params(struct nl_msg *msg, + struct hostapd_freq_params *freq) +{ + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); + if (freq->vht_enabled) { + switch (freq->bandwidth) { + case 20: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_20); + break; + case 40: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_40); + break; + case 80: + if (freq->center_freq2) + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_80P80); + else + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_80); + break; + case 160: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_160); + break; + default: + return -EINVAL; + } + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1); + if (freq->center_freq2) + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, + freq->center_freq2); + } else if (freq->ht_enabled) { + switch (freq->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; + } + } + return 0; + +nla_put_failure: + return -ENOBUFS; +} + + +static int wpa_driver_nl80211_set_freq(struct i802_bss *bss, + struct hostapd_freq_params *freq) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + wpa_printf(MSG_DEBUG, + "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", + freq->freq, freq->ht_enabled, freq->vht_enabled, + freq->bandwidth, freq->center_freq1, freq->center_freq2); + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (nl80211_put_freq_params(msg, freq) < 0) + goto nla_put_failure; + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret == 0) { + bss->freq = freq->freq; + return 0; + } + wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): " + "%d (%s)", freq->freq, ret, strerror(-ret)); +nla_put_failure: + nlmsg_free(msg); + return -1; +} + + +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); + if (flags & WPA_STA_TDLS_PEER) + f |= BIT(NL80211_STA_FLAG_TDLS_PEER); + + return f; +} + + +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; + struct nl80211_sta_flag_update upd; + int ret = -ENOBUFS; + + if ((params->flags & WPA_STA_TDLS_PEER) && + !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR, + params->set ? "Set" : "Add", MAC2STR(params->addr)); + nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION : + NL80211_CMD_NEW_STATION); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, + params->supp_rates); + wpa_hexdump(MSG_DEBUG, " * supported rates", params->supp_rates, + params->supp_rates_len); + if (!params->set) { + if (params->aid) { + wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + } else { + /* + * cfg80211 validates that AID is non-zero, so we have + * to make this a non-zero value for the TDLS case where + * a dummy STA entry is used for now. + */ + wpa_printf(MSG_DEBUG, " * aid=1 (TDLS workaround)"); + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); + } + wpa_printf(MSG_DEBUG, " * listen_interval=%u", + params->listen_interval); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval); + } else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) { + wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid); + NLA_PUT_U16(msg, NL80211_ATTR_PEER_AID, params->aid); + } + if (params->ht_capabilities) { + wpa_hexdump(MSG_DEBUG, " * ht_capabilities", + (u8 *) params->ht_capabilities, + sizeof(*params->ht_capabilities)); + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, + sizeof(*params->ht_capabilities), + params->ht_capabilities); + } + + if (params->vht_capabilities) { + wpa_hexdump(MSG_DEBUG, " * vht_capabilities", + (u8 *) params->vht_capabilities, + sizeof(*params->vht_capabilities)); + NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, + sizeof(*params->vht_capabilities), + params->vht_capabilities); + } + + wpa_printf(MSG_DEBUG, " * capability=0x%x", params->capability); + NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability); + + if (params->ext_capab) { + wpa_hexdump(MSG_DEBUG, " * ext_capab", + params->ext_capab, params->ext_capab_len); + NLA_PUT(msg, NL80211_ATTR_STA_EXT_CAPABILITY, + params->ext_capab_len, params->ext_capab); + } + + if (params->supp_channels) { + wpa_hexdump(MSG_DEBUG, " * supported channels", + params->supp_channels, params->supp_channels_len); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS, + params->supp_channels_len, params->supp_channels); + } + + if (params->supp_oper_classes) { + wpa_hexdump(MSG_DEBUG, " * supported operating classes", + params->supp_oper_classes, + params->supp_oper_classes_len); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + params->supp_oper_classes_len, + params->supp_oper_classes); + } + + os_memset(&upd, 0, sizeof(upd)); + upd.mask = sta_flags_nl80211(params->flags); + upd.set = upd.mask; + wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x", + upd.set, upd.mask); + NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + + if (params->flags & WPA_STA_WMM) { + struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME); + + if (!wme) + goto nla_put_failure; + + wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo); + NLA_PUT_U8(msg, NL80211_STA_WME_UAPSD_QUEUES, + params->qosinfo & WMM_QOSINFO_STA_AC_MASK); + NLA_PUT_U8(msg, NL80211_STA_WME_MAX_SP, + (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) & + WMM_QOSINFO_STA_SP_MASK); + nla_nest_end(msg, wme); + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION " + "result: %d (%s)", params->set ? "SET" : "NEW", ret, + strerror(-ret)); + if (ret == -EEXIST) + ret = 0; + nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); + + 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); + wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR + " --> %d (%s)", + bss->ifname, MAC2STR(addr), ret, strerror(-ret)); + if (ret == -ENOENT) + return 0; + return ret; + nla_put_failure: + nlmsg_free(msg); + 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); + + /* stop listening for EAPOL on this interface */ + del_ifidx(drv, ifidx); + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); + + if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return; + msg = NULL; + nla_put_failure: + nlmsg_free(msg); + wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx); +} + + +static const char * nl80211_iftype_str(enum nl80211_iftype mode) +{ + switch (mode) { + case NL80211_IFTYPE_ADHOC: + return "ADHOC"; + case NL80211_IFTYPE_STATION: + return "STATION"; + case NL80211_IFTYPE_AP: + return "AP"; + case NL80211_IFTYPE_AP_VLAN: + return "AP_VLAN"; + case NL80211_IFTYPE_WDS: + return "WDS"; + case NL80211_IFTYPE_MONITOR: + return "MONITOR"; + case NL80211_IFTYPE_MESH_POINT: + return "MESH_POINT"; + case NL80211_IFTYPE_P2P_CLIENT: + return "P2P_CLIENT"; + case NL80211_IFTYPE_P2P_GO: + return "P2P_GO"; + case NL80211_IFTYPE_P2P_DEVICE: + return "P2P_DEVICE"; + default: + return "unknown"; + } +} + + +static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, + const char *ifname, + enum nl80211_iftype iftype, + const u8 *addr, int wds, + int (*handler)(struct nl_msg *, void *), + void *arg) +{ + struct nl_msg *msg; + int ifidx; + int ret = -ENOBUFS; + + wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)", + iftype, nl80211_iftype_str(iftype)); + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE); + if (nl80211_set_iface_id(msg, drv->first_bss) < 0) + goto nla_put_failure; + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); + + if (iftype == NL80211_IFTYPE_MONITOR) { + struct nlattr *flags; + + flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS); + if (!flags) + goto nla_put_failure; + + NLA_PUT_FLAG(msg, NL80211_MNTR_FLAG_COOK_FRAMES); + + nla_nest_end(msg, flags); + } else if (wds) { + NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds); + } + + ret = send_and_recv_msgs(drv, msg, handler, arg); + msg = NULL; + if (ret) { + nla_put_failure: + nlmsg_free(msg); + wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)", + ifname, ret, strerror(-ret)); + return ret; + } + + if (iftype == NL80211_IFTYPE_P2P_DEVICE) + return 0; + + ifidx = if_nametoindex(ifname); + wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d", + ifname, ifidx); + + if (ifidx <= 0) + return -1; + + /* start listening for EAPOL on this interface */ + add_ifidx(drv, ifidx); + + if (addr && iftype != NL80211_IFTYPE_MONITOR && + linux_set_ifhwaddr(drv->global->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 (*handler)(struct nl_msg *, void *), + void *arg, int use_existing) +{ + int ret; + + ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler, + arg); + + /* if error occurred and interface exists already */ + if (ret == -ENFILE && if_nametoindex(ifname)) { + if (use_existing) { + wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s", + ifname); + return -ENFILE; + } + 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, handler, arg); + } + + if (ret >= 0 && is_p2p_net_interface(iftype)) + 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) +{ + struct ieee80211_hdr *hdr = (void *)buf; + u16 fc; + union wpa_event_data event; + + if (len < sizeof(*hdr)) + return; + + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); + event.rx_from_unknown.addr = hdr->addr2; + event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) == + (WLAN_FC_FROMDS | WLAN_FC_TODS); + 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) { + wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s", + strerror(errno)); + return; + } + + if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { + wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame"); + return; + } + + while (1) { + ret = ieee80211_radiotap_iterator_next(&iter); + if (ret == -ENOENT) + break; + if (ret) { + wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)", + 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_DBM_ANTSIGNAL: + ssi_signal = (s8) *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. + * + * For now allow all To DS data frames through. + */ + /* load the IEEE 802.11 frame control field */ + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0), + /* mask off frame type, version and DS status */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03), + /* accept frame if version 0, type 2 and To DS, fall through otherwise + */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0), + +#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 = ARRAY_SIZE(msock_filter_insns), + .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))) { + wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +static void nl80211_remove_monitor_interface( + struct wpa_driver_nl80211_data *drv) +{ + if (drv->monitor_refcount > 0) + drv->monitor_refcount--; + wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d", + drv->monitor_refcount); + if (drv->monitor_refcount > 0) + return; + + 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; + + if (drv->monitor_ifidx >= 0) { + drv->monitor_refcount++; + wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d", + drv->monitor_refcount); + return 0; + } + + if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) { + /* + * P2P interface name is of the format p2p-%s-%d. For monitor + * interface name corresponding to P2P GO, replace "p2p-" with + * "mon-" to retain the same interface name length and to + * indicate that it is a monitor interface. + */ + snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4); + } else { + /* Non-P2P interface with AP functionality. */ + 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, NULL, NULL, 0); + + if (drv->monitor_ifidx == -EOPNOTSUPP) { + /* + * This is backward compatibility for a few versions of + * the kernel only that didn't advertise the right + * attributes for the only driver that then supported + * AP mode w/o monitor -- ath6kl. + */ + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support " + "monitor interface type - try to run without it"); + drv->device_ap_sme = 1; + } + + if (drv->monitor_ifidx < 0) + return -1; + + if (linux_set_iface_flags(drv->global->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) { + wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s", + strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s", + strerror(errno)); + goto error; + } + + optlen = sizeof(optval); + optval = 20; + if (setsockopt + (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s", + strerror(errno)); + goto error; + } + + if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, + drv, NULL)) { + wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket"); + goto error; + } + + drv->monitor_refcount++; + return 0; + error: + nl80211_remove_monitor_interface(drv); + return -1; +} + + +static int nl80211_setup_ap(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d", + bss->ifname, drv->device_ap_sme, drv->use_monitor); + + /* + * Disable Probe Request reporting unless we need it in this way for + * devices that include the AP SME, in the other case (unless using + * monitor iface) we'll get it through the nl_mgmt socket instead. + */ + if (!drv->device_ap_sme) + wpa_driver_nl80211_probe_req_report(bss, 0); + + if (!drv->device_ap_sme && !drv->use_monitor) + if (nl80211_mgmt_subscribe_ap(bss)) + return -1; + + if (drv->device_ap_sme && !drv->use_monitor) + if (nl80211_mgmt_subscribe_ap_dev_sme(bss)) + return -1; + + if (!drv->device_ap_sme && drv->use_monitor && + nl80211_create_monitor_interface(drv) && + !drv->device_ap_sme) + return -1; + + if (drv->device_ap_sme && + wpa_driver_nl80211_probe_req_report(bss, 1) < 0) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to enable " + "Probe Request frame reporting in AP mode"); + /* Try to survive without this */ + } + + return 0; +} + + +static void nl80211_teardown_ap(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d", + bss->ifname, drv->device_ap_sme, drv->use_monitor); + if (drv->device_ap_sme) { + wpa_driver_nl80211_probe_req_report(bss, 0); + if (!drv->use_monitor) + nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)"); + } else if (drv->use_monitor) + nl80211_remove_monitor_interface(drv); + else + nl80211_mgmt_unsubscribe(bss, "AP teardown"); + + bss->beacon_set = 0; +} + + +static int nl80211_send_eapol_data(struct i802_bss *bss, + const u8 *addr, const u8 *data, + size_t data_len) +{ + struct sockaddr_ll ll; + int ret; + + if (bss->drv->eapol_tx_sock < 0) { + wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL"); + return -1; + } + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = bss->ifindex; + ll.sll_protocol = htons(ETH_P_PAE); + ll.sll_halen = ETH_ALEN; + os_memcpy(ll.sll_addr, addr, ETH_ALEN); + ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0, + (struct sockaddr *) &ll, sizeof(ll)); + if (ret < 0) + wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s", + strerror(errno)); + + return ret; +} + + +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, u32 flags) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; + int qos = flags & WPA_STA_WMM; + + if (drv->device_ap_sme || !drv->use_monitor) + return nl80211_send_eapol_data(bss, addr, data, data_len); + + len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)", + (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 (qos) { + hdr->frame_control |= + host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); + } + + 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 (qos) { + /* Set highest priority in QoS header */ + pos[0] = 7; + pos[1] = 0; + pos += 2; + } + + 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(bss, (u8 *) hdr, len, encrypt, 0, + 0, 0, 0, 0); + 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 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; + struct nlattr *flags; + struct nl80211_sta_flag_update upd; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); + + 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. + */ + flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS); + if (!flags) + goto nla_put_failure; + if (total_flags & WPA_STA_AUTHORIZED) + NLA_PUT_FLAG(msg, NL80211_STA_FLAG_AUTHORIZED); + + if (total_flags & WPA_STA_WMM) + NLA_PUT_FLAG(msg, NL80211_STA_FLAG_WME); + + if (total_flags & WPA_STA_SHORT_PREAMBLE) + NLA_PUT_FLAG(msg, NL80211_STA_FLAG_SHORT_PREAMBLE); + + if (total_flags & WPA_STA_MFP) + NLA_PUT_FLAG(msg, NL80211_STA_FLAG_MFP); + + if (total_flags & WPA_STA_TDLS_PEER) + NLA_PUT_FLAG(msg, NL80211_STA_FLAG_TDLS_PEER); + + nla_nest_end(msg, flags); + + 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); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + enum nl80211_iftype nlmode, old_mode; + struct hostapd_freq_params freq = { + .freq = params->freq, + }; + + if (params->p2p) { + wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P " + "group (GO)"); + nlmode = NL80211_IFTYPE_P2P_GO; + } else + nlmode = NL80211_IFTYPE_AP; + + old_mode = drv->nlmode; + if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) { + nl80211_remove_monitor_interface(drv); + return -1; + } + + if (wpa_driver_nl80211_set_freq(drv->first_bss, &freq)) { + if (old_mode != nlmode) + wpa_driver_nl80211_set_mode(drv->first_bss, old_mode); + nl80211_remove_monitor_interface(drv); + return -1; + } + + 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; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS); + 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: + if (wpa_driver_nl80211_set_mode(drv->first_bss, + NL80211_IFTYPE_STATION)) { + wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " + "station mode"); + } + + 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, + NL80211_IFTYPE_ADHOC)) { + wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " + "IBSS mode"); + return -1; + } + +retry: + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS); + 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->bssid && params->fixed_bssid) { + wpa_printf(MSG_DEBUG, " * BSSID=" MACSTR, + MAC2STR(params->bssid)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); + } + + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) { + wpa_printf(MSG_DEBUG, " * control port"); + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); + } + + 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 nl80211_connect_common(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params, + struct nl_msg *msg) +{ + 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->bg_scan_period >= 0) { + wpa_printf(MSG_DEBUG, " * bg scan period=%d", + params->bg_scan_period); + NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD, + params->bg_scan_period); + } + + 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->wpa_proto) { + enum nl80211_wpa_versions ver = 0; + + if (params->wpa_proto & WPA_PROTO_WPA) + ver |= NL80211_WPA_VERSION_1; + if (params->wpa_proto & WPA_PROTO_RSN) + ver |= NL80211_WPA_VERSION_2; + + wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver); + NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); + } + + if (params->pairwise_suite != WPA_CIPHER_NONE) { + u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite); + wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher); + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); + } + + if (params->group_suite != WPA_CIPHER_NONE) { + u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite); + wpa_printf(MSG_DEBUG, " * group=0x%x", cipher); + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); + } + + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK || + params->key_mgmt_suite == WPA_KEY_MGMT_CCKM) { + int mgmt = WLAN_AKM_SUITE_PSK; + + switch (params->key_mgmt_suite) { + case WPA_KEY_MGMT_CCKM: + mgmt = WLAN_AKM_SUITE_CCKM; + break; + case WPA_KEY_MGMT_IEEE8021X: + mgmt = WLAN_AKM_SUITE_8021X; + break; + case WPA_KEY_MGMT_FT_IEEE8021X: + mgmt = WLAN_AKM_SUITE_FT_8021X; + break; + case WPA_KEY_MGMT_FT_PSK: + mgmt = WLAN_AKM_SUITE_FT_PSK; + break; + case WPA_KEY_MGMT_PSK: + default: + mgmt = WLAN_AKM_SUITE_PSK; + break; + } + NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt); + } + + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); + + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) + NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); + + if (params->disable_ht) + NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT); + + if (params->htcaps && params->htcaps_mask) { + int sz = sizeof(struct ieee80211_ht_capabilities); + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps); + NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, + params->htcaps_mask); + } + +#ifdef CONFIG_VHT_OVERRIDES + if (params->disable_vht) { + wpa_printf(MSG_DEBUG, " * VHT disabled"); + NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT); + } + + if (params->vhtcaps && params->vhtcaps_mask) { + int sz = sizeof(struct ieee80211_vht_capabilities); + NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps); + NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz, + params->vhtcaps_mask); + } +#endif /* CONFIG_VHT_OVERRIDES */ + + if (params->p2p) + wpa_printf(MSG_DEBUG, " * P2P group"); + + return 0; +nla_put_failure: + return -1; +} + + +static int wpa_driver_nl80211_try_connect( + struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + struct nl_msg *msg; + enum nl80211_auth_type type; + int ret; + int algs; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); + nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT); + + ret = nl80211_connect_common(drv, params, msg); + if (ret) + goto nla_put_failure; + + algs = 0; + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_SHARED) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_LEAP) + algs++; + if (algs > 1) { + wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " + "selection"); + goto skip_auth_type; + } + + 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); + +skip_auth_type: + 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_connect( + struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + int ret = wpa_driver_nl80211_try_connect(drv, params); + if (ret == -EALREADY) { + /* + * cfg80211 does not currently accept new connections if + * we are already connected. As a workaround, force + * disconnection and try again. + */ + wpa_printf(MSG_DEBUG, "nl80211: Explicitly " + "disconnecting before reassociation " + "attempt"); + if (wpa_driver_nl80211_disconnect( + drv, WLAN_REASON_PREV_AUTH_NOT_VALID)) + return -1; + ret = wpa_driver_nl80211_try_connect(drv, params); + } + 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; + 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)) { + enum nl80211_iftype nlmode = params->p2p ? + NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + + if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) + return -1; + return wpa_driver_nl80211_connect(drv, params); + } + + nl80211_mark_disconnected(drv); + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", + drv->ifindex); + nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE); + + ret = nl80211_connect_common(drv, params, msg); + if (ret) + goto nla_put_failure; + + 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_dbg(drv->ctx, MSG_DEBUG, + "nl80211: MLME command failed (assoc): 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, enum nl80211_iftype mode) +{ + struct nl_msg *msg; + int ret = -ENOBUFS; + + wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)", + ifindex, mode, nl80211_iftype_str(mode)); + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE); + if (nl80211_set_iface_id(msg, drv->first_bss) < 0) + goto nla_put_failure; + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (!ret) + return 0; +nla_put_failure: + nlmsg_free(msg); + 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(struct i802_bss *bss, + enum nl80211_iftype nlmode) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + int i; + int was_ap = is_ap_interface(drv->nlmode); + int res; + + res = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (res && nlmode == nl80211_get_ifmode(bss)) + res = 0; + + if (res == 0) { + drv->nlmode = nlmode; + ret = 0; + goto done; + } + + if (res == -ENODEV) + return -1; + + 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. + */ + wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting " + "interface down"); + for (i = 0; i < 10; i++) { + res = i802_set_iface_flags(bss, 0); + if (res == -EACCES || res == -ENODEV) + break; + if (res == 0) { + /* Try to set the mode again while the interface is + * down */ + ret = nl80211_set_mode(drv, drv->ifindex, nlmode); + if (ret == -EACCES) + break; + res = i802_set_iface_flags(bss, 1); + if (res && !ret) + ret = -1; + else if (ret != -EBUSY) + break; + } else + wpa_printf(MSG_DEBUG, "nl80211: Failed to set " + "interface down"); + os_sleep(0, 100000); + } + + if (!ret) { + wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while " + "interface is down"); + drv->nlmode = nlmode; + drv->ignore_if_down_event = 1; + } + +done: + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d " + "from %d failed", nlmode, drv->nlmode); + return ret; + } + + if (is_p2p_net_interface(nlmode)) + nl80211_disable_11b_rates(drv, drv->ifindex, 1); + else if (drv->disabled_11b_rates) + nl80211_disable_11b_rates(drv, drv->ifindex, 0); + + if (is_ap_interface(nlmode)) { + nl80211_mgmt_unsubscribe(bss, "start AP"); + /* Setup additional AP mode functionality if needed */ + if (nl80211_setup_ap(bss)) + return -1; + } else if (was_ap) { + /* Remove additional AP mode functionality */ + nl80211_teardown_ap(bss); + } else { + nl80211_mgmt_unsubscribe(bss, "mode change"); + } + + if (!bss->in_deinit && !is_ap_interface(nlmode) && + nl80211_mgmt_subscribe_non_ap(bss) < 0) + wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " + "frame processing - ignore for now"); + + return 0; +} + + +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)); + if (drv->extended_capa && drv->extended_capa_mask) { + capa->extended_capa = drv->extended_capa; + capa->extended_capa_mask = drv->extended_capa_mask; + capa->extended_capa_len = drv->extended_capa_len; + } + + if ((capa->flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + !drv->allow_p2p_device) { + wpa_printf(MSG_DEBUG, "nl80211: Do not indicate P2P_DEVICE support (p2p_device=1 driver param not specified)"); + capa->flags &= ~WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; + } + + 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, "nl80211: Set %s operstate %d->%d (%s)", + bss->ifname, drv->operstate, state, + state ? "UP" : "DORMANT"); + drv->operstate = state; + return netlink_send_oper_ifla(drv->global->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; + int ret = -ENOBUFS; + + if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) { + wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated"); + return 0; + } + + wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for " + MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid)); + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); + + 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); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (!ret) + return 0; + nla_put_failure: + nlmsg_free(msg); + wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)", + ret, strerror(-ret)); + return ret; +} + + +/* Set kernel driver on given frequency (MHz) */ +static int i802_set_freq(void *priv, struct hostapd_freq_params *freq) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_set_freq(bss, freq); +} + + +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; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY); + + 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: + nlmsg_free(msg); + return -ENOBUFS; +} + + +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; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); + 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); + msg = NULL; + if (!ret) + return 0; +nla_put_failure: + nlmsg_free(msg); + 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; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); + 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); + msg = NULL; + if (!ret) + return 0; +nla_put_failure: + nlmsg_free(msg); + 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; + int res; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)", + bss->ifname); + nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); + + /* + * XXX: FIX! this needs to flush all VLANs too + */ + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, + if_nametoindex(bss->ifname)); + + res = send_and_recv_msgs(drv, msg, NULL, NULL); + if (res) { + wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d " + "(%s)", res, strerror(-res)); + } + return res; + nla_put_failure: + nlmsg_free(msg); + 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 }, + [NL80211_STA_INFO_TX_FAILED] = { .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]); + if (stats[NL80211_STA_INFO_TX_FAILED]) + data->tx_retry_failed = + nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); + + return NL_SKIP; +} + +static int i802_read_sta_data(struct i802_bss *bss, + struct hostap_sta_driver_data *data, + const u8 *addr) +{ + 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; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); + + 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: + nlmsg_free(msg); + 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; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); + + 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; + + switch (queue) { + case 0: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO); + break; + case 1: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI); + break; + case 2: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE); + break; + case 3: + NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK); + break; + } + /* 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; + msg = NULL; + nla_put_failure: + nlmsg_free(msg); + return -1; +} + + +static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr, + const char *ifname, int vlan_id) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -ENOBUFS; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR + ", ifname=%s[%d], vlan_id=%d)", + bss->ifname, if_nametoindex(bss->ifname), + MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); + + 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); + msg = 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: + nlmsg_free(msg); + return ret; +} + + +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 wpa_driver_nl80211_data *drv = bss->drv; + struct ieee80211_mgmt mgmt; + + if (drv->device_ap_sme) + return wpa_driver_nl80211_sta_remove(bss, addr); + + 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), 0, 0, 0, 0, + 0); +} + + +static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ieee80211_mgmt mgmt; + + if (drv->device_ap_sme) + return wpa_driver_nl80211_sta_remove(bss, addr); + + 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), 0, 0, 0, 0, + 0); +} + + +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_array(old, drv->num_if_indices + 1, + sizeof(int)); + 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 int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, + const char *bridge_ifname, char *ifname_wds) +{ + 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); + if (ifname_wds) + os_strlcpy(ifname_wds, name, IFNAMSIZ + 1); + + wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR + " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name); + if (val) { + if (!if_nametoindex(name)) { + if (nl80211_create_iface(drv, name, + NL80211_IFTYPE_AP_VLAN, + bss->addr, 1, NULL, NULL, 0) < + 0) + return -1; + if (bridge_ifname && + linux_br_add_if(drv->global->ioctl_sock, + bridge_ifname, name) < 0) + return -1; + } + if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA " + "interface %s up", name); + } + return i802_set_sta_vlan(priv, addr, name, 0); + } else { + if (bridge_ifname) + linux_br_del_if(drv->global->ioctl_sock, bridge_ifname, + name); + + 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) { + wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s", + strerror(errno)); + return; + } + + if (have_ifidx(drv, lladdr.sll_ifindex)) + drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len); +} + + +static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, + struct i802_bss *bss, + const char *brname, const char *ifname) +{ + int ifindex; + char in_br[IFNAMSIZ]; + + os_strlcpy(bss->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->global->ioctl_sock, brname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add the " + "bridge interface %s: %s", + brname, strerror(errno)); + return -1; + } + bss->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->global->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->global->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; + } + bss->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_drv_init(hapd, params->ifname, + params->global_priv, 1, + params->bssid); + 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; + } + + 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 (params->num_bridge && params->bridge[0] && + i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0) + goto failed; + + drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); + if (drv->eapol_sock < 0) { + wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s", + strerror(errno)); + goto failed; + } + + if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) + { + wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol"); + goto failed; + } + + if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, + params->own_addr)) + goto failed; + + memcpy(bss->addr, params->own_addr, ETH_ALEN); + + return bss; + +failed: + wpa_driver_nl80211_deinit(bss); + return NULL; +} + + +static void i802_deinit(void *priv) +{ + struct i802_bss *bss = priv; + wpa_driver_nl80211_deinit(bss); +} + + +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_P2P_CLIENT: + case WPA_IF_P2P_GROUP: + return NL80211_IFTYPE_P2P_CLIENT; + case WPA_IF_AP_VLAN: + return NL80211_IFTYPE_AP_VLAN; + case WPA_IF_AP_BSS: + return NL80211_IFTYPE_AP; + case WPA_IF_P2P_GO: + return NL80211_IFTYPE_P2P_GO; + case WPA_IF_P2P_DEVICE: + return NL80211_IFTYPE_P2P_DEVICE; + } + return -1; +} + + +#ifdef CONFIG_P2P + +static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) +{ + struct wpa_driver_nl80211_data *drv; + dl_list_for_each(drv, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0) + return 1; + } + return 0; +} + + +static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv, + u8 *new_addr) +{ + unsigned int idx; + + if (!drv->global) return -1; + + os_memcpy(new_addr, drv->first_bss->addr, ETH_ALEN); + for (idx = 0; idx < 64; idx++) { + new_addr[0] = drv->first_bss->addr[0] | 0x02; + new_addr[0] ^= idx << 2; + if (!nl80211_addr_in_use(drv->global, new_addr)) + break; + } + if (idx == 64) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address " + MACSTR, MAC2STR(new_addr)); + + return 0; +} + +#endif /* CONFIG_P2P */ + + +struct wdev_info { + u64 wdev_id; + int wdev_id_set; + u8 macaddr[ETH_ALEN]; +}; + +static int nl80211_wdev_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct wdev_info *wi = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_WDEV]) { + wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wi->wdev_id_set = 1; + } + + if (tb[NL80211_ATTR_MAC]) + os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + + return NL_SKIP; +} + + +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, + const char *bridge, int use_existing) +{ + enum nl80211_iftype nlmode; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ifidx; + int added = 1; + + if (addr) + os_memcpy(if_addr, addr, ETH_ALEN); + nlmode = wpa_driver_nl80211_if_type(type); + if (nlmode == NL80211_IFTYPE_P2P_DEVICE) { + struct wdev_info p2pdev_info; + + os_memset(&p2pdev_info, 0, sizeof(p2pdev_info)); + ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, + 0, nl80211_wdev_handler, + &p2pdev_info, use_existing); + if (!p2pdev_info.wdev_id_set || ifidx != 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s", + ifname); + return -1; + } + + drv->global->if_add_wdevid = p2pdev_info.wdev_id; + drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set; + if (!is_zero_ether_addr(p2pdev_info.macaddr)) + os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created", + ifname, + (long long unsigned int) p2pdev_info.wdev_id); + } else { + ifidx = nl80211_create_iface(drv, ifname, nlmode, addr, + 0, NULL, NULL, use_existing); + if (use_existing && ifidx == -ENFILE) { + added = 0; + ifidx = if_nametoindex(ifname); + } else if (ifidx < 0) { + return -1; + } + } + + if (!addr) { + if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) + os_memcpy(if_addr, bss->addr, ETH_ALEN); + else if (linux_get_ifhwaddr(drv->global->ioctl_sock, + bss->ifname, if_addr) < 0) { + if (added) + nl80211_remove_iface(drv, ifidx); + return -1; + } + } + +#ifdef CONFIG_P2P + if (!addr && + (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || + type == WPA_IF_P2P_GO)) { + /* Enforce unique P2P Interface Address */ + u8 new_addr[ETH_ALEN]; + + if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname, + new_addr) < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + if (nl80211_addr_in_use(drv->global, new_addr)) { + wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " + "for P2P group interface"); + if (nl80211_p2p_interface_addr(drv, new_addr) < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, + new_addr) < 0) { + nl80211_remove_iface(drv, ifidx); + return -1; + } + } + os_memcpy(if_addr, new_addr, ETH_ALEN); + } +#endif /* CONFIG_P2P */ + + if (type == WPA_IF_AP_BSS) { + struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss)); + if (new_bss == NULL) { + if (added) + nl80211_remove_iface(drv, ifidx); + return -1; + } + + if (bridge && + i802_check_bridge(drv, new_bss, bridge, ifname) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to add the new " + "interface %s to a bridge %s", + ifname, bridge); + if (added) + nl80211_remove_iface(drv, ifidx); + os_free(new_bss); + return -1; + } + + if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1)) + { + nl80211_remove_iface(drv, ifidx); + os_free(new_bss); + return -1; + } + os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ); + os_memcpy(new_bss->addr, if_addr, ETH_ALEN); + new_bss->ifindex = ifidx; + new_bss->drv = drv; + new_bss->next = drv->first_bss->next; + new_bss->freq = drv->first_bss->freq; + new_bss->ctx = bss_ctx; + new_bss->added_if = added; + drv->first_bss->next = new_bss; + if (drv_priv) + *drv_priv = new_bss; + nl80211_init_bss(new_bss); + + /* Subscribe management frames for this WPA_IF_AP_BSS */ + if (nl80211_setup_ap(new_bss)) + return -1; } + + if (drv->global) + drv->global->if_add_ifindex = ifidx; + return 0; } -/** - * wpa_driver_nl80211_set_ifflags - Set interface flags (SIOCSIFFLAGS) - * @drv: driver_nl80211 private data - * @flags: New value for flags - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_set_ifflags(struct wpa_driver_nl80211_data *drv, - int flags) +static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, + enum wpa_driver_if_type type, + const char *ifname) +{ + 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 added_if=%d", + __func__, type, ifname, ifindex, bss->added_if); + if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex)) + nl80211_remove_iface(drv, ifindex); + + if (type != WPA_IF_AP_BSS) + return 0; + + if (bss->added_if_into_bridge) { + if (linux_br_del_if(drv->global->ioctl_sock, bss->brname, + bss->ifname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "interface %s from bridge %s: %s", + bss->ifname, bss->brname, strerror(errno)); + } + if (bss->added_bridge) { + if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) + wpa_printf(MSG_INFO, "nl80211: Failed to remove " + "bridge %s: %s", + bss->brname, strerror(errno)); + } + + if (bss != drv->first_bss) { + struct i802_bss *tbss; + + wpa_printf(MSG_DEBUG, "nl80211: Not the first BSS - remove it"); + for (tbss = drv->first_bss; tbss; tbss = tbss->next) { + if (tbss->next == bss) { + tbss->next = bss->next; + /* Unsubscribe management frames */ + nl80211_teardown_ap(bss); + nl80211_destroy_bss(bss); + os_free(bss); + bss = NULL; + break; + } + } + if (bss) + wpa_printf(MSG_INFO, "nl80211: %s - could not find " + "BSS %p in the list", __func__, bss); + } else { + wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context"); + nl80211_teardown_ap(bss); + if (!bss->added_if && !drv->first_bss->next) + wpa_driver_nl80211_del_beacon(drv); + nl80211_destroy_bss(bss); + if (!bss->added_if) + i802_set_iface_flags(bss, 0); + if (drv->first_bss->next) { + drv->first_bss = drv->first_bss->next; + drv->ctx = drv->first_bss->ctx; + os_free(bss); + } else { + wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to"); + } + } + + return 0; +} + + +static int cookie_handler(struct nl_msg *msg, void *arg) { - return wpa_driver_nl80211_set_ifflags_ifname(drv, drv->ifname, flags); + 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; } -/** - * 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) +static int nl80211_send_frame_cmd(struct i802_bss *bss, + unsigned int freq, unsigned int wait, + const u8 *buf, size_t buf_len, + u64 *cookie_out, int no_cck, int no_ack, + int offchanok) { - struct wpa_driver_nl80211_data *drv = priv; - char alpha2[3]; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + u64 cookie; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d " + "no_ack=%d offchanok=%d", + freq, wait, no_cck, no_ack, offchanok); + wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len); + nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + if (freq) + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + if (wait) + NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait); + if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) + NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); + if (no_cck) + NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); + if (no_ack) + NLA_PUT_FLAG(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK); + + NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf); + + cookie = 0; + ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " + "(%s) (freq=%u wait=%u)", ret, strerror(-ret), + freq, wait); + goto nla_put_failure; + } + wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; " + "cookie 0x%llx", no_ack ? " (no ACK)" : "", + (long long unsigned int) cookie); + + if (cookie_out) + *cookie_out = no_ack ? (u64) -1 : cookie; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_send_action(struct i802_bss *bss, + unsigned int freq, + unsigned int wait_time, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, + int no_cck) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + u8 *buf; + struct ieee80211_hdr *hdr; + + wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, " + "freq=%u MHz wait=%d ms no_cck=%d)", + drv->ifindex, freq, wait_time, no_cck); + + 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 (is_ap_interface(drv->nlmode) && + (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || + (int) freq == bss->freq || drv->device_ap_sme || + !drv->use_monitor)) + ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len, + 0, freq, no_cck, 1, + wait_time); + else + ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf, + 24 + data_len, + &drv->send_action_cookie, + no_cck, 0, 1); + + os_free(buf); + return ret; +} + + +static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) +{ + 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; + + wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx", + (long long unsigned int) drv->send_action_cookie); + nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL); + + if (nl80211_set_iface_id(msg, bss) < 0) goto nla_put_failure; + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie); - alpha2[0] = alpha2_arg[0]; - alpha2[1] = alpha2_arg[1]; - alpha2[2] = '\0'; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d " + "(%s)", ret, strerror(-ret)); + + nla_put_failure: + nlmsg_free(msg); +} - 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; +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; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + 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); + msg = NULL; + 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; + drv->pending_remain_on_chan = 1; + return 0; + } + wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " + "(freq=%d duration=%u): %d (%s)", + freq, duration, ret, strerror(-ret)); nla_put_failure: - return -EINVAL; + nlmsg_free(msg); + return -1; } -static int wpa_driver_nl80211_set_probe_req_ie(void *priv, const u8 *ies, - size_t ies_len) +static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret = -1; + 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 -ENOMEM; + return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_MGMT_EXTRA_IE, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); - NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, 4 /* ProbeReq */); - if (ies) - NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; - ret = 0; + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = 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: + nlmsg_free(msg); + return -1; +} + + +static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (!report) { + if (bss->nl_preq && drv->device_ap_sme && + is_ap_interface(drv->nlmode)) { + /* + * Do not disable Probe Request reporting that was + * enabled in nl80211_setup_ap(). + */ + wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of " + "Probe Request reporting nl_preq=%p while " + "in AP mode", bss->nl_preq); + } else if (bss->nl_preq) { + wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request " + "reporting nl_preq=%p", bss->nl_preq); + nl80211_destroy_eloop_handle(&bss->nl_preq); + } + return 0; + } + + if (bss->nl_preq) { + wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting " + "already on! nl_preq=%p", bss->nl_preq); + return 0; + } + + bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq"); + if (bss->nl_preq == NULL) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request " + "reporting nl_preq=%p", bss->nl_preq); + + if (nl80211_register_frame(bss, bss->nl_preq, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_REQ << 4), + NULL, 0) < 0) + goto out_err; + + nl80211_register_eloop_read(&bss->nl_preq, + wpa_driver_nl80211_event_receive, + bss->nl_cb); + + return 0; + + out_err: + nl_destroy_handles(&bss->nl_preq); + return -1; +} + + +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; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK); + 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; + if (disabled) { + 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)); + } else + drv->disabled_11b_rates = disabled; + return ret; nla_put_failure: - return -ENOBUFS; + nlmsg_free(msg); + return -1; +} + + +static int wpa_driver_nl80211_deinit_ap(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!is_ap_interface(drv->nlmode)) + return -1; + wpa_driver_nl80211_del_beacon(drv); + + /* + * If the P2P GO interface was dynamically added, then it is + * possible that the interface change to station is not possible. + */ + if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic) + return 0; + + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); +} + + +static int wpa_driver_nl80211_stop_ap(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!is_ap_interface(drv->nlmode)) + return -1; + wpa_driver_nl80211_del_beacon(drv); + bss->beacon_set = 0; + return 0; +} + + +static int wpa_driver_nl80211_deinit_p2p_cli(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT) + return -1; + + /* + * If the P2P Client interface was dynamically added, then it is + * possible that the interface change to station is not possible. + */ + if (bss->if_dynamic) + return 0; + + return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION); } -#ifdef CONFIG_CLIENT_MLME +static void wpa_driver_nl80211_resume(void *priv) +{ + struct i802_bss *bss = priv; + + if (i802_set_iface_flags(bss, 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; + const u8 *own_addr = bss->addr; + + 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, 0, + drv->bssid, own_addr, drv->bssid, + data, data_len, 0); + os_free(data); + + return ret; +} + -static int nl80211_set_vif(struct wpa_driver_nl80211_data *drv, - int drop_unencrypted, int userspace_mlme) +static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) { -#ifdef NL80211_CMD_SET_VIF + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; + struct nlattr *cqm; int ret = -1; + wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " + "hysteresis=%d", threshold, hysteresis); + msg = nlmsg_alloc(); if (!msg) - return -ENOMEM; + return -1; - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_SET_VIF, 0); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM); - if (drop_unencrypted >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_VIF_DROP_UNENCRYPTED, - drop_unencrypted); - if (userspace_mlme >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_VIF_USERSPACE_MLME, - userspace_mlme); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - ret = 0; + cqm = nla_nest_start(msg, NL80211_ATTR_CQM); + if (cqm == NULL) + goto nla_put_failure; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold); + NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis); + nla_nest_end(msg, cqm); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - return ret; + msg = NULL; nla_put_failure: - return -ENOBUFS; -#else /* NL80211_CMD_SET_VIF */ - return -1; -#endif /* NL80211_CMD_SET_VIF */ + nlmsg_free(msg); + return ret; } -static int wpa_driver_nl80211_set_userspace_mlme( - struct wpa_driver_nl80211_data *drv, int enabled) -{ - return nl80211_set_vif(drv, -1, enabled); +static int get_channel_width(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wpa_signal_info *sig_change = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + sig_change->center_frq1 = -1; + sig_change->center_frq2 = -1; + sig_change->chanwidth = CHAN_WIDTH_UNKNOWN; + + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) { + sig_change->chanwidth = convert2width( + nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_CENTER_FREQ1]) + sig_change->center_frq1 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + sig_change->center_frq2 = + nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + } + + return NL_SKIP; } -static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, - int ifidx) +static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig) { struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) - goto nla_put_failure; + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_INTERFACE); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + return send_and_recv_msgs(drv, msg, get_channel_width, sig); - 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, "nl80211: Failed to remove interface."); + nlmsg_free(msg); + return -ENOBUFS; } -static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, - const char *ifname, enum nl80211_iftype iftype) +static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) { - struct nl_msg *msg, *flags = NULL; - int ifidx, err; - 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, if_nametoindex(drv->ifname)); - NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); - - if (iftype == NL80211_IFTYPE_MONITOR) { - 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; - } + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int res; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret) { - nla_put_failure: - wpa_printf(MSG_ERROR, "nl80211: Failed to create interface %d", - ret); - return ret; - } + os_memset(si, 0, sizeof(*si)); + res = nl80211_get_link_signal(drv, si); + if (res != 0) + return res; - ifidx = if_nametoindex(ifname); - if (ifidx <= 0) - return -1; + res = nl80211_get_channel_width(drv, si); + if (res != 0) + return res; - return ifidx; + return nl80211_get_link_noise(drv, si); } -static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) +static int wpa_driver_nl80211_shared_freq(void *priv) { - struct wpa_driver_nl80211_data *drv = eloop_ctx; - int len; - unsigned char buf[3000]; - struct ieee80211_radiotap_iterator iter; - int ret; - int injected = 0, failed = 0, rxflags = 0; - struct ieee80211_rx_status rx_status; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - perror("recv"); - return; - } - - if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len)) { - wpa_printf(MSG_DEBUG, "nl80211: received invalid radiotap " - "frame"); - return; - } + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct wpa_driver_nl80211_data *driver; + int freq = 0; - os_memset(&rx_status, 0, sizeof(rx_status)); + /* + * If the same PHY is in connected state with some other interface, + * then retrieve the assoc freq. + */ + wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s", + drv->phyname); + + dl_list_for_each(driver, &drv->global->interfaces, + struct wpa_driver_nl80211_data, list) { + if (drv == driver || + os_strcmp(drv->phyname, driver->phyname) != 0 || + !driver->associated) + continue; - while (1) { - ret = ieee80211_radiotap_iterator_next(&iter); - if (ret == -ENOENT) - break; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: received invalid " - "radiotap frame (%d)", 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((*(u16 *) 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 - * rx_status.channel = XXX; - */ - break; - case IEEE80211_RADIOTAP_RATE: - break; - case IEEE80211_RADIOTAP_DB_ANTSIGNAL: - rx_status.ssi = *iter.this_arg; - break; - } + wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s " + MACSTR, + driver->phyname, driver->first_bss->ifname, + MAC2STR(driver->first_bss->addr)); + if (is_ap_interface(driver->nlmode)) + freq = driver->first_bss->freq; + else + freq = nl80211_get_assoc_freq(driver); + wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d", + drv->phyname, freq); } - if (rxflags && injected) - return; + if (!freq) + wpa_printf(MSG_DEBUG, "nl80211: No shared interface for " + "PHY (%s) in associated state", drv->phyname); - if (!injected) { - wpa_supplicant_sta_rx(drv->ctx, buf + iter.max_length, - len - iter.max_length, &rx_status); - } else if (failed) { - /* TX failure callback */ - } else { - /* TX success (ACK) callback */ - } + return freq; } -static int wpa_driver_nl80211_create_monitor_interface( - struct wpa_driver_nl80211_data *drv) +static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, + int encrypt) { - char buf[IFNAMSIZ]; - struct sockaddr_ll ll; - int optval, flags; - socklen_t optlen; - - os_snprintf(buf, IFNAMSIZ, "mon.%s", drv->ifname); - buf[IFNAMSIZ - 1] = '\0'; + struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0, + 0, 0, 0, 0); +} - drv->monitor_ifidx = - nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR); - if (drv->monitor_ifidx < 0) - return -1; +static int nl80211_set_param(void *priv, const char *param) +{ + wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); + if (param == NULL) + return 0; - if (wpa_driver_nl80211_get_ifflags_ifname(drv, buf, &flags) != 0 || - wpa_driver_nl80211_set_ifflags_ifname(drv, buf, flags | IFF_UP) != - 0) { - wpa_printf(MSG_ERROR, "nl80211: Could not set interface '%s' " - "UP", buf); - goto error; - } +#ifdef CONFIG_P2P + if (os_strstr(param, "use_p2p_group_interface=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; - os_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; + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface"); + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; } - if (bind(drv->monitor_sock, (struct sockaddr *) &ll, - sizeof(ll)) < 0) { - perror("monitor socket bind"); - goto error; + if (os_strstr(param, "p2p_device=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->allow_p2p_device = 1; } +#endif /* CONFIG_P2P */ - 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 (os_strstr(param, "use_monitor=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->use_monitor = 1; } - if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, - drv, NULL)) { - wpa_printf(MSG_ERROR, "nl80211: Could not register monitor " - "read socket"); - goto error; + if (os_strstr(param, "force_connect_cmd=1")) { + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME; } return 0; - - error: - nl80211_remove_iface(drv, drv->monitor_ifidx); - return -1; } -#endif /* CONFIG_CLIENT_MLME */ - -/** - * 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) +static void * nl80211_global_init(void) { - int s, ret; - struct sockaddr_nl local; - struct wpa_driver_nl80211_data *drv; + struct nl80211_global *global; + struct netlink_config *cfg; - drv = os_zalloc(sizeof(*drv)); - if (drv == NULL) + global = os_zalloc(sizeof(*global)); + if (global == NULL) return NULL; - drv->ctx = ctx; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - - 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; + global->ioctl_sock = -1; + dl_list_init(&global->interfaces); + global->if_add_ifindex = -1; + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + goto err; + + cfg->ctx = global; + cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink; + cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink; + global->netlink = netlink_init(cfg); + if (global->netlink == NULL) { + os_free(cfg); + goto err; } - 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; - } + if (wpa_driver_nl80211_init_nl_global(global) < 0) + goto err; - if (genl_connect(drv->nl_handle)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " - "netlink"); - goto err3; + global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (global->ioctl_sock < 0) { + wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s", + strerror(errno)); + goto err; } - 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; - } + return global; - 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; - } +err: + nl80211_global_deinit(global); + return NULL; +} - ret = nl_get_multicast_id(drv, "nl80211", "scan"); - if (ret >= 0) - ret = nl_socket_add_membership(drv->nl_handle, ret); - if (ret < 0) { - wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " - "membership for scan events: %d (%s)", - ret, strerror(-ret)); - goto err4; - } - eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle), - wpa_driver_nl80211_event_receive, drv, ctx); - drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (drv->ioctl_sock < 0) { - perror("socket(PF_INET,SOCK_DGRAM)"); - goto err5; +static void nl80211_global_deinit(void *priv) +{ + struct nl80211_global *global = priv; + if (global == NULL) + return; + if (!dl_list_empty(&global->interfaces)) { + wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at " + "nl80211_global_deinit", + dl_list_len(&global->interfaces)); } - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - goto err6; - } + if (global->netlink) + netlink_deinit(global->netlink); - os_memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); - goto err6; - } + nl_destroy_handles(&global->nl); - eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_wext, drv, - ctx); - drv->wext_event_sock = s; + if (global->nl_event) + nl80211_destroy_eloop_handle(&global->nl_event); - wpa_driver_nl80211_finish_drv_init(drv); + nl_cb_put(global->nl_cb); - return drv; + if (global->ioctl_sock >= 0) + close(global->ioctl_sock); -err6: - close(drv->ioctl_sock); -err5: - genl_family_put(drv->nl80211); -err4: - nl_cache_free(drv->nl_cache); -err3: - nl_handle_destroy(drv->nl_handle); -err2: - nl_cb_put(drv->nl_cb); -err1: - os_free(drv); - return NULL; + os_free(global); } -static void -wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) +static const char * nl80211_get_radio_name(void *priv) { - int flags; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + return drv->phyname; +} - drv->ifindex = if_nametoindex(drv->ifname); - if (wpa_driver_nl80211_set_mode(drv, 0) < 0) { - printf("Could not configure driver to use managed mode\n"); - } +static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, + const u8 *pmkid) +{ + struct nl_msg *msg; - if (wpa_driver_nl80211_get_ifflags(drv, &flags) != 0) - printf("Could not get interface '%s' flags\n", drv->ifname); - else if (!(flags & IFF_UP)) { - if (wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP) != 0) { - printf("Could not set interface '%s' UP\n", - drv->ifname); - } - } + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; - /* - * Make sure that the driver does not have any obsolete PMKID entries. - */ - wpa_driver_nl80211_flush_pmkid(drv); + nl80211_cmd(bss->drv, msg, 0, cmd); - wpa_driver_nl80211_get_range(drv); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + if (pmkid) + NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid); + if (bssid) + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT); + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(msg); + 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) +static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) { - struct wpa_driver_nl80211_data *drv = priv; - int flags; - -#ifdef CONFIG_CLIENT_MLME - if (drv->monitor_sock >= 0) { - eloop_unregister_read_sock(drv->monitor_sock); - close(drv->monitor_sock); - } - if (drv->monitor_ifidx > 0) - nl80211_remove_iface(drv, drv->monitor_ifidx); - if (drv->capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) - wpa_driver_nl80211_set_userspace_mlme(drv, 0); -#endif /* CONFIG_CLIENT_MLME */ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid)); + return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid); +} - eloop_cancel_timeout(wpa_driver_nl80211_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. - */ - (void) wpa_driver_nl80211_set_bssid(drv, - (u8 *) "\x00\x00\x00\x00\x00\x00"); +static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, + MAC2STR(bssid)); + return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid); +} - wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP); - eloop_unregister_read_sock(drv->wext_event_sock); +static int nl80211_flush_pmkid(void *priv) +{ + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs"); + return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL); +} - if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) - (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); - close(drv->wext_event_sock); - close(drv->ioctl_sock); - os_free(drv->assoc_req_ies); - os_free(drv->assoc_resp_ies); +static void clean_survey_results(struct survey_results *survey_results) +{ + struct freq_survey *survey, *tmp; - eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle)); - genl_family_put(drv->nl80211); - nl_cache_free(drv->nl_cache); - nl_handle_destroy(drv->nl_handle); - nl_cb_put(drv->nl_cb); + if (dl_list_empty(&survey_results->survey_list)) + return; - os_free(drv); + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, + struct freq_survey, list) { + dl_list_del(&survey->list); + os_free(survey); + } } -/** - * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion - * @eloop_ctx: Unused - * @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) +static void add_survey(struct nlattr **sinfo, u32 ifidx, + struct dl_list *survey_list) { - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); -} + struct freq_survey *survey; + survey = os_zalloc(sizeof(struct freq_survey)); + if (!survey) + return; -/** - * wpa_driver_nl80211_scan - Request the driver to initiate scan - * @priv: Pointer to private wext data from wpa_driver_nl80211_init() - * @ssid: Specific SSID to scan for (ProbeReq) or %NULL to scan for - * all SSIDs (either active scan with broadcast SSID or passive - * scan - * @ssid_len: Length of the SSID - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_scan(void *priv, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_nl80211_data *drv = priv; - int ret = 0, timeout; - struct nl_msg *msg, *ssids; + survey->ifidx = ifidx; + survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + survey->filled = 0; - msg = nlmsg_alloc(); - ssids = nlmsg_alloc(); - if (!msg || !ssids) { - nlmsg_free(msg); - nlmsg_free(ssids); - return -1; + if (sinfo[NL80211_SURVEY_INFO_NOISE]) { + survey->nf = (int8_t) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + survey->filled |= SURVEY_HAS_NF; } - 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); + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) { + survey->channel_time = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]); + survey->filled |= SURVEY_HAS_CHAN_TIME; + } - if (ssid && ssid_len) { - /* Request an active scan for a specific SSID */ - NLA_PUT(ssids, 1, ssid_len, ssid); - } else { - /* Request an active scan for wildcard SSID */ - NLA_PUT(ssids, 1, 0, ""); + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) { + survey->channel_time_busy = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]); + survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY; } - nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); - 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)); - goto nla_put_failure; + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) { + survey->channel_time_rx = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]); + survey->filled |= SURVEY_HAS_CHAN_TIME_RX; } - /* 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 SIOCGIWSCAN events to notify - * when scan is complete, so use longer timeout to avoid race - * conditions with scanning and following association request. - */ - timeout = 30; + if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) { + survey->channel_time_tx = + nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]); + survey->filled |= SURVEY_HAS_CHAN_TIME_TX; } - 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); - return ret; + wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)", + survey->freq, + survey->nf, + (unsigned long int) survey->channel_time, + (unsigned long int) survey->channel_time_busy, + (unsigned long int) survey->channel_time_tx, + (unsigned long int) survey->channel_time_rx, + survey->filled); + + dl_list_add_tail(survey_list, &survey->list); +} + + +static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq, + unsigned int freq_filter) +{ + if (!freq_filter) + return 1; + + return freq_filter == surveyed_freq; } -static int bss_info_handler(struct nl_msg *msg, void *arg) +static int survey_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 }, + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + struct survey_results *survey_results; + u32 surveyed_freq = 0; + u32 ifidx; + + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, }; - struct wpa_scan_results *res = arg; - struct wpa_scan_res **tmp; - struct wpa_scan_res *r; - const u8 *ie; - size_t ie_len; + + survey_results = (struct survey_results *) arg; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); - if (!tb[NL80211_ATTR_BSS]) + + if (!tb[NL80211_ATTR_IFINDEX]) return NL_SKIP; - if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], - bss_policy)) + + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) + return NL_SKIP; + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) { + wpa_printf(MSG_ERROR, "nl80211: Invalid survey data"); 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; } - r = os_zalloc(sizeof(*r) + ie_len); - if (r == NULL) + surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + + if (!check_survey_ok(sinfo, surveyed_freq, + survey_results->freq_filter)) 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]); - if (bss[NL80211_BSS_SIGNAL_UNSPEC]) - r->qual = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); - if (bss[NL80211_BSS_SIGNAL_MBM]) - r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); - if (bss[NL80211_BSS_TSF]) - r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); - r->ie_len = ie_len; - if (ie) - os_memcpy(r + 1, ie, ie_len); - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); - if (tmp == NULL) { - os_free(r); + if (survey_results->freq_filter && + survey_results->freq_filter != surveyed_freq) { + wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz", + surveyed_freq); return NL_SKIP; } - tmp[res->num++] = r; - res->res = tmp; + + add_survey(sinfo, ifidx, &survey_results->survey_list); return NL_SKIP; } -/** - * 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) +static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - struct wpa_scan_results *res; - int ret; + int err = -ENOBUFS; + union wpa_event_data data; + struct survey_results *survey_results; + + os_memset(&data, 0, sizeof(data)); + survey_results = &data.survey_results; + + dl_list_init(&survey_results->survey_list); - res = os_zalloc(sizeof(*res)); - if (res == NULL) - return 0; 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); + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - ret = send_and_recv_msgs(drv, msg, bss_info_handler, res); - msg = NULL; - if (ret == 0) { - wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", - (unsigned long) res->num); - return res; + if (freq) + data.survey_results.freq_filter = freq; + + do { + wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data"); + err = send_and_recv_msgs(drv, msg, survey_handler, + survey_results); + } while (err > 0); + + if (err) { + wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data"); + goto out_clean; } - wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " - "(%s)", ret, strerror(-ret)); + + wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data); + +out_clean: + clean_survey_results(survey_results); nla_put_failure: - nlmsg_free(msg); - wpa_scan_results_free(res); - return NULL; + return err; } -static int wpa_driver_nl80211_get_range(void *priv) +static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, + const u8 *replay_ctr) { - struct wpa_driver_nl80211_data *drv = priv; - struct iw_range *range; - struct iwreq iwr; - int minlen; - size_t buflen; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *replay_nested; + struct nl_msg *msg; - /* - * 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; - drv->capa.auth = WPA_DRIVER_AUTH_OPEN | - WPA_DRIVER_AUTH_SHARED | - WPA_DRIVER_AUTH_LEAP; - wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x", - drv->capa.key_mgmt, drv->capa.enc); - } else { - wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " - "assuming WPA is not supported"); - } + msg = nlmsg_alloc(); + if (!msg) + return; - os_free(range); - return 0; -} + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); -static int wpa_driver_nl80211_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_nl80211_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); + if (!replay_nested) + goto nla_put_failure; + + NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek); + NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck); + NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, + replay_ctr); + + nla_nest_end(msg, replay_nested); - return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_WPA_ENABLED, - enabled); + send_and_recv_msgs(drv, msg, NULL, NULL); + return; + nla_put_failure: + nlmsg_free(msg); } -static int wpa_driver_nl80211_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) +static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr, + const u8 *addr, int qos) { - struct wpa_driver_nl80211_data *drv = priv; - int err; - struct nl_msg *msg; + /* send data frame to poll STA and check whether + * this frame is ACKed */ + struct { + struct ieee80211_hdr hdr; + u16 qos_ctl; + } STRUCT_PACKED nulldata; + size_t size; - wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " - "seq_len=%lu key_len=%lu", - __func__, alg, addr, key_idx, set_tx, - (unsigned long) seq_len, (unsigned long) key_len); + /* Send data frame to poll STA and check whether this frame is ACKed */ - msg = nlmsg_alloc(); - if (msg == NULL) - return -1; + os_memset(&nulldata, 0, sizeof(nulldata)); - if (alg == WPA_ALG_NONE) { - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, - NL80211_CMD_DEL_KEY, 0); + if (qos) { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_QOS_NULL); + size = sizeof(nulldata); } 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, - 0x000FAC01); - else - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - 0x000FAC05); - break; - case WPA_ALG_TKIP: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); - break; - case WPA_ALG_CCMP: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); - break; -#ifdef CONFIG_IEEE80211W - case WPA_ALG_IGTK: - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06); - break; -#endif /* CONFIG_IEEE80211W */ - default: - nlmsg_free(msg); - return -1; - } - } - - 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); + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_NULLFUNC); + size = sizeof(struct ieee80211_hdr); } - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - err = send_and_recv_msgs(drv, msg, NULL, NULL); - if (err) { - wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d", err); - return -1; - } + nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); + os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN); + os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); - if (set_tx && alg != WPA_ALG_NONE) { - msg = nlmsg_alloc(); - if (msg == NULL) - return -1; + if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0, + 0, 0) < 0) + wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to " + "send poll frame"); +} - 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, drv->ifindex); - NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); +static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, + int qos) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; - err = send_and_recv_msgs(drv, msg, NULL, NULL); - if (err) { - wpa_printf(MSG_DEBUG, "nl80211: set default key " - "failed; err=%d", err); - return -1; - } + if (!drv->poll_command_supported) { + nl80211_send_null_frame(bss, own_addr, addr, qos); + return; } - return 0; - -nla_put_failure: - return -ENOBUFS; -} - + msg = nlmsg_alloc(); + if (!msg) + return; -static int wpa_driver_nl80211_set_countermeasures(void *priv, - int enabled) -{ - struct wpa_driver_nl80211_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_nl80211_set_auth_param(drv, - IW_AUTH_TKIP_COUNTERMEASURES, - enabled); -} + nl80211_cmd(drv, msg, 0, NL80211_CMD_PROBE_CLIENT); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); -static int wpa_driver_nl80211_set_drop_unencrypted(void *priv, - int enabled) -{ - struct wpa_driver_nl80211_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - drv->use_crypt = enabled; - return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, - enabled); + send_and_recv_msgs(drv, msg, NULL, NULL); + return; + nla_put_failure: + nlmsg_free(msg); } -static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int cmd, int reason_code) +static int nl80211_set_power_save(struct i802_bss *bss, int enabled) { - struct iwreq iwr; - struct iw_mlme mlme; - int ret = 0; + struct nl_msg *msg; - 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; - } + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; - return ret; + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_SET_POWER_SAVE); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, + enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; } -static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, - int reason_code) +static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps, + int ctwindow) { - struct wpa_driver_nl80211_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); -} + struct i802_bss *bss = priv; + wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d " + "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow); -static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - struct wpa_driver_nl80211_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DISASSOC, - reason_code); + if (opp_ps != -1 || ctwindow != -1) { +#ifdef ANDROID_P2P + wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow); +#else /* ANDROID_P2P */ + return -1; /* Not yet supported */ +#endif /* ANDROID_P2P */ + } + + if (legacy_ps == -1) + return 0; + if (legacy_ps != 0 && legacy_ps != 1) + return -1; /* Not yet supported */ + + return nl80211_set_power_save(bss, legacy_ps); } -static int wpa_driver_nl80211_set_gen_ie(void *priv, const u8 *ie, - size_t ie_len) +static int nl80211_start_radar_detection(void *priv, + struct hostapd_freq_params *freq) { - struct wpa_driver_nl80211_data *drv = priv; - struct iwreq iwr; - int ret = 0; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; - 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_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", + freq->freq, freq->ht_enabled, freq->vht_enabled, + freq->bandwidth, freq->center_freq1, freq->center_freq2); - if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { - perror("ioctl[SIOCSIWGENIE]"); - ret = -1; + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) { + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar " + "detection"); + return -1; } - return ret; -} + msg = nlmsg_alloc(); + if (!msg) + return -1; + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); -static int wpa_driver_nl80211_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; + if (freq->vht_enabled) { + switch (freq->bandwidth) { + case 20: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_20); + break; + case 40: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_40); + break; + case 80: + if (freq->center_freq2) + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_80P80); + else + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_80); + break; + case 160: + NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, + NL80211_CHAN_WIDTH_160); + break; + default: + return -1; + } + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1); + if (freq->center_freq2) + NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, + freq->center_freq2); + } else if (freq->ht_enabled) { + switch (freq->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; + } } -} - -static int wpa_driver_nl80211_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: + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret == 0) return 0; - } + wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: " + "%d (%s)", ret, strerror(-ret)); +nla_put_failure: + return -1; } +#ifdef CONFIG_TDLS -static int -wpa_driver_nl80211_auth_alg_fallback(struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) +static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *buf, size_t len) { - struct iwreq iwr; - int ret = 0; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; - wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " - "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; - 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; + if (!dst) + return -EINVAL; - /* - * 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). - */ + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; - if (!drv->use_crypt) { - iwr.u.encoding.flags |= IW_ENCODE_DISABLED; - } else { - if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) - iwr.u.encoding.flags |= IW_ENCODE_OPEN; - if (params->auth_alg & AUTH_ALG_SHARED_KEY) - iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; - } + nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token); + NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code); + NLA_PUT(msg, NL80211_ATTR_IE, len, buf); - if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { - perror("ioctl[SIOCSIWENCODE]"); - ret = -1; - } + return send_and_recv_msgs(drv, msg, NULL, NULL); - return ret; +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; } -static int wpa_driver_nl80211_associate( - void *priv, struct wpa_driver_associate_params *params) +static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) { - struct wpa_driver_nl80211_data *drv = priv; - int ret = 0; - int allow_unencrypted_eapol; - int value; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + enum nl80211_tdls_operation nl80211_oper; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + + switch (oper) { + case TDLS_DISCOVERY_REQ: + nl80211_oper = NL80211_TDLS_DISCOVERY_REQ; + break; + case TDLS_SETUP: + nl80211_oper = NL80211_TDLS_SETUP; + break; + case TDLS_TEARDOWN: + nl80211_oper = NL80211_TDLS_TEARDOWN; + break; + case TDLS_ENABLE_LINK: + nl80211_oper = NL80211_TDLS_ENABLE_LINK; + break; + case TDLS_DISABLE_LINK: + nl80211_oper = NL80211_TDLS_DISABLE_LINK; + break; + case TDLS_ENABLE: + return 0; + case TDLS_DISABLE: + return 0; + default: + return -EINVAL; + } + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER); + NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); +#endif /* CONFIG TDLS */ - /* - * If the driver did not support SIOCSIWAUTH, fallback to - * SIOCSIWENCODE here. - */ - if (drv->auth_alg_fallback && - wpa_driver_nl80211_auth_alg_fallback(drv, params) < 0) - ret = -1; - if (!params->bssid && - wpa_driver_nl80211_set_bssid(drv, NULL) < 0) - ret = -1; +#ifdef ANDROID - /* 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_nl80211_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_nl80211_set_auth_param(drv, - IW_AUTH_WPA_VERSION, value) < 0) - ret = -1; - value = wpa_driver_nl80211_cipher2wext(params->pairwise_suite); - if (wpa_driver_nl80211_set_auth_param(drv, - IW_AUTH_CIPHER_PAIRWISE, value) < 0) - ret = -1; - value = wpa_driver_nl80211_cipher2wext(params->group_suite); - if (wpa_driver_nl80211_set_auth_param(drv, - IW_AUTH_CIPHER_GROUP, value) < 0) - ret = -1; - value = wpa_driver_nl80211_keymgmt2wext(params->key_mgmt_suite); - if (wpa_driver_nl80211_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_nl80211_set_auth_param(drv, - IW_AUTH_PRIVACY_INVOKED, value) < 0) - ret = -1; +typedef struct android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +} android_wifi_priv_cmd; - /* 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_nl80211_set_auth_param(drv, - IW_AUTH_RX_UNENCRYPTED_EAPOL, - allow_unencrypted_eapol) < 0) - ret = -1; - if (params->freq && wpa_driver_nl80211_set_freq(drv, params->freq) < 0) - ret = -1; - if (wpa_driver_nl80211_set_ssid(drv, params->ssid, params->ssid_len) < 0) - ret = -1; - if (params->bssid && - wpa_driver_nl80211_set_bssid(drv, params->bssid) < 0) - ret = -1; +static int drv_errors = 0; - return ret; +static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) +{ + drv_errors++; + if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv_errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } } -static int wpa_driver_nl80211_set_auth_alg(void *priv, int auth_alg) +static int android_priv_cmd(struct i802_bss *bss, const char *cmd) { - struct wpa_driver_nl80211_data *drv = priv; - int algs = 0, res; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + char buf[MAX_DRV_CMD_SIZE]; + int ret; + + os_memset(&ifr, 0, sizeof(ifr)); + os_memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + + os_memset(buf, 0, sizeof(buf)); + os_strlcpy(buf, cmd, sizeof(buf)); + + priv_cmd.buf = buf; + priv_cmd.used_len = sizeof(buf); + priv_cmd.total_len = sizeof(buf); + ifr.ifr_data = &priv_cmd; - if (auth_alg & AUTH_ALG_OPEN_SYSTEM) - algs |= IW_AUTH_ALG_OPEN_SYSTEM; - if (auth_alg & AUTH_ALG_SHARED_KEY) - algs |= IW_AUTH_ALG_SHARED_KEY; - if (auth_alg & AUTH_ALG_LEAP) - algs |= IW_AUTH_ALG_LEAP; - if (algs == 0) { - /* at least one algorithm should be set */ - algs = IW_AUTH_ALG_OPEN_SYSTEM; + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: failed to issue private commands", + __func__); + wpa_driver_send_hang_msg(drv); + return ret; } - res = wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, - algs); - drv->auth_alg_fallback = res == -2; - return res; + drv_errors = 0; + return 0; } -/** - * wpa_driver_nl80211_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE - * @priv: Pointer to private wext data from wpa_driver_nl80211_init() - * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_set_mode(void *priv, int mode) +static int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params) { - struct wpa_driver_nl80211_data *drv = priv; - int ret = -1, flags; - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return -1; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + int ret = 0, i = 0, bp; + char buf[WEXT_PNO_MAX_COMMAND_SIZE]; + + bp = WEXT_PNOSETUP_HEADER_SIZE; + os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); + buf[bp++] = WEXT_PNO_TLV_PREFIX; + buf[bp++] = WEXT_PNO_TLV_VERSION; + buf[bp++] = WEXT_PNO_TLV_SUBVERSION; + buf[bp++] = WEXT_PNO_TLV_RESERVED; + + while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) { + /* Check that there is enough space needed for 1 more SSID, the + * other sections and null termination */ + if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN + + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf)) + break; + wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + buf[bp++] = WEXT_PNO_SSID_SECTION; + buf[bp++] = params->ssids[i].ssid_len; + os_memcpy(&buf[bp], params->ssids[i].ssid, + params->ssids[i].ssid_len); + bp += params->ssids[i].ssid_len; + i++; + } - 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, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, - mode ? NL80211_IFTYPE_ADHOC : NL80211_IFTYPE_STATION); + buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", + WEXT_PNO_SCAN_INTERVAL); + bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (!ret) - return 0; - else - goto try_again; + buf[bp++] = WEXT_PNO_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_REPEAT); + bp += WEXT_PNO_REPEAT_LENGTH; -nla_put_failure: - wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode: %d (%s)", - ret, strerror(-ret)); - return -1; + buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_MAX_REPEAT); + bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; -try_again: - /* 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 (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) { - (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); + memset(&ifr, 0, sizeof(ifr)); + memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); - /* Try to set the mode again while the interface is down */ - msg = nlmsg_alloc(); - if (!msg) - return -1; + priv_cmd.buf = buf; + priv_cmd.used_len = bp; + priv_cmd.total_len = bp; + ifr.ifr_data = &priv_cmd; - 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, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, - mode ? NL80211_IFTYPE_ADHOC : - NL80211_IFTYPE_STATION); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret) { - wpa_printf(MSG_ERROR, "Failed to set interface %s " - "mode(try_again): %d (%s)", - drv->ifname, ret, strerror(-ret)); - } + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); - /* Ignore return value of get_ifflags to ensure that the device - * is always up like it was before this function was called. - */ - (void) wpa_driver_nl80211_get_ifflags(drv, &flags); - (void) wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP); + if (ret < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", + ret); + wpa_driver_send_hang_msg(drv); + return ret; } - return ret; + drv_errors = 0; + + return android_priv_cmd(bss, "PNOFORCE 1"); } -static int wpa_driver_nl80211_pmksa(struct wpa_driver_nl80211_data *drv, - u32 cmd, const u8 *bssid, const u8 *pmkid) +static int android_pno_stop(struct i802_bss *bss) { - struct iwreq iwr; - struct iw_pmksa pmksa; - int ret = 0; + return android_priv_cmd(bss, "PNOFORCE 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); +#endif /* ANDROID */ - if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { - if (errno != EOPNOTSUPP) - perror("ioctl[SIOCSIWPMKSA]"); - ret = -1; - } - return ret; +static int 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; + return wpa_driver_nl80211_set_key(ifname, bss, alg, addr, key_idx, + set_tx, seq, seq_len, key, key_len); } -static int wpa_driver_nl80211_add_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int driver_nl80211_scan2(void *priv, + struct wpa_driver_scan_params *params) { - struct wpa_driver_nl80211_data *drv = priv; - return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); + struct i802_bss *bss = priv; + return wpa_driver_nl80211_scan(bss, params); } -static int wpa_driver_nl80211_remove_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int driver_nl80211_deauthenticate(void *priv, const u8 *addr, + int reason_code) { - struct wpa_driver_nl80211_data *drv = priv; - return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); + struct i802_bss *bss = priv; + return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code); } -static int wpa_driver_nl80211_flush_pmkid(void *priv) +static int driver_nl80211_authenticate(void *priv, + struct wpa_driver_auth_params *params) { - struct wpa_driver_nl80211_data *drv = priv; - return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); + struct i802_bss *bss = priv; + return wpa_driver_nl80211_authenticate(bss, params); } -static int wpa_driver_nl80211_get_capa(void *priv, - struct wpa_driver_capa *capa) +static void driver_nl80211_deinit(void *priv) { - struct wpa_driver_nl80211_data *drv = priv; - if (!drv->has_capability) - return -1; - os_memcpy(capa, &drv->capa, sizeof(*capa)); - return 0; + struct i802_bss *bss = priv; + wpa_driver_nl80211_deinit(bss); } -static int wpa_driver_nl80211_set_operstate(void *priv, int state) +static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, + const char *ifname) { - struct wpa_driver_nl80211_data *drv = priv; - - wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", - __func__, drv->operstate, state, state ? "UP" : "DORMANT"); - drv->operstate = state; - return wpa_driver_nl80211_send_oper_ifla( - drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); + struct i802_bss *bss = priv; + return wpa_driver_nl80211_if_remove(bss, type, ifname); } -#ifdef CONFIG_CLIENT_MLME -static int wpa_driver_nl80211_open_mlme(struct wpa_driver_nl80211_data *drv) +static int driver_nl80211_send_mlme(void *priv, const u8 *data, + size_t data_len, int noack) { - if (wpa_driver_nl80211_set_userspace_mlme(drv, 1) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to enable userspace " - "MLME"); - return -1; - } - if (wpa_driver_nl80211_create_monitor_interface(drv)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to create monitor " - "interface"); - return -1; - } - return 0; + struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack, + 0, 0, 0, 0); } -#endif /* CONFIG_CLIENT_MLME */ -static int wpa_driver_nl80211_set_param(void *priv, const char *param) +static int driver_nl80211_sta_remove(void *priv, const u8 *addr) { -#ifdef CONFIG_CLIENT_MLME - struct wpa_driver_nl80211_data *drv = priv; - - if (param == NULL) - return 0; - - wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); - - if (os_strstr(param, "use_mlme=1")) { - wpa_printf(MSG_DEBUG, "nl80211: Using user space MLME"); - drv->capa.flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; - - if (wpa_driver_nl80211_open_mlme(drv)) - return -1; - } -#endif /* CONFIG_CLIENT_MLME */ - - return 0; + struct i802_bss *bss = priv; + return wpa_driver_nl80211_sta_remove(bss, addr); } -#ifdef CONFIG_CLIENT_MLME - -struct phy_info_arg { - u16 *num_modes; - struct wpa_hw_modes *modes; -}; +static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr, + const char *ifname, int vlan_id) +{ + struct i802_bss *bss = priv; + return i802_set_sta_vlan(bss, addr, ifname, vlan_id); +} -static int phy_info_handler(struct nl_msg *msg, void *arg) +static int driver_nl80211_read_sta_data(void *priv, + struct hostap_sta_driver_data *data, + const u8 *addr) { - 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 i802_bss *bss = priv; + return i802_read_sta_data(bss, data, addr); +} - 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 }, - }; - 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 }, - }; +static int driver_nl80211_send_action(void *priv, unsigned int freq, + unsigned int wait_time, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, + int no_cck) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src, + bssid, data, data_len, no_cck); +} - struct nlattr *nl_band; - struct nlattr *nl_freq; - struct nlattr *nl_rate; - int rem_band, rem_freq, rem_rate; - struct wpa_hw_modes *mode; - int idx, mode_is_set; - nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); +static int driver_nl80211_probe_req_report(void *priv, int report) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_probe_req_report(bss, report); +} - 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; +static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md, + const u8 *ies, size_t ies_len) +{ + int ret; + struct nl_msg *msg; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + u16 mdid = WPA_GET_LE16(md); - mode_is_set = 0; + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; - mode = &phy_info->modes[*(phy_info->num_modes)]; - os_memset(mode, 0, sizeof(*mode)); - *(phy_info->num_modes) += 1; + wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs"); + nl80211_cmd(drv, msg, 0, NL80211_CMD_UPDATE_FT_IES); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies); + NLA_PUT_U16(msg, NL80211_ATTR_MDID, mdid); - nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), - nla_len(nl_band), NULL); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed " + "err=%d (%s)", ret, strerror(-ret)); + } - 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++; - } + return ret; - mode->channels = os_zalloc(mode->num_channels * - sizeof(struct wpa_channel_data)); - if (!mode->channels) - return NL_SKIP; +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} - 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; +const u8 * wpa_driver_nl80211_get_macaddr(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; - mode->channels[idx].freq = nla_get_u32( - tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); - mode->channels[idx].flag |= WPA_CHAN_W_SCAN | - WPA_CHAN_W_ACTIVE_SCAN | - WPA_CHAN_W_IBSS; - - if (!mode_is_set) { - /* crude heuristic */ - if (mode->channels[idx].freq < 4000) - mode->mode = WPA_MODE_IEEE80211B; - else - mode->mode = WPA_MODE_IEEE80211A; - mode_is_set = 1; - } + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) + return NULL; - /* 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 &= ~WPA_CHAN_W_SCAN; - if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) - mode->channels[idx].flag &= - ~WPA_CHAN_W_ACTIVE_SCAN; - if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) - mode->channels[idx].flag &= ~WPA_CHAN_W_IBSS; - 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++; - } + return bss->addr; +} - mode->rates = os_zalloc(mode->num_rates * - sizeof(struct wpa_rate_data)); - if (!mode->rates) - return NL_SKIP; - idx = 0; +static const char * scan_state_str(enum scan_states scan_state) +{ + switch (scan_state) { + case NO_SCAN: + return "NO_SCAN"; + case SCAN_REQUESTED: + return "SCAN_REQUESTED"; + case SCAN_STARTED: + return "SCAN_STARTED"; + case SCAN_COMPLETED: + return "SCAN_COMPLETED"; + case SCAN_ABORTED: + return "SCAN_ABORTED"; + case SCHED_SCAN_STARTED: + return "SCHED_SCAN_STARTED"; + case SCHED_SCAN_STOPPED: + return "SCHED_SCAN_STOPPED"; + case SCHED_SCAN_RESULTS: + return "SCHED_SCAN_RESULTS"; + } - 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].rate = nla_get_u32( - tb_rate[NL80211_BITRATE_ATTR_RATE]); + return "??"; +} - /* crude heuristic */ - if (mode->mode == WPA_MODE_IEEE80211B && - mode->rates[idx].rate > 200) - mode->mode = WPA_MODE_IEEE80211G; - if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) - mode->rates[idx].flags |= WPA_RATE_PREAMBLE2; +static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, + "ifindex=%d\n" + "ifname=%s\n" + "brname=%s\n" + "addr=" MACSTR "\n" + "freq=%d\n" + "%s%s%s%s%s", + bss->ifindex, + bss->ifname, + bss->brname, + MAC2STR(bss->addr), + bss->freq, + bss->beacon_set ? "beacon_set=1\n" : "", + bss->added_if_into_bridge ? + "added_if_into_bridge=1\n" : "", + bss->added_bridge ? "added_bridge=1\n" : "", + bss->in_deinit ? "in_deinit=1\n" : "", + bss->if_dynamic ? "if_dynamic=1\n" : ""); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + if (bss->wdev_id_set) { + res = os_snprintf(pos, end - pos, "wdev_id=%llu\n", + (unsigned long long) bss->wdev_id); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } - idx++; - } + res = os_snprintf(pos, end - pos, + "phyname=%s\n" + "drv_ifindex=%d\n" + "operstate=%d\n" + "scan_state=%s\n" + "auth_bssid=" MACSTR "\n" + "auth_attempt_bssid=" MACSTR "\n" + "bssid=" MACSTR "\n" + "prev_bssid=" MACSTR "\n" + "associated=%d\n" + "assoc_freq=%u\n" + "monitor_sock=%d\n" + "monitor_ifidx=%d\n" + "monitor_refcount=%d\n" + "last_mgmt_freq=%u\n" + "eapol_tx_sock=%d\n" + "%s%s%s%s%s%s%s%s%s%s%s%s%s", + drv->phyname, + drv->ifindex, + drv->operstate, + scan_state_str(drv->scan_state), + MAC2STR(drv->auth_bssid), + MAC2STR(drv->auth_attempt_bssid), + MAC2STR(drv->bssid), + MAC2STR(drv->prev_bssid), + drv->associated, + drv->assoc_freq, + drv->monitor_sock, + drv->monitor_ifidx, + drv->monitor_refcount, + drv->last_mgmt_freq, + drv->eapol_tx_sock, + drv->ignore_if_down_event ? + "ignore_if_down_event=1\n" : "", + drv->scan_complete_events ? + "scan_complete_events=1\n" : "", + drv->disabled_11b_rates ? + "disabled_11b_rates=1\n" : "", + drv->pending_remain_on_chan ? + "pending_remain_on_chan=1\n" : "", + drv->in_interface_list ? "in_interface_list=1\n" : "", + drv->device_ap_sme ? "device_ap_sme=1\n" : "", + drv->poll_command_supported ? + "poll_command_supported=1\n" : "", + drv->data_tx_status ? "data_tx_status=1\n" : "", + drv->scan_for_auth ? "scan_for_auth=1\n" : "", + drv->retry_auth ? "retry_auth=1\n" : "", + drv->use_monitor ? "use_monitor=1\n" : "", + drv->ignore_next_local_disconnect ? + "ignore_next_local_disconnect=1\n" : "", + drv->allow_p2p_device ? "allow_p2p_device=1\n" : ""); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + if (drv->has_capability) { + res = os_snprintf(pos, end - pos, + "capa.key_mgmt=0x%x\n" + "capa.enc=0x%x\n" + "capa.auth=0x%x\n" + "capa.flags=0x%x\n" + "capa.max_scan_ssids=%d\n" + "capa.max_sched_scan_ssids=%d\n" + "capa.sched_scan_supported=%d\n" + "capa.max_match_sets=%d\n" + "capa.max_remain_on_chan=%u\n" + "capa.max_stations=%u\n" + "capa.probe_resp_offloads=0x%x\n" + "capa.max_acl_mac_addrs=%u\n" + "capa.num_multichan_concurrent=%u\n", + drv->capa.key_mgmt, + drv->capa.enc, + drv->capa.auth, + drv->capa.flags, + drv->capa.max_scan_ssids, + drv->capa.max_sched_scan_ssids, + drv->capa.sched_scan_supported, + drv->capa.max_match_sets, + drv->capa.max_remain_on_chan, + drv->capa.max_stations, + drv->capa.probe_resp_offloads, + drv->capa.max_acl_mac_addrs, + drv->capa.num_multichan_concurrent); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; } - return NL_SKIP; + return pos - buf; } -static struct wpa_hw_modes * -wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings) { - struct wpa_driver_nl80211_data *drv = priv; - struct nl_msg *msg; - struct phy_info_arg result = { - .num_modes = num_modes, - .modes = NULL, - }; + if (settings->head) + NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, + settings->head_len, settings->head); - *num_modes = 0; - *flags = 0; + if (settings->tail) + NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, + settings->tail_len, settings->tail); - msg = nlmsg_alloc(); - if (!msg) - return NULL; + if (settings->beacon_ies) + NLA_PUT(msg, NL80211_ATTR_IE, + settings->beacon_ies_len, settings->beacon_ies); - genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, - 0, NL80211_CMD_GET_WIPHY, 0); + if (settings->proberesp_ies) + NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, + settings->proberesp_ies_len, settings->proberesp_ies); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + if (settings->assocresp_ies) + NLA_PUT(msg, + NL80211_ATTR_IE_ASSOC_RESP, + settings->assocresp_ies_len, settings->assocresp_ies); - if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) - return result.modes; -nla_put_failure: - return NULL; -} + if (settings->probe_resp) + NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, + settings->probe_resp_len, settings->probe_resp); + return 0; -static int wpa_driver_nl80211_set_channel(void *priv, wpa_hw_mode phymode, - int chan, int freq) -{ - return wpa_driver_nl80211_set_freq(priv, freq); +nla_put_failure: + return -ENOBUFS; } -static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, - size_t data_len) +static int nl80211_switch_channel(void *priv, struct csa_settings *settings) { - struct wpa_driver_nl80211_data *drv = priv; - __u8 rtap_hdr[] = { - 0x00, 0x00, /* radiotap version */ - 0x0e, 0x00, /* radiotap length */ - 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ - 0x0c, /* F_WEP | F_FRAG (encrypt/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 = data_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, - }; + struct nl_msg *msg; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *beacon_csa; + int ret = -ENOBUFS; - if (sendmsg(drv->monitor_sock, &msg, 0) < 0) { - perror("send[MLME]"); - return -1; + wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)", + settings->cs_count, settings->block_tx, + settings->freq_params.freq, settings->freq_params.bandwidth, + settings->freq_params.center_freq1, + settings->freq_params.center_freq2); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_AP_CSA)) { + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command"); + return -EOPNOTSUPP; } - return 0; -} + if ((drv->nlmode != NL80211_IFTYPE_AP) && + (drv->nlmode != NL80211_IFTYPE_P2P_GO)) + return -EOPNOTSUPP; + /* check settings validity */ + if (!settings->beacon_csa.tail || + ((settings->beacon_csa.tail_len <= + settings->counter_offset_beacon) || + (settings->beacon_csa.tail[settings->counter_offset_beacon] != + settings->cs_count))) + return -EINVAL; -static int wpa_driver_nl80211_mlme_add_sta(void *priv, const u8 *addr, - const u8 *supp_rates, - size_t supp_rates_len) -{ - struct wpa_driver_nl80211_data *drv = priv; - struct nl_msg *msg; - int ret = -1; + if (settings->beacon_csa.probe_resp && + ((settings->beacon_csa.probe_resp_len <= + settings->counter_offset_presp) || + (settings->beacon_csa.probe_resp[settings->counter_offset_presp] != + settings->cs_count))) + return -EINVAL; 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); - + nl80211_cmd(drv, msg, 0, NL80211_CMD_CHANNEL_SWITCH); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - /* TODO: Get proper Association ID and listen interval */ - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); - NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, supp_rates_len, - supp_rates); - NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, 1); + NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, settings->cs_count); + ret = nl80211_put_freq_params(msg, &settings->freq_params); + if (ret) + goto error; + + if (settings->block_tx) + NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX); + + /* beacon_after params */ + ret = set_beacon_data(msg, &settings->beacon_after); + if (ret) + goto error; + + /* beacon_csa params */ + beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES); + if (!beacon_csa) + goto nla_put_failure; + + ret = set_beacon_data(msg, &settings->beacon_csa); + if (ret) + goto error; + NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_BEACON, + settings->counter_offset_beacon); + + if (settings->beacon_csa.probe_resp) + NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_PRESP, + settings->counter_offset_presp); + + nla_nest_end(msg, beacon_csa); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - /* ignore EEXIST, this happens if a STA associates while associated */ - if (ret == -EEXIST || ret >= 0) - ret = 0; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)", + ret, strerror(-ret)); + } + return ret; nla_put_failure: + ret = -ENOBUFS; +error: + nlmsg_free(msg); + wpa_printf(MSG_DEBUG, "nl80211: Could not build channel switch request"); return ret; } -static int wpa_driver_nl80211_mlme_remove_sta(void *priv, const u8 *addr) +static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set, + u8 qos_map_set_len) { - struct wpa_driver_nl80211_data *drv = priv; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret = -1; + 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); + wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map", + qos_map_set, qos_map_set_len); + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_QOS_MAP); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - - ret = 0; + NLA_PUT(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set); ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed"); + return ret; nla_put_failure: + nlmsg_free(msg); return -ENOBUFS; } -#endif /* CONFIG_CLIENT_MLME */ - 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_wpa = wpa_driver_nl80211_set_wpa, - .set_key = wpa_driver_nl80211_set_key, - .set_countermeasures = wpa_driver_nl80211_set_countermeasures, - .set_drop_unencrypted = wpa_driver_nl80211_set_drop_unencrypted, - .scan = wpa_driver_nl80211_scan, + .set_key = driver_nl80211_set_key, + .scan2 = driver_nl80211_scan2, + .sched_scan = wpa_driver_nl80211_sched_scan, + .stop_sched_scan = wpa_driver_nl80211_stop_sched_scan, .get_scan_results2 = wpa_driver_nl80211_get_scan_results, - .deauthenticate = wpa_driver_nl80211_deauthenticate, - .disassociate = wpa_driver_nl80211_disassociate, - .set_mode = wpa_driver_nl80211_set_mode, + .deauthenticate = driver_nl80211_deauthenticate, + .authenticate = driver_nl80211_authenticate, .associate = wpa_driver_nl80211_associate, - .set_auth_alg = wpa_driver_nl80211_set_auth_alg, - .init = wpa_driver_nl80211_init, - .deinit = wpa_driver_nl80211_deinit, - .set_param = wpa_driver_nl80211_set_param, - .add_pmkid = wpa_driver_nl80211_add_pmkid, - .remove_pmkid = wpa_driver_nl80211_remove_pmkid, - .flush_pmkid = wpa_driver_nl80211_flush_pmkid, + .global_init = nl80211_global_init, + .global_deinit = nl80211_global_deinit, + .init2 = wpa_driver_nl80211_init, + .deinit = 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_probe_req_ie = wpa_driver_nl80211_set_probe_req_ie, -#ifdef CONFIG_CLIENT_MLME + .get_country = wpa_driver_nl80211_get_country, + .set_ap = wpa_driver_nl80211_set_ap, + .set_acl = wpa_driver_nl80211_set_acl, + .if_add = wpa_driver_nl80211_if_add, + .if_remove = driver_nl80211_if_remove, + .send_mlme = driver_nl80211_send_mlme, .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, - .set_channel = wpa_driver_nl80211_set_channel, - .set_ssid = wpa_driver_nl80211_set_ssid, - .set_bssid = wpa_driver_nl80211_set_bssid, - .send_mlme = wpa_driver_nl80211_send_mlme, - .mlme_add_sta = wpa_driver_nl80211_mlme_add_sta, - .mlme_remove_sta = wpa_driver_nl80211_mlme_remove_sta, -#endif /* CONFIG_CLIENT_MLME */ + .sta_add = wpa_driver_nl80211_sta_add, + .sta_remove = driver_nl80211_sta_remove, + .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol, + .sta_set_flags = wpa_driver_nl80211_sta_set_flags, + .hapd_init = i802_init, + .hapd_deinit = i802_deinit, + .set_wds_sta = i802_set_wds_sta, + .get_seqnum = i802_get_seqnum, + .flush = i802_flush, + .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_tx_queue_params = i802_set_tx_queue_params, + .set_sta_vlan = driver_nl80211_set_sta_vlan, + .sta_deauth = i802_sta_deauth, + .sta_disassoc = i802_sta_disassoc, + .read_sta_data = driver_nl80211_read_sta_data, + .set_freq = i802_set_freq, + .send_action = driver_nl80211_send_action, + .send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait, + .remain_on_channel = wpa_driver_nl80211_remain_on_channel, + .cancel_remain_on_channel = + wpa_driver_nl80211_cancel_remain_on_channel, + .probe_req_report = driver_nl80211_probe_req_report, + .deinit_ap = wpa_driver_nl80211_deinit_ap, + .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli, + .resume = wpa_driver_nl80211_resume, + .send_ft_action = nl80211_send_ft_action, + .signal_monitor = nl80211_signal_monitor, + .signal_poll = nl80211_signal_poll, + .send_frame = nl80211_send_frame, + .shared_freq = wpa_driver_nl80211_shared_freq, + .set_param = nl80211_set_param, + .get_radio_name = nl80211_get_radio_name, + .add_pmkid = nl80211_add_pmkid, + .remove_pmkid = nl80211_remove_pmkid, + .flush_pmkid = nl80211_flush_pmkid, + .set_rekey_info = nl80211_set_rekey_info, + .poll_client = nl80211_poll_client, + .set_p2p_powersave = nl80211_set_p2p_powersave, + .start_dfs_cac = nl80211_start_radar_detection, + .stop_ap = wpa_driver_nl80211_stop_ap, +#ifdef CONFIG_TDLS + .send_tdls_mgmt = nl80211_send_tdls_mgmt, + .tdls_oper = nl80211_tdls_oper, +#endif /* CONFIG_TDLS */ + .update_ft_ies = wpa_driver_nl80211_update_ft_ies, + .get_mac_addr = wpa_driver_nl80211_get_macaddr, + .get_survey = wpa_driver_nl80211_get_survey, + .status = wpa_driver_nl80211_status, + .switch_channel = nl80211_switch_channel, +#ifdef ANDROID_P2P + .set_noa = wpa_driver_set_p2p_noa, + .get_noa = wpa_driver_get_p2p_noa, + .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie, +#endif /* ANDROID_P2P */ +#ifdef ANDROID + .driver_cmd = wpa_driver_nl80211_driver_cmd, +#endif /* ANDROID */ + .set_qos_map = nl80211_set_qos_map, }; diff --git a/contrib/hostapd/src/drivers/driver_none.c b/contrib/hostapd/src/drivers/driver_none.c new file mode 100644 index 0000000000..d75c14b182 --- /dev/null +++ b/contrib/hostapd/src/drivers/driver_none.c @@ -0,0 +1,93 @@ +/* + * Driver interface for RADIUS server or WPS ER only (no driver) + * Copyright (c) 2008, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/drivers/driver_openbsd.c b/contrib/hostapd/src/drivers/driver_openbsd.c new file mode 100644 index 0000000000..e94eda08f6 --- /dev/null +++ b/contrib/hostapd/src/drivers/driver_openbsd.c @@ -0,0 +1,136 @@ +/* + * Driver interaction with OpenBSD net80211 layer + * Copyright (c) 2013, Mark Kettenis + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include +#include +#include +#include + +#include "common.h" +#include "driver.h" + +struct openbsd_driver_data { + char ifname[IFNAMSIZ + 1]; + void *ctx; + + int sock; /* open socket for 802.11 ioctls */ +}; + + +static int +wpa_driver_openbsd_get_ssid(void *priv, u8 *ssid) +{ + struct openbsd_driver_data *drv = priv; + 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; +} + +static int +wpa_driver_openbsd_get_bssid(void *priv, u8 *bssid) +{ + struct openbsd_driver_data *drv = priv; + struct ieee80211_bssid id; + + os_strlcpy(id.i_name, drv->ifname, sizeof(id.i_name)); + if (ioctl(drv->sock, SIOCG80211BSSID, &id) < 0) + return -1; + + os_memcpy(bssid, id.i_bssid, IEEE80211_ADDR_LEN); + return 0; +} + + +static int +wpa_driver_openbsd_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + return 0; +} + + +static int +wpa_driver_openbsd_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 openbsd_driver_data *drv = priv; + struct ieee80211_keyavail keyavail; + + if (alg != WPA_ALG_PMK || key_len > IEEE80211_PMK_LEN) + return -1; + + memset(&keyavail, 0, sizeof(keyavail)); + os_strlcpy(keyavail.i_name, drv->ifname, sizeof(keyavail.i_name)); + if (wpa_driver_openbsd_get_bssid(priv, keyavail.i_macaddr) < 0) + return -1; + memcpy(keyavail.i_key, key, key_len); + + if (ioctl(drv->sock, SIOCS80211KEYAVAIL, &keyavail) < 0) + return -1; + + return 0; +} + +static void * +wpa_driver_openbsd_init(void *ctx, const char *ifname) +{ + struct openbsd_driver_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail; + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + return drv; + +fail: + os_free(drv); + return NULL; +} + + +static void +wpa_driver_openbsd_deinit(void *priv) +{ + struct openbsd_driver_data *drv = priv; + + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_openbsd_ops = { + .name = "openbsd", + .desc = "OpenBSD 802.11 support", + .get_ssid = wpa_driver_openbsd_get_ssid, + .get_bssid = wpa_driver_openbsd_get_bssid, + .get_capa = wpa_driver_openbsd_get_capa, + .set_key = wpa_driver_openbsd_set_key, + .init = wpa_driver_openbsd_init, + .deinit = wpa_driver_openbsd_deinit, +}; diff --git a/contrib/hostapd/src/drivers/driver_prism54.c b/contrib/hostapd/src/drivers/driver_prism54.c deleted file mode 100644 index e64e762edd..0000000000 --- a/contrib/hostapd/src/drivers/driver_prism54.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Linux Prism54.org driver - * Copyright (c) 2003-2005, Jouni Malinen - * Copyright (c) 2004, Luis R. Rodriguez - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by 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 - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "driver_wext.h" -#include "driver_hostap.h" - -struct wpa_driver_prism54_data { - void *wext; /* private data for driver_wext */ - void *ctx; - char ifname[IFNAMSIZ + 1]; - int sock; -}; - -#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12 -#define PRISM54_HOSTAPD SIOCIWFIRSTPRIV+25 -#define PRISM54_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+26 - -static void show_set_key_error(struct prism2_hostapd_param *); - -static int hostapd_ioctl_prism54(struct wpa_driver_prism54_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, PRISM54_HOSTAPD, &iwr) < 0) { - int ret = errno; - if (show_err) - perror("ioctl[PRISM54_HOSTAPD]"); - return ret; - } - - return 0; -} - - -static int wpa_driver_prism54_set_wpa_ie(struct wpa_driver_prism54_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_prism54(drv, param, blen, 1); - - os_free(param); - - return res; -} - - -/* This is called at wpa_supplicant daemon init time */ -static int wpa_driver_prism54_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_prism54_data *drv = priv; - struct prism2_hostapd_param *param; - int res; - size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; - if (blen < sizeof(*param)) - blen = sizeof(*param); - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = PRISM54_SET_WPA; - param->u.generic_elem.len = 0; - res = hostapd_ioctl_prism54(drv, param, blen, 1); - - os_free(param); - - return res; -} - - -static int wpa_driver_prism54_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) -{ - struct wpa_driver_prism54_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"; - return -1; - break; - case WPA_ALG_WEP: - alg_name = "WEP"; - return -1; - break; - case WPA_ALG_TKIP: - alg_name = "TKIP"; - break; - case WPA_ALG_CCMP: - alg_name = "CCMP"; - return -1; - 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_prism54(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_prism54_set_countermeasures(void *priv, - int enabled) -{ - /* FIX */ - printf("wpa_driver_prism54_set_countermeasures - not yet " - "implemented\n"); - return 0; -} - - -static int wpa_driver_prism54_set_drop_unencrypted(void *priv, - int enabled) -{ - struct wpa_driver_prism54_data *drv = priv; - struct prism2_hostapd_param *param; - int res; - size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; - if (blen < sizeof(*param)) - blen = sizeof(*param); - - param = os_zalloc(blen); - if (param == NULL) - return -1; - - param->cmd = PRISM54_DROP_UNENCRYPTED; - param->u.generic_elem.len = 0; - res = hostapd_ioctl_prism54(drv, param, blen, 1); - - os_free(param); - - return res; -} - - -static int wpa_driver_prism54_deauthenticate(void *priv, const u8 *addr, - int reason_code) -{ - /* FIX */ - printf("wpa_driver_prism54_deauthenticate - not yet implemented\n"); - return 0; -} - - -static int wpa_driver_prism54_disassociate(void *priv, const u8 *addr, - int reason_code) -{ - /* FIX */ - printf("wpa_driver_prism54_disassociate - not yet implemented\n"); - return 0; -} - - -static int -wpa_driver_prism54_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_prism54_data *drv = priv; - int ret = 0; - - if (wpa_driver_prism54_set_wpa_ie(drv, params->wpa_ie, - params->wpa_ie_len) < 0) - ret = -1; - if (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; - - 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_prism54_get_bssid(void *priv, u8 *bssid) -{ - struct wpa_driver_prism54_data *drv = priv; - return wpa_driver_wext_get_bssid(drv->wext, bssid); -} - - -static int wpa_driver_prism54_get_ssid(void *priv, u8 *ssid) -{ - struct wpa_driver_prism54_data *drv = priv; - return wpa_driver_wext_get_ssid(drv->wext, ssid); -} - - -static int wpa_driver_prism54_scan(void *priv, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_prism54_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); -} - - -static struct wpa_scan_results * -wpa_driver_prism54_get_scan_results(void *priv) -{ - struct wpa_driver_prism54_data *drv = priv; - return wpa_driver_wext_get_scan_results(drv->wext); -} - - -static int wpa_driver_prism54_set_operstate(void *priv, int state) -{ - struct wpa_driver_prism54_data *drv = priv; - return wpa_driver_wext_set_operstate(drv->wext, state); -} - - -static void * wpa_driver_prism54_init(void *ctx, const char *ifname) -{ - struct wpa_driver_prism54_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; - } - - return drv; -} - - -static void wpa_driver_prism54_deinit(void *priv) -{ - struct wpa_driver_prism54_data *drv = priv; - wpa_driver_wext_deinit(drv->wext); - close(drv->sock); - os_free(drv); -} - - -const struct wpa_driver_ops wpa_driver_prism54_ops = { - .name = "prism54", - .desc = "Prism54.org driver (Intersil Prism GT/Duette/Indigo)", - .get_bssid = wpa_driver_prism54_get_bssid, - .get_ssid = wpa_driver_prism54_get_ssid, - .set_wpa = wpa_driver_prism54_set_wpa, - .set_key = wpa_driver_prism54_set_key, - .set_countermeasures = wpa_driver_prism54_set_countermeasures, - .set_drop_unencrypted = wpa_driver_prism54_set_drop_unencrypted, - .scan = wpa_driver_prism54_scan, - .get_scan_results2 = wpa_driver_prism54_get_scan_results, - .deauthenticate = wpa_driver_prism54_deauthenticate, - .disassociate = wpa_driver_prism54_disassociate, - .associate = wpa_driver_prism54_associate, - .init = wpa_driver_prism54_init, - .deinit = wpa_driver_prism54_deinit, - .set_operstate = wpa_driver_prism54_set_operstate, -}; diff --git a/contrib/hostapd/src/drivers/driver_privsep.c b/contrib/hostapd/src/drivers/driver_privsep.c index fdf299dc84..ed88e71c3a 100644 --- a/contrib/hostapd/src/drivers/driver_privsep.c +++ b/contrib/hostapd/src/drivers/driver_privsep.c @@ -2,14 +2,8 @@ * WPA Supplicant - privilege separated driver interface * Copyright (c) 2007-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,7 +12,7 @@ #include "common.h" #include "driver.h" #include "eloop.h" -#include "privsep_commands.h" +#include "common/privsep_commands.h" struct wpa_driver_privsep_data { @@ -102,18 +96,12 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, } -static int wpa_driver_privsep_set_wpa(void *priv, int enabled) -{ - struct wpa_driver_privsep_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_WPA, &enabled, - sizeof(enabled), NULL, NULL); -} - - -static int wpa_driver_privsep_scan(void *priv, const u8 *ssid, size_t ssid_len) +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); @@ -164,7 +152,7 @@ wpa_driver_privsep_get_scan_results2(void *priv) return NULL; } - results->res = os_zalloc(num * sizeof(struct wpa_scan_res *)); + results->res = os_calloc(num, sizeof(struct wpa_scan_res *)); if (results->res == NULL) { os_free(results); os_free(buf); @@ -196,10 +184,11 @@ wpa_driver_privsep_get_scan_results2(void *priv) } -static int wpa_driver_privsep_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) +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; @@ -315,18 +304,8 @@ static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, } -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, wpa_event_type event, +static void wpa_driver_privsep_event_assoc(void *ctx, + enum wpa_event_type event, u8 *buf, size_t len) { union wpa_event_data data; @@ -438,24 +417,7 @@ static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len) { if (len < ETH_ALEN) return; - - wpa_supplicant_rx_eapol(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN); -} - - -static void wpa_driver_privsep_event_sta_rx(void *ctx, u8 *buf, size_t len) -{ -#ifdef CONFIG_CLIENT_MLME - struct ieee80211_rx_status *rx_status; - - if (len < sizeof(*rx_status)) - return; - rx_status = (struct ieee80211_rx_status *) buf; - buf += sizeof(*rx_status); - len -= sizeof(*rx_status); - - wpa_supplicant_sta_rx(ctx, buf, len, rx_status); -#endif /* CONFIG_CLIENT_MLME */ + drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN); } @@ -535,10 +497,6 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, event_len); break; - case PRIVSEP_EVENT_STA_RX: - wpa_driver_privsep_event_sta_rx(drv->ctx, event_buf, - event_len); - break; } os_free(buf); @@ -682,7 +640,7 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) 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)"); + perror("privsep-set-params priv-sock: bind(PF_UNIX)"); close(drv->priv_socket); drv->priv_socket = -1; unlink(drv->own_socket_path); @@ -707,7 +665,7 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) 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)"); + perror("privsep-set-params cmd-sock: bind(PF_UNIX)"); close(drv->cmd_socket); drv->cmd_socket = -1; unlink(drv->own_cmd_path); @@ -747,15 +705,6 @@ static const u8 * wpa_driver_privsep_get_mac_addr(void *priv) } -static int wpa_driver_privsep_set_mode(void *priv, int mode) -{ - struct wpa_driver_privsep_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s mode=%d", __func__, mode); - return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_MODE, &mode, sizeof(mode), - NULL, NULL); -} - - static int wpa_driver_privsep_set_country(void *priv, const char *alpha2) { struct wpa_driver_privsep_data *drv = priv; @@ -768,52 +717,23 @@ static int wpa_driver_privsep_set_country(void *priv, const char *alpha2) struct wpa_driver_ops wpa_driver_privsep_ops = { "privsep", "wpa_supplicant privilege separated driver", - wpa_driver_privsep_get_bssid, - wpa_driver_privsep_get_ssid, - wpa_driver_privsep_set_wpa, - wpa_driver_privsep_set_key, - wpa_driver_privsep_init, - wpa_driver_privsep_deinit, - wpa_driver_privsep_set_param, - NULL /* set_countermeasures */, - NULL /* set_drop_unencrypted */, - wpa_driver_privsep_scan, - NULL /* get_scan_results */, - wpa_driver_privsep_deauthenticate, - wpa_driver_privsep_disassociate, - wpa_driver_privsep_associate, - NULL /* set_auth_alg */, - NULL /* add_pmkid */, - NULL /* remove_pmkid */, - NULL /* flush_pmkid */, - wpa_driver_privsep_get_capa, - NULL /* poll */, - NULL /* get_ifname */, - wpa_driver_privsep_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_privsep_get_scan_results2, - NULL /* set_probe_req_ie */, - wpa_driver_privsep_set_mode, - wpa_driver_privsep_set_country, - NULL /* global_init */, - NULL /* global_deinit */, - NULL /* init2 */, - NULL /* get_interfaces */ + .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, + .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_supplicant_drivers[] = +struct wpa_driver_ops *wpa_drivers[] = { &wpa_driver_privsep_ops, NULL diff --git a/contrib/hostapd/src/drivers/driver_ps3.c b/contrib/hostapd/src/drivers/driver_ps3.c deleted file mode 100644 index fde3425e28..0000000000 --- a/contrib/hostapd/src/drivers/driver_ps3.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * WPA Supplicant - PS3 Linux wireless extension driver interface - * Copyright 2007, 2008 Sony Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" -#include -#include "wireless_copy.h" -#include "common.h" -#include "wpa_common.h" -#include "driver.h" -#include "eloop.h" -#include "driver_wext.h" -#include "ieee802_11_defs.h" - -static int wpa_driver_ps3_set_wpa_key(struct wpa_driver_wext_data *drv, - struct wpa_driver_associate_params *params) -{ - int ret, i; - struct iwreq iwr; - char *buf, *str; - - if (!params->psk && !params->passphrase) { - wpa_printf(MSG_INFO, "%s:no PSK error", __func__); - return -EINVAL; - } - - os_memset(&iwr, 0, sizeof(iwr)); - if (params->psk) { - /* includes null */ - iwr.u.data.length = PMK_LEN * 2 + 1; - buf = os_malloc(iwr.u.data.length); - if (!buf) - return -ENOMEM; - str = buf; - for (i = 0; i < PMK_LEN; i++) { - str += snprintf(str, iwr.u.data.length - (str - buf), - "%02x", params->psk[i]); - } - } else if (params->passphrase) { - /* including quotations and null */ - iwr.u.data.length = strlen(params->passphrase) + 3; - buf = os_malloc(iwr.u.data.length); - if (!buf) - return -ENOMEM; - buf[0] = '"'; - os_memcpy(buf + 1, params->passphrase, iwr.u.data.length - 3); - buf[iwr.u.data.length - 2] = '"'; - buf[iwr.u.data.length - 1] = '\0'; - } else - return -EINVAL; - iwr.u.data.pointer = (caddr_t) buf; - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - ret = ioctl(drv->ioctl_sock, SIOCIWFIRSTPRIV, &iwr); - os_free(buf); - - return ret; -} - -static int wpa_driver_ps3_set_wep_keys(struct wpa_driver_wext_data *drv, - struct wpa_driver_associate_params *params) -{ - int ret, i; - struct iwreq iwr; - - for (i = 0; i < 4; i++) { - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); - iwr.u.encoding.flags = i + 1; - if (params->wep_key_len[i]) { - iwr.u.encoding.pointer = (caddr_t) params->wep_key[i]; - iwr.u.encoding.length = params->wep_key_len[i]; - } else - iwr.u.encoding.flags = IW_ENCODE_NOKEY | - IW_ENCODE_DISABLED; - - if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { - perror("ioctl[SIOCSIWENCODE]"); - ret = -1; - } - } - return ret; -} - -static int wpa_driver_ps3_associate(void *priv, - struct wpa_driver_associate_params *params) -{ - struct wpa_driver_wext_data *drv = priv; - int ret, value; - - wpa_printf(MSG_DEBUG, "%s: <-", __func__); - - /* clear BSSID */ - if (!params->bssid && - wpa_driver_wext_set_bssid(drv, NULL) < 0) - ret = -1; - - if (wpa_driver_wext_set_mode(drv, params->mode) < 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; - - /* set selected BSSID */ - if (params->bssid && - wpa_driver_wext_set_bssid(drv, params->bssid) < 0) - ret = -1; - - switch (params->group_suite) { - case CIPHER_NONE: - ret = 0; - break; - case CIPHER_WEP40: - case CIPHER_WEP104: - ret = wpa_driver_ps3_set_wep_keys(drv, params); - break; - case CIPHER_TKIP: - case CIPHER_CCMP: - ret = wpa_driver_ps3_set_wpa_key(drv, params); - break; - } - - /* start to associate */ - ret = wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len); - - wpa_printf(MSG_DEBUG, "%s: ->", __func__); - - return ret; -} - -static int wpa_driver_ps3_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - int ret; - wpa_printf(MSG_DEBUG, "%s:<-", __func__); - - ret = wpa_driver_wext_get_capa(priv, capa); - if (ret) { - wpa_printf(MSG_INFO, "%s: base wext returns error %d", - __func__, ret); - return ret; - } - /* PS3 hypervisor does association and 4way handshake by itself */ - capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; - wpa_printf(MSG_DEBUG, "%s:->", __func__); - return 0; -} - -const struct wpa_driver_ops wpa_driver_ps3_ops = { - .name = "ps3", - .desc = "PLAYSTATION3 Linux wireless extension driver", - .get_bssid = wpa_driver_wext_get_bssid, - .get_ssid = wpa_driver_wext_get_ssid, - .scan = wpa_driver_wext_scan, - .get_scan_results2 = wpa_driver_wext_get_scan_results, - .associate = wpa_driver_ps3_associate, /* PS3 */ - .init = wpa_driver_wext_init, - .deinit = wpa_driver_wext_deinit, - .get_capa = wpa_driver_ps3_get_capa, /* PS3 */ -}; diff --git a/contrib/hostapd/src/drivers/driver_ralink.c b/contrib/hostapd/src/drivers/driver_ralink.c deleted file mode 100644 index e9313cb33e..0000000000 --- a/contrib/hostapd/src/drivers/driver_ralink.c +++ /dev/null @@ -1,1505 +0,0 @@ -/* - * WPA Supplicant - driver interaction with Ralink Wireless Client - * Copyright (c) 2003-2006, Jouni Malinen - * Copyright (c) 2007, Snowpin Lee - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by 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 - -#include "wireless_copy.h" -#include "common.h" -#include "driver.h" -#include "l2_packet/l2_packet.h" -#include "eloop.h" -#include "ieee802_11_defs.h" -#include "priv_netlink.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; - int event_sock; - 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; -}; - -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; - - 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; - - data.assoc_info.req_ies = os_malloc(bytes); - if (data.assoc_info.req_ies == NULL) - return; - - data.assoc_info.req_ies_len = bytes; - os_memcpy(data.assoc_info.req_ies, spos, 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; - - - data.assoc_info.resp_ies = os_malloc(bytes); - if (data.assoc_info.resp_ies == NULL) - goto done; - - data.assoc_info.resp_ies_len = bytes; - os_memcpy(data.assoc_info.resp_ies, spos, bytes); - } - - wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); - - /* free allocated memory */ - done: - os_free(data.assoc_info.resp_ies); - os_free(data.assoc_info.req_ies); - } -} - -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_printf(MSG_DEBUG, "Custom wireless event: " - "receive ASSOCIATED_EVENT !!!"); - /* determine whether the dynamic-WEP is used or - * not */ -#if 0 - if (wpa_s && wpa_s->current_ssid && - wpa_s->current_ssid->key_mgmt == - WPA_KEY_MGMT_IEEE8021X_NO_WPA) { - if ((wpa_s->current_ssid->eapol_flags & - (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { - //wpa_printf(MSG_DEBUG, "The current ssid - (%s), eapol_flag = %d.\n", - // wpa_ssid_txt(wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len),wpa_s->current_ssid->eapol_flags); - ieee8021x_required_key = TRUE; - } - - 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); - } - - wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d).\n", ieee8021x_required_key ? "TRUE" : "FALSE", - wpa_s->current_ssid->eapol_flags); - } -#endif - - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); - } 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_REPORT_AP_INFO) { - if (drv->ap_scan != 1) { - typedef struct PACKED { - UCHAR bssid[MAC_ADDR_LEN]; - UCHAR ssid[MAX_LEN_OF_SSID]; - INT ssid_len; - UCHAR wpa_ie[40]; - INT wpa_ie_len; - UCHAR rsn_ie[40]; - INT rsn_ie_len; - INT freq; - USHORT caps; - } *PAPINFO; - - wpa_printf(MSG_DEBUG, "Custom wireless" - " event: receive " - "RT_REPORT_AP_INFO !!!"); - //printf("iwe->u.data.length = %d\n", iwe->u.data.length); - //wpa_hexdump(MSG_DEBUG, "AP_Info: ", buf, iwe->u.data.length); -#if 0 - wpa_s->num_scan_results = 1; - if (wpa_s->scan_results) - os_free(wpa_s->scan_results); - wpa_s->scan_results = os_malloc(sizeof(struct wpa_scan_result) + 1); - if (wpa_s->scan_results) { - PAPINFO pApInfo = (PAPINFO)buf; - os_memcpy(wpa_s->scan_results[0].bssid, pApInfo->bssid, ETH_ALEN); - os_memcpy(wpa_s->scan_results[0].ssid, pApInfo->ssid, pApInfo->ssid_len); - wpa_s->scan_results[0].ssid_len = pApInfo->ssid_len; - if (pApInfo->wpa_ie_len > 0) { - os_memcpy(wpa_s->scan_results[0].wpa_ie, pApInfo->wpa_ie, pApInfo->wpa_ie_len); - wpa_s->scan_results[0].wpa_ie_len = pApInfo->wpa_ie_len; - } else if (pApInfo->rsn_ie_len > 0) { - os_memcpy(wpa_s->scan_results[0].rsn_ie, pApInfo->rsn_ie, pApInfo->rsn_ie_len); - wpa_s->scan_results[0].rsn_ie_len = pApInfo->rsn_ie_len; - } - wpa_s->scan_results[0].caps = pApInfo->caps; - wpa_s->scan_results[0].freq = pApInfo->freq; - } else { - wpa_printf("wpa_s->scan_" - "results fail to " - "os_malloc!!\n"); - } -#endif - } - } 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(struct wpa_driver_ralink_data *drv, - void *ctx, struct nlmsghdr *h, int len) -{ - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr * attr; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (len < (int) sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); - wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg)); - - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - nlmsg_len; - wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen); - if (attrlen < 0) - return; - - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); - 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 void wpa_driver_ralink_event_receive(int sock, void *ctx, - void *sock_ctx) -{ - char buf[8192]; - int left; - struct sockaddr_nl from; - socklen_t fromlen; - struct nlmsghdr *h; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - fromlen = sizeof(from); - left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, - (struct sockaddr *) &from, &fromlen); - - if (left < 0) { - if (errno != EINTR && errno != EAGAIN) - perror("recvfrom(netlink)"); - return; - } - - h = (struct nlmsghdr *) buf; - wpa_hexdump(MSG_DEBUG, "h: ", (u8 *)h, h->nlmsg_len); - - 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, "Malformed netlink message: " - "len=%d left=%d plen=%d", len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - wpa_driver_ralink_event_rtm_newlink(ctx, sock_ctx, h, - plen); - break; - } - - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); - } - - if (left > 0) { - wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " - "message", left); - } - -} - -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 int -ralink_set_iface_flags(void *priv, int dev_up) -{ - struct wpa_driver_ralink_data *drv = priv; - struct ifreq ifr; - - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->ioctl_sock < 0) - return -1; - - os_memset(&ifr, 0, sizeof(ifr)); - os_snprintf(ifr.ifr_name, IFNAMSIZ, "%s", drv->ifname); - - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCGIFFLAGS]"); - return -1; - } - - if (dev_up) - ifr.ifr_flags |= IFF_UP; - else - ifr.ifr_flags &= ~IFF_UP; - - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { - perror("ioctl[SIOCSIFFLAGS]"); - return -1; - } - - return 0; -} - -static void * wpa_driver_ralink_init(void *ctx, const char *ifname) -{ - int s; - struct wpa_driver_ralink_data *drv; - struct ifreq ifr; - struct sockaddr_nl local; - UCHAR enable_wpa_supplicant = 0; - - 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; - - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - os_memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } - - eloop_register_read_sock(s, wpa_driver_ralink_event_receive, drv, ctx); - drv->event_sock = s; - drv->no_of_pmkid = 4; /* Number of PMKID saved supported */ - - ralink_set_iface_flags(drv, 1); /* mark up during setup */ - 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); - ralink_set_iface_flags(drv, 0); - } - - eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); - eloop_unregister_read_sock(drv->event_sock); - close(drv->event_sock); - 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, const u8 *ssid, size_t ssid_len) -{ - 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 (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); */ - - 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 int -wpa_driver_ralink_get_scan_results(void *priv, - struct wpa_scan_result *results, - size_t max_size) -{ - struct wpa_driver_ralink_data *drv = priv; - UCHAR *buf = NULL; - NDIS_802_11_BSSID_LIST_EX *wsr; - NDIS_WLAN_BSSID_EX *wbi; - struct iwreq iwr; - int rv = 0; - size_t ap_num; - u8 *pos, *end; - - if (drv->g_driver_down == 1) - return -1; - wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); - - if (drv->we_version_compiled >= 17) { - buf = os_zalloc(8192); - iwr.u.data.length = 8192; - } else { - buf = os_zalloc(4096); - iwr.u.data.length = 4096; - } - if (buf == NULL) - return -1; - - 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 ((rv = ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr)) < 0) { - wpa_printf(MSG_DEBUG, "ioctl fail: rv = %d", rv); - os_free(buf); - return -1; - } - - os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); - - for (ap_num = 0, wbi = wsr->Bssid; ap_num < wsr->NumberOfItems; - ++ap_num) { - os_memcpy(results[ap_num].bssid, &wbi->MacAddress, ETH_ALEN); - os_memcpy(results[ap_num].ssid, wbi->Ssid.Ssid, - wbi->Ssid.SsidLength); - results[ap_num].ssid_len = wbi->Ssid.SsidLength; - results[ap_num].freq = (wbi->Configuration.DSConfig / 1000); - - /* get ie's */ - wpa_hexdump(MSG_DEBUG, "RALINK: AP IEs", - (u8 *) wbi + sizeof(*wbi) - 1, wbi->IELength); - - pos = (u8 *) wbi + sizeof(*wbi) - 1; - end = (u8 *) wbi + sizeof(*wbi) + wbi->IELength; - - if (wbi->IELength < sizeof(NDIS_802_11_FIXED_IEs)) - break; - - pos += sizeof(NDIS_802_11_FIXED_IEs) - 2; - os_memcpy(&results[ap_num].caps, pos, 2); - pos += 2; - - while (pos + 1 < end && pos + 2 + pos[1] <= end) { - u8 ielen = 2 + pos[1]; - - if (ielen > SSID_MAX_WPA_IE_LEN) { - pos += ielen; - continue; - } - - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && - pos[1] >= 4 && - os_memcmp(pos + 2, "\x00\x50\xf2\x01", 4) == 0) { - os_memcpy(results[ap_num].wpa_ie, pos, ielen); - results[ap_num].wpa_ie_len = ielen; - } else if (pos[0] == WLAN_EID_RSN) { - os_memcpy(results[ap_num].rsn_ie, pos, ielen); - results[ap_num].rsn_ie_len = ielen; - } - pos += ielen; - } - - wbi = (NDIS_WLAN_BSSID_EX *) ((u8 *) wbi + wbi->Length); - } - - os_free(buf); - return ap_num; -} - -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 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(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) -{ - 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__); - - 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) { - 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_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; - - 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->wpa_ie == NULL || params->wpa_ie_len == 0) { - if (params->auth_alg & AUTH_ALG_SHARED_KEY) { - if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) - 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; - else - ieee8021xMode = 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); - } - - if (ralink_set_oid(drv, OID_802_11_WEP_STATUS, - (char *) &encr, sizeof(encr)) < 0) { - wpa_printf(MSG_DEBUG, "RALINK: Failed to set " - "OID_802_11_WEP_STATUS(%d)", - (int) 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, - .scan = wpa_driver_ralink_scan, - .get_scan_results = 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/contrib/hostapd/src/drivers/driver_ralink.h b/contrib/hostapd/src/drivers/driver_ralink.h deleted file mode 100644 index ddf44de232..0000000000 --- a/contrib/hostapd/src/drivers/driver_ralink.h +++ /dev/null @@ -1,382 +0,0 @@ -/* - * WPA Supplicant - driver_ralink exported functions - * Copyright (c) 2003-2005, Jouni Malinen - * Copyright (c) 2007, Snowpin Lee - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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 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_REPORT_AP_INFO 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/contrib/hostapd/src/drivers/driver_roboswitch.c b/contrib/hostapd/src/drivers/driver_roboswitch.c index bc11a48484..0a9078a4ab 100644 --- a/contrib/hostapd/src/drivers/driver_roboswitch.c +++ b/contrib/hostapd/src/drivers/driver_roboswitch.c @@ -2,31 +2,21 @@ * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include -#include #include #include #include +#include #include "common.h" #include "driver.h" #include "l2_packet/l2_packet.h" -#ifndef ETH_P_EAPOL -#define ETH_P_EAPOL 0x888e -#endif - #define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */ /* MII access registers */ @@ -183,10 +173,8 @@ static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr, 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) { - wpa_supplicant_rx_eapol(drv->ctx, src_addr, buf + 14, - len - 14); - } + os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0) + drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14); } @@ -205,6 +193,15 @@ static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid) } +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; @@ -361,7 +358,7 @@ static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) /* 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 and */ + /* find the '.' separating and */ while (sep > drv->ifname && *sep != '.') sep--; if (sep <= drv->ifname) { wpa_printf(MSG_INFO, "%s: No . pair in " @@ -469,6 +466,7 @@ const struct wpa_driver_ops wpa_driver_roboswitch_ops = { .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, diff --git a/contrib/hostapd/src/drivers/driver_test.c b/contrib/hostapd/src/drivers/driver_test.c index 2a41cf260a..7d306553dd 100644 --- a/contrib/hostapd/src/drivers/driver_test.c +++ b/contrib/hostapd/src/drivers/driver_test.c @@ -1,71 +1,1246 @@ /* - * WPA Supplicant - testing driver interface - * Copyright (c) 2004-2008, Jouni Malinen + * Testing driver interface for a simulated network driver + * Copyright (c) 2004-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -/* Make dure we get winsock2.h for Windows build to get sockaddr_storage */ -#include "build_config.h" -#ifdef CONFIG_NATIVE_WINDOWS -#include -#endif /* CONFIG_NATIVE_WINDOWS */ +/* Make sure we get winsock2.h for Windows build to get sockaddr_storage */ +#include "build_config.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "utils/includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS +#include +#include +#include +#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 "wps/wps.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 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, u32 flags) +{ + 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 = ð + 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 = ð + 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, int noack) +{ + 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, 0); +} + + +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'; -#include "includes.h" + wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); -#ifndef CONFIG_NATIVE_WINDOWS -#include -#include -#include -#define DRIVER_TEST_UNIX -#endif /* CONFIG_NATIVE_WINDOWS */ + 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); + } +} -#include "common.h" -#include "driver.h" -#include "l2_packet/l2_packet.h" -#include "eloop.h" -#include "sha1.h" -#include "ieee802_11_defs.h" +static int test_driver_set_generic_elem(void *priv, + const u8 *elem, size_t elem_len) +{ + struct test_driver_bss *bss = priv; -struct wpa_driver_test_global { - int dummy; -}; + os_free(bss->ie); -struct wpa_driver_test_data { - struct wpa_driver_test_global *global; - void *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; - u8 bssid[ETH_ALEN]; - u8 ssid[32]; - size_t ssid_len; -#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; -}; + 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, + const struct wpabuf *assocresp) +{ + 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, + const char *bridge, int use_existing) +{ + 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 || type == WPA_IF_P2P_GO || + type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) + 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 || type == WPA_IF_P2P_GO || + type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) + return test_driver_bss_remove(priv, ifname); + 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); + if (len < 0) + return -1; + wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len); + + if ((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); + drv->global = params->global_priv; + + 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("test-driver-init: 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) @@ -88,13 +1263,6 @@ static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx) } -static int wpa_driver_test_set_wpa(void *priv, int enabled) -{ - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return 0; -} - - static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) { wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); @@ -129,11 +1297,31 @@ static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv, 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) + 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)); @@ -151,10 +1339,41 @@ static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv, #endif /* DRIVER_TEST_UNIX */ -static int wpa_driver_test_scan(void *priv, const u8 *ssid, size_t ssid_len) -{ - struct wpa_driver_test_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); +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; @@ -186,7 +1405,8 @@ static int wpa_driver_test_scan(void *priv, const u8 *ssid, size_t ssid_len) static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; struct wpa_scan_results *res; size_t i; @@ -194,7 +1414,7 @@ static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) if (res == NULL) return NULL; - res->res = os_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *)); + res->res = os_calloc(drv->num_scanres, sizeof(struct wpa_scan_res *)); if (res->res == NULL) { os_free(res); return NULL; @@ -216,22 +1436,37 @@ static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) } -static int wpa_driver_test_set_key(void *priv, wpa_alg alg, const u8 *addr, +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: priv=%p alg=%d key_idx=%d set_tx=%d", - __func__, priv, alg, key_idx, set_tx); - if (addr) { + 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) { + 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; } - if (key) { - wpa_hexdump(MSG_DEBUG, " key", key, key_len); - } + return 0; } @@ -239,12 +1474,14 @@ static int wpa_driver_test_set_key(void *priv, wpa_alg alg, const u8 *addr, static int wpa_driver_test_associate( void *priv, struct wpa_driver_associate_params *params) { - struct wpa_driver_test_data *drv = priv; + 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); + wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); if (params->bssid) { wpa_printf(MSG_DEBUG, " bssid=" MACSTR, MAC2STR(params->bssid)); @@ -264,8 +1501,23 @@ static int wpa_driver_test_associate( } 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) { + 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, @@ -276,8 +1528,20 @@ static int wpa_driver_test_associate( } #endif /* DRIVER_TEST_UNIX */ - if (drv->test_socket >= 0 && - (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) { + 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); @@ -311,10 +1575,22 @@ static int wpa_driver_test_associate( return -1; } - os_memcpy(drv->ssid, params->ssid, params->ssid_len); - drv->ssid_len = params->ssid_len; + 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); } @@ -324,17 +1600,17 @@ static int wpa_driver_test_associate( static int wpa_driver_test_get_bssid(void *priv, u8 *bssid) { - struct wpa_driver_test_data *drv = priv; - os_memcpy(bssid, drv->bssid, ETH_ALEN); + 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 wpa_driver_test_data *drv = priv; - os_memcpy(ssid, drv->ssid, 32); - return drv->ssid_len; + struct test_driver_bss *dbss = priv; + os_memcpy(ssid, dbss->ssid, 32); + return dbss->ssid_len; } @@ -363,26 +1639,33 @@ static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv) static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr, int reason_code) { - struct wpa_driver_test_data *drv = priv; + 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(drv->bssid, 0, ETH_ALEN); + 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) +static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { - struct wpa_driver_test_data *drv = priv; - wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", - __func__, MAC2STR(addr), reason_code); - os_memset(drv->bssid, 0, ETH_ALEN); - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); - return wpa_driver_test_send_disassoc(drv); + 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; } @@ -396,6 +1679,7 @@ static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, 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) { @@ -464,8 +1748,16 @@ static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, pos = pos2 + 1; while (*pos == ' ') pos++; - if (os_strncmp(pos, "PRIVACY", 7) == 0) + 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]); @@ -478,8 +1770,12 @@ static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv, socklen_t fromlen, const char *data) { + struct test_driver_bss *bss; + + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + /* ASSOCRESP BSSID */ - if (hwaddr_aton(data, drv->bssid)) { + if (hwaddr_aton(data, bss->bssid)) { wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in " "assocresp"); } @@ -509,15 +1805,20 @@ static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv, socklen_t fromlen, const u8 *data, size_t data_len) { - const u8 *src = drv->bssid; + 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; - } - wpa_supplicant_rx_eapol(drv->ctx, src, data, data_len); + } else + src = bss->bssid; + + drv_event_eapol_rx(drv->ctx, src, data, data_len); } @@ -526,11 +1827,115 @@ static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, socklen_t fromlen, const u8 *data, size_t data_len) { -#ifdef CONFIG_CLIENT_MLME - struct ieee80211_rx_status rx_status; - os_memset(&rx_status, 0, sizeof(rx_status)); - wpa_supplicant_sta_rx(drv->ctx, data, data_len, &rx_status); -#endif /* CONFIG_CLIENT_MLME */ + int freq = 0, own_freq; + union wpa_event_data event; + const struct ieee80211_mgmt *mgmt; + u16 fc; + 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); + + mgmt = (const struct ieee80211_mgmt *) data; + fc = le_to_host16(mgmt->frame_control); + + if (drv->probe_req_report && data_len >= 24) { + 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.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; + 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); } @@ -544,6 +1949,11 @@ static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx, 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; @@ -576,6 +1986,10 @@ static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx, } 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); @@ -588,32 +2002,34 @@ 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 = os_zalloc(sizeof(*drv)); + 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->ctx = ctx; drv->test_socket = -1; /* Set dummy BSSID and SSID for testing. */ - drv->bssid[0] = 0x02; - drv->bssid[1] = 0x00; - drv->bssid[2] = 0x00; - drv->bssid[3] = 0x00; - drv->bssid[4] = 0x00; - drv->bssid[5] = 0x01; - os_memcpy(drv->ssid, "test", 5); - drv->ssid_len = 4; + 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; + } - /* 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), - "wpa_supplicant test mac addr generation", - NULL, 0, drv->own_addr + 1, ETH_ALEN - 1); eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); - return drv; + return bss; } @@ -635,21 +2051,42 @@ static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv) static void wpa_driver_test_deinit(void *priv) { - struct wpa_driver_test_data *drv = 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) + const char *dir, int ap) { #ifdef DRIVER_TEST_UNIX static unsigned int counter = 0; @@ -662,8 +2099,8 @@ static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, drv->own_socket_path = os_malloc(len); if (drv->own_socket_path == NULL) return -1; - os_snprintf(drv->own_socket_path, len, "%s/STA-" MACSTR, - dir, MAC2STR(drv->own_addr)); + 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) @@ -686,7 +2123,7 @@ static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, 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)"); + perror("test-driver-attach: bind(PF_UNIX)"); close(drv->test_socket); unlink(drv->own_socket_path); os_free(drv->own_socket_path); @@ -750,7 +2187,8 @@ static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv, static int wpa_driver_test_set_param(void *priv, const char *param) { - struct wpa_driver_test_data *drv = priv; + 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); @@ -790,7 +2228,7 @@ static int wpa_driver_test_set_param(void *priv, const char *param) end = os_strchr(drv->test_dir, ' '); if (end) *end = '\0'; - if (wpa_driver_test_attach(drv, drv->test_dir)) + if (wpa_driver_test_attach(drv, drv->test_dir, 0)) return -1; } else { pos = os_strstr(param, "test_udp="); @@ -805,7 +2243,7 @@ static int wpa_driver_test_set_param(void *priv, const char *param) if (wpa_driver_test_attach_udp(drv, dst)) return -1; os_free(dst); - } else if (wpa_driver_test_attach(drv, NULL)) + } else if (wpa_driver_test_attach(drv, NULL, 0)) return -1; } @@ -814,20 +2252,14 @@ static int wpa_driver_test_set_param(void *priv, const char *param) 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 wpa_driver_test_data *drv = 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; } @@ -836,7 +2268,8 @@ static const u8 * wpa_driver_test_get_mac_addr(void *priv) static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, const u8 *data, size_t data_len) { - struct wpa_driver_test_data *drv = priv; + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; char *msg; size_t msg_len; struct l2_ethhdr eth; @@ -861,7 +2294,7 @@ static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, os_memcpy(msg + 6, ð, sizeof(eth)); os_memcpy(msg + 6 + sizeof(eth), data, data_len); - if (os_memcmp(dest, drv->bssid, ETH_ALEN) == 0 || + 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; @@ -908,7 +2341,6 @@ static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) { - struct wpa_driver_test_data *drv = priv; os_memset(capa, 0, sizeof(*capa)); capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | @@ -924,8 +2356,12 @@ static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) 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->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE; + capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; + capa->max_scan_ssids = 2; + capa->max_remain_on_chan = 60000; return 0; } @@ -947,284 +2383,296 @@ static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr, } -#ifdef CONFIG_CLIENT_MLME -static struct wpa_hw_modes * -wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +static void * wpa_driver_test_global_init(void) { - struct wpa_hw_modes *modes; - - *num_modes = 1; - *flags = 0; - modes = os_zalloc(*num_modes * sizeof(struct wpa_hw_modes)); - if (modes == NULL) - return NULL; - modes[0].mode = WPA_MODE_IEEE80211G; - modes[0].num_channels = 1; - modes[0].num_rates = 1; - modes[0].channels = os_zalloc(sizeof(struct wpa_channel_data)); - modes[0].rates = os_zalloc(sizeof(struct wpa_rate_data)); - if (modes[0].channels == NULL || modes[0].rates == NULL) { - wpa_supplicant_sta_free_hw_features(modes, *num_modes); - return NULL; - } - modes[0].channels[0].chan = 1; - modes[0].channels[0].freq = 2412; - modes[0].channels[0].flag = WPA_CHAN_W_SCAN | WPA_CHAN_W_ACTIVE_SCAN; - modes[0].rates[0].rate = 10; - modes[0].rates[0].flags = WPA_RATE_BASIC | WPA_RATE_SUPPORTED | - WPA_RATE_CCK | WPA_RATE_MANDATORY; + struct wpa_driver_test_global *global; - return modes; + global = os_zalloc(sizeof(*global)); + return global; } -static int wpa_driver_test_set_channel(void *priv, wpa_hw_mode phymode, - int chan, int freq) +static void wpa_driver_test_global_deinit(void *priv) { - wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d", - __func__, phymode, chan, freq); - return 0; + struct wpa_driver_test_global *global = priv; + os_free(global); } -static int wpa_driver_test_send_mlme(void *priv, const u8 *data, - size_t data_len) +static struct wpa_interface_info * +wpa_driver_test_get_interfaces(void *global_priv) { - struct wpa_driver_test_data *drv = priv; - struct msghdr msg; - struct iovec io[2]; - struct sockaddr_un addr; - const u8 *dest; - struct dirent *dent; - DIR *dir; + /* struct wpa_driver_test_global *global = priv; */ + struct wpa_interface_info *iface; - wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len); - if (data_len < 10) - return -1; - dest = data + 4; + 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"; + } - io[0].iov_base = "MLME "; - io[0].iov_len = 5; - io[1].iov_base = (u8 *) data; - io[1].iov_len = data_len; + return iface; +} - os_memset(&msg, 0, sizeof(msg)); - msg.msg_iov = io; - msg.msg_iovlen = 2; - if (os_memcmp(dest, drv->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 (os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) - { - 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); +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; - if (sendmsg(drv->test_socket, &msg, 0) < 0) - perror("sendmsg(test_socket)"); - } - closedir(dir); - return 0; - } 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); + *num_modes = 3; + *flags = 0; + modes = os_calloc(*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_calloc(11, sizeof(struct hostapd_channel_data)); + modes[0].rates = os_calloc(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; } - - if (sendmsg(drv->test_socket, &msg, 0) < 0) { - perror("sendmsg(test_socket)"); - return -1; + 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_calloc(11, sizeof(struct hostapd_channel_data)); + modes[1].rates = os_calloc(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_calloc(1, sizeof(struct hostapd_channel_data)); + modes[2].rates = os_calloc(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 0; + 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_mlme_add_sta(void *priv, const u8 *addr, - const u8 *supp_rates, - size_t supp_rates_len) +static int wpa_driver_test_set_freq(void *priv, + struct hostapd_freq_params *freq) { - wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); + 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_mlme_remove_sta(void *priv, const u8 *addr) +static int wpa_driver_test_send_action(void *priv, unsigned int freq, + unsigned int wait, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, + int no_cck) { - wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); - return 0; + 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, 0); + os_free(buf); + return ret; } -static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid, - size_t ssid_len) +static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) { - wpa_printf(MSG_DEBUG, "%s", __func__); - return 0; -} + struct wpa_driver_test_data *drv = eloop_ctx; + union wpa_event_data data; + wpa_printf(MSG_DEBUG, "test: Remain-on-channel timeout"); -static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid) -{ - wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid)); - return 0; + 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; + + drv->remain_on_channel_freq = 0; + + wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); } -#endif /* CONFIG_CLIENT_MLME */ -static int wpa_driver_test_set_probe_req_ie(void *priv, const u8 *ies, - size_t ies_len) +static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq, + unsigned int duration) { - struct wpa_driver_test_data *drv = priv; - - os_free(drv->probe_req_ie); - if (ies) { - drv->probe_req_ie = os_malloc(ies_len); - if (drv->probe_req_ie == NULL) { - drv->probe_req_ie_len = 0; - return -1; - } - os_memcpy(drv->probe_req_ie, ies, ies_len); - drv->probe_req_ie_len = ies_len; - } else { - drv->probe_req_ie = NULL; - drv->probe_req_ie_len = 0; + 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; } - return 0; -} + 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); -static void * wpa_driver_test_global_init(void) -{ - struct wpa_driver_test_global *global; + 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); - global = os_zalloc(sizeof(*global)); - return global; + return 0; } -static void wpa_driver_test_global_deinit(void *priv) +static int wpa_driver_test_cancel_remain_on_channel(void *priv) { - struct wpa_driver_test_global *global = priv; - os_free(global); + 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 struct wpa_interface_info * -wpa_driver_test_get_interfaces(void *global_priv) +static int wpa_driver_test_probe_req_report(void *priv, int report) { - /* 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; + 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", - wpa_driver_test_get_bssid, - wpa_driver_test_get_ssid, - wpa_driver_test_set_wpa, - wpa_driver_test_set_key, - NULL /* init */, - wpa_driver_test_deinit, - wpa_driver_test_set_param, - NULL /* set_countermeasures */, - NULL /* set_drop_unencrypted */, - wpa_driver_test_scan, - NULL /* get_scan_results */, - wpa_driver_test_deauthenticate, - wpa_driver_test_disassociate, - wpa_driver_test_associate, - NULL /* set_auth_alg */, - NULL /* add_pmkid */, - NULL /* remove_pmkid */, - NULL /* flush_pmkid */, - wpa_driver_test_get_capa, - NULL /* poll */, - NULL /* get_ifname */, - wpa_driver_test_get_mac_addr, - wpa_driver_test_send_eapol, - NULL /* set_operstate */, - wpa_driver_test_mlme_setprotection, -#ifdef CONFIG_CLIENT_MLME - wpa_driver_test_get_hw_feature_data, - wpa_driver_test_set_channel, - wpa_driver_test_set_ssid, - wpa_driver_test_set_bssid, - wpa_driver_test_send_mlme, - wpa_driver_test_mlme_add_sta, - wpa_driver_test_mlme_remove_sta, -#else /* CONFIG_CLIENT_MLME */ - 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 */, -#endif /* CONFIG_CLIENT_MLME */ - NULL /* update_ft_ies */, - NULL /* send_ft_action */, - wpa_driver_test_get_scan_results2, - wpa_driver_test_set_probe_req_ie, - NULL /* set_mode */, - NULL /* set_country */, - wpa_driver_test_global_init, - wpa_driver_test_global_deinit, - wpa_driver_test_init2, - wpa_driver_test_get_interfaces + .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, + .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, + .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, + .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/contrib/hostapd/src/drivers/driver_wext.c b/contrib/hostapd/src/drivers/driver_wext.c index e771d37ded..e5734bddc3 100644 --- a/contrib/hostapd/src/drivers/driver_wext.c +++ b/contrib/hostapd/src/drivers/driver_wext.c @@ -1,15 +1,9 @@ /* - * WPA Supplicant - driver interaction with generic Linux Wireless Extensions - * Copyright (c) 2003-2007, Jouni Malinen + * Driver interaction with generic Linux Wireless Extensions + * Copyright (c) 2003-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -20,82 +14,28 @@ #include "includes.h" #include +#include +#include +#include #include -#include "wireless_copy.h" +#include "linux_wext.h" #include "common.h" -#include "driver.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 "rfkill.h" +#include "driver.h" #include "driver_wext.h" -#include "ieee802_11_defs.h" -#include "wpa_common.h" - static int wpa_driver_wext_flush_pmkid(void *priv); static int wpa_driver_wext_get_range(void *priv); -static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); +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_send_oper_ifla(struct wpa_driver_wext_data *drv, - 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 = drv->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 = (struct rtattr *) - ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); - 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, "WEXT: Operstate: linkmode=%d, operstate=%d", - linkmode, operstate); - - ret = send(drv->event_sock, &req, req.hdr.nlmsg_len, 0); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " - "%s (assume operstate is not supported)", - strerror(errno)); - } - - return ret < 0 ? -1 : 0; -} +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, @@ -304,6 +244,7 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { char *spos; int bytes; + u8 *req_ies = NULL, *resp_ies = NULL; spos = custom + 17; @@ -312,12 +253,12 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) return; bytes /= 2; - data.assoc_info.req_ies = os_malloc(bytes); - if (data.assoc_info.req_ies == NULL) - return; - + 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; - hexstr2bin(spos, data.assoc_info.req_ies, bytes); spos += bytes * 2; @@ -332,19 +273,19 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) goto done; bytes /= 2; - data.assoc_info.resp_ies = os_malloc(bytes); - if (data.assoc_info.resp_ies == NULL) + 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; - hexstr2bin(spos, data.assoc_info.resp_ies, bytes); } wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); done: - os_free(data.assoc_info.resp_ies); - os_free(data.assoc_info.req_ies); + 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)) { @@ -460,24 +401,24 @@ static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv) os_memset(&data, 0, sizeof(data)); if (drv->assoc_req_ies) { data.assoc_info.req_ies = drv->assoc_req_ies; - drv->assoc_req_ies = NULL; 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; - drv->assoc_resp_ies = NULL; data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; } wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); - os_free(data.assoc_info.req_ies); - os_free(data.assoc_info.resp_ies); + 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, - void *ctx, char *data, int len) + char *data, int len) { struct iw_event iwe_buf, *iwe = &iwe_buf; char *pos, *end, *custom, *buf; @@ -525,12 +466,13 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, drv->assoc_req_ies = NULL; os_free(drv->assoc_resp_ies); drv->assoc_resp_ies = NULL; - wpa_supplicant_event(ctx, EVENT_DISASSOC, + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); } else { wpa_driver_wext_event_assoc_ies(drv); - wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, + NULL); } break; case IWEVMICHAELMICFAILURE: @@ -540,7 +482,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, return; } wpa_driver_wext_event_wireless_michaelmicfailure( - ctx, custom, iwe->u.data.length); + drv->ctx, custom, iwe->u.data.length); break; case IWEVCUSTOM: if (custom + iwe->u.data.length > end) { @@ -548,19 +490,18 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, "IWEVCUSTOM length"); return; } - buf = os_malloc(iwe->u.data.length + 1); + buf = dup_binstr(custom, iwe->u.data.length); if (buf == NULL) return; - os_memcpy(buf, custom, iwe->u.data.length); - buf[iwe->u.data.length] = '\0'; - wpa_driver_wext_event_wireless_custom(ctx, buf); + 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, ctx); - wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + drv, drv->ctx); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, + NULL); break; case IWEVASSOCREQIE: if (custom + iwe->u.data.length > end) { @@ -597,8 +538,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, - void *ctx, char *buf, size_t len, - int del) + char *buf, size_t len, int del) { union wpa_event_data event; @@ -615,32 +555,42 @@ static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, del ? "removed" : "added"); if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { - if (del) + if (del) { + if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "WEXT: if_removed " + "already set - ignore event"); + return; + } drv->if_removed = 1; - else + } else { + if (if_nametoindex(drv->ifname) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: Interface %s " + "does not exist - ignore " + "RTM_NEWLINK", + drv->ifname); + return; + } + if (!drv->if_removed) { + wpa_printf(MSG_DEBUG, "WEXT: if_removed " + "already cleared - ignore event"); + return; + } drv->if_removed = 0; + } } - wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); } static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv, - struct nlmsghdr *h) + u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; + int attrlen, rta_len; struct rtattr *attr; - ifi = NLMSG_DATA(h); - - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - nlmsg_len; - if (attrlen < 0) - return 0; - - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + attrlen = len; + attr = (struct rtattr *) buf; rta_len = RTA_ALIGN(sizeof(struct rtattr)); while (RTA_OK(attr, attrlen)) { @@ -659,12 +609,12 @@ static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv, static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv, - int ifindex, struct nlmsghdr *h) + 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, h)) { + 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"); @@ -676,20 +626,15 @@ static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv, } -static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv, - void *ctx, struct nlmsghdr *h, - size_t len) +static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len) { - struct ifinfomsg *ifi; - int attrlen, nlmsg_len, rta_len; - struct rtattr * attr; - - if (len < sizeof(*ifi)) - return; - - ifi = NLMSG_DATA(h); + struct wpa_driver_wext_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + char namebuf[IFNAMSIZ]; - if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, h)) { + 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; @@ -702,6 +647,35 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv, (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { + wpa_printf(MSG_DEBUG, "WEXT: Interface down"); + drv->if_disabled = 1; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL); + } + + if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + if (if_indextoname(ifi->ifi_index, namebuf) && + linux_iface_up(drv->ioctl_sock, drv->ifname) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " + "event since interface %s is down", + namebuf); + } else if (if_nametoindex(drv->ifname) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " + "event since interface %s does not exist", + drv->ifname); + } else if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "WEXT: Ignore interface up " + "event since interface %s is marked " + "removed", drv->ifname); + } else { + wpa_printf(MSG_DEBUG, "WEXT: Interface up"); + drv->if_disabled = 0; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, + NULL); + } + } + /* * Some drivers send the association event before the operup event--in * this case, lifting operstate in wpa_driver_wext_set_operstate() @@ -711,24 +685,20 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv, if (drv->operstate == 1 && (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && !(ifi->ifi_flags & IFF_RUNNING)) - wpa_driver_wext_send_oper_ifla(drv, -1, IF_OPER_UP); - - nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); - - attrlen = h->nlmsg_len - nlmsg_len; - if (attrlen < 0) - return; + netlink_send_oper_ifla(drv->netlink, drv->ifindex, + -1, IF_OPER_UP); - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + 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, ctx, ((char *) attr) + rta_len, + drv, ((char *) attr) + rta_len, attr->rta_len - rta_len); } else if (attr->rta_type == IFLA_IFNAME) { - wpa_driver_wext_event_link(drv, ctx, + wpa_driver_wext_event_link(drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 0); } @@ -737,31 +707,20 @@ static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv, } -static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv, - void *ctx, struct nlmsghdr *h, - size_t len) +static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len) { - 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; + struct wpa_driver_wext_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; - attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + 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, ctx, + wpa_driver_wext_event_link(drv, ((char *) attr) + rta_len, attr->rta_len - rta_len, 1); } @@ -770,126 +729,59 @@ static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv, } -static void wpa_driver_wext_event_receive(int sock, void *eloop_ctx, - void *sock_ctx) +static void wpa_driver_wext_rfkill_blocked(void *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) - perror("recvfrom(netlink)"); - return; - } - - h = (struct nlmsghdr *) buf; - while (left >= (int) sizeof(*h)) { - int len, plen; - - len = h->nlmsg_len; - plen = len - sizeof(*h); - if (len > left || plen < 0) { - wpa_printf(MSG_DEBUG, "Malformed netlink message: " - "len=%d left=%d plen=%d", - len, left, plen); - break; - } - - switch (h->nlmsg_type) { - case RTM_NEWLINK: - wpa_driver_wext_event_rtm_newlink(eloop_ctx, sock_ctx, - h, plen); - break; - case RTM_DELLINK: - wpa_driver_wext_event_rtm_dellink(eloop_ctx, sock_ctx, - h, plen); - break; - } - - len = NLMSG_ALIGN(len); - left -= len; - h = (struct nlmsghdr *) ((char *) h + len); - } - - if (left > 0) { - wpa_printf(MSG_DEBUG, "%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; - } + wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ } -static int wpa_driver_wext_get_ifflags_ifname(struct wpa_driver_wext_data *drv, - const char *ifname, int *flags) +static void wpa_driver_wext_rfkill_unblocked(void *ctx) { - struct ifreq ifr; - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { - perror("ioctl[SIOCGIFFLAGS]"); - return -1; + struct wpa_driver_wext_data *drv = ctx; + wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked"); + if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) { + wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP " + "after rfkill unblock"); + return; } - *flags = ifr.ifr_flags & 0xffff; - return 0; + /* rtnetlink ifup handler will report interface as enabled */ } -/** - * wpa_driver_wext_get_ifflags - Get interface flags (SIOCGIFFLAGS) - * @drv: driver_wext private data - * @flags: Pointer to returned flags value - * Returns: 0 on success, -1 on failure - */ -int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags) +static void wext_get_phy_name(struct wpa_driver_wext_data *drv) { - return wpa_driver_wext_get_ifflags_ifname(drv, drv->ifname, flags); -} - + /* Find phy (radio) to which this interface belongs */ + char buf[90], *pos; + int f, rv; -static int wpa_driver_wext_set_ifflags_ifname(struct wpa_driver_wext_data *drv, - const char *ifname, int flags) -{ - struct ifreq ifr; - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_flags = flags & 0xffff; - if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - perror("SIOCSIFFLAGS"); - return -1; + drv->phyname[0] = '\0'; + snprintf(buf, sizeof(buf) - 1, "/sys/class/net/%s/phy80211/name", + drv->ifname); + f = open(buf, O_RDONLY); + if (f < 0) { + wpa_printf(MSG_DEBUG, "Could not open file %s: %s", + buf, strerror(errno)); + return; } - return 0; -} + rv = read(f, drv->phyname, sizeof(drv->phyname) - 1); + close(f); + if (rv < 0) { + wpa_printf(MSG_DEBUG, "Could not read file %s: %s", + buf, strerror(errno)); + return; + } -/** - * wpa_driver_wext_set_ifflags - Set interface flags (SIOCSIFFLAGS) - * @drv: driver_wext private data - * @flags: New value for flags - * Returns: 0 on success, -1 on failure - */ -int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags) -{ - return wpa_driver_wext_set_ifflags_ifname(drv, drv->ifname, flags); + drv->phyname[rv] = '\0'; + pos = os_strchr(drv->phyname, '\n'); + if (pos) + *pos = '\0'; + wpa_printf(MSG_DEBUG, "wext: interface %s phy: %s", + drv->ifname, drv->phyname); } @@ -902,9 +794,11 @@ int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags) */ void * wpa_driver_wext_init(void *ctx, const char *ifname) { - int s; - struct sockaddr_nl local; struct wpa_driver_wext_data *drv; + struct netlink_config *cfg; + struct rfkill_config *rcfg; + char path[128]; + struct stat buf; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) @@ -912,64 +806,85 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) 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; + wext_get_phy_name(drv); + } + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { perror("socket(PF_INET,SOCK_DGRAM)"); - os_free(drv); - return NULL; + 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; + } + + rcfg = os_zalloc(sizeof(*rcfg)); + if (rcfg == NULL) + goto err3; + rcfg->ctx = drv; + os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname)); + rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked; + rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked; + drv->rfkill = rfkill_init(rcfg); + if (drv->rfkill == NULL) { + wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available"); + os_free(rcfg); } - s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (s < 0) { - perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } + drv->mlme_sock = -1; - os_memset(&local, 0, sizeof(local)); - local.nl_family = AF_NETLINK; - local.nl_groups = RTMGRP_LINK; - if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { - perror("bind(netlink)"); - close(s); - close(drv->ioctl_sock); - os_free(drv); - return NULL; - } + if (wpa_driver_wext_finish_drv_init(drv) < 0) + goto err3; - eloop_register_read_sock(s, wpa_driver_wext_event_receive, drv, ctx); - drv->event_sock = s; + wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1); - drv->mlme_sock = -1; + return drv; - wpa_driver_wext_finish_drv_init(drv); +err3: + rfkill_deinit(drv->rfkill); + netlink_deinit(drv->netlink); +err2: + close(drv->ioctl_sock); +err1: + os_free(drv); + return NULL; +} - return drv; + +static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); } -static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) +static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) { - int flags; + int send_rfkill_event = 0; - if (wpa_driver_wext_get_ifflags(drv, &flags) != 0) - printf("Could not get interface '%s' flags\n", drv->ifname); - else if (!(flags & IFF_UP)) { - if (wpa_driver_wext_set_ifflags(drv, flags | IFF_UP) != 0) { - printf("Could not set interface '%s' UP\n", - drv->ifname); + if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) { + if (rfkill_is_blocked(drv->rfkill)) { + wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable " + "interface '%s' due to rfkill", + drv->ifname); + drv->if_disabled = 1; + send_rfkill_event = 1; } else { - /* - * Wait some time to allow driver to initialize before - * starting configuring the driver. This seems to be - * needed at least some drivers that load firmware etc. - * when the interface is set up. - */ - wpa_printf(MSG_DEBUG, "Interface %s set UP - waiting " - "a second for the driver to complete " - "initialization", drv->ifname); - sleep(1); + wpa_printf(MSG_ERROR, "WEXT: Could not set " + "interface '%s' UP", drv->ifname); + return -1; } } @@ -979,7 +894,9 @@ static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) wpa_driver_wext_flush_pmkid(drv); if (wpa_driver_wext_set_mode(drv, 0) < 0) { - printf("Could not configure driver to use managed mode\n"); + wpa_printf(MSG_DEBUG, "Could not configure driver to use " + "managed mode"); + /* Try to use it anyway */ } wpa_driver_wext_get_range(drv); @@ -1008,7 +925,15 @@ static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) wpa_driver_wext_alternative_ifindex(drv, ifname2); } - wpa_driver_wext_send_oper_ifla(drv, 1, IF_OPER_DORMANT); + netlink_send_oper_ifla(drv->netlink, drv->ifindex, + 1, IF_OPER_DORMANT); + + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill, + drv, drv->ctx); + } + + return 0; } @@ -1022,7 +947,8 @@ static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) void wpa_driver_wext_deinit(void *priv) { struct wpa_driver_wext_data *drv = priv; - int flags; + + wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0); eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); @@ -1032,16 +958,15 @@ void wpa_driver_wext_deinit(void *priv) */ wpa_driver_wext_disconnect(drv); - wpa_driver_wext_send_oper_ifla(priv, 0, IF_OPER_UP); + netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); + netlink_deinit(drv->netlink); + rfkill_deinit(drv->rfkill); - eloop_unregister_read_sock(drv->event_sock); if (drv->mlme_sock >= 0) eloop_unregister_read_sock(drv->mlme_sock); - if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) - (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP); + (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); - close(drv->event_sock); close(drv->ioctl_sock); if (drv->mlme_sock >= 0) close(drv->mlme_sock); @@ -1069,18 +994,17 @@ void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx) /** * wpa_driver_wext_scan - Request the driver to initiate scan * @priv: Pointer to private wext data from wpa_driver_wext_init() - * @ssid: Specific SSID to scan for (ProbeReq) or %NULL to scan for - * all SSIDs (either active scan with broadcast SSID or passive - * scan - * @ssid_len: Length of the SSID + * @param: Scan parameters (specific SSID to scan for (ProbeReq), etc.) * Returns: 0 on success, -1 on failure */ -int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len) +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)", @@ -1109,7 +1033,7 @@ int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len) /* Not all drivers generate "scan completed" wireless event, so try to * read results after a timeout. */ - timeout = 5; + timeout = 10; if (drv->scan_complete_events) { /* * The driver seems to deliver SIOCGIWSCAN events to notify @@ -1255,12 +1179,29 @@ static void wext_get_scan_freq(struct iw_event *iwe, } -static void wext_get_scan_qual(struct iw_event *iwe, +static void wext_get_scan_qual(struct wpa_driver_wext_data *drv, + 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; + if ((iwe->u.qual.updated & IW_QUAL_DBM) || + ((iwe->u.qual.level != 0) && + (iwe->u.qual.level > drv->max_level))) { + if (iwe->u.qual.level >= 64) + res->res.level -= 0x100; + if (iwe->u.qual.noise >= 64) + res->res.noise -= 0x100; + } } @@ -1349,8 +1290,9 @@ static void wext_get_scan_custom(struct iw_event *iwe, tmp = os_realloc(res->ie, res->ie_len + bytes); if (tmp == NULL) return; - hexstr2bin(spos, tmp + res->ie_len, bytes); 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; @@ -1363,8 +1305,9 @@ static void wext_get_scan_custom(struct iw_event *iwe, tmp = os_realloc(res->ie, res->ie_len + bytes); if (tmp == NULL) return; - hexstr2bin(spos, tmp + res->ie_len, bytes); 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; @@ -1377,7 +1320,10 @@ static void wext_get_scan_custom(struct iw_event *iwe, return; } bytes /= 2; - hexstr2bin(spos, bin, bytes); + if (hexstr2bin(spos, bin, bytes) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid TSF value"); + return; + } res->res.tsf += WPA_GET_BE64(bin); } } @@ -1448,8 +1394,8 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, if (data->ie) os_memcpy(pos, data->ie, data->ie_len); - tmp = os_realloc(res->res, - (res->num + 1) * sizeof(struct wpa_scan_res *)); + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); if (tmp == NULL) { os_free(r); return; @@ -1457,7 +1403,7 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, tmp[res->num++] = r; res->res = tmp; } - + /** * wpa_driver_wext_get_scan_results - Fetch the latest scan results @@ -1467,7 +1413,7 @@ static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, 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; + size_t len; int first; u8 *res_buf; struct iw_event iwe_buf, *iwe = &iwe_buf; @@ -1479,7 +1425,6 @@ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) if (res_buf == NULL) return NULL; - ap_num = 0; first = 1; res = os_zalloc(sizeof(*res)); @@ -1531,7 +1476,7 @@ struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) wext_get_scan_freq(iwe, &data); break; case IWEVQUAL: - wext_get_scan_qual(iwe, &data); + wext_get_scan_qual(drv, iwe, &data); break; case SIOCGIWENCODE: wext_get_scan_encode(iwe, &data); @@ -1610,6 +1555,7 @@ static int wpa_driver_wext_get_range(void *priv) } drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104; + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP128; 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) @@ -1619,6 +1565,7 @@ static int wpa_driver_wext_get_range(void *priv) 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", @@ -1628,21 +1575,13 @@ static int wpa_driver_wext_get_range(void *priv) "assuming WPA is not supported"); } + drv->max_level = range->max_qual.level; + os_free(range); return 0; } -static int wpa_driver_wext_set_wpa(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_WPA_ENABLED, - enabled); -} - - static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, const u8 *psk) { @@ -1680,7 +1619,7 @@ static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, } -static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg, +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, @@ -1709,8 +1648,7 @@ static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg, 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) + if (addr == NULL || is_broadcast_ether_addr(addr)) ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY; if (set_tx) ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY; @@ -1800,7 +1738,7 @@ static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg, * 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(void *priv, wpa_alg alg, +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) @@ -1929,17 +1867,37 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) } if (iwr.u.mode == IW_MODE_INFRA) { + /* Clear the BSSID selection */ + if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Failed to clear BSSID " + "selection on disconnect"); + } + + if (drv->cfg80211) { + /* + * cfg80211 supports SIOCSIWMLME commands, so there is + * no need for the random SSID hack, but clear the + * SSID. + */ + if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Failed to clear " + "SSID on 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). + * 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). */ - wpa_driver_wext_set_bssid(drv, null_bssid); - for (i = 0; i < 32; i++) ssid[i] = rand() & 0xFF; - wpa_driver_wext_set_ssid(drv, ssid, 32); + if (wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus " + "SSID to disconnect"); + } } } @@ -1956,18 +1914,6 @@ static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr, } -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) { @@ -1992,15 +1938,15 @@ static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, int wpa_driver_wext_cipher2wext(int cipher) { switch (cipher) { - case CIPHER_NONE: + case WPA_CIPHER_NONE: return IW_AUTH_CIPHER_NONE; - case CIPHER_WEP40: + case WPA_CIPHER_WEP40: return IW_AUTH_CIPHER_WEP40; - case CIPHER_TKIP: + case WPA_CIPHER_TKIP: return IW_AUTH_CIPHER_TKIP; - case CIPHER_CCMP: + case WPA_CIPHER_CCMP: return IW_AUTH_CIPHER_CCMP; - case CIPHER_WEP104: + case WPA_CIPHER_WEP104: return IW_AUTH_CIPHER_WEP104; default: return 0; @@ -2011,10 +1957,10 @@ int wpa_driver_wext_cipher2wext(int cipher) int wpa_driver_wext_keymgmt2wext(int keymgmt) { switch (keymgmt) { - case KEY_MGMT_802_1X: - case KEY_MGMT_802_1X_NO_WPA: + case WPA_KEY_MGMT_IEEE8021X: + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: return IW_AUTH_KEY_MGMT_802_1X; - case KEY_MGMT_PSK: + case WPA_KEY_MGMT_PSK: return IW_AUTH_KEY_MGMT_PSK; default: return 0; @@ -2051,9 +1997,9 @@ wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv, if (!drv->use_crypt) { iwr.u.encoding.flags |= IW_ENCODE_DISABLED; } else { - if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + if (params->auth_alg & WPA_AUTH_ALG_OPEN) iwr.u.encoding.flags |= IW_ENCODE_OPEN; - if (params->auth_alg & AUTH_ALG_SHARED_KEY) + if (params->auth_alg & WPA_AUTH_ALG_SHARED) iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; } @@ -2076,6 +2022,22 @@ int wpa_driver_wext_associate(void *priv, 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. @@ -2115,9 +2077,9 @@ int wpa_driver_wext_associate(void *priv, 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 || + value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE || + params->pairwise_suite != WPA_CIPHER_NONE || + params->group_suite != WPA_CIPHER_NONE || params->wpa_ie_len; if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_PRIVACY_INVOKED, value) < 0) @@ -2126,8 +2088,8 @@ int wpa_driver_wext_associate(void *priv, /* 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) + if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK) allow_unencrypted_eapol = 0; else allow_unencrypted_eapol = 1; @@ -2155,11 +2117,15 @@ int wpa_driver_wext_associate(void *priv, #endif /* CONFIG_IEEE80211W */ if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0) ret = -1; - if (wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) + 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; } @@ -2170,11 +2136,11 @@ 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 & AUTH_ALG_OPEN_SYSTEM) + if (auth_alg & WPA_AUTH_ALG_OPEN) algs |= IW_AUTH_ALG_OPEN_SYSTEM; - if (auth_alg & AUTH_ALG_SHARED_KEY) + if (auth_alg & WPA_AUTH_ALG_SHARED) algs |= IW_AUTH_ALG_SHARED_KEY; - if (auth_alg & AUTH_ALG_LEAP) + if (auth_alg & WPA_AUTH_ALG_LEAP) algs |= IW_AUTH_ALG_LEAP; if (algs == 0) { /* at least one algorithm should be set */ @@ -2198,7 +2164,7 @@ int wpa_driver_wext_set_mode(void *priv, int mode) { struct wpa_driver_wext_data *drv = priv; struct iwreq iwr; - int ret = -1, flags; + int ret = -1; unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA; os_memset(&iwr, 0, sizeof(iwr)); @@ -2228,9 +2194,7 @@ int wpa_driver_wext_set_mode(void *priv, int mode) goto done; } - if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) { - (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP); - + 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) @@ -2238,11 +2202,7 @@ int wpa_driver_wext_set_mode(void *priv, int mode) else ret = 0; - /* Ignore return value of get_ifflags to ensure that the device - * is always up like it was before this function was called. - */ - (void) wpa_driver_wext_get_ifflags(drv, &flags); - (void) wpa_driver_wext_set_ifflags(drv, flags | IFF_UP); + (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1); } done: @@ -2338,8 +2298,8 @@ int wpa_driver_wext_set_operstate(void *priv, int state) wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", __func__, drv->operstate, state, state ? "UP" : "DORMANT"); drv->operstate = state; - return wpa_driver_wext_send_oper_ifla( - drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); + return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1, + state ? IF_OPER_UP : IF_OPER_DORMANT); } @@ -2349,22 +2309,24 @@ int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv) } +static const char * wext_get_radio_name(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + return drv->phyname; +} + + 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_wpa = wpa_driver_wext_set_wpa, .set_key = wpa_driver_wext_set_key, .set_countermeasures = wpa_driver_wext_set_countermeasures, - .set_drop_unencrypted = wpa_driver_wext_set_drop_unencrypted, - .scan = wpa_driver_wext_scan, + .scan2 = wpa_driver_wext_scan, .get_scan_results2 = wpa_driver_wext_get_scan_results, .deauthenticate = wpa_driver_wext_deauthenticate, - .disassociate = wpa_driver_wext_disassociate, - .set_mode = wpa_driver_wext_set_mode, .associate = wpa_driver_wext_associate, - .set_auth_alg = wpa_driver_wext_set_auth_alg, .init = wpa_driver_wext_init, .deinit = wpa_driver_wext_deinit, .add_pmkid = wpa_driver_wext_add_pmkid, @@ -2372,4 +2334,5 @@ const struct wpa_driver_ops wpa_driver_wext_ops = { .flush_pmkid = wpa_driver_wext_flush_pmkid, .get_capa = wpa_driver_wext_get_capa, .set_operstate = wpa_driver_wext_set_operstate, + .get_radio_name = wext_get_radio_name, }; diff --git a/contrib/hostapd/src/drivers/driver_wext.h b/contrib/hostapd/src/drivers/driver_wext.h index b89c2cb2fd..b4b5960a77 100644 --- a/contrib/hostapd/src/drivers/driver_wext.h +++ b/contrib/hostapd/src/drivers/driver_wext.h @@ -2,14 +2,8 @@ * WPA Supplicant - driver_wext exported functions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef DRIVER_WEXT_H @@ -19,13 +13,16 @@ struct wpa_driver_wext_data { void *ctx; - int event_sock; + struct netlink_data *netlink; int ioctl_sock; int mlme_sock; char ifname[IFNAMSIZ + 1]; + char phyname[32]; int ifindex; int ifindex2; int if_removed; + int if_disabled; + struct rfkill_data *rfkill; u8 *assoc_req_ies; size_t assoc_req_ies_len; u8 *assoc_resp_ies; @@ -43,21 +40,23 @@ struct wpa_driver_wext_data { char mlmedev[IFNAMSIZ + 1]; int scan_complete_events; + + int cfg80211; /* whether driver is using cfg80211 */ + + u8 max_level; }; -int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags); -int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags); 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(void *priv, wpa_alg alg, +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, const u8 *ssid, size_t ssid_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); diff --git a/contrib/hostapd/src/drivers/driver_wired.c b/contrib/hostapd/src/drivers/driver_wired.c index 098991a1a4..21f5e4248f 100644 --- a/contrib/hostapd/src/drivers/driver_wired.c +++ b/contrib/hostapd/src/drivers/driver_wired.c @@ -1,15 +1,10 @@ /* - * WPA Supplicant - wired Ethernet driver interface - * Copyright (c) 2005-2007, Jouni Malinen + * Wired Ethernet driver interface + * Copyright (c) 2005-2009, Jouni Malinen + * Copyright (c) 2004, Gunter Burchardt * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,27 +12,378 @@ #include #ifdef __linux__ #include +#include +#include #endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #include -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ +#include +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ +#ifdef __sun__ +#include +#endif /* __sun__ */ #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; - char ifname[IFNAMSIZ + 1]; 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, u32 flags) +{ + 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; @@ -53,6 +399,14 @@ static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) } +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; @@ -101,11 +455,43 @@ static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) } +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status) +{ + struct ifmediareq ifmr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifmr, 0, sizeof(ifmr)); + os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { + perror("ioctl[SIOCGIFMEDIA]"); + close(s); + return -1; + } + close(s); + *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID); + + return 0; +} +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ + + static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) { struct ifreq ifr; int s; +#ifdef __sun__ + return -1; +#endif /* __sun__ */ + s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); @@ -118,7 +504,7 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) ifr.ifr_hwaddr.sa_family = AF_UNSPEC; os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); #endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) { struct sockaddr_dl *dlp; dlp = (struct sockaddr_dl *) &ifr.ifr_addr; @@ -128,9 +514,9 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) dlp->sdl_nlen = 0; dlp->sdl_alen = ETH_ALEN; dlp->sdl_slen = 0; - os_memcpy(LLADDR(dlp), addr, ETH_ALEN); + os_memcpy(LLADDR(dlp), addr, ETH_ALEN); } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) { struct sockaddr *sap; @@ -151,34 +537,6 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) } -static int wpa_driver_wired_membership(struct wpa_driver_wired_data *drv, - const u8 *addr, int add) -{ -#ifdef __linux__ - struct packet_mreq mreq; - - if (drv->pf_sock == -1) - return -1; - - os_memset(&mreq, 0, sizeof(mreq)); - mreq.mr_ifindex = if_nametoindex(drv->ifname); - mreq.mr_type = PACKET_MR_MULTICAST; - mreq.mr_alen = ETH_ALEN; - os_memcpy(mreq.mr_address, addr, ETH_ALEN); - - if (setsockopt(drv->pf_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__ */ -} - - static void * wpa_driver_wired_init(void *ctx, const char *ifname) { struct wpa_driver_wired_data *drv; @@ -195,16 +553,18 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname) if (drv->pf_sock < 0) perror("socket(PF_PACKET)"); #else /* __linux__ */ - drv->pf_sock = -1; + 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 (wpa_driver_wired_membership(drv, pae_group_addr, 1) == 0) { + 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; @@ -231,6 +591,16 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname) __func__); drv->iff_allmulti = 1; } +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + { + int status; + wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", + __func__); + while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 && + status == 0) + sleep(1); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ return drv; } @@ -242,7 +612,9 @@ static void wpa_driver_wired_deinit(void *priv) int flags; if (drv->membership && - wpa_driver_wired_membership(drv, pae_group_addr, 0) < 0) { + 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__); } @@ -271,16 +643,20 @@ static void wpa_driver_wired_deinit(void *priv) if (drv->pf_sock != -1) close(drv->pf_sock); - + os_free(drv); } const struct wpa_driver_ops wpa_driver_wired_ops = { .name = "wired", - .desc = "wpa_supplicant wired Ethernet driver", + .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/contrib/hostapd/src/drivers/drivers.c b/contrib/hostapd/src/drivers/drivers.c index d278797d7d..446ab63926 100644 --- a/contrib/hostapd/src/drivers/drivers.c +++ b/contrib/hostapd/src/drivers/drivers.c @@ -1,19 +1,14 @@ /* - * WPA Supplicant / driver interface list + * Driver interface list * Copyright (c) 2004-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" - +#include "utils/includes.h" +#include "utils/common.h" +#include "driver.h" #ifdef CONFIG_DRIVER_WEXT extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ @@ -24,31 +19,15 @@ extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ #ifdef CONFIG_DRIVER_HOSTAP extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_PRISM54 -extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */ -#endif /* CONFIG_DRIVER_PRISM54 */ -#ifdef CONFIG_DRIVER_HERMES -extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */ -#endif /* CONFIG_DRIVER_HERMES */ #ifdef CONFIG_DRIVER_MADWIFI extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ #endif /* CONFIG_DRIVER_MADWIFI */ -#ifdef CONFIG_DRIVER_ATMEL -extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */ -#endif /* CONFIG_DRIVER_ATMEL */ -#ifdef CONFIG_DRIVER_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_OPENBSD +extern struct wpa_driver_ops wpa_driver_openbsd_ops; /* driver_openbsd.c */ +#endif /* CONFIG_DRIVER_OPENBSD */ #ifdef CONFIG_DRIVER_NDIS extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */ #endif /* CONFIG_DRIVER_NDIS */ @@ -58,59 +37,38 @@ extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ #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_PS3 -extern struct wpa_driver_ops wpa_driver_ps3_ops; /* driver_ps3.c */ -#endif /* CONFIG_DRIVER_PS3 */ -#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_supplicant_drivers[] = +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_WEXT + &wpa_driver_wext_ops, +#endif /* CONFIG_DRIVER_WEXT */ #ifdef CONFIG_DRIVER_HOSTAP &wpa_driver_hostap_ops, #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_PRISM54 - &wpa_driver_prism54_ops, -#endif /* CONFIG_DRIVER_PRISM54 */ -#ifdef CONFIG_DRIVER_HERMES - &wpa_driver_hermes_ops, -#endif /* CONFIG_DRIVER_HERMES */ #ifdef CONFIG_DRIVER_MADWIFI &wpa_driver_madwifi_ops, #endif /* CONFIG_DRIVER_MADWIFI */ -#ifdef CONFIG_DRIVER_ATMEL - &wpa_driver_atmel_ops, -#endif /* CONFIG_DRIVER_ATMEL */ -#ifdef CONFIG_DRIVER_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_OPENBSD + &wpa_driver_openbsd_ops, +#endif /* CONFIG_DRIVER_OPENBSD */ #ifdef CONFIG_DRIVER_NDIS &wpa_driver_ndis_ops, #endif /* CONFIG_DRIVER_NDIS */ @@ -120,20 +78,14 @@ struct wpa_driver_ops *wpa_supplicant_drivers[] = #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_PS3 - &wpa_driver_ps3_ops, -#endif /* CONFIG_DRIVER_PS3 */ -#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/contrib/hostapd/src/drivers/linux_ioctl.c b/contrib/hostapd/src/drivers/linux_ioctl.c new file mode 100644 index 0000000000..837971d25c --- /dev/null +++ b/contrib/hostapd/src/drivers/linux_ioctl.c @@ -0,0 +1,221 @@ +/* + * Linux ioctl helper functions for driver wrappers + * Copyright (c) 2002-2010, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include +#include + +#include "utils/common.h" +#include "linux_ioctl.h" + + +int linux_set_iface_flags(int sock, const char *ifname, int dev_up) +{ + struct ifreq ifr; + int ret; + + if (sock < 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { + ret = errno ? -errno : -999; + wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s", + ifname, strerror(errno)); + return ret; + } + + 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) { + ret = errno ? -errno : -999; + wpa_printf(MSG_ERROR, "Could not set interface %s flags (%s): " + "%s", + ifname, dev_up ? "UP" : "DOWN", strerror(errno)); + return ret; + } + + return 0; +} + + +int linux_iface_up(int sock, const char *ifname) +{ + struct ifreq ifr; + int ret; + + if (sock < 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { + ret = errno ? -errno : -999; + wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s", + ifname, strerror(errno)); + return ret; + } + + return !!(ifr.ifr_flags & IFF_UP); +} + + +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; + ssize_t res; + + os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge", + ifname); + res = readlink(path, brlink, sizeof(brlink)); + if (res < 0 || (size_t) res >= sizeof(brlink)) + return -1; + brlink[res] = '\0'; + pos = os_strrchr(brlink, '/'); + if (pos == NULL) + return -1; + pos++; + os_strlcpy(brname, pos, IFNAMSIZ); + return 0; +} diff --git a/contrib/hostapd/src/drivers/linux_ioctl.h b/contrib/hostapd/src/drivers/linux_ioctl.h new file mode 100644 index 0000000000..c03fe6e9a3 --- /dev/null +++ b/contrib/hostapd/src/drivers/linux_ioctl.h @@ -0,0 +1,22 @@ +/* + * Linux ioctl helper functions for driver wrappers + * Copyright (c) 2002-2010, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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_iface_up(int sock, const char *ifname); +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/contrib/hostapd/src/drivers/linux_wext.h b/contrib/hostapd/src/drivers/linux_wext.h new file mode 100644 index 0000000000..55cf955318 --- /dev/null +++ b/contrib/hostapd/src/drivers/linux_wext.h @@ -0,0 +1,45 @@ +/* + * Driver interaction with generic Linux Wireless Extensions + * Copyright (c) 2003-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef LINUX_WEXT_H +#define LINUX_WEXT_H + +#ifndef ANDROID + +/* + * Avoid including other kernel header to avoid conflicts with C library + * headers. + */ +#define _LINUX_TYPES_H +#define _LINUX_SOCKET_H +#define _LINUX_IF_H + +#include +#include +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 /* ANDROID */ + +#include + +#ifndef IW_ENCODE_ALG_PMK +#define IW_ENCODE_ALG_PMK 4 +#endif + +#ifndef IW_ENC_CAPA_4WAY_HANDSHAKE +#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 +#endif + +#endif /* LINUX_WEXT_H */ diff --git a/contrib/hostapd/src/drivers/ndis_events.c b/contrib/hostapd/src/drivers/ndis_events.c index f6eaa7c9f7..93673a3632 100644 --- a/contrib/hostapd/src/drivers/ndis_events.c +++ b/contrib/hostapd/src/drivers/ndis_events.c @@ -2,14 +2,8 @@ * ndis_events - Receive NdisMIndicateStatus() events using WMI * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #define _WIN32_WINNT 0x0400 diff --git a/contrib/hostapd/src/drivers/netlink.c b/contrib/hostapd/src/drivers/netlink.c new file mode 100644 index 0000000000..2fa20b1ebb --- /dev/null +++ b/contrib/hostapd/src/drivers/netlink.c @@ -0,0 +1,228 @@ +/* + * Netlink helper functions for driver wrappers + * Copyright (c) 2002-2014, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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), + (u8 *) 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->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); + + netlink->cfg = cfg; + + 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); +} + + +static const char * linkmode_str(int mode) +{ + switch (mode) { + case -1: + return "no change"; + case 0: + return "kernel-control"; + case 1: + return "userspace-control"; + } + return "?"; +} + + +static const char * operstate_str(int state) +{ + switch (state) { + case -1: + return "no change"; + case IF_OPER_DORMANT: + return "IF_OPER_DORMANT"; + case IF_OPER_UP: + return "IF_OPER_UP"; + } + return "?"; +} + + +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: ifindex=%d linkmode=%d (%s), operstate=%d (%s)", + ifindex, linkmode, linkmode_str(linkmode), + operstate, operstate_str(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/contrib/hostapd/src/drivers/netlink.h b/contrib/hostapd/src/drivers/netlink.h new file mode 100644 index 0000000000..3a7340e515 --- /dev/null +++ b/contrib/hostapd/src/drivers/netlink.h @@ -0,0 +1,28 @@ +/* + * Netlink helper functions for driver wrappers + * Copyright (c) 2002-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NETLINK_H +#define NETLINK_H + +struct netlink_data; +struct ifinfomsg; + +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/contrib/hostapd/src/drivers/nl80211_copy.h b/contrib/hostapd/src/drivers/nl80211_copy.h new file mode 100644 index 0000000000..91054fd660 --- /dev/null +++ b/contrib/hostapd/src/drivers/nl80211_copy.h @@ -0,0 +1,4040 @@ +#ifndef __LINUX_NL80211_H +#define __LINUX_NL80211_H +/* + * 802.11 netlink interface public header + * + * Copyright 2006-2010 Johannes Berg + * Copyright 2008 Michael Wu + * Copyright 2008 Luis Carlos Cobo + * Copyright 2008 Michael Buesch + * Copyright 2008, 2009 Luis R. Rodriguez + * Copyright 2008 Jouni Malinen + * Copyright 2008 Colin McCabe + * + * 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 + +#define NL80211_GENL_NAME "nl80211" + +/** + * 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. + * + * Station handling varies per interface type and depending on the driver's + * capabilities. + * + * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS + * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows: + * - a setup station entry is added, not yet authorized, without any rate + * or capability information, this just exists to avoid race conditions + * - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid + * to add rate and capability information to the station and at the same + * time mark it authorized. + * - %NL80211_TDLS_ENABLE_LINK is then used + * - after this, the only valid operation is to remove it by tearing down + * the TDLS link (%NL80211_TDLS_DISABLE_LINK) + * + * TODO: need more info for other interface types + */ + +/** + * DOC: Frame transmission/registration support + * + * Frame transmission and registration support exists to allow userspace + * management entities such as wpa_supplicant react to management frames + * that are not being handled by the kernel. This includes, for example, + * certain classes of action frames that cannot be handled in the kernel + * for various reasons. + * + * Frame registration is done on a per-interface basis and registrations + * cannot be removed other than by closing the socket. It is possible to + * specify a registration filter to register, for example, only for a + * certain type of action frame. In particular with action frames, those + * that userspace registers for will not be returned as unhandled by the + * driver, so that the registered application has to take responsibility + * for doing that. + * + * The type of frame that can be registered for is also dependent on the + * driver and interface type. The frame types are advertised in wiphy + * attributes so applications know what to expect. + * + * NOTE: When an interface changes type while registrations are active, + * these registrations are ignored until the interface type is + * changed again. This means that changing the interface type can + * lead to a situation that couldn't otherwise be produced, but + * any such registrations will be dormant in the sense that they + * will not be serviced, i.e. they will not receive any frames. + * + * Frame transmission allows userspace to send for example the required + * responses to action frames. It is subject to some sanity checking, + * but many frames can be transmitted. When a frame was transmitted, its + * status is indicated to the sending socket. + * + * For more technical details, see the corresponding command descriptions + * below. + */ + +/** + * DOC: Virtual interface / concurrency capabilities + * + * Some devices are able to operate with virtual MACs, they can have + * more than one virtual interface. The capability handling for this + * is a bit complex though, as there may be a number of restrictions + * on the types of concurrency that are supported. + * + * To start with, each device supports the interface types listed in + * the %NL80211_ATTR_SUPPORTED_IFTYPES attribute, but by listing the + * types there no concurrency is implied. + * + * Once concurrency is desired, more attributes must be observed: + * To start with, since some interface types are purely managed in + * software, like the AP-VLAN type in mac80211 for example, there's + * an additional list of these, they can be added at any time and + * are only restricted by some semantic restrictions (e.g. AP-VLAN + * cannot be added without a corresponding AP interface). This list + * is exported in the %NL80211_ATTR_SOFTWARE_IFTYPES attribute. + * + * Further, the list of supported combinations is exported. This is + * in the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute. Basically, + * it exports a list of "groups", and at any point in time the + * interfaces that are currently active must fall into any one of + * the advertised groups. Within each group, there are restrictions + * on the number of interfaces of different types that are supported + * and also the number of different channels, along with potentially + * some other restrictions. See &enum nl80211_if_combination_attrs. + * + * All together, these attributes define the concurrency of virtual + * interfaces that a given device supports. + */ + +/** + * DOC: packet coalesce support + * + * In most cases, host that receives IPv4 and IPv6 multicast/broadcast + * packets does not do anything with these packets. Therefore the + * reception of these unwanted packets causes unnecessary processing + * and power consumption. + * + * Packet coalesce feature helps to reduce number of received interrupts + * to host by buffering these packets in firmware/hardware for some + * predefined time. Received interrupt will be generated when one of the + * following events occur. + * a) Expiration of hardware timer whose expiration time is set to maximum + * coalescing delay of matching coalesce rule. + * b) Coalescing buffer in hardware reaches it's limit. + * c) Packet doesn't match any of the configured coalesce rules. + * + * User needs to configure following parameters for creating a coalesce + * rule. + * a) Maximum coalescing delay + * b) List of packet patterns which needs to be matched + * c) Condition for coalescence. pattern 'match' or 'no match' + * Multiple such rules can be created. + */ + +/** + * 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 (and the + * attributes determining the channel width; this is used for setting + * monitor mode channel), %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. + * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL + * instead, the support here is for backward compatibility only. + * @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: (not used) + * @NL80211_CMD_SET_BEACON: change the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL + * attributes. For drivers that generate the beacon and probe responses + * internally, the following attributes must be provided: %NL80211_ATTR_IE, + * %NL80211_ATTR_IE_PROBE_RESP and %NL80211_ATTR_IE_ASSOC_RESP. + * @NL80211_CMD_START_AP: Start AP operation on an AP interface, parameters + * are like for %NL80211_CMD_SET_BEACON, and additionally parameters that + * do not change are used, these include %NL80211_ATTR_BEACON_INTERVAL, + * %NL80211_ATTR_DTIM_PERIOD, %NL80211_ATTR_SSID, + * %NL80211_ATTR_HIDDEN_SSID, %NL80211_ATTR_CIPHERS_PAIRWISE, + * %NL80211_ATTR_CIPHER_GROUP, %NL80211_ATTR_WPA_VERSIONS, + * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, + * %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_INACTIVITY_TIMEOUT, + * %NL80211_ATTR_ACL_POLICY and %NL80211_ATTR_MAC_ADDRS. + * The channel to use can be set on the interface or be given using the + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. + * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP + * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface + * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP + * + * @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_MPATH: Create a new mesh path for the destination given by + * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP. + * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by + * %NL80211_ATTR_MAC. + * @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 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_CONFIG: Get mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MESH_CONFIG: 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_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * probe requests at CCK rate or not. + * @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_START_SCHED_SCAN: start a scheduled scan at certain + * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL. + * Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS) + * are passed, they are used in the probe requests. For + * broadcast, a broadcast SSID must be passed (ie. an empty + * string). If no SSID is passed, no probe requests are sent and + * a passive scan is performed. %NL80211_ATTR_SCAN_FREQUENCIES, + * if passed, define which channels should be scanned; if not + * passed, all channels allowed for the current regulatory domain + * are used. Extra IEs can also be passed from the userspace by + * using the %NL80211_ATTR_IE attribute. + * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT + * if scheduled scan is not running. + * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan + * results available. + * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has + * stopped. The driver may issue this event at any time during a + * scheduled scan. One reason for stopping the scan is if the hardware + * does not support starting an association or a normal scan while running + * a scheduled scan. This event is also sent when the + * %NL80211_CMD_STOP_SCHED_SCAN command is received or when the interface + * is brought down while a scheduled scan was running. + * + * @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_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC + * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC + * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. + * + * @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_USE_MFP, + * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT. + * Background scan period can optionally be + * specified in %NL80211_ATTR_BG_SCAN_PERIOD, + * if not specified default background scan configuration + * in driver is used and if period value is 0, bg scan will be disabled. + * This attribute is ignored if driver does not support roam scan. + * 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_IFINDEX is used to specify which interface (and thus + * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the + * frequency for the operation. + * %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_FRAME: Register for receiving certain mgmt frames + * (via @NL80211_CMD_FRAME) for processing in userspace. This command + * requires an interface index, a frame type attribute (optional for + * backward compatibility reasons, if not given assumes action frames) + * 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_REGISTER_ACTION: Alias for @NL80211_CMD_REGISTER_FRAME for + * backward compatibility + * @NL80211_CMD_FRAME: Management frame TX request and RX notification. This + * command is used both as a request to transmit a management frame and + * as an event indicating reception of a 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 is used + * to indicate on which channel the frame is to be transmitted or was + * received. If this channel is not the current channel (remain-on-channel + * or the operational channel) the device will switch to the given channel + * and transmit the frame, optionally waiting for a response for the time + * specified using %NL80211_ATTR_DURATION. 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_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * management frames at CCK rate or not in 2GHz band. + * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this + * command may be used with the corresponding cookie to cancel the wait + * time if it is known that it is no longer necessary. + * @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility. + * @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame + * transmitted with %NL80211_CMD_FRAME. %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_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for + * backward compatibility. + * + * @NL80211_CMD_SET_POWER_SAVE: Set powersave, using %NL80211_ATTR_PS_STATE + * @NL80211_CMD_GET_POWER_SAVE: Get powersave status in %NL80211_ATTR_PS_STATE + * + * @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_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ + * and the attributes determining channel width) the given interface + * (identifed by %NL80211_ATTR_IFINDEX) shall operate on. + * In case multiple channels are supported by the device, the mechanism + * with which it switches channels is implementation-defined. + * When a monitor interface is given, it can only switch channel while + * no other interfaces are operating to avoid disturbing the operation + * of any other interfaces, and other interfaces will again take + * precedence when they are used. + * + * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. + * + * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial + * mesh config parameters may be given. + * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the + * network is determined by the network interface. + * + * @NL80211_CMD_UNPROT_DEAUTHENTICATE: Unprotected deauthentication frame + * notification. This event is used to indicate that an unprotected + * deauthentication frame was dropped when MFP is in use. + * @NL80211_CMD_UNPROT_DISASSOCIATE: Unprotected disassociation frame + * notification. This event is used to indicate that an unprotected + * disassociation frame was dropped when MFP is in use. + * + * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a + * beacon or probe response from a compatible mesh peer. This is only + * sent while no station information (sta_info) exists for the new peer + * candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH, + * @NL80211_MESH_SETUP_USERSPACE_AMPE, or + * @NL80211_MESH_SETUP_USERSPACE_MPM is set. On reception of this + * notification, userspace may decide to create a new station + * (@NL80211_CMD_NEW_STATION). To stop this notification from + * reoccurring, the userspace authentication daemon may want to create the + * new station with the AUTHENTICATED flag unset and maybe change it later + * depending on the authentication result. + * + * @NL80211_CMD_GET_WOWLAN: get Wake-on-Wireless-LAN (WoWLAN) settings. + * @NL80211_CMD_SET_WOWLAN: set Wake-on-Wireless-LAN (WoWLAN) settings. + * Since wireless is more complex than wired ethernet, it supports + * various triggers. These triggers can be configured through this + * command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For + * more background information, see + * http://wireless.kernel.org/en/users/Documentation/WoWLAN. + * The @NL80211_CMD_SET_WOWLAN command can also be used as a notification + * from the driver reporting the wakeup reason. In this case, the + * @NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason + * for the wakeup, if it was caused by wireless. If it is not present + * in the wakeup notification, the wireless device didn't cause the + * wakeup but reports that it was woken up. + * + * @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver + * the necessary information for supporting GTK rekey offload. This + * feature is typically used during WoWLAN. The configuration data + * is contained in %NL80211_ATTR_REKEY_DATA (which is nested and + * contains the data in sub-attributes). After rekeying happened, + * this command may also be sent by the driver as an MLME event to + * inform userspace of the new replay counter. + * + * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace + * of PMKSA caching dandidates. + * + * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). + * In addition, this can be used as an event to request userspace to take + * actions on TDLS links (set up a new link or tear down an existing one). + * In such events, %NL80211_ATTR_TDLS_OPERATION indicates the requested + * operation, %NL80211_ATTR_MAC contains the peer MAC address, and + * %NL80211_ATTR_REASON_CODE the reason code to be used (only with + * %NL80211_TDLS_TEARDOWN). + * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The + * %NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be + * sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as + * 802.11 management frames, while TDLS action codes (802.11-2012 + * 8.5.13.1) will be encapsulated and sent as data frames. The currently + * supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES + * and the currently supported TDLS actions codes are given in + * &enum ieee80211_tdls_actioncode. + * + * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP + * (or GO) interface (i.e. hostapd) to ask for unexpected frames to + * implement sending deauth to stations that send unexpected class 3 + * frames. Also used as the event sent by the kernel when such a frame + * is received. + * For the event, the %NL80211_ATTR_MAC attribute carries the TA and + * other attributes like the interface index are present. + * If used as the command it must have an interface index and you can + * only unsubscribe from the event by closing the socket. Subscription + * is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events. + * + * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the + * associated station identified by %NL80211_ATTR_MAC sent a 4addr frame + * and wasn't already in a 4-addr VLAN. The event will be sent similarly + * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. + * + * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface + * by sending a null data frame to it and reporting when the frame is + * acknowleged. This is used to allow timing out inactive clients. Uses + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a + * direct reply with an %NL80211_ATTR_COOKIE that is later used to match + * up the event with the request. The event includes the same data and + * has %NL80211_ATTR_ACK set if the frame was ACKed. + * + * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from + * other BSSes when any interfaces are in AP mode. This helps implement + * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME + * messages. Note that per PHY only one application may register. + * + * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether + * No Acknowledgement Policy should be applied. + * + * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels + * independently of the userspace SME, send this event indicating + * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the + * attributes determining channel width. + * + * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by + * its %NL80211_ATTR_WDEV identifier. It must have been created with + * %NL80211_CMD_NEW_INTERFACE previously. After it has been started, the + * P2P Device can be used for P2P operations, e.g. remain-on-channel and + * public action frame TX. + * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by + * its %NL80211_ATTR_WDEV identifier. + * + * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to + * notify userspace that AP has rejected the connection request from a + * station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON + * is used for this. + * + * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames + * for IBSS or MESH vif. + * + * @NL80211_CMD_SET_MAC_ACL: sets ACL for MAC address based access control. + * This is to be used with the drivers advertising the support of MAC + * address based access control. List of MAC addresses is passed in + * %NL80211_ATTR_MAC_ADDRS and ACL policy is passed in + * %NL80211_ATTR_ACL_POLICY. Driver will enable ACL with this list, if it + * is not already done. The new list will replace any existing list. Driver + * will clear its ACL when the list of MAC addresses passed is empty. This + * command is used in AP/P2P GO mode. Driver has to make sure to clear its + * ACL list during %NL80211_CMD_STOP_AP. + * + * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once + * a radar is detected or the channel availability scan (CAC) has finished + * or was aborted, or a radar was detected, usermode will be notified with + * this event. This command is also used to notify userspace about radars + * while operating on this channel. + * %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the + * event. + * + * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features, + * i.e. features for the nl80211 protocol rather than device features. + * Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap. + * + * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition + * Information Element to the WLAN driver + * + * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver + * to the supplicant. This will carry the target AP's MAC address along + * with the relevant Information Elements. This event is used to report + * received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE). + * + * @NL80211_CMD_CRIT_PROTOCOL_START: Indicates user-space will start running + * a critical protocol that needs more reliability in the connection to + * complete. + * + * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can + * return back to normal. + * + * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules. + * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules. + * + * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the + * the new channel information (Channel Switch Announcement - CSA) + * in the beacon for some time (as defined in the + * %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the + * new channel. Userspace provides the new channel information (using + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel + * width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform + * other station that transmission must be blocked until the channel + * switch is complete. + * + * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified + * by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in + * %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in + * %NL80211_ATTR_VENDOR_DATA. + * For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is + * used in the wiphy data as a nested attribute containing descriptions + * (&struct nl80211_vendor_cmd_info) of the supported vendor commands. + * This may also be sent as an event with the same attributes. + * + * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values. + * The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If + * that attribute is not included, QoS mapping is disabled. Since this + * QoS mapping is relevant for IP packets, it is only valid during an + * association. This is cleared on disassociation and AP restart. + * + * @NL80211_CMD_MAX: highest used command number + * @__NL80211_CMD_AFTER_LAST: internal use + */ +enum nl80211_commands { +/* don't change the order or add anything between, 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_START_AP, + NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP, + NL80211_CMD_STOP_AP, + NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP, + + 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_CONFIG, + NL80211_CMD_SET_MESH_CONFIG, + + 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_FRAME, + NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME, + NL80211_CMD_FRAME, + NL80211_CMD_ACTION = NL80211_CMD_FRAME, + NL80211_CMD_FRAME_TX_STATUS, + NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS, + + NL80211_CMD_SET_POWER_SAVE, + NL80211_CMD_GET_POWER_SAVE, + + NL80211_CMD_SET_CQM, + NL80211_CMD_NOTIFY_CQM, + + NL80211_CMD_SET_CHANNEL, + NL80211_CMD_SET_WDS_PEER, + + NL80211_CMD_FRAME_WAIT_CANCEL, + + NL80211_CMD_JOIN_MESH, + NL80211_CMD_LEAVE_MESH, + + NL80211_CMD_UNPROT_DEAUTHENTICATE, + NL80211_CMD_UNPROT_DISASSOCIATE, + + NL80211_CMD_NEW_PEER_CANDIDATE, + + NL80211_CMD_GET_WOWLAN, + NL80211_CMD_SET_WOWLAN, + + NL80211_CMD_START_SCHED_SCAN, + NL80211_CMD_STOP_SCHED_SCAN, + NL80211_CMD_SCHED_SCAN_RESULTS, + NL80211_CMD_SCHED_SCAN_STOPPED, + + NL80211_CMD_SET_REKEY_OFFLOAD, + + NL80211_CMD_PMKSA_CANDIDATE, + + NL80211_CMD_TDLS_OPER, + NL80211_CMD_TDLS_MGMT, + + NL80211_CMD_UNEXPECTED_FRAME, + + NL80211_CMD_PROBE_CLIENT, + + NL80211_CMD_REGISTER_BEACONS, + + NL80211_CMD_UNEXPECTED_4ADDR_FRAME, + + NL80211_CMD_SET_NOACK_MAP, + + NL80211_CMD_CH_SWITCH_NOTIFY, + + NL80211_CMD_START_P2P_DEVICE, + NL80211_CMD_STOP_P2P_DEVICE, + + NL80211_CMD_CONN_FAILED, + + NL80211_CMD_SET_MCAST_RATE, + + NL80211_CMD_SET_MAC_ACL, + + NL80211_CMD_RADAR_DETECT, + + NL80211_CMD_GET_PROTOCOL_FEATURES, + + NL80211_CMD_UPDATE_FT_IES, + NL80211_CMD_FT_EVENT, + + NL80211_CMD_CRIT_PROTOCOL_START, + NL80211_CMD_CRIT_PROTOCOL_STOP, + + NL80211_CMD_GET_COALESCE, + NL80211_CMD_SET_COALESCE, + + NL80211_CMD_CHANNEL_SWITCH, + + NL80211_CMD_VENDOR, + + NL80211_CMD_SET_QOS_MAP, + + /* 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 + +#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS + +/* source-level API compatibility */ +#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG +#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG +#define NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE NL80211_MESH_SETUP_IE + +/** + * 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//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, + * defines the channel together with the (deprecated) + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes + * %NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 + * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values + * of &enum nl80211_chan_width, describing the channel width. See the + * documentation of the enum for more information. + * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the + * channel, used for anything but 20 MHz bandwidth + * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the + * channel, used only for 80+80 MHz bandwidth + * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ + * if HT20 or HT40 are to be used (i.e., HT 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 + * This attribute is now deprecated. + * @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_WDEV: wireless device identifier, used for pseudo-devices + * that don't have a netdev (u64) + * + * @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_KEY_DEFAULT: Flag attribute indicating the key is default key + * @NL80211_ATTR_KEY_DEFAULT_MGMT: Flag attribute indicating the key is the + * default management key + * @NL80211_ATTR_CIPHER_SUITES_PAIRWISE: For crypto settings for connect or + * other commands, indicates which pairwise cipher suites are used + * @NL80211_ATTR_CIPHER_SUITE_GROUP: For crypto settings for connect or + * other commands, indicates which group cipher suite is used + * + * @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_STA_PLINK_ACTION: action to perform on the mesh peer link + * (see &enum nl80211_plink_action). + * @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.11 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_NUM_SCHED_SCAN_SSIDS: number of SSIDs you can + * scan with a single scheduled 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_MAX_SCHED_SCAN_IE_LEN: maximum length of information + * elements that can be added to a scheduled scan request + * @NL80211_ATTR_MAX_MATCH_SETS: maximum number of sets that can be + * used with @NL80211_ATTR_SCHED_SCAN_MATCH, a wiphy attribute. + * + * @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 and %NL80211_CMD_CONNECT requests + * + * @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_CONTROL_PORT_ETHERTYPE: A 16-bit value indicating the + * ethertype that will be used for key negotiation. It can be + * specified with the associate and connect commands. If it is not + * specified, the value defaults to 0x888E (PAE, 802.1X). This + * attribute is also used as a flag in the wiphy information to + * indicate that protocols other than PAE are supported. + * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with + * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom + * ethertype frames used for key negotiation must not be encrypted. + * + * @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. This is also used with NEW_BEACON to + * indicate that the BSS is to use protection. + * + * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT, ASSOCIATE, and NEW_BEACON + * to indicate which unicast key ciphers will be used with the connection + * (an array of u32). + * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT, ASSOCIATE, and NEW_BEACON to + * indicate which group key cipher will be used with the connection (a + * u32). + * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT, ASSOCIATE, and NEW_BEACON 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, ASSOCIATE, and NEW_BEACON 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_MAX_REMAIN_ON_CHANNEL_DURATION: Device attribute that + * specifies the maximum duration that can be requested with the + * remain-on-channel 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_FRAME. + * @NL80211_ATTR_FRAME_TYPE: A u16 indicating the frame type/subtype for the + * @NL80211_CMD_REGISTER_FRAME command. + * @NL80211_ATTR_TX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be transmitted with + * %NL80211_CMD_FRAME. + * @NL80211_ATTR_RX_FRAME_TYPES: wiphy capability attribute, which is a + * nested attribute of %NL80211_ATTR_FRAME_TYPE attributes, containing + * information about which frame types can be registered for RX. + * + * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was + * acknowledged by the recipient. + * + * @NL80211_ATTR_PS_STATE: powersave state, using &enum nl80211_ps_state values. + * + * @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_AP_ISOLATE: (AP mode) Do not forward traffic between stations + * connected to this BSS. + * + * @NL80211_ATTR_WIPHY_TX_POWER_SETTING: Transmit power setting type. See + * &enum nl80211_tx_power_setting for possible values. + * @NL80211_ATTR_WIPHY_TX_POWER_LEVEL: Transmit power level in signed mBm units. + * This is used in association with @NL80211_ATTR_WIPHY_TX_POWER_SETTING + * for non-automatic settings. + * + * @NL80211_ATTR_SUPPORT_IBSS_RSN: The device supports IBSS RSN, which mostly + * means support for per-station GTKs. + * + * @NL80211_ATTR_WIPHY_ANTENNA_TX: Bitmap of allowed antennas for transmitting. + * This can be used to mask out antennas which are not attached or should + * not be used for transmitting. If an antenna is not selected in this + * bitmap the hardware is not allowed to transmit on this antenna. + * + * Each bit represents one antenna, starting with antenna 1 at the first + * bit. Depending on which antennas are selected in the bitmap, 802.11n + * drivers can derive which chainmasks to use (if all antennas belonging to + * a particular chain are disabled this chain should be disabled) and if + * a chain has diversity antennas wether diversity should be used or not. + * HT capabilities (STBC, TX Beamforming, Antenna selection) can be + * derived from the available chains after applying the antenna mask. + * Non-802.11n drivers can derive wether to use diversity or not. + * Drivers may reject configurations or RX/TX mask combinations they cannot + * support by returning -EINVAL. + * + * @NL80211_ATTR_WIPHY_ANTENNA_RX: Bitmap of allowed antennas for receiving. + * This can be used to mask out antennas which are not attached or should + * not be used for receiving. If an antenna is not selected in this bitmap + * the hardware should not be configured to receive on this antenna. + * For a more detailed description see @NL80211_ATTR_WIPHY_ANTENNA_TX. + * + * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available + * for configuration as TX antennas via the above parameters. + * + * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available + * for configuration as RX antennas via the above parameters. + * + * @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS + * + * @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be + * transmitted on another channel when the channel given doesn't match + * the current channel. If the current channel doesn't match and this + * flag isn't set, the frame will be rejected. This is also used as an + * nl80211 capability flag. + * + * @NL80211_ATTR_BSS_HT_OPMODE: HT operation mode (u16) + * + * @NL80211_ATTR_KEY_DEFAULT_TYPES: A nested attribute containing flags + * attributes, specifying what a key should be set as default as. + * See &enum nl80211_key_default_types. + * + * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters. These cannot be + * changed once the mesh is active. + * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute + * containing attributes from &enum nl80211_meshconf_params. + * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver + * allows auth frames in a mesh to be passed to userspace for processing via + * the @NL80211_MESH_SETUP_USERSPACE_AUTH flag. + * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in + * &enum nl80211_plink_state. Used when userspace is driving the peer link + * management state machine. @NL80211_MESH_SETUP_USERSPACE_AMPE or + * @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled. + * + * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy + * capabilities, the supported WoWLAN triggers + * @NL80211_ATTR_WOWLAN_TRIGGERS: used by %NL80211_CMD_SET_WOWLAN to + * indicate which WoW triggers should be enabled. This is also + * used by %NL80211_CMD_GET_WOWLAN to get the currently enabled WoWLAN + * triggers. + * + * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan + * cycles, in msecs. + * + * @NL80211_ATTR_SCHED_SCAN_MATCH: Nested attribute with one or more + * sets of attributes to match during scheduled scans. Only BSSs + * that match any of the sets will be reported. These are + * pass-thru filter rules. + * For a match to succeed, the BSS must match all attributes of a + * set. Since not every hardware supports matching all types of + * attributes, there is no guarantee that the reported BSSs are + * fully complying with the match sets and userspace needs to be + * able to ignore them by itself. + * Thus, the implementation is somewhat hardware-dependent, but + * this is only an optimization and the userspace application + * needs to handle all the non-filtered results anyway. + * If the match attributes don't make sense when combined with + * the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID + * is included in the probe request, but the match attributes + * will never let it go through), -EINVAL may be returned. + * If ommited, no filtering is done. + * + * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported + * interface combinations. In each nested item, it contains attributes + * defined in &enum nl80211_if_combination_attrs. + * @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like + * %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that + * are managed in software: interfaces of these types aren't subject to + * any restrictions in their number or combinations. + * + * @NL80211_ATTR_REKEY_DATA: nested attribute containing the information + * necessary for GTK rekeying in the device, see &enum nl80211_rekey_data. + * + * @NL80211_ATTR_SCAN_SUPP_RATES: rates per to be advertised as supported in scan, + * nested array attribute containing an entry for each band, with the entry + * being a list 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_HIDDEN_SSID: indicates whether SSID is to be hidden from Beacon + * and Probe Response (when response to wildcard Probe Request); see + * &enum nl80211_hidden_ssid, represented as a u32 + * + * @NL80211_ATTR_IE_PROBE_RESP: Information element(s) for Probe Response frame. + * This is used with %NL80211_CMD_NEW_BEACON and %NL80211_CMD_SET_BEACON to + * provide extra IEs (e.g., WPS/P2P IE) into Probe Response frames when the + * driver (or firmware) replies to Probe Request frames. + * @NL80211_ATTR_IE_ASSOC_RESP: Information element(s) for (Re)Association + * Response frames. This is used with %NL80211_CMD_NEW_BEACON and + * %NL80211_CMD_SET_BEACON to provide extra IEs (e.g., WPS/P2P IE) into + * (Re)Association Response frames when the driver (or firmware) replies to + * (Re)Association Request frames. + * + * @NL80211_ATTR_STA_WME: Nested attribute containing the wme configuration + * of the station, see &enum nl80211_sta_wme_attr. + * @NL80211_ATTR_SUPPORT_AP_UAPSD: the device supports uapsd when working + * as AP. + * + * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of + * roaming to another AP in the same ESS if the signal lever is low. + * + * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching + * candidate information, see &enum nl80211_pmksa_candidate_attr. + * + * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not + * for management frames transmission. In order to avoid p2p probe/action + * frames are being transmitted at CCK rate in 2GHz band, the user space + * applications use this attribute. + * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and + * %NL80211_CMD_FRAME commands. + * + * @NL80211_ATTR_TDLS_ACTION: Low level TDLS action code (e.g. link setup + * request, link setup confirm, link teardown, etc.). Values are + * described in the TDLS (802.11z) specification. + * @NL80211_ATTR_TDLS_DIALOG_TOKEN: Non-zero token for uniquely identifying a + * TDLS conversation between two devices. + * @NL80211_ATTR_TDLS_OPERATION: High level TDLS operation; see + * &enum nl80211_tdls_operation, represented as a u8. + * @NL80211_ATTR_TDLS_SUPPORT: A flag indicating the device can operate + * as a TDLS peer sta. + * @NL80211_ATTR_TDLS_EXTERNAL_SETUP: The TDLS discovery/setup and teardown + * procedures should be performed by sending TDLS packets via + * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be + * used for asking the driver to perform a TDLS operation. + * + * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices + * that have AP support to indicate that they have the AP SME integrated + * with support for the features listed in this attribute, see + * &enum nl80211_ap_sme_features. + * + * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells + * the driver to not wait for an acknowledgement. Note that due to this, + * it will also not give a status callback nor return a cookie. This is + * mostly useful for probe responses to save airtime. + * + * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from + * &enum nl80211_feature_flags and is advertised in wiphy information. + * @NL80211_ATTR_PROBE_RESP_OFFLOAD: Indicates that the HW responds to probe + * requests while operating in AP-mode. + * This attribute holds a bitmap of the supported protocols for + * offloading (see &enum nl80211_probe_resp_offload_support_attr). + * + * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire + * probe-response frame. The DA field in the 802.11 header is zero-ed out, + * to be filled by the FW. + * @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable + * this feature. Currently, only supported in mac80211 drivers. + * @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the + * ATTR_HT_CAPABILITY to which attention should be paid. + * Currently, only mac80211 NICs support this feature. + * The values that may be configured are: + * MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40 + * AMPDU density and AMPDU factor. + * All values are treated as suggestions and may be ignored + * by the driver as required. The actual values may be seen in + * the station debugfs ht_caps file. + * + * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country + * abides to when initiating radiation on DFS channels. A country maps + * to one DFS region. + * + * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of + * up to 16 TIDs. + * + * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be + * used by the drivers which has MLME in firmware and does not have support + * to report per station tx/rx activity to free up the staion entry from + * the list. This needs to be used when the driver advertises the + * capability to timeout the stations. + * + * @NL80211_ATTR_RX_SIGNAL_DBM: signal strength in dBm (as a 32-bit int); + * this attribute is (depending on the driver capabilities) added to + * received frames indicated with %NL80211_CMD_FRAME. + * + * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds + * or 0 to disable background scan. + * + * @NL80211_ATTR_USER_REG_HINT_TYPE: type of regulatory hint passed from + * userspace. If unset it is assumed the hint comes directly from + * a user. If set code could specify exactly what type of source + * was used to provide the hint. For the different types of + * allowed user regulatory hints see nl80211_user_reg_hint_type. + * + * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected + * the connection request from a station. nl80211_connect_failed_reason + * enum has different reasons of connection failure. + * + * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts + * with the Authentication transaction sequence number field. + * + * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32) + * + * @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with + * the START_AP and SET_BSS commands + * @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the + * START_AP and SET_BSS commands. This can have the values 0 or 1; + * if not given in START_AP 0 is assumed, if not given in SET_BSS + * no change is made. + * + * @NL80211_ATTR_LOCAL_MESH_POWER_MODE: local mesh STA link-specific power mode + * defined in &enum nl80211_mesh_power_mode. + * + * @NL80211_ATTR_ACL_POLICY: ACL policy, see &enum nl80211_acl_policy, + * carried in a u32 attribute + * + * @NL80211_ATTR_MAC_ADDRS: Array of nested MAC addresses, used for + * MAC ACL. + * + * @NL80211_ATTR_MAC_ACL_MAX: u32 attribute to advertise the maximum + * number of MAC addresses that a device can support for MAC + * ACL. + * + * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace, + * contains a value of enum nl80211_radar_event (u32). + * + * @NL80211_ATTR_EXT_CAPA: 802.11 extended capabilities that the kernel driver + * has and handles. The format is the same as the IE contents. See + * 802.11-2012 8.4.2.29 for more information. + * @NL80211_ATTR_EXT_CAPA_MASK: Extended capabilities that the kernel driver + * has set in the %NL80211_ATTR_EXT_CAPA value, for multibit fields. + * + * @NL80211_ATTR_STA_CAPABILITY: Station capabilities (u16) are advertised to + * the driver, e.g., to enable TDLS power save (PU-APSD). + * + * @NL80211_ATTR_STA_EXT_CAPABILITY: Station extended capabilities are + * advertised to the driver, e.g., to enable TDLS off channel operations + * and PU-APSD. + * + * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see + * &enum nl80211_protocol_features, the attribute is a u32. + * + * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports + * receiving the data for a single wiphy split across multiple + * messages, given with wiphy dump message + * + * @NL80211_ATTR_MDID: Mobility Domain Identifier + * + * @NL80211_ATTR_IE_RIC: Resource Information Container Information + * Element + * + * @NL80211_ATTR_CRIT_PROT_ID: critical protocol identifier requiring increased + * reliability, see &enum nl80211_crit_proto_id (u16). + * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which + * the connection should have increased reliability (u16). + * + * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16). + * This is similar to @NL80211_ATTR_STA_AID but with a difference of being + * allowed to be used with the first @NL80211_CMD_SET_STATION command to + * update a TDLS peer STA entry. + * + * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. + * + * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's + * until the channel switch event. + * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission + * must be blocked on the current channel (before the channel switch + * operation). + * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information + * for the time while performing a channel switch. + * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter + * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL). + * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter + * field in the probe response (%NL80211_ATTR_PROBE_RESP). + * + * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. + * As specified in the &enum nl80211_rxmgmt_flags. + * + * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels. + * + * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported + * supported operating classes. + * + * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space + * controls DFS operation in IBSS mode. If the flag is included in + * %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS + * channels and reports radar events to userspace. Userspace is required + * to react to radar events, e.g. initiate a channel switch or leave the + * IBSS network. + * + * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports + * 5 MHz channel bandwidth. + * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports + * 10 MHz channel bandwidth. + * + * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode + * Notification Element based on association request when used with + * %NL80211_CMD_NEW_STATION; u8 attribute. + * + * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if + * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet) + * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command + * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this + * attribute is also used for vendor command feature advertisement + * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy + * info, containing a nested array of possible events + * + * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This + * data is in the format defined for the payload of the QoS Map Set element + * in IEEE Std 802.11-2012, 8.4.2.97. + * + * @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 between, 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_CONFIG, + + 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, + + NL80211_ATTR_AP_ISOLATE, + + NL80211_ATTR_WIPHY_TX_POWER_SETTING, + NL80211_ATTR_WIPHY_TX_POWER_LEVEL, + + NL80211_ATTR_TX_FRAME_TYPES, + NL80211_ATTR_RX_FRAME_TYPES, + NL80211_ATTR_FRAME_TYPE, + + NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + + NL80211_ATTR_SUPPORT_IBSS_RSN, + + NL80211_ATTR_WIPHY_ANTENNA_TX, + NL80211_ATTR_WIPHY_ANTENNA_RX, + + NL80211_ATTR_MCAST_RATE, + + NL80211_ATTR_OFFCHANNEL_TX_OK, + + NL80211_ATTR_BSS_HT_OPMODE, + + NL80211_ATTR_KEY_DEFAULT_TYPES, + + NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, + + NL80211_ATTR_MESH_SETUP, + + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, + NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + + NL80211_ATTR_SUPPORT_MESH_AUTH, + NL80211_ATTR_STA_PLINK_STATE, + + NL80211_ATTR_WOWLAN_TRIGGERS, + NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, + + NL80211_ATTR_SCHED_SCAN_INTERVAL, + + NL80211_ATTR_INTERFACE_COMBINATIONS, + NL80211_ATTR_SOFTWARE_IFTYPES, + + NL80211_ATTR_REKEY_DATA, + + NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, + NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, + + NL80211_ATTR_SCAN_SUPP_RATES, + + NL80211_ATTR_HIDDEN_SSID, + + NL80211_ATTR_IE_PROBE_RESP, + NL80211_ATTR_IE_ASSOC_RESP, + + NL80211_ATTR_STA_WME, + NL80211_ATTR_SUPPORT_AP_UAPSD, + + NL80211_ATTR_ROAM_SUPPORT, + + NL80211_ATTR_SCHED_SCAN_MATCH, + NL80211_ATTR_MAX_MATCH_SETS, + + NL80211_ATTR_PMKSA_CANDIDATE, + + NL80211_ATTR_TX_NO_CCK_RATE, + + NL80211_ATTR_TDLS_ACTION, + NL80211_ATTR_TDLS_DIALOG_TOKEN, + NL80211_ATTR_TDLS_OPERATION, + NL80211_ATTR_TDLS_SUPPORT, + NL80211_ATTR_TDLS_EXTERNAL_SETUP, + + NL80211_ATTR_DEVICE_AP_SME, + + NL80211_ATTR_DONT_WAIT_FOR_ACK, + + NL80211_ATTR_FEATURE_FLAGS, + + NL80211_ATTR_PROBE_RESP_OFFLOAD, + + NL80211_ATTR_PROBE_RESP, + + NL80211_ATTR_DFS_REGION, + + NL80211_ATTR_DISABLE_HT, + NL80211_ATTR_HT_CAPABILITY_MASK, + + NL80211_ATTR_NOACK_MAP, + + NL80211_ATTR_INACTIVITY_TIMEOUT, + + NL80211_ATTR_RX_SIGNAL_DBM, + + NL80211_ATTR_BG_SCAN_PERIOD, + + NL80211_ATTR_WDEV, + + NL80211_ATTR_USER_REG_HINT_TYPE, + + NL80211_ATTR_CONN_FAILED_REASON, + + NL80211_ATTR_SAE_DATA, + + NL80211_ATTR_VHT_CAPABILITY, + + NL80211_ATTR_SCAN_FLAGS, + + NL80211_ATTR_CHANNEL_WIDTH, + NL80211_ATTR_CENTER_FREQ1, + NL80211_ATTR_CENTER_FREQ2, + + NL80211_ATTR_P2P_CTWINDOW, + NL80211_ATTR_P2P_OPPPS, + + NL80211_ATTR_LOCAL_MESH_POWER_MODE, + + NL80211_ATTR_ACL_POLICY, + + NL80211_ATTR_MAC_ADDRS, + + NL80211_ATTR_MAC_ACL_MAX, + + NL80211_ATTR_RADAR_EVENT, + + NL80211_ATTR_EXT_CAPA, + NL80211_ATTR_EXT_CAPA_MASK, + + NL80211_ATTR_STA_CAPABILITY, + NL80211_ATTR_STA_EXT_CAPABILITY, + + NL80211_ATTR_PROTOCOL_FEATURES, + NL80211_ATTR_SPLIT_WIPHY_DUMP, + + NL80211_ATTR_DISABLE_VHT, + NL80211_ATTR_VHT_CAPABILITY_MASK, + + NL80211_ATTR_MDID, + NL80211_ATTR_IE_RIC, + + NL80211_ATTR_CRIT_PROT_ID, + NL80211_ATTR_MAX_CRIT_PROT_DURATION, + + NL80211_ATTR_PEER_AID, + + NL80211_ATTR_COALESCE_RULE, + + NL80211_ATTR_CH_SWITCH_COUNT, + NL80211_ATTR_CH_SWITCH_BLOCK_TX, + NL80211_ATTR_CSA_IES, + NL80211_ATTR_CSA_C_OFF_BEACON, + NL80211_ATTR_CSA_C_OFF_PRESP, + + NL80211_ATTR_RXMGMT_FLAGS, + + NL80211_ATTR_STA_SUPPORTED_CHANNELS, + + NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + + NL80211_ATTR_HANDLE_DFS, + + NL80211_ATTR_SUPPORT_5_MHZ, + NL80211_ATTR_SUPPORT_10_MHZ, + + NL80211_ATTR_OPMODE_NOTIF, + + NL80211_ATTR_VENDOR_ID, + NL80211_ATTR_VENDOR_SUBCMD, + NL80211_ATTR_VENDOR_DATA, + NL80211_ATTR_VENDOR_EVENTS, + + NL80211_ATTR_QOS_MAP, + + /* 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 +#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG + +/* + * 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_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS + +#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_HT_RATES 77 +#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_VHT_CAPABILITY_LEN 12 + +#define NL80211_MAX_NR_CIPHER_SUITES 5 +#define NL80211_MAX_NR_AKM_SUITES 2 + +#define NL80211_MIN_REMAIN_ON_CHANNEL_TIME 10 + +/* default RSSI threshold for scan results if none specified. */ +#define NL80211_SCAN_RSSI_THOLD_OFF -300 + +#define NL80211_CQM_TXE_MAX_INTVL 1800 + +/** + * 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; VLAN interfaces + * are a bit special in that they must always be tied to a pre-existing + * AP type interface. + * @NL80211_IFTYPE_WDS: wireless distribution interface + * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames + * @NL80211_IFTYPE_MESH_POINT: mesh point + * @NL80211_IFTYPE_P2P_CLIENT: P2P client + * @NL80211_IFTYPE_P2P_GO: P2P group owner + * @NL80211_IFTYPE_P2P_DEVICE: P2P device interface type, this is not a netdev + * and therefore can't be created in the normal ways, use the + * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE + * commands to create and destroy one + * @NL80211_IFTYPE_MAX: highest interface type number currently defined + * @NUM_NL80211_IFTYPES: number of defined interface types + * + * 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, + NL80211_IFTYPE_P2P_CLIENT, + NL80211_IFTYPE_P2P_GO, + NL80211_IFTYPE_P2P_DEVICE, + + /* keep last */ + NUM_NL80211_IFTYPES, + NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 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_INVALID: attribute number 0 is reserved + * @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 + * @NL80211_STA_FLAG_AUTHENTICATED: station is authenticated + * @NL80211_STA_FLAG_TDLS_PEER: station is a TDLS peer -- this flag should + * only be used in managed mode (even in the flags mask). Note that the + * flag can't be changed, it is only valid while adding a station, and + * attempts to change it will silently be ignored (rather than rejected + * as errors.) + * @NL80211_STA_FLAG_ASSOCIATED: station is associated; used with drivers + * that support %NL80211_FEATURE_FULL_AP_CLIENT_STATE to transition a + * previously added station into associated state + * @NL80211_STA_FLAG_MAX: highest station flag number currently defined + * @__NL80211_STA_FLAG_AFTER_LAST: internal use + */ +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, + NL80211_STA_FLAG_AUTHENTICATED, + NL80211_STA_FLAG_TDLS_PEER, + NL80211_STA_FLAG_ASSOCIATED, + + /* keep last */ + __NL80211_STA_FLAG_AFTER_LAST, + NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 +}; + +#define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER + +/** + * 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. + * There are 2 attributes for bitrate, a legacy one that represents + * a 16-bit value, and new one that represents a 32-bit value. + * If the rate value fits into 16 bit, both attributes are reported + * with the same value. If the rate is too high to fit into 16 bits + * (>6.5535Gbps) only 32-bit attribute is included. + * User space tools encouraged to use the 32-bit attribute and fall + * back to the 16-bit one for compatibility with older kernels. + * + * @__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_BITRATE32: total bitrate (u32, 100kbit/s) + * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8) + * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8) + * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate + * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate + * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate + * @__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, + NL80211_RATE_INFO_BITRATE32, + NL80211_RATE_INFO_VHT_MCS, + NL80211_RATE_INFO_VHT_NSS, + NL80211_RATE_INFO_80_MHZ_WIDTH, + NL80211_RATE_INFO_80P80_MHZ_WIDTH, + NL80211_RATE_INFO_160_MHZ_WIDTH, + + /* keep last */ + __NL80211_RATE_INFO_AFTER_LAST, + NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_bss_param - BSS information collected by STA + * + * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM + * when getting information about the bitrate of a station. + * + * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved + * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag) + * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8) + * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16) + * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined + * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use + */ +enum nl80211_sta_bss_param { + __NL80211_STA_BSS_PARAM_INVALID, + NL80211_STA_BSS_PARAM_CTS_PROT, + NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, + NL80211_STA_BSS_PARAM_DTIM_PERIOD, + NL80211_STA_BSS_PARAM_BEACON_INTERVAL, + + /* keep last */ + __NL80211_STA_BSS_PARAM_AFTER_LAST, + NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_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_RX_BYTES64: total received bytes (u64, from this station) + * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station) + * @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_rate_info + * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this + * station) + * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station) + * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station) + * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm) + * @NL80211_STA_INFO_LLID: the station's mesh LLID + * @NL80211_STA_INFO_PLID: the station's mesh PLID + * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station + * (see %enum nl80211_plink_state) + * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested + * attribute, like NL80211_STA_INFO_TX_BITRATE. + * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute + * containing info as possible, see &enum nl80211_sta_bss_param + * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected + * @NL80211_STA_INFO_STA_FLAGS: Contains a struct nl80211_sta_flag_update. + * @NL80211_STA_INFO_BEACON_LOSS: count of times beacon loss was detected (u32) + * @NL80211_STA_INFO_T_OFFSET: timing offset with respect to this STA (s64) + * @NL80211_STA_INFO_LOCAL_PM: local mesh STA link-specific power mode + * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode + * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards + * non-peer STA + * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU + * Contains a nested array of signal strength attributes (u8, dBm) + * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average + * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. + * @__NL80211_STA_INFO_AFTER_LAST: internal + * @NL80211_STA_INFO_MAX: highest possible station info attribute + */ +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, + NL80211_STA_INFO_TX_RETRIES, + NL80211_STA_INFO_TX_FAILED, + NL80211_STA_INFO_SIGNAL_AVG, + NL80211_STA_INFO_RX_BITRATE, + NL80211_STA_INFO_BSS_PARAM, + NL80211_STA_INFO_CONNECTED_TIME, + NL80211_STA_INFO_STA_FLAGS, + NL80211_STA_INFO_BEACON_LOSS, + NL80211_STA_INFO_T_OFFSET, + NL80211_STA_INFO_LOCAL_PM, + NL80211_STA_INFO_PEER_PM, + NL80211_STA_INFO_NONPEER_PM, + NL80211_STA_INFO_RX_BYTES64, + NL80211_STA_INFO_TX_BYTES64, + NL80211_STA_INFO_CHAIN_SIGNAL, + NL80211_STA_INFO_CHAIN_SIGNAL_AVG, + + /* 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_MPATH_INFO_FRAME_QLEN: number of queued frames for this destination + * @NL80211_MPATH_INFO_SN: destination sequence number + * @NL80211_MPATH_INFO_METRIC: metric (cost) of this mesh path + * @NL80211_MPATH_INFO_EXPTIME: expiration time for the path, in msec from now + * @NL80211_MPATH_INFO_FLAGS: mesh path flags, enumerated in + * &enum nl80211_mpath_flags; + * @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec + * @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries + * @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number + * currently defind + * @__NL80211_MPATH_INFO_AFTER_LAST: internal use + */ +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 + * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as + * defined in 802.11ac + * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined + * @__NL80211_BAND_ATTR_AFTER_LAST: internal use + */ +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, + + NL80211_BAND_ATTR_VHT_MCS_SET, + NL80211_BAND_ATTR_VHT_CAPA, + + /* 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_INVALID: attribute number 0 is reserved + * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz + * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current + * regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation + * are permitted on this channel, this includes sending probe + * requests, or modes of operation that require beaconing. + * @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). + * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS + * (enum nl80211_dfs_state) + * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long + * this channel is in this DFS state. + * @NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: HT40- isn't possible with this + * channel as the control channel + * @NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: HT40+ isn't possible with this + * channel as the control channel + * @NL80211_FREQUENCY_ATTR_NO_80MHZ: any 80 MHz channel using this channel + * as the primary or any of the secondary channels isn't possible, + * this includes 80+80 channels + * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel + * using this channel as the primary or any of the secondary channels + * isn't possible + * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number + * currently defined + * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use + */ +enum nl80211_frequency_attr { + __NL80211_FREQUENCY_ATTR_INVALID, + NL80211_FREQUENCY_ATTR_FREQ, + NL80211_FREQUENCY_ATTR_DISABLED, + NL80211_FREQUENCY_ATTR_NO_IR, + __NL80211_FREQUENCY_ATTR_NO_IBSS, + NL80211_FREQUENCY_ATTR_RADAR, + NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + NL80211_FREQUENCY_ATTR_DFS_STATE, + NL80211_FREQUENCY_ATTR_DFS_TIME, + NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, + NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, + NL80211_FREQUENCY_ATTR_NO_80MHZ, + NL80211_FREQUENCY_ATTR_NO_160MHZ, + + /* 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 +#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN NL80211_FREQUENCY_ATTR_NO_IR +#define NL80211_FREQUENCY_ATTR_NO_IBSS NL80211_FREQUENCY_ATTR_NO_IR +#define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR + +/** + * enum nl80211_bitrate_attr - bitrate attributes + * @__NL80211_BITRATE_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps + * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported + * in 2.4 GHz band. + * @NL80211_BITRATE_ATTR_MAX: highest bitrate attribute number + * currently defined + * @__NL80211_BITRATE_ATTR_AFTER_LAST: internal use + */ +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. cfg80211 only processes the country + * code from the IE, and relies on the regulatory domain information + * structure passed by userspace (CRDA) from our wireless-regdb. + * If a channel is enabled but the country code indicates it should + * be disabled we disable the channel and re-enable it upon disassociation. + */ +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_REG_RULE_ATTR_INVALID: attribute number 0 is reserved + * @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). + * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number + * currently defined + * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use + */ +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_sched_scan_match_attr - scheduled scan match attributes + * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, + * only report BSS with matching SSID. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a + * BSS in scan results. Filtering is turned off if not specified. + * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter + * attribute number currently defined + * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use + */ +enum nl80211_sched_scan_match_attr { + __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID, + + NL80211_SCHED_SCAN_MATCH_ATTR_SSID, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + + /* keep last */ + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, + NL80211_SCHED_SCAN_MATCH_ATTR_MAX = + __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1 +}; + +/* only for backward compatibility */ +#define NL80211_ATTR_SCHED_SCAN_MATCH_SSID NL80211_SCHED_SCAN_MATCH_ATTR_SSID + +/** + * 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_NO_IR: no mechanisms that initiate radiation are allowed, + * this includes probe requests or modes of operation that require + * beaconing. + */ +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_NO_IR = 1<<7, + __NL80211_RRF_NO_IBSS = 1<<8, +}; + +#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR +#define NL80211_RRF_NO_IBSS NL80211_RRF_NO_IR +#define NL80211_RRF_NO_IR NL80211_RRF_NO_IR + +/* For backport compatibility with older userspace */ +#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS) + +/** + * enum nl80211_dfs_regions - regulatory DFS regions + * + * @NL80211_DFS_UNSET: Country has no DFS master region specified + * @NL80211_DFS_FCC: Country follows DFS master rules from FCC + * @NL80211_DFS_ETSI: Country follows DFS master rules from ETSI + * @NL80211_DFS_JP: Country follows DFS master rules from JP/MKK/Telec + */ +enum nl80211_dfs_regions { + NL80211_DFS_UNSET = 0, + NL80211_DFS_FCC = 1, + NL80211_DFS_ETSI = 2, + NL80211_DFS_JP = 3, +}; + +/** + * enum nl80211_user_reg_hint_type - type of user regulatory hint + * + * @NL80211_USER_REG_HINT_USER: a user sent the hint. This is always + * assumed if the attribute is not set. + * @NL80211_USER_REG_HINT_CELL_BASE: the hint comes from a cellular + * base station. Device drivers that have been tested to work + * properly to support this type of hint can enable these hints + * by setting the NL80211_FEATURE_CELL_BASE_REG_HINTS feature + * capability on the struct wiphy. The wireless core will + * ignore all cell base station hints until at least one device + * present has been registered with the wireless core that + * has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a + * supported feature. + */ +enum nl80211_user_reg_hint_type { + NL80211_USER_REG_HINT_USER = 0, + NL80211_USER_REG_HINT_CELL_BASE = 1, +}; + +/** + * 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) + * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used + * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio + * spent on this channel + * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary + * channel was sensed busy (either due to activity or energy detect) + * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension + * channel was sensed busy + * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent + * receiving data + * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent + * transmitting data + * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number + * currently defined + * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use + */ +enum nl80211_survey_info { + __NL80211_SURVEY_INFO_INVALID, + NL80211_SURVEY_INFO_FREQUENCY, + NL80211_SURVEY_INFO_NOISE, + NL80211_SURVEY_INFO_IN_USE, + NL80211_SURVEY_INFO_CHANNEL_TIME, + NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, + NL80211_SURVEY_INFO_CHANNEL_TIME_RX, + NL80211_SURVEY_INFO_CHANNEL_TIME_TX, + + /* 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_ACTIVE: use the configured MAC address + * and ACK incoming unicast packets. + * + * @__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, + NL80211_MNTR_FLAG_ACTIVE, + + /* keep last */ + __NL80211_MNTR_FLAG_AFTER_LAST, + NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mesh_power_mode - mesh power save modes + * + * @NL80211_MESH_POWER_UNKNOWN: The mesh power mode of the mesh STA is + * not known or has not been set yet. + * @NL80211_MESH_POWER_ACTIVE: Active mesh power mode. The mesh STA is + * in Awake state all the time. + * @NL80211_MESH_POWER_LIGHT_SLEEP: Light sleep mode. The mesh STA will + * alternate between Active and Doze states, but will wake up for + * neighbor's beacons. + * @NL80211_MESH_POWER_DEEP_SLEEP: Deep sleep mode. The mesh STA will + * alternate between Active and Doze states, but may not wake up + * for neighbor's beacons. + * + * @__NL80211_MESH_POWER_AFTER_LAST - internal use + * @NL80211_MESH_POWER_MAX - highest possible power save level + */ + +enum nl80211_mesh_power_mode { + NL80211_MESH_POWER_UNKNOWN, + NL80211_MESH_POWER_ACTIVE, + NL80211_MESH_POWER_LIGHT_SLEEP, + NL80211_MESH_POWER_DEEP_SLEEP, + + __NL80211_MESH_POWER_AFTER_LAST, + NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1 +}; + +/** + * enum nl80211_meshconf_params - mesh configuration parameters + * + * Mesh configuration parameters. These can be changed while the mesh is + * active. + * + * @__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 initial 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. Disabled if + * @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are + * set. + * + * @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_HWMP_ROOTMODE: whether root mode is enabled or not + * + * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a + * source mesh point for path selection elements. + * + * @NL80211_MESHCONF_HWMP_RANN_INTERVAL: The interval of time (in TUs) between + * root announcements are transmitted. + * + * @NL80211_MESHCONF_GATE_ANNOUNCEMENTS: Advertise that this mesh station has + * access to a broader network beyond the MBSS. This is done via Root + * Announcement frames. + * + * @NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which a mesh STA can send only one Action frame containing a + * PERR element. + * + * @NL80211_MESHCONF_FORWARDING: set Mesh STA as forwarding or non-forwarding + * or forwarding entity (default is TRUE - forwarding entity) + * + * @NL80211_MESHCONF_RSSI_THRESHOLD: RSSI threshold in dBm. This specifies the + * threshold for average signal strength of candidate station to establish + * a peer link. + * + * @NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: maximum number of neighbors + * to synchronize to for 11s default synchronization method + * (see 11C.12.2.2) + * + * @NL80211_MESHCONF_HT_OPMODE: set mesh HT protection mode. + * + * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * + * @NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: The time (in TUs) for + * which mesh STAs receiving a proactive PREQ shall consider the forwarding + * information to the root mesh STA to be valid. + * + * @NL80211_MESHCONF_HWMP_ROOT_INTERVAL: The interval of time (in TUs) between + * proactive PREQs are transmitted. + * + * @NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: The minimum interval of time + * (in TUs) during which a mesh STA can send only one Action frame + * containing a PREQ element for root path confirmation. + * + * @NL80211_MESHCONF_POWER_MODE: Default mesh power mode for new peer links. + * type &enum nl80211_mesh_power_mode (u32) + * + * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs) + * + * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've + * established peering with for longer than this time (in seconds), then + * remove it from the STA's list of peers. Default is 30 minutes. + * + * @__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, + NL80211_MESHCONF_ELEMENT_TTL, + NL80211_MESHCONF_HWMP_RANN_INTERVAL, + NL80211_MESHCONF_GATE_ANNOUNCEMENTS, + NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, + NL80211_MESHCONF_FORWARDING, + NL80211_MESHCONF_RSSI_THRESHOLD, + NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, + NL80211_MESHCONF_HT_OPMODE, + NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, + NL80211_MESHCONF_HWMP_ROOT_INTERVAL, + NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, + NL80211_MESHCONF_POWER_MODE, + NL80211_MESHCONF_AWAKE_WINDOW, + NL80211_MESHCONF_PLINK_TIMEOUT, + + /* keep last */ + __NL80211_MESHCONF_ATTR_AFTER_LAST, + NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mesh_setup_params - mesh setup parameters + * + * Mesh setup parameters. These are used to start/join a mesh and cannot be + * changed while the mesh is active. + * + * @__NL80211_MESH_SETUP_INVALID: Internal use + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a + * vendor specific path selection algorithm or disable it to use the + * default HWMP. + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a + * vendor specific path metric or disable it to use the default Airtime + * metric. + * + * @NL80211_MESH_SETUP_IE: Information elements for this mesh, for instance, a + * robust security network ie, or a vendor specific information element + * that vendors will use to identify the path selection methods and + * metrics in use. + * + * @NL80211_MESH_SETUP_USERSPACE_AUTH: Enable this option if an authentication + * daemon will be authenticating mesh candidates. + * + * @NL80211_MESH_SETUP_USERSPACE_AMPE: Enable this option if an authentication + * daemon will be securing peer link frames. AMPE is a secured version of + * Mesh Peering Management (MPM) and is implemented with the assistance of + * a userspace daemon. When this flag is set, the kernel will send peer + * management frames to a userspace daemon that will implement AMPE + * functionality (security capabilities selection, key confirmation, and + * key management). When the flag is unset (default), the kernel can + * autonomously complete (unsecured) mesh peering without the need of a + * userspace daemon. + * + * @NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: Enable this option to use a + * vendor specific synchronization method or disable it to use the default + * neighbor offset synchronization + * + * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will + * implement an MPM which handles peer allocation and state. + * + * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication + * method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE). + * Default is no authentication method required. + * + * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number + * + * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use + */ +enum nl80211_mesh_setup_params { + __NL80211_MESH_SETUP_INVALID, + NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL, + NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC, + NL80211_MESH_SETUP_IE, + NL80211_MESH_SETUP_USERSPACE_AUTH, + NL80211_MESH_SETUP_USERSPACE_AMPE, + NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, + NL80211_MESH_SETUP_USERSPACE_MPM, + NL80211_MESH_SETUP_AUTH_PROTOCOL, + + /* keep last */ + __NL80211_MESH_SETUP_ATTR_AFTER_LAST, + NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_txq_attr - TX queue parameter attributes + * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved + * @NL80211_TXQ_ATTR_AC: AC identifier (NL80211_AC_*) + * @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_AC, + 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_ac { + NL80211_AC_VO, + NL80211_AC_VI, + NL80211_AC_BE, + NL80211_AC_BK, + NL80211_NUM_ACS +}; + +/* backward compat */ +#define NL80211_TXQ_ATTR_QUEUE NL80211_TXQ_ATTR_AC +#define NL80211_TXQ_Q_VO NL80211_AC_VO +#define NL80211_TXQ_Q_VI NL80211_AC_VI +#define NL80211_TXQ_Q_BE NL80211_AC_BE +#define NL80211_TXQ_Q_BK NL80211_AC_BK + +/** + * enum nl80211_channel_type - channel type + * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel + * @NL80211_CHAN_HT20: 20 MHz HT channel + * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel + * below the control channel + * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel + * above the control channel + */ +enum nl80211_channel_type { + NL80211_CHAN_NO_HT, + NL80211_CHAN_HT20, + NL80211_CHAN_HT40MINUS, + NL80211_CHAN_HT40PLUS +}; + +/** + * enum nl80211_chan_width - channel width definitions + * + * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH + * attribute. + * + * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel + * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel + * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well + * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel + * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel + */ +enum nl80211_chan_width { + NL80211_CHAN_WIDTH_20_NOHT, + NL80211_CHAN_WIDTH_20, + NL80211_CHAN_WIDTH_40, + NL80211_CHAN_WIDTH_80, + NL80211_CHAN_WIDTH_80P80, + NL80211_CHAN_WIDTH_160, + NL80211_CHAN_WIDTH_5, + NL80211_CHAN_WIDTH_10, +}; + +/** + * enum nl80211_bss_scan_width - control channel width for a BSS + * + * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute. + * + * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible + * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide + * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide + */ +enum nl80211_bss_scan_width { + NL80211_BSS_CHAN_WIDTH_20, + NL80211_BSS_CHAN_WIDTH_10, + NL80211_BSS_CHAN_WIDTH_5, +}; + +/** + * enum nl80211_bss - netlink attributes for a BSS + * + * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets) + * @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_CHAN_WIDTH: channel width of the control channel + * (u32, enum nl80211_bss_scan_width) + * @__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, + NL80211_BSS_CHAN_WIDTH, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_bss_status - BSS "status" + * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS. + * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS. + * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS. + * + * The BSS status is a BSS attribute in scan dumps, which + * indicates the status the interface has wrt. this BSS. + */ +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_SAE: Simultaneous authentication of equals + * @__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, + NL80211_AUTHTYPE_SAE, + + /* 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) + * @NUM_NL80211_KEYTYPES: number of defined key types + */ +enum nl80211_key_type { + NL80211_KEYTYPE_GROUP, + NL80211_KEYTYPE_PAIRWISE, + NL80211_KEYTYPE_PEERKEY, + + NUM_NL80211_KEYTYPES +}; + +/** + * 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_default_types - key default types + * @__NL80211_KEY_DEFAULT_TYPE_INVALID: invalid + * @NL80211_KEY_DEFAULT_TYPE_UNICAST: key should be used as default + * unicast key + * @NL80211_KEY_DEFAULT_TYPE_MULTICAST: key should be used as default + * multicast key + * @NUM_NL80211_KEY_DEFAULT_TYPES: number of default types + */ +enum nl80211_key_default_types { + __NL80211_KEY_DEFAULT_TYPE_INVALID, + NL80211_KEY_DEFAULT_TYPE_UNICAST, + NL80211_KEY_DEFAULT_TYPE_MULTICAST, + + NUM_NL80211_KEY_DEFAULT_TYPES +}; + +/** + * 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_TYPE: the key type from enum nl80211_key_type, if not + * specified the default depends on whether a MAC address was + * given with the command using the key or not (u32) + * @NL80211_KEY_DEFAULT_TYPES: A nested attribute containing flags + * attributes, specifying what a key should be set as default as. + * See &enum nl80211_key_default_types. + * @__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, + NL80211_KEY_TYPE, + NL80211_KEY_DEFAULT_TYPES, + + /* 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_HT: HT (MCS) rates allowed for TX rate selection + * in an array of MCS numbers. + * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection, + * see &struct nl80211_txrate_vht + * @__NL80211_TXRATE_AFTER_LAST: internal + * @NL80211_TXRATE_MAX: highest TX rate attribute + */ +enum nl80211_tx_rate_attributes { + __NL80211_TXRATE_INVALID, + NL80211_TXRATE_LEGACY, + NL80211_TXRATE_HT, + NL80211_TXRATE_VHT, + + /* keep last */ + __NL80211_TXRATE_AFTER_LAST, + NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 +}; + +#define NL80211_TXRATE_MCS NL80211_TXRATE_HT +#define NL80211_VHT_NSS_MAX 8 + +/** + * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap + * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) + */ +struct nl80211_txrate_vht { + __u16 mcs[NL80211_VHT_NSS_MAX]; +}; + +/** + * 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) + * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) + */ +enum nl80211_band { + NL80211_BAND_2GHZ, + NL80211_BAND_5GHZ, + NL80211_BAND_60GHZ, +}; + +/** + * enum nl80211_ps_state - powersave state + * @NL80211_PS_DISABLED: powersave is disabled + * @NL80211_PS_ENABLED: powersave is enabled + */ +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_PKT_LOSS_EVENT: a u32 value indicating that this many + * consecutive packets were not acknowledged by the peer + * @NL80211_ATTR_CQM_TXE_RATE: TX error rate in %. Minimum % of TX failures + * during the given %NL80211_ATTR_CQM_TXE_INTVL before an + * %NL80211_CMD_NOTIFY_CQM with reported %NL80211_ATTR_CQM_TXE_RATE and + * %NL80211_ATTR_CQM_TXE_PKTS is generated. + * @NL80211_ATTR_CQM_TXE_PKTS: number of attempted packets in a given + * %NL80211_ATTR_CQM_TXE_INTVL before %NL80211_ATTR_CQM_TXE_RATE is + * checked. + * @NL80211_ATTR_CQM_TXE_INTVL: interval in seconds. Specifies the periodic + * interval in which %NL80211_ATTR_CQM_TXE_PKTS and + * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an + * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. + * @__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, + NL80211_ATTR_CQM_PKT_LOSS_EVENT, + NL80211_ATTR_CQM_TXE_RATE, + NL80211_ATTR_CQM_TXE_PKTS, + NL80211_ATTR_CQM_TXE_INTVL, + + /* 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 + * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss. + * (Note that deauth/disassoc will still follow if the AP is not + * available. This event might get used as roaming event, etc.) + */ +enum nl80211_cqm_rssi_threshold_event { + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + NL80211_CQM_RSSI_BEACON_LOSS_EVENT, +}; + + +/** + * enum nl80211_tx_power_setting - TX power adjustment + * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power + * @NL80211_TX_POWER_LIMITED: limit TX power by the mBm parameter + * @NL80211_TX_POWER_FIXED: fix TX power to the mBm parameter + */ +enum nl80211_tx_power_setting { + NL80211_TX_POWER_AUTOMATIC, + NL80211_TX_POWER_LIMITED, + NL80211_TX_POWER_FIXED, +}; + +/** + * enum nl80211_packet_pattern_attr - packet pattern attribute + * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute + * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has + * a zero bit are ignored + * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have + * a bit for each byte in the pattern. The lowest-order bit corresponds + * to the first byte of the pattern, but the bytes of the pattern are + * in a little-endian-like format, i.e. the 9th byte of the pattern + * corresponds to the lowest-order bit in the second byte of the mask. + * For example: The match 00:xx:00:00:xx:00:00:00:00:xx:xx:xx (where + * xx indicates "don't care") would be represented by a pattern of + * twelve zero bytes, and a mask of "0xed,0x01". + * Note that the pattern matching is done as though frames were not + * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked + * first (including SNAP header unpacking) and then matched. + * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after + * these fixed number of bytes of received packet + * @NUM_NL80211_PKTPAT: number of attributes + * @MAX_NL80211_PKTPAT: max attribute number + */ +enum nl80211_packet_pattern_attr { + __NL80211_PKTPAT_INVALID, + NL80211_PKTPAT_MASK, + NL80211_PKTPAT_PATTERN, + NL80211_PKTPAT_OFFSET, + + NUM_NL80211_PKTPAT, + MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1, +}; + +/** + * struct nl80211_pattern_support - packet pattern support information + * @max_patterns: maximum number of patterns supported + * @min_pattern_len: minimum length of each pattern + * @max_pattern_len: maximum length of each pattern + * @max_pkt_offset: maximum Rx packet offset + * + * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when + * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in + * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of + * %NL80211_ATTR_COALESCE_RULE in the capability information given + * by the kernel to userspace. + */ +struct nl80211_pattern_support { + __u32 max_patterns; + __u32 min_pattern_len; + __u32 max_pattern_len; + __u32 max_pkt_offset; +} __attribute__((packed)); + +/* only for backward compatibility */ +#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID +#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK +#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN +#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET +#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT +#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT +#define nl80211_wowlan_pattern_support nl80211_pattern_support + +/** + * enum nl80211_wowlan_triggers - WoWLAN trigger definitions + * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put + * the chip into a special state -- works best with chips that have + * support for low-power operation already (flag) + * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect + * is detected is implementation-specific (flag) + * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed + * by 16 repetitions of MAC addr, anywhere in payload) (flag) + * @NL80211_WOWLAN_TRIG_PKT_PATTERN: wake up on the specified packet patterns + * which are passed in an array of nested attributes, each nested attribute + * defining a with attributes from &struct nl80211_wowlan_trig_pkt_pattern. + * Each pattern defines a wakeup packet. Packet offset is associated with + * each pattern which is used while matching the pattern. The matching is + * done on the MSDU, i.e. as though the packet was an 802.3 packet, so the + * pattern matching is done after the packet is converted to the MSDU. + * + * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute + * carrying a &struct nl80211_pattern_support. + * + * When reporting wakeup. it is a u32 attribute containing the 0-based + * index of the pattern that caused the wakeup, in the patterns passed + * to the kernel when configuring. + * @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be + * used when setting, used only to indicate that GTK rekeying is supported + * by the device (flag) + * @NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: wake up on GTK rekey failure (if + * done by the device) (flag) + * @NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: wake up on EAP Identity Request + * packet (flag) + * @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag) + * @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released + * (on devices that have rfkill in the device) (flag) + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains + * the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame + * may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN + * attribute contains the original length. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11 + * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211 + * attribute if the packet was truncated somewhere. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the + * 802.11 packet that caused the wakeup, e.g. a magic packet. The frame may + * be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute + * contains the original length. + * @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3 + * packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 + * attribute if the packet was truncated somewhere. + * @NL80211_WOWLAN_TRIG_TCP_CONNECTION: TCP connection wake, see DOC section + * "TCP connection wakeup" for more details. This is a nested attribute + * containing the exact information for establishing and keeping alive + * the TCP connection. + * @NL80211_WOWLAN_TRIG_TCP_WAKEUP_MATCH: For wakeup reporting only, the + * wakeup packet was received on the TCP connection + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: For wakeup reporting only, the + * TCP connection was lost or failed to be established + * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only, + * the TCP connection ran out of tokens to use for data to send to the + * service + * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers + * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number + * + * These nested attributes are used to configure the wakeup triggers and + * to report the wakeup reason(s). + */ +enum nl80211_wowlan_triggers { + __NL80211_WOWLAN_TRIG_INVALID, + NL80211_WOWLAN_TRIG_ANY, + NL80211_WOWLAN_TRIG_DISCONNECT, + NL80211_WOWLAN_TRIG_MAGIC_PKT, + NL80211_WOWLAN_TRIG_PKT_PATTERN, + NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED, + NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, + NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, + NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, + NL80211_WOWLAN_TRIG_RFKILL_RELEASE, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023, + NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN, + NL80211_WOWLAN_TRIG_TCP_CONNECTION, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST, + NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS, + + /* keep last */ + NUM_NL80211_WOWLAN_TRIG, + MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1 +}; + +/** + * DOC: TCP connection wakeup + * + * Some devices can establish a TCP connection in order to be woken up by a + * packet coming in from outside their network segment, or behind NAT. If + * configured, the device will establish a TCP connection to the given + * service, and periodically send data to that service. The first data + * packet is usually transmitted after SYN/ACK, also ACKing the SYN/ACK. + * The data packets can optionally include a (little endian) sequence + * number (in the TCP payload!) that is generated by the device, and, also + * optionally, a token from a list of tokens. This serves as a keep-alive + * with the service, and for NATed connections, etc. + * + * During this keep-alive period, the server doesn't send any data to the + * client. When receiving data, it is compared against the wakeup pattern + * (and mask) and if it matches, the host is woken up. Similarly, if the + * connection breaks or cannot be established to start with, the host is + * also woken up. + * + * Developer's note: ARP offload is required for this, otherwise TCP + * response packets might not go through correctly. + */ + +/** + * struct nl80211_wowlan_tcp_data_seq - WoWLAN TCP data sequence + * @start: starting value + * @offset: offset of sequence number in packet + * @len: length of the sequence value to write, 1 through 4 + * + * Note: don't confuse with the TCP sequence number(s), this is for the + * keepalive packet payload. The actual value is written into the packet + * in little endian. + */ +struct nl80211_wowlan_tcp_data_seq { + __u32 start, offset, len; +}; + +/** + * struct nl80211_wowlan_tcp_data_token - WoWLAN TCP data token config + * @offset: offset of token in packet + * @len: length of each token + * @token_stream: stream of data to be used for the tokens, the length must + * be a multiple of @len for this to make sense + */ +struct nl80211_wowlan_tcp_data_token { + __u32 offset, len; + __u8 token_stream[]; +}; + +/** + * struct nl80211_wowlan_tcp_data_token_feature - data token features + * @min_len: minimum token length + * @max_len: maximum token length + * @bufsize: total available token buffer size (max size of @token_stream) + */ +struct nl80211_wowlan_tcp_data_token_feature { + __u32 min_len, max_len, bufsize; +}; + +/** + * enum nl80211_wowlan_tcp_attrs - WoWLAN TCP connection parameters + * @__NL80211_WOWLAN_TCP_INVALID: invalid number for nested attributes + * @NL80211_WOWLAN_TCP_SRC_IPV4: source IPv4 address (in network byte order) + * @NL80211_WOWLAN_TCP_DST_IPV4: destination IPv4 address + * (in network byte order) + * @NL80211_WOWLAN_TCP_DST_MAC: destination MAC address, this is given because + * route lookup when configured might be invalid by the time we suspend, + * and doing a route lookup when suspending is no longer possible as it + * might require ARP querying. + * @NL80211_WOWLAN_TCP_SRC_PORT: source port (u16); optional, if not given a + * socket and port will be allocated + * @NL80211_WOWLAN_TCP_DST_PORT: destination port (u16) + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD: data packet payload, at least one byte. + * For feature advertising, a u32 attribute holding the maximum length + * of the data payload. + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: data packet sequence configuration + * (if desired), a &struct nl80211_wowlan_tcp_data_seq. For feature + * advertising it is just a flag + * @NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: data packet token configuration, + * see &struct nl80211_wowlan_tcp_data_token and for advertising see + * &struct nl80211_wowlan_tcp_data_token_feature. + * @NL80211_WOWLAN_TCP_DATA_INTERVAL: data interval in seconds, maximum + * interval in feature advertising (u32) + * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a + * u32 attribute holding the maximum length + * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for + * feature advertising. The mask works like @NL80211_PKTPAT_MASK + * but on the TCP payload only. + * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes + * @MAX_NL80211_WOWLAN_TCP: highest attribute number + */ +enum nl80211_wowlan_tcp_attrs { + __NL80211_WOWLAN_TCP_INVALID, + NL80211_WOWLAN_TCP_SRC_IPV4, + NL80211_WOWLAN_TCP_DST_IPV4, + NL80211_WOWLAN_TCP_DST_MAC, + NL80211_WOWLAN_TCP_SRC_PORT, + NL80211_WOWLAN_TCP_DST_PORT, + NL80211_WOWLAN_TCP_DATA_PAYLOAD, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, + NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, + NL80211_WOWLAN_TCP_DATA_INTERVAL, + NL80211_WOWLAN_TCP_WAKE_PAYLOAD, + NL80211_WOWLAN_TCP_WAKE_MASK, + + /* keep last */ + NUM_NL80211_WOWLAN_TCP, + MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1 +}; + +/** + * struct nl80211_coalesce_rule_support - coalesce rule support information + * @max_rules: maximum number of rules supported + * @pat: packet pattern support information + * @max_delay: maximum supported coalescing delay in msecs + * + * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the + * capability information given by the kernel to userspace. + */ +struct nl80211_coalesce_rule_support { + __u32 max_rules; + struct nl80211_pattern_support pat; + __u32 max_delay; +} __attribute__((packed)); + +/** + * enum nl80211_attr_coalesce_rule - coalesce rule attribute + * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute + * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing + * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence, + * see &enum nl80211_coalesce_condition. + * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched + * after these fixed number of bytes of received packet + * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes + * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number + */ +enum nl80211_attr_coalesce_rule { + __NL80211_COALESCE_RULE_INVALID, + NL80211_ATTR_COALESCE_RULE_DELAY, + NL80211_ATTR_COALESCE_RULE_CONDITION, + NL80211_ATTR_COALESCE_RULE_PKT_PATTERN, + + /* keep last */ + NUM_NL80211_ATTR_COALESCE_RULE, + NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1 +}; + +/** + * enum nl80211_coalesce_condition - coalesce rule conditions + * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns + * in a rule are matched. + * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns + * in a rule are not matched. + */ +enum nl80211_coalesce_condition { + NL80211_COALESCE_CONDITION_MATCH, + NL80211_COALESCE_CONDITION_NO_MATCH +}; + +/** + * enum nl80211_iface_limit_attrs - limit attributes + * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) + * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that + * can be chosen from this set of interface types (u32) + * @NL80211_IFACE_LIMIT_TYPES: nested attribute containing a + * flag attribute for each interface type in this set + * @NUM_NL80211_IFACE_LIMIT: number of attributes + * @MAX_NL80211_IFACE_LIMIT: highest attribute number + */ +enum nl80211_iface_limit_attrs { + NL80211_IFACE_LIMIT_UNSPEC, + NL80211_IFACE_LIMIT_MAX, + NL80211_IFACE_LIMIT_TYPES, + + /* keep last */ + NUM_NL80211_IFACE_LIMIT, + MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1 +}; + +/** + * enum nl80211_if_combination_attrs -- interface combination attributes + * + * @NL80211_IFACE_COMB_UNSPEC: (reserved) + * @NL80211_IFACE_COMB_LIMITS: Nested attributes containing the limits + * for given interface types, see &enum nl80211_iface_limit_attrs. + * @NL80211_IFACE_COMB_MAXNUM: u32 attribute giving the total number of + * interfaces that can be created in this group. This number doesn't + * apply to interfaces purely managed in software, which are listed + * in a separate attribute %NL80211_ATTR_INTERFACES_SOFTWARE. + * @NL80211_IFACE_COMB_STA_AP_BI_MATCH: flag attribute specifying that + * beacon intervals within this group must be all the same even for + * infrastructure and AP/GO combinations, i.e. the GO(s) must adopt + * the infrastructure network's beacon interval. + * @NL80211_IFACE_COMB_NUM_CHANNELS: u32 attribute specifying how many + * different channels may be used within this group. + * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap + * of supported channel widths for radar detection. + * @NUM_NL80211_IFACE_COMB: number of attributes + * @MAX_NL80211_IFACE_COMB: highest attribute number + * + * Examples: + * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 + * => allows an AP and a STA that must match BIs + * + * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8 + * => allows 8 of AP/GO + * + * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 + * => allows two STAs on different channels + * + * numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4 + * => allows a STA plus three P2P interfaces + * + * The list of these four possiblities could completely be contained + * within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate + * that any of these groups must match. + * + * "Combinations" of just a single interface will not be listed here, + * a single interface of any valid interface type is assumed to always + * be possible by itself. This means that implicitly, for each valid + * interface type, the following group always exists: + * numbers = [ #{} <= 1 ], channels = 1, max = 1 + */ +enum nl80211_if_combination_attrs { + NL80211_IFACE_COMB_UNSPEC, + NL80211_IFACE_COMB_LIMITS, + NL80211_IFACE_COMB_MAXNUM, + NL80211_IFACE_COMB_STA_AP_BI_MATCH, + NL80211_IFACE_COMB_NUM_CHANNELS, + NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, + + /* keep last */ + NUM_NL80211_IFACE_COMB, + MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1 +}; + + +/** + * enum nl80211_plink_state - state of a mesh peer link finite state machine + * + * @NL80211_PLINK_LISTEN: initial state, considered the implicit + * state of non existant mesh peer links + * @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to + * this mesh peer + * @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received + * from this mesh peer + * @NL80211_PLINK_CNF_RCVD: mesh plink confirm frame has been + * received from this mesh peer + * @NL80211_PLINK_ESTAB: mesh peer link is established + * @NL80211_PLINK_HOLDING: mesh peer link is being closed or cancelled + * @NL80211_PLINK_BLOCKED: all frames transmitted from this mesh + * plink are discarded + * @NUM_NL80211_PLINK_STATES: number of peer link states + * @MAX_NL80211_PLINK_STATES: highest numerical value of plink states + */ +enum nl80211_plink_state { + NL80211_PLINK_LISTEN, + NL80211_PLINK_OPN_SNT, + NL80211_PLINK_OPN_RCVD, + NL80211_PLINK_CNF_RCVD, + NL80211_PLINK_ESTAB, + NL80211_PLINK_HOLDING, + NL80211_PLINK_BLOCKED, + + /* keep last */ + NUM_NL80211_PLINK_STATES, + MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1 +}; + +/** + * enum nl80211_plink_action - actions to perform in mesh peers + * + * @NL80211_PLINK_ACTION_NO_ACTION: perform no action + * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment + * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer + * @NUM_NL80211_PLINK_ACTIONS: number of possible actions + */ +enum plink_actions { + NL80211_PLINK_ACTION_NO_ACTION, + NL80211_PLINK_ACTION_OPEN, + NL80211_PLINK_ACTION_BLOCK, + + NUM_NL80211_PLINK_ACTIONS, +}; + + +#define NL80211_KCK_LEN 16 +#define NL80211_KEK_LEN 16 +#define NL80211_REPLAY_CTR_LEN 8 + +/** + * enum nl80211_rekey_data - attributes for GTK rekey offload + * @__NL80211_REKEY_DATA_INVALID: invalid number for nested attributes + * @NL80211_REKEY_DATA_KEK: key encryption key (binary) + * @NL80211_REKEY_DATA_KCK: key confirmation key (binary) + * @NL80211_REKEY_DATA_REPLAY_CTR: replay counter (binary) + * @NUM_NL80211_REKEY_DATA: number of rekey attributes (internal) + * @MAX_NL80211_REKEY_DATA: highest rekey attribute (internal) + */ +enum nl80211_rekey_data { + __NL80211_REKEY_DATA_INVALID, + NL80211_REKEY_DATA_KEK, + NL80211_REKEY_DATA_KCK, + NL80211_REKEY_DATA_REPLAY_CTR, + + /* keep last */ + NUM_NL80211_REKEY_DATA, + MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1 +}; + +/** + * enum nl80211_hidden_ssid - values for %NL80211_ATTR_HIDDEN_SSID + * @NL80211_HIDDEN_SSID_NOT_IN_USE: do not hide SSID (i.e., broadcast it in + * Beacon frames) + * @NL80211_HIDDEN_SSID_ZERO_LEN: hide SSID by using zero-length SSID element + * in Beacon frames + * @NL80211_HIDDEN_SSID_ZERO_CONTENTS: hide SSID by using correct length of SSID + * element in Beacon frames but zero out each byte in the SSID + */ +enum nl80211_hidden_ssid { + NL80211_HIDDEN_SSID_NOT_IN_USE, + NL80211_HIDDEN_SSID_ZERO_LEN, + NL80211_HIDDEN_SSID_ZERO_CONTENTS +}; + +/** + * enum nl80211_sta_wme_attr - station WME attributes + * @__NL80211_STA_WME_INVALID: invalid number for nested attribute + * @NL80211_STA_WME_UAPSD_QUEUES: bitmap of uapsd queues. the format + * is the same as the AC bitmap in the QoS info field. + * @NL80211_STA_WME_MAX_SP: max service period. the format is the same + * as the MAX_SP field in the QoS info field (but already shifted down). + * @__NL80211_STA_WME_AFTER_LAST: internal + * @NL80211_STA_WME_MAX: highest station WME attribute + */ +enum nl80211_sta_wme_attr { + __NL80211_STA_WME_INVALID, + NL80211_STA_WME_UAPSD_QUEUES, + NL80211_STA_WME_MAX_SP, + + /* keep last */ + __NL80211_STA_WME_AFTER_LAST, + NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 +}; + +/** + * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates + * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes + * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher + * priority) + * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets) + * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag) + * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes + * (internal) + * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute + * (internal) + */ +enum nl80211_pmksa_candidate_attr { + __NL80211_PMKSA_CANDIDATE_INVALID, + NL80211_PMKSA_CANDIDATE_INDEX, + NL80211_PMKSA_CANDIDATE_BSSID, + NL80211_PMKSA_CANDIDATE_PREAUTH, + + /* keep last */ + NUM_NL80211_PMKSA_CANDIDATE, + MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 +}; + +/** + * enum nl80211_tdls_operation - values for %NL80211_ATTR_TDLS_OPERATION + * @NL80211_TDLS_DISCOVERY_REQ: Send a TDLS discovery request + * @NL80211_TDLS_SETUP: Setup TDLS link + * @NL80211_TDLS_TEARDOWN: Teardown a TDLS link which is already established + * @NL80211_TDLS_ENABLE_LINK: Enable TDLS link + * @NL80211_TDLS_DISABLE_LINK: Disable TDLS link + */ +enum nl80211_tdls_operation { + NL80211_TDLS_DISCOVERY_REQ, + NL80211_TDLS_SETUP, + NL80211_TDLS_TEARDOWN, + NL80211_TDLS_ENABLE_LINK, + NL80211_TDLS_DISABLE_LINK, +}; + +/* + * enum nl80211_ap_sme_features - device-integrated AP features + * Reserved for future use, no bits are defined in + * NL80211_ATTR_DEVICE_AP_SME yet. +enum nl80211_ap_sme_features { +}; + */ + +/** + * enum nl80211_feature_flags - device/driver features + * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back + * TX status to the socket error queue when requested with the + * socket option. + * @NL80211_FEATURE_HT_IBSS: This driver supports IBSS with HT datarates. + * @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up + * the connected inactive stations in AP mode. + * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested + * to work properly to suppport receiving regulatory hints from + * cellular base stations. + * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active + * P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel + * in the interface combinations, even when it's only used for scan + * and remain-on-channel. This could be due to, for example, the + * remain-on-channel implementation requiring a channel context. + * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of + * equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station + * mode + * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan + * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported + * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif + * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting + * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform + * OBSS scans and generate 20/40 BSS coex reports. This flag is used only + * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. + * @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window + * setting + * @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic + * powersave + * @NL80211_FEATURE_FULL_AP_CLIENT_STATE: The driver supports full state + * transitions for AP clients. Without this flag (and if the driver + * doesn't have the AP SME in the device) the driver supports adding + * stations only when they're associated and adds them in associated + * state (to later be transitioned into authorized), with this flag + * they should be added before even sending the authentication reply + * and then transitioned into authenticated, associated and authorized + * states using station flags. + * Note that even for drivers that support this, the default is to add + * stations in authenticated/associated state, so to add unauthenticated + * stations the authenticated/associated bits have to be set in the mask. + * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits + * (HT40, VHT 80/160 MHz) if this flag is set + * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh + * Peering Management entity which may be implemented by registering for + * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is + * still generated by the driver. + * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor + * interface. An active monitor interface behaves like a normal monitor + * interface, but gets added to the driver. It ensures that incoming + * unicast packets directed at the configured interface address get ACKed. + */ +enum nl80211_feature_flags { + NL80211_FEATURE_SK_TX_STATUS = 1 << 0, + NL80211_FEATURE_HT_IBSS = 1 << 1, + NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2, + NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3, + NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4, + NL80211_FEATURE_SAE = 1 << 5, + NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, + NL80211_FEATURE_SCAN_FLUSH = 1 << 7, + NL80211_FEATURE_AP_SCAN = 1 << 8, + NL80211_FEATURE_VIF_TXPOWER = 1 << 9, + NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, + NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11, + NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, + /* bit 13 is reserved */ + NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14, + NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15, + NL80211_FEATURE_USERSPACE_MPM = 1 << 16, + NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17, +}; + +/** + * enum nl80211_probe_resp_offload_support_attr - optional supported + * protocols for probe-response offloading by the driver/FW. + * To be used with the %NL80211_ATTR_PROBE_RESP_OFFLOAD attribute. + * Each enum value represents a bit in the bitmap of supported + * protocols. Typically a subset of probe-requests belonging to a + * supported protocol will be excluded from offload and uploaded + * to the host. + * + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: Support for WPS ver. 1 + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: Support for WPS ver. 2 + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: Support for P2P + * @NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: Support for 802.11u + */ +enum nl80211_probe_resp_offload_support_attr { + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1<<0, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1<<1, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1<<2, + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1<<3, +}; + +/** + * enum nl80211_connect_failed_reason - connection request failed reasons + * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be + * handled by the AP is reached. + * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Connection request is rejected due to ACL. + */ +enum nl80211_connect_failed_reason { + NL80211_CONN_FAIL_MAX_CLIENTS, + NL80211_CONN_FAIL_BLOCKED_CLIENT, +}; + +/** + * enum nl80211_scan_flags - scan request control flags + * + * Scan request control flags are used to control the handling + * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN + * requests. + * + * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority + * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning + * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured + * as AP and the beaconing has already been configured. This attribute is + * dangerous because will destroy stations performance as a lot of frames + * will be lost while scanning off-channel, therefore it must be used only + * when really needed + */ +enum nl80211_scan_flags { + NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, + NL80211_SCAN_FLAG_FLUSH = 1<<1, + NL80211_SCAN_FLAG_AP = 1<<2, +}; + +/** + * enum nl80211_acl_policy - access control policy + * + * Access control policy is applied on a MAC list set by + * %NL80211_CMD_START_AP and %NL80211_CMD_SET_MAC_ACL, to + * be used with %NL80211_ATTR_ACL_POLICY. + * + * @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are + * listed in ACL, i.e. allow all the stations which are not listed + * in ACL to authenticate. + * @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow the stations which are listed + * in ACL, i.e. deny all the stations which are not listed in ACL. + */ +enum nl80211_acl_policy { + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED, + NL80211_ACL_POLICY_DENY_UNLESS_LISTED, +}; + +/** + * enum nl80211_radar_event - type of radar event for DFS operation + * + * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace + * about detected radars or success of the channel available check (CAC) + * + * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is + * now unusable. + * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished, + * the channel is now available. + * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no + * change to the channel status. + * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is + * over, channel becomes usable. + */ +enum nl80211_radar_event { + NL80211_RADAR_DETECTED, + NL80211_RADAR_CAC_FINISHED, + NL80211_RADAR_CAC_ABORTED, + NL80211_RADAR_NOP_FINISHED, +}; + +/** + * enum nl80211_dfs_state - DFS states for channels + * + * Channel states used by the DFS code. + * + * @NL80211_DFS_USABLE: The channel can be used, but channel availability + * check (CAC) must be performed before using it for AP or IBSS. + * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it + * is therefore marked as not available. + * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available. + */ +enum nl80211_dfs_state { + NL80211_DFS_USABLE, + NL80211_DFS_UNAVAILABLE, + NL80211_DFS_AVAILABLE, +}; + +/** + * enum enum nl80211_protocol_features - nl80211 protocol features + * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting + * wiphy dumps (if requested by the application with the attribute + * %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the + * wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or + * %NL80211_ATTR_WDEV. + */ +enum nl80211_protocol_features { + NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0, +}; + +/** + * enum nl80211_crit_proto_id - nl80211 critical protocol identifiers + * + * @NL80211_CRIT_PROTO_UNSPEC: protocol unspecified. + * @NL80211_CRIT_PROTO_DHCP: BOOTP or DHCPv6 protocol. + * @NL80211_CRIT_PROTO_EAPOL: EAPOL protocol. + * @NL80211_CRIT_PROTO_APIPA: APIPA protocol. + * @NUM_NL80211_CRIT_PROTO: must be kept last. + */ +enum nl80211_crit_proto_id { + NL80211_CRIT_PROTO_UNSPEC, + NL80211_CRIT_PROTO_DHCP, + NL80211_CRIT_PROTO_EAPOL, + NL80211_CRIT_PROTO_APIPA, + /* add other protocols before this one */ + NUM_NL80211_CRIT_PROTO +}; + +/* maximum duration for critical protocol measures */ +#define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */ + +/** + * enum nl80211_rxmgmt_flags - flags for received management frame. + * + * Used by cfg80211_rx_mgmt() + * + * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver. + */ +enum nl80211_rxmgmt_flags { + NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, +}; + +/* + * If this flag is unset, the lower 24 bits are an OUI, if set + * a Linux nl80211 vendor ID is used (no such IDs are allocated + * yet, so that's not valid so far) + */ +#define NL80211_VENDOR_ID_IS_LINUX 0x80000000 + +/** + * struct nl80211_vendor_cmd_info - vendor command data + * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the + * value is a 24-bit OUI; if it is set then a separately allocated ID + * may be used, but no such IDs are allocated yet. New IDs should be + * added to this file when needed. + * @subcmd: sub-command ID for the command + */ +struct nl80211_vendor_cmd_info { + __u32 vendor_id; + __u32 subcmd; +}; + +#endif /* __LINUX_NL80211_H */ diff --git a/contrib/hostapd/src/drivers/priv_netlink.h b/contrib/hostapd/src/drivers/priv_netlink.h index 2a31e251a0..62320880c1 100644 --- a/contrib/hostapd/src/drivers/priv_netlink.h +++ b/contrib/hostapd/src/drivers/priv_netlink.h @@ -2,14 +2,8 @@ * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions. * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PRIV_NETLINK_H @@ -53,8 +47,17 @@ #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)) @@ -66,6 +69,7 @@ (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))) +#define RTA_PAYLOAD(rta) ((int) ((rta)->rta_len) - RTA_LENGTH(0)) struct sockaddr_nl diff --git a/contrib/hostapd/src/drivers/radiotap.c b/contrib/hostapd/src/drivers/radiotap.c deleted file mode 100644 index 804473fa4b..0000000000 --- a/contrib/hostapd/src/drivers/radiotap.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Radiotap parser - * - * Copyright 2007 Andy Green - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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 - * 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<arg)) & - (1<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/contrib/hostapd/src/drivers/radiotap_iter.h b/contrib/hostapd/src/drivers/radiotap_iter.h deleted file mode 100644 index 92a798a670..0000000000 --- a/contrib/hostapd/src/drivers/radiotap_iter.h +++ /dev/null @@ -1,41 +0,0 @@ -#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/contrib/hostapd/src/drivers/rfkill.c b/contrib/hostapd/src/drivers/rfkill.c new file mode 100644 index 0000000000..45b26c46b6 --- /dev/null +++ b/contrib/hostapd/src/drivers/rfkill.c @@ -0,0 +1,188 @@ +/* + * Linux rfkill helper functions for driver wrappers + * Copyright (c) 2010, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "rfkill.h" + +#define RFKILL_EVENT_SIZE_V1 8 + +struct rfkill_event { + u32 idx; + u8 type; + u8 op; + u8 soft; + u8 hard; +} STRUCT_PACKED; + +enum rfkill_operation { + RFKILL_OP_ADD = 0, + RFKILL_OP_DEL, + RFKILL_OP_CHANGE, + RFKILL_OP_CHANGE_ALL, +}; + +enum rfkill_type { + RFKILL_TYPE_ALL = 0, + RFKILL_TYPE_WLAN, + RFKILL_TYPE_BLUETOOTH, + RFKILL_TYPE_UWB, + RFKILL_TYPE_WIMAX, + RFKILL_TYPE_WWAN, + RFKILL_TYPE_GPS, + RFKILL_TYPE_FM, + NUM_RFKILL_TYPES, +}; + + +struct rfkill_data { + struct rfkill_config *cfg; + int fd; + int blocked; +}; + + +static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct rfkill_data *rfkill = eloop_ctx; + struct rfkill_event event; + ssize_t len; + int new_blocked; + + len = read(rfkill->fd, &event, sizeof(event)); + if (len < 0) { + wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s", + strerror(errno)); + return; + } + if (len != RFKILL_EVENT_SIZE_V1) { + wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size " + "%d (expected %d)", + (int) len, RFKILL_EVENT_SIZE_V1); + return; + } + wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d " + "op=%u soft=%u hard=%u", + event.idx, event.type, event.op, event.soft, + event.hard); + if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN) + return; + + if (event.hard) { + wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); + new_blocked = 1; + } else if (event.soft) { + wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); + new_blocked = 1; + } else { + wpa_printf(MSG_INFO, "rfkill: WLAN unblocked"); + new_blocked = 0; + } + + if (new_blocked != rfkill->blocked) { + rfkill->blocked = new_blocked; + if (new_blocked) + rfkill->cfg->blocked_cb(rfkill->cfg->ctx); + else + rfkill->cfg->unblocked_cb(rfkill->cfg->ctx); + } +} + + +struct rfkill_data * rfkill_init(struct rfkill_config *cfg) +{ + struct rfkill_data *rfkill; + struct rfkill_event event; + ssize_t len; + + rfkill = os_zalloc(sizeof(*rfkill)); + if (rfkill == NULL) + return NULL; + + rfkill->cfg = cfg; + rfkill->fd = open("/dev/rfkill", O_RDONLY); + if (rfkill->fd < 0) { + wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control " + "device"); + goto fail; + } + + if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) { + wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: " + "%s", strerror(errno)); + goto fail2; + } + + for (;;) { + len = read(rfkill->fd, &event, sizeof(event)); + if (len < 0) { + if (errno == EAGAIN) + break; /* No more entries */ + wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s", + strerror(errno)); + break; + } + if (len != RFKILL_EVENT_SIZE_V1) { + wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size " + "%d (expected %d)", + (int) len, RFKILL_EVENT_SIZE_V1); + continue; + } + wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d " + "op=%u soft=%u hard=%u", + event.idx, event.type, event.op, event.soft, + event.hard); + if (event.op != RFKILL_OP_ADD || + event.type != RFKILL_TYPE_WLAN) + continue; + if (event.hard) { + wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); + rfkill->blocked = 1; + } else if (event.soft) { + wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); + rfkill->blocked = 1; + } + } + + eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL); + + return rfkill; + +fail2: + close(rfkill->fd); +fail: + os_free(rfkill); + return NULL; +} + + +void rfkill_deinit(struct rfkill_data *rfkill) +{ + if (rfkill == NULL) + return; + + if (rfkill->fd >= 0) { + eloop_unregister_read_sock(rfkill->fd); + close(rfkill->fd); + } + + os_free(rfkill->cfg); + os_free(rfkill); +} + + +int rfkill_is_blocked(struct rfkill_data *rfkill) +{ + if (rfkill == NULL) + return 0; + + return rfkill->blocked; +} diff --git a/contrib/hostapd/src/drivers/rfkill.h b/contrib/hostapd/src/drivers/rfkill.h new file mode 100644 index 0000000000..0412ac3305 --- /dev/null +++ b/contrib/hostapd/src/drivers/rfkill.h @@ -0,0 +1,25 @@ +/* + * Linux rfkill helper functions for driver wrappers + * Copyright (c) 2010, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RFKILL_H +#define RFKILL_H + +struct rfkill_data; + +struct rfkill_config { + void *ctx; + char ifname[IFNAMSIZ]; + void (*blocked_cb)(void *ctx); + void (*unblocked_cb)(void *ctx); +}; + +struct rfkill_data * rfkill_init(struct rfkill_config *cfg); +void rfkill_deinit(struct rfkill_data *rfkill); +int rfkill_is_blocked(struct rfkill_data *rfkill); + +#endif /* RFKILL_H */ diff --git a/contrib/hostapd/src/drivers/scan_helpers.c b/contrib/hostapd/src/drivers/scan_helpers.c deleted file mode 100644 index 63387701ea..0000000000 --- a/contrib/hostapd/src/drivers/scan_helpers.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * WPA Supplicant - Helper functions for scan result processing - * Copyright (c) 2007-2008, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "drivers/driver.h" -#include "ieee802_11_defs.h" - - -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; -} - - -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; -} - - -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); -} - - -/* 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; -} - - -void wpa_scan_sort_results(struct wpa_scan_results *res) -{ - qsort(res->res, res->num, sizeof(struct wpa_scan_res *), - wpa_scan_result_compar); -} diff --git a/contrib/hostapd/src/eap_common/chap.c b/contrib/hostapd/src/eap_common/chap.c index a088aff64e..820d18a416 100644 --- a/contrib/hostapd/src/eap_common/chap.c +++ b/contrib/hostapd/src/eap_common/chap.c @@ -1,25 +1,18 @@ /* * CHAP-MD5 (RFC 1994) - * Copyright (c) 2007, Jouni Malinen + * Copyright (c) 2007-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "md5.h" -#include "crypto.h" +#include "crypto/crypto.h" #include "chap.h" -void chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, +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]; @@ -31,5 +24,5 @@ void chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, len[1] = secret_len; addr[2] = challenge; len[2] = challenge_len; - md5_vector(3, addr, len, response); + return md5_vector(3, addr, len, response); } diff --git a/contrib/hostapd/src/eap_common/chap.h b/contrib/hostapd/src/eap_common/chap.h index 209dc8a48f..a791505f99 100644 --- a/contrib/hostapd/src/eap_common/chap.h +++ b/contrib/hostapd/src/eap_common/chap.h @@ -1,15 +1,9 @@ /* * CHAP-MD5 (RFC 1994) - * Copyright (c) 2007, Jouni Malinen + * Copyright (c) 2007-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef CHAP_H @@ -17,7 +11,7 @@ #define CHAP_MD5_LEN 16 -void chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, - size_t challenge_len, u8 *response); +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/contrib/hostapd/src/eap_common/eap_common.c b/contrib/hostapd/src/eap_common/eap_common.c index 4afa1ddb2a..7b077cb9f5 100644 --- a/contrib/hostapd/src/eap_common/eap_common.c +++ b/contrib/hostapd/src/eap_common/eap_common.c @@ -1,15 +1,9 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,6 +12,41 @@ #include "eap_defs.h" #include "eap_common.h" +/** + * eap_hdr_len_valid - Validate EAP header length field + * @msg: EAP frame (starting with EAP header) + * @min_payload: Minimum payload length needed + * Returns: 1 for valid header, 0 for invalid + * + * This is a helper function that does minimal validation of EAP messages. The + * length field is verified to be large enough to include the header and not + * too large to go beyond the end of the buffer. + */ +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload) +{ + const struct eap_hdr *hdr; + size_t len; + + if (msg == NULL) + return 0; + + hdr = wpabuf_head(msg); + + if (wpabuf_len(msg) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP: Too short EAP frame"); + return 0; + } + + len = be_to_host16(hdr->length); + if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) { + wpa_printf(MSG_INFO, "EAP: Invalid EAP length"); + return 0; + } + + return 1; +} + + /** * eap_hdr_validate - Validate EAP header * @vendor: Expected EAP Vendor-Id (0 = IETF) @@ -41,19 +70,11 @@ const u8 * eap_hdr_validate(int vendor, EapType eap_type, 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"); + if (!eap_hdr_len_valid(msg, 1)) return NULL; - } + hdr = wpabuf_head(msg); 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) { diff --git a/contrib/hostapd/src/eap_common/eap_common.h b/contrib/hostapd/src/eap_common/eap_common.h index b95e76b94f..8850c1fe55 100644 --- a/contrib/hostapd/src/eap_common/eap_common.h +++ b/contrib/hostapd/src/eap_common/eap_common.h @@ -1,15 +1,9 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_COMMON_H @@ -17,6 +11,7 @@ #include "wpabuf.h" +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload); 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, diff --git a/contrib/hostapd/src/eap_common/eap_defs.h b/contrib/hostapd/src/eap_common/eap_defs.h index 0efe7ab77e..f5890bec27 100644 --- a/contrib/hostapd/src/eap_common/eap_defs.h +++ b/contrib/hostapd/src/eap_common/eap_defs.h @@ -2,14 +2,8 @@ * EAP server/peer: Shared EAP definitions * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_DEFS_H @@ -66,8 +60,10 @@ typedef enum { 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_AKA_PRIME = 50 /* RFC 5448 */, EAP_TYPE_GPSK = 51 /* RFC 5433 */, + EAP_TYPE_PWD = 52 /* RFC 5931 */, + EAP_TYPE_EKE = 53 /* RFC 6124 */, EAP_TYPE_EXPANDED = 254 /* RFC 3748 */ } EapType; @@ -76,9 +72,13 @@ typedef enum { enum { EAP_VENDOR_IETF = 0, EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, - EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */ + EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */, + EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */ }; +#define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP +#define EAP_VENDOR_TYPE_UNAUTH_TLS 1 + #define EAP_MSK_LEN 64 #define EAP_EMSK_LEN 64 diff --git a/contrib/hostapd/src/eap_common/eap_eke_common.c b/contrib/hostapd/src/eap_common/eap_eke_common.c new file mode 100644 index 0000000000..a62ac8e046 --- /dev/null +++ b/contrib/hostapd/src/eap_common/eap_eke_common.c @@ -0,0 +1,768 @@ +/* + * EAP server/peer: EAP-EKE shared routines + * Copyright (c) 2011-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/dh_groups.h" +#include "crypto/random.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "eap_common/eap_defs.h" +#include "eap_eke_common.h" + + +static int eap_eke_dh_len(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return 128; + case EAP_EKE_DHGROUP_EKE_5: + return 192; + case EAP_EKE_DHGROUP_EKE_14: + return 256; + case EAP_EKE_DHGROUP_EKE_15: + return 384; + case EAP_EKE_DHGROUP_EKE_16: + return 512; + } + + return -1; +} + + +static int eap_eke_dhcomp_len(u8 dhgroup, u8 encr) +{ + int dhlen; + + dhlen = eap_eke_dh_len(dhgroup); + if (dhlen < 0) + return -1; + if (encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + return AES_BLOCK_SIZE + dhlen; +} + + +static const struct dh_group * eap_eke_dh_group(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return dh_groups_get(2); + case EAP_EKE_DHGROUP_EKE_5: + return dh_groups_get(5); + case EAP_EKE_DHGROUP_EKE_14: + return dh_groups_get(14); + case EAP_EKE_DHGROUP_EKE_15: + return dh_groups_get(15); + case EAP_EKE_DHGROUP_EKE_16: + return dh_groups_get(16); + } + + return NULL; +} + + +static int eap_eke_dh_generator(u8 group) +{ + switch (group) { + case EAP_EKE_DHGROUP_EKE_2: + return 5; + case EAP_EKE_DHGROUP_EKE_5: + return 31; + case EAP_EKE_DHGROUP_EKE_14: + return 11; + case EAP_EKE_DHGROUP_EKE_15: + return 5; + case EAP_EKE_DHGROUP_EKE_16: + return 5; + } + + return -1; +} + + +static int eap_eke_pnonce_len(u8 mac) +{ + int mac_len; + + if (mac == EAP_EKE_MAC_HMAC_SHA1) + mac_len = SHA1_MAC_LEN; + else if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + mac_len = SHA256_MAC_LEN; + else + return -1; + + return AES_BLOCK_SIZE + 16 + mac_len; +} + + +static int eap_eke_pnonce_ps_len(u8 mac) +{ + int mac_len; + + if (mac == EAP_EKE_MAC_HMAC_SHA1) + mac_len = SHA1_MAC_LEN; + else if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + mac_len = SHA256_MAC_LEN; + else + return -1; + + return AES_BLOCK_SIZE + 2 * 16 + mac_len; +} + + +static int eap_eke_prf_len(u8 prf) +{ + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return 20; + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return 32; + return -1; +} + + +static int eap_eke_nonce_len(u8 prf) +{ + int prf_len; + + prf_len = eap_eke_prf_len(prf); + if (prf_len < 0) + return -1; + + if (prf_len > 2 * 16) + return (prf_len + 1) / 2; + + return 16; +} + + +static int eap_eke_auth_len(u8 prf) +{ + switch (prf) { + case EAP_EKE_PRF_HMAC_SHA1: + return SHA1_MAC_LEN; + case EAP_EKE_PRF_HMAC_SHA2_256: + return SHA256_MAC_LEN; + } + + return -1; +} + + +int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) +{ + int generator; + u8 gen; + const struct dh_group *dh; + size_t pub_len, i; + + generator = eap_eke_dh_generator(group); + if (generator < 0 || generator > 255) + return -1; + gen = generator; + + dh = eap_eke_dh_group(group); + if (dh == NULL) + return -1; + + /* x = random number 2 .. p-1 */ + if (random_get_bytes(ret_priv, dh->prime_len)) + return -1; + if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) { + /* Make sure private value is smaller than prime */ + ret_priv[0] = 0; + } + for (i = 0; i < dh->prime_len - 1; i++) { + if (ret_priv[i]) + break; + } + if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1)) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value", + ret_priv, dh->prime_len); + + /* y = g ^ x (mod p) */ + pub_len = dh->prime_len; + if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len, + dh->prime, dh->prime_len, ret_pub, &pub_len) < 0) + return -1; + if (pub_len < dh->prime_len) { + size_t pad = dh->prime_len - pub_len; + os_memmove(ret_pub + pad, ret_pub, pub_len); + os_memset(ret_pub, 0, pad); + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value", + ret_pub, dh->prime_len); + + return 0; +} + + +static int eap_eke_prf(u8 prf, const u8 *key, size_t key_len, const u8 *data, + size_t data_len, const u8 *data2, size_t data2_len, + u8 *res) +{ + const u8 *addr[2]; + size_t len[2]; + size_t num_elem = 1; + + addr[0] = data; + len[0] = data_len; + if (data2) { + num_elem++; + addr[1] = data2; + len[1] = data2_len; + } + + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return hmac_sha1_vector(key, key_len, num_elem, addr, len, res); + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return hmac_sha256_vector(key, key_len, num_elem, addr, len, + res); + return -1; +} + + +static int eap_eke_prf_hmac_sha1(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *res, size_t len) +{ + u8 hash[SHA1_MAC_LEN]; + u8 idx; + const u8 *addr[3]; + size_t vlen[3]; + int ret; + + idx = 0; + addr[0] = hash; + vlen[0] = SHA1_MAC_LEN; + addr[1] = data; + vlen[1] = data_len; + addr[2] = &idx; + vlen[2] = 1; + + while (len > 0) { + idx++; + if (idx == 1) + ret = hmac_sha1_vector(key, key_len, 2, &addr[1], + &vlen[1], hash); + else + ret = hmac_sha1_vector(key, key_len, 3, addr, vlen, + hash); + if (ret < 0) + return -1; + if (len > SHA1_MAC_LEN) { + os_memcpy(res, hash, SHA1_MAC_LEN); + res += SHA1_MAC_LEN; + len -= SHA1_MAC_LEN; + } else { + os_memcpy(res, hash, len); + len = 0; + } + } + + return 0; +} + + +static int eap_eke_prf_hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *res, size_t len) +{ + u8 hash[SHA256_MAC_LEN]; + u8 idx; + const u8 *addr[3]; + size_t vlen[3]; + int ret; + + idx = 0; + addr[0] = hash; + vlen[0] = SHA256_MAC_LEN; + addr[1] = data; + vlen[1] = data_len; + addr[2] = &idx; + vlen[2] = 1; + + while (len > 0) { + idx++; + if (idx == 1) + ret = hmac_sha256_vector(key, key_len, 2, &addr[1], + &vlen[1], hash); + else + ret = hmac_sha256_vector(key, key_len, 3, addr, vlen, + hash); + if (ret < 0) + return -1; + if (len > SHA256_MAC_LEN) { + os_memcpy(res, hash, SHA256_MAC_LEN); + res += SHA256_MAC_LEN; + len -= SHA256_MAC_LEN; + } else { + os_memcpy(res, hash, len); + len = 0; + } + } + + return 0; +} + + +static int eap_eke_prfplus(u8 prf, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, u8 *res, size_t len) +{ + if (prf == EAP_EKE_PRF_HMAC_SHA1) + return eap_eke_prf_hmac_sha1(key, key_len, data, data_len, res, + len); + if (prf == EAP_EKE_PRF_HMAC_SHA2_256) + return eap_eke_prf_hmac_sha256(key, key_len, data, data_len, + res, len); + return -1; +} + + +int eap_eke_derive_key(struct eap_eke_session *sess, + const u8 *password, size_t password_len, + const u8 *id_s, size_t id_s_len, const u8 *id_p, + size_t id_p_len, u8 *key) +{ + u8 zeros[EAP_EKE_MAX_HASH_LEN]; + u8 temp[EAP_EKE_MAX_HASH_LEN]; + size_t key_len = 16; /* Only AES-128-CBC is used here */ + u8 *id; + + /* temp = prf(0+, password) */ + os_memset(zeros, 0, sess->prf_len); + if (eap_eke_prf(sess->prf, zeros, sess->prf_len, + password, password_len, NULL, 0, temp) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: temp = prf(0+, password)", + temp, sess->prf_len); + + /* key = prf+(temp, ID_S | ID_P) */ + id = os_malloc(id_s_len + id_p_len); + if (id == NULL) + return -1; + os_memcpy(id, id_s, id_s_len); + os_memcpy(id + id_s_len, id_p, id_p_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: ID_S | ID_P", + id, id_s_len + id_p_len); + if (eap_eke_prfplus(sess->prf, temp, sess->prf_len, + id, id_s_len + id_p_len, key, key_len) < 0) { + os_free(id); + return -1; + } + os_free(id); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: key = prf+(temp, ID_S | ID_P)", + key, key_len); + + return 0; +} + + +int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub, + u8 *ret_dhcomp) +{ + u8 pub[EAP_EKE_MAX_DH_LEN]; + int dh_len; + u8 iv[AES_BLOCK_SIZE]; + + dh_len = eap_eke_dh_len(sess->dhgroup); + if (dh_len < 0) + return -1; + + /* + * DHComponent = Encr(key, y) + * + * All defined DH groups use primes that have length devisible by 16, so + * no need to do extra padding for y (= pub). + */ + if (sess->encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + if (random_get_bytes(iv, AES_BLOCK_SIZE)) + return -1; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Encr(key, y)", + iv, AES_BLOCK_SIZE); + os_memcpy(pub, dhpub, dh_len); + if (aes_128_cbc_encrypt(key, iv, pub, dh_len) < 0) + return -1; + os_memcpy(ret_dhcomp, iv, AES_BLOCK_SIZE); + os_memcpy(ret_dhcomp + AES_BLOCK_SIZE, pub, dh_len); + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent = Encr(key, y)", + ret_dhcomp, AES_BLOCK_SIZE + dh_len); + + return 0; +} + + +int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, + const u8 *dhpriv, const u8 *peer_dhcomp) +{ + u8 zeros[EAP_EKE_MAX_HASH_LEN]; + u8 peer_pub[EAP_EKE_MAX_DH_LEN]; + u8 modexp[EAP_EKE_MAX_DH_LEN]; + size_t len; + const struct dh_group *dh; + + if (sess->encr != EAP_EKE_ENCR_AES128_CBC) + return -1; + + dh = eap_eke_dh_group(sess->dhgroup); + if (dh == NULL) + return -1; + + /* Decrypt peer DHComponent */ + os_memcpy(peer_pub, peer_dhcomp + AES_BLOCK_SIZE, dh->prime_len); + if (aes_128_cbc_decrypt(key, peer_dhcomp, peer_pub, dh->prime_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt DHComponent"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted peer DH pubkey", + peer_pub, dh->prime_len); + + /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */ + len = dh->prime_len; + if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len, + dh->prime, dh->prime_len, modexp, &len) < 0) + return -1; + if (len < dh->prime_len) { + size_t pad = dh->prime_len - len; + os_memmove(modexp + pad, modexp, len); + os_memset(modexp, 0, pad); + } + + os_memset(zeros, 0, sess->auth_len); + if (eap_eke_prf(sess->prf, zeros, sess->auth_len, modexp, dh->prime_len, + NULL, 0, sess->shared_secret) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: SharedSecret", + sess->shared_secret, sess->auth_len); + + return 0; +} + + +int eap_eke_derive_ke_ki(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len) +{ + u8 buf[EAP_EKE_MAX_KE_LEN + EAP_EKE_MAX_KI_LEN]; + size_t ke_len, ki_len; + u8 *data; + size_t data_len; + const char *label = "EAP-EKE Keys"; + size_t label_len; + + /* + * Ke | Ki = prf+(SharedSecret, "EAP-EKE Keys" | ID_S | ID_P) + * Ke = encryption key + * Ki = integrity protection key + * Length of each key depends on the selected algorithms. + */ + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + ke_len = 16; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + ki_len = 20; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + ki_len = 32; + else + return -1; + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + os_memcpy(data, label, label_len); + os_memcpy(data + label_len, id_s, id_s_len); + os_memcpy(data + label_len + id_s_len, id_p, id_p_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, buf, ke_len + ki_len) < 0) { + os_free(data); + return -1; + } + + os_memcpy(sess->ke, buf, ke_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ke", sess->ke, ke_len); + os_memcpy(sess->ki, buf + ke_len, ki_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ki", sess->ki, ki_len); + + os_free(data); + return 0; +} + + +int eap_eke_derive_ka(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s) +{ + u8 *data, *pos; + size_t data_len; + const char *label = "EAP-EKE Ka"; + size_t label_len; + + /* + * Ka = prf+(SharedSecret, "EAP-EKE Ka" | ID_S | ID_P | Nonce_P | + * Nonce_S) + * Ka = authentication key + * Length of the key depends on the selected algorithms. + */ + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, label, label_len); + pos += label_len; + os_memcpy(pos, id_s, id_s_len); + pos += id_s_len; + os_memcpy(pos, id_p, id_p_len); + pos += id_p_len; + os_memcpy(pos, nonce_p, sess->nonce_len); + pos += sess->nonce_len; + os_memcpy(pos, nonce_s, sess->nonce_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, sess->ka, sess->prf_len) < 0) { + os_free(data); + return -1; + } + os_free(data); + + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka", sess->ka, sess->prf_len); + + return 0; +} + + +int eap_eke_derive_msk(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s, + u8 *msk, u8 *emsk) +{ + u8 *data, *pos; + size_t data_len; + const char *label = "EAP-EKE Exported Keys"; + size_t label_len; + u8 buf[EAP_MSK_LEN + EAP_EMSK_LEN]; + + /* + * MSK | EMSK = prf+(SharedSecret, "EAP-EKE Exported Keys" | ID_S | + * ID_P | Nonce_P | Nonce_S) + */ + + label_len = os_strlen(label); + data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, label, label_len); + pos += label_len; + os_memcpy(pos, id_s, id_s_len); + pos += id_s_len; + os_memcpy(pos, id_p, id_p_len); + pos += id_p_len; + os_memcpy(pos, nonce_p, sess->nonce_len); + pos += sess->nonce_len; + os_memcpy(pos, nonce_s, sess->nonce_len); + if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len, + data, data_len, buf, EAP_MSK_LEN + EAP_EMSK_LEN) < + 0) { + os_free(data); + return -1; + } + os_free(data); + + os_memcpy(msk, buf, EAP_MSK_LEN); + os_memcpy(emsk, buf + EAP_MSK_LEN, EAP_EMSK_LEN); + os_memset(buf, 0, sizeof(buf)); + + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: MSK", msk, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: EMSK", msk, EAP_EMSK_LEN); + + return 0; +} + + +static int eap_eke_mac(u8 mac, const u8 *key, const u8 *data, size_t data_len, + u8 *res) +{ + if (mac == EAP_EKE_MAC_HMAC_SHA1) + return hmac_sha1(key, SHA1_MAC_LEN, data, data_len, res); + if (mac == EAP_EKE_MAC_HMAC_SHA2_256) + return hmac_sha256(key, SHA256_MAC_LEN, data, data_len, res); + return -1; +} + + +int eap_eke_prot(struct eap_eke_session *sess, + const u8 *data, size_t data_len, + u8 *prot, size_t *prot_len) +{ + size_t block_size, icv_len, pad; + u8 *pos, *iv, *e; + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + block_size = AES_BLOCK_SIZE; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + icv_len = SHA1_MAC_LEN; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + icv_len = SHA256_MAC_LEN; + else + return -1; + + pad = data_len % block_size; + if (pad) + pad = block_size - pad; + + if (*prot_len < block_size + data_len + pad + icv_len) { + wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data"); + } + pos = prot; + + if (random_get_bytes(pos, block_size)) + return -1; + iv = pos; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Prot()", iv, block_size); + pos += block_size; + + e = pos; + os_memcpy(pos, data, data_len); + pos += data_len; + if (pad) { + if (random_get_bytes(pos, pad)) + return -1; + pos += pad; + } + + if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0) + return -1; + + if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0) + return -1; + pos += icv_len; + + *prot_len = pos - prot; + return 0; +} + + +int eap_eke_decrypt_prot(struct eap_eke_session *sess, + const u8 *prot, size_t prot_len, + u8 *data, size_t *data_len) +{ + size_t block_size, icv_len; + u8 icv[EAP_EKE_MAX_HASH_LEN]; + + if (sess->encr == EAP_EKE_ENCR_AES128_CBC) + block_size = AES_BLOCK_SIZE; + else + return -1; + + if (sess->mac == EAP_EKE_PRF_HMAC_SHA1) + icv_len = SHA1_MAC_LEN; + else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256) + icv_len = SHA256_MAC_LEN; + else + return -1; + + if (prot_len < 2 * block_size + icv_len) + return -1; + if ((prot_len - icv_len) % block_size) + return -1; + + if (eap_eke_mac(sess->mac, sess->ki, prot + block_size, + prot_len - block_size - icv_len, icv) < 0) + return -1; + if (os_memcmp(icv, prot + prot_len - icv_len, icv_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: ICV mismatch in Prot() data"); + return -1; + } + + if (*data_len < prot_len - block_size - icv_len) { + wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for decrypted Prot() data"); + return -1; + } + + *data_len = prot_len - block_size - icv_len; + os_memcpy(data, prot + block_size, *data_len); + if (aes_128_cbc_decrypt(sess->ke, prot, data, *data_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt Prot() data"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted Prot() data", + data, *data_len); + + return 0; +} + + +int eap_eke_auth(struct eap_eke_session *sess, const char *label, + const struct wpabuf *msgs, u8 *auth) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Auth(%s)", label); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka for Auth", + sess->ka, sess->auth_len); + wpa_hexdump_buf(MSG_MSGDUMP, "EAP-EKE: Messages for Auth", msgs); + return eap_eke_prf(sess->prf, sess->ka, sess->auth_len, + (const u8 *) label, os_strlen(label), + wpabuf_head(msgs), wpabuf_len(msgs), auth); +} + + +int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, + u8 prf, u8 mac) +{ + sess->dhgroup = dhgroup; + sess->encr = encr; + sess->prf = prf; + sess->mac = mac; + + sess->prf_len = eap_eke_prf_len(prf); + if (sess->prf_len < 0) + return -1; + sess->nonce_len = eap_eke_nonce_len(prf); + if (sess->nonce_len < 0) + return -1; + sess->auth_len = eap_eke_auth_len(prf); + if (sess->auth_len < 0) + return -1; + sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr); + if (sess->dhcomp_len < 0) + return -1; + sess->pnonce_len = eap_eke_pnonce_len(sess->mac); + if (sess->pnonce_len < 0) + return -1; + sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac); + if (sess->pnonce_ps_len < 0) + return -1; + + return 0; +} + + +void eap_eke_session_clean(struct eap_eke_session *sess) +{ + os_memset(sess->shared_secret, 0, EAP_EKE_MAX_HASH_LEN); + os_memset(sess->ke, 0, EAP_EKE_MAX_KE_LEN); + os_memset(sess->ki, 0, EAP_EKE_MAX_KI_LEN); + os_memset(sess->ka, 0, EAP_EKE_MAX_KA_LEN); +} diff --git a/contrib/hostapd/src/eap_common/eap_eke_common.h b/contrib/hostapd/src/eap_common/eap_eke_common.h new file mode 100644 index 0000000000..a4c042225d --- /dev/null +++ b/contrib/hostapd/src/eap_common/eap_eke_common.h @@ -0,0 +1,114 @@ +/* + * EAP server/peer: EAP-EKE shared routines + * Copyright (c) 2011-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_EKE_COMMON_H +#define EAP_EKE_COMMON_H + +/* EKE Exchange */ +#define EAP_EKE_ID 1 +#define EAP_EKE_COMMIT 2 +#define EAP_EKE_CONFIRM 3 +#define EAP_EKE_FAILURE 4 + +/* Diffie-Hellman Group Registry */ +#define EAP_EKE_DHGROUP_EKE_2 1 +#define EAP_EKE_DHGROUP_EKE_5 2 +#define EAP_EKE_DHGROUP_EKE_14 3 /* mandatory to implement */ +#define EAP_EKE_DHGROUP_EKE_15 4 +#define EAP_EKE_DHGROUP_EKE_16 5 + +/* Encryption Algorithm Registry */ +#define EAP_EKE_ENCR_AES128_CBC 1 /* mandatory to implement */ + +/* Pseudo Random Function Registry */ +#define EAP_EKE_PRF_HMAC_SHA1 1 /* mandatory to implement */ +#define EAP_EKE_PRF_HMAC_SHA2_256 2 + +/* Keyed Message Digest (MAC) Registry */ +#define EAP_EKE_MAC_HMAC_SHA1 1 /* mandatory to implement */ +#define EAP_EKE_MAC_HMAC_SHA2_256 2 + +/* Identity Type Registry */ +#define EAP_EKE_ID_OPAQUE 1 +#define EAP_EKE_ID_NAI 2 +#define EAP_EKE_ID_IPv4 3 +#define EAP_EKE_ID_IPv6 4 +#define EAP_EKE_ID_FQDN 5 +#define EAP_EKE_ID_DN 6 + +/* Failure-Code */ +#define EAP_EKE_FAIL_NO_ERROR 1 +#define EAP_EKE_FAIL_PROTO_ERROR 2 +#define EAP_EKE_FAIL_PASSWD_NOT_FOUND 3 +#define EAP_EKE_FAIL_AUTHENTICATION_FAIL 4 +#define EAP_EKE_FAIL_AUTHORIZATION_FAIL 5 +#define EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN 6 +#define EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR 0xffffffff + +#define EAP_EKE_MAX_DH_LEN 512 +#define EAP_EKE_MAX_HASH_LEN 32 +#define EAP_EKE_MAX_KEY_LEN 16 +#define EAP_EKE_MAX_KE_LEN 16 +#define EAP_EKE_MAX_KI_LEN 32 +#define EAP_EKE_MAX_KA_LEN 32 +#define EAP_EKE_MAX_NONCE_LEN 16 + +struct eap_eke_session { + /* Selected proposal */ + u8 dhgroup; + u8 encr; + u8 prf; + u8 mac; + + u8 shared_secret[EAP_EKE_MAX_HASH_LEN]; + u8 ke[EAP_EKE_MAX_KE_LEN]; + u8 ki[EAP_EKE_MAX_KI_LEN]; + u8 ka[EAP_EKE_MAX_KA_LEN]; + + int prf_len; + int nonce_len; + int auth_len; + int dhcomp_len; + int pnonce_len; + int pnonce_ps_len; +}; + +int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr, + u8 prf, u8 mac); +void eap_eke_session_clean(struct eap_eke_session *sess); +int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub); +int eap_eke_derive_key(struct eap_eke_session *sess, + const u8 *password, size_t password_len, + const u8 *id_s, size_t id_s_len, const u8 *id_p, + size_t id_p_len, u8 *key); +int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub, + u8 *ret_dhcomp); +int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, + const u8 *dhpriv, const u8 *peer_dhcomp); +int eap_eke_derive_ke_ki(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len); +int eap_eke_derive_ka(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s); +int eap_eke_derive_msk(struct eap_eke_session *sess, + const u8 *id_s, size_t id_s_len, + const u8 *id_p, size_t id_p_len, + const u8 *nonce_p, const u8 *nonce_s, + u8 *msk, u8 *emsk); +int eap_eke_prot(struct eap_eke_session *sess, + const u8 *data, size_t data_len, + u8 *prot, size_t *prot_len); +int eap_eke_decrypt_prot(struct eap_eke_session *sess, + const u8 *prot, size_t prot_len, + u8 *data, size_t *data_len); +int eap_eke_auth(struct eap_eke_session *sess, const char *label, + const struct wpabuf *msgs, u8 *auth); + +#endif /* EAP_EKE_COMMON_H */ diff --git a/contrib/hostapd/src/eap_common/eap_fast_common.c b/contrib/hostapd/src/eap_common/eap_fast_common.c index 4d3deafa0c..04b987d237 100644 --- a/contrib/hostapd/src/eap_common/eap_fast_common.c +++ b/contrib/hostapd/src/eap_common/eap_fast_common.c @@ -2,21 +2,15 @@ * EAP-FAST common helper functions (RFC 4851) * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "sha1.h" -#include "tls.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "eap_defs.h" #include "eap_tlv_common.h" #include "eap_fast_common.h" @@ -133,9 +127,9 @@ u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, 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)) + if (tls_prf_sha1_md5(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); diff --git a/contrib/hostapd/src/eap_common/eap_fast_common.h b/contrib/hostapd/src/eap_common/eap_fast_common.h index c85fd37fd4..895561747b 100644 --- a/contrib/hostapd/src/eap_common/eap_fast_common.h +++ b/contrib/hostapd/src/eap_common/eap_fast_common.h @@ -2,14 +2,8 @@ * EAP-FAST definitions (RFC 4851) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_FAST_H diff --git a/contrib/hostapd/src/eap_common/eap_gpsk_common.c b/contrib/hostapd/src/eap_common/eap_gpsk_common.c index 414610cf5a..7a33215f9b 100644 --- a/contrib/hostapd/src/eap_common/eap_gpsk_common.c +++ b/contrib/hostapd/src/eap_common/eap_gpsk_common.c @@ -2,25 +2,16 @@ * EAP server/peer: EAP-GPSK shared routines * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/sha256.h" #include "eap_defs.h" -#include "aes_wrap.h" -#include "crypto.h" -#ifdef EAP_GPSK_SHA256 -#include "sha256.h" -#endif /* EAP_GPSK_SHA256 */ #include "eap_gpsk_common.h" @@ -349,6 +340,141 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, } +static int eap_gpsk_derive_mid_helper(u32 csuite_specifier, + u8 *kdf_out, size_t kdf_out_len, + const u8 *psk, const u8 *seed, + size_t seed_len, u8 method_type) +{ + u8 *pos, *data; + size_t data_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; + break; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + gkdf = eap_gpsk_gkdf_sha256; + break; +#endif /* EAP_GPSK_SHA256 */ + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in " + "Session-Id derivation", csuite_specifier); + return -1; + } + +#define SID_LABEL "Method ID" + /* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */ + data_len = strlen(SID_LABEL) + 1 + 6 + seed_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + os_memcpy(pos, SID_LABEL, strlen(SID_LABEL)); + pos += strlen(SID_LABEL); +#undef SID_LABEL + os_memcpy(pos, &method_type, 1); + pos += 1; + 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(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation", + data, data_len); + + if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) { + os_free(data); + return -1; + } + os_free(data); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len); + + return 0; +} + + +/** + * eap_gpsk_session_id - Derive EAP-GPSK Session ID + * @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 + * @method_type: EAP Authentication Method Type + * @sid: Buffer for 17-byte Session ID + * @sid_len: Buffer for returning length of Session ID + * Returns: 0 on success, -1 on failure + */ +int eap_gpsk_derive_session_id(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 method_type, u8 *sid, size_t *sid_len) +{ + u8 *seed, *pos; + u8 kdf_out[16]; + size_t seed_len; + int ret; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)", + vendor, specifier); + + if (vendor != EAP_GPSK_VENDOR_IETF) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); + + /* + * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server + * (= seed) + * KS = 16, CSuite_Sel = 0x00000000 0x0001 + * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || + * CSuite_Sel || inputString) + */ + 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 Session-Id 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); + + ret = eap_gpsk_derive_mid_helper(specifier, + kdf_out, sizeof(kdf_out), + psk, seed, seed_len, + method_type); + + sid[0] = method_type; + os_memcpy(sid + 1, kdf_out, sizeof(kdf_out)); + *sid_len = 1 + sizeof(kdf_out); + + os_free(seed); + + return ret; +} + + /** * eap_gpsk_mic_len - Get the length of the MIC * @vendor: CSuite/Vendor diff --git a/contrib/hostapd/src/eap_common/eap_gpsk_common.h b/contrib/hostapd/src/eap_common/eap_gpsk_common.h index a30ab97ffa..fbcd54732b 100644 --- a/contrib/hostapd/src/eap_common/eap_gpsk_common.h +++ b/contrib/hostapd/src/eap_common/eap_gpsk_common.h @@ -2,14 +2,8 @@ * EAP server/peer: EAP-GPSK shared routines * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_GPSK_COMMON_H @@ -59,6 +53,12 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, 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); +int eap_gpsk_derive_session_id(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 method_type, u8 *sid, size_t *sid_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); diff --git a/contrib/hostapd/src/eap_common/eap_ikev2_common.c b/contrib/hostapd/src/eap_common/eap_ikev2_common.c index e9a9c55eb3..6095fd8ad7 100644 --- a/contrib/hostapd/src/eap_common/eap_ikev2_common.c +++ b/contrib/hostapd/src/eap_common/eap_ikev2_common.c @@ -2,14 +2,8 @@ * EAP-IKEv2 common routines * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/hostapd/src/eap_common/eap_ikev2_common.h b/contrib/hostapd/src/eap_common/eap_ikev2_common.h index a9fc2caae7..329ccc4d7a 100644 --- a/contrib/hostapd/src/eap_common/eap_ikev2_common.h +++ b/contrib/hostapd/src/eap_common/eap_ikev2_common.h @@ -2,14 +2,8 @@ * EAP-IKEv2 definitions * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_IKEV2_COMMON_H diff --git a/contrib/hostapd/src/eap_common/eap_pax_common.c b/contrib/hostapd/src/eap_common/eap_pax_common.c index 80110469dc..b3bbacc63e 100644 --- a/contrib/hostapd/src/eap_common/eap_pax_common.c +++ b/contrib/hostapd/src/eap_common/eap_pax_common.c @@ -2,20 +2,14 @@ * EAP server/peer: EAP-PAX shared routines * Copyright (c) 2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "sha1.h" +#include "crypto/sha1.h" #include "eap_pax_common.h" diff --git a/contrib/hostapd/src/eap_common/eap_pax_common.h b/contrib/hostapd/src/eap_common/eap_pax_common.h index dcc171ec2c..fb03df253f 100644 --- a/contrib/hostapd/src/eap_common/eap_pax_common.h +++ b/contrib/hostapd/src/eap_common/eap_pax_common.h @@ -2,14 +2,8 @@ * EAP server/peer: EAP-PAX shared routines * Copyright (c) 2005-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_PAX_COMMON_H diff --git a/contrib/hostapd/src/eap_common/eap_peap_common.c b/contrib/hostapd/src/eap_common/eap_peap_common.c index 14625f9639..68b8878c93 100644 --- a/contrib/hostapd/src/eap_common/eap_peap_common.c +++ b/contrib/hostapd/src/eap_common/eap_peap_common.c @@ -1,26 +1,20 @@ /* * EAP-PEAP common routines - * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2008-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "sha1.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) +int 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; @@ -75,7 +69,8 @@ void peap_prfplus(int version, const u8 *key, size_t key_len, while (pos < buf_len) { counter++; plen = buf_len - pos; - hmac_sha1_vector(key, key_len, 5, addr, len, hash); + if (hmac_sha1_vector(key, key_len, 5, addr, len, hash) < 0) + return -1; if (plen >= SHA1_MAC_LEN) { os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); pos += SHA1_MAC_LEN; @@ -85,4 +80,6 @@ void peap_prfplus(int version, const u8 *key, size_t key_len, } len[0] = SHA1_MAC_LEN; } + + return 0; } diff --git a/contrib/hostapd/src/eap_common/eap_peap_common.h b/contrib/hostapd/src/eap_common/eap_peap_common.h index f59afb07d0..7aad0dff78 100644 --- a/contrib/hostapd/src/eap_common/eap_peap_common.h +++ b/contrib/hostapd/src/eap_common/eap_peap_common.h @@ -1,22 +1,16 @@ /* * EAP-PEAP common routines - * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2008-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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); +int 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/contrib/hostapd/src/eap_common/eap_psk_common.c b/contrib/hostapd/src/eap_common/eap_psk_common.c index 0def3e8853..638102ffee 100644 --- a/contrib/hostapd/src/eap_common/eap_psk_common.c +++ b/contrib/hostapd/src/eap_common/eap_psk_common.c @@ -2,20 +2,14 @@ * EAP server/peer: EAP-PSK shared routines * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "aes_wrap.h" +#include "crypto/aes_wrap.h" #include "eap_defs.h" #include "eap_psk_common.h" diff --git a/contrib/hostapd/src/eap_common/eap_psk_common.h b/contrib/hostapd/src/eap_common/eap_psk_common.h index 8adc0541ee..8bc2c3c4cf 100644 --- a/contrib/hostapd/src/eap_common/eap_psk_common.h +++ b/contrib/hostapd/src/eap_common/eap_psk_common.h @@ -2,14 +2,8 @@ * EAP server/peer: EAP-PSK shared routines * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_PSK_COMMON_H diff --git a/contrib/hostapd/src/eap_common/eap_pwd_common.c b/contrib/hostapd/src/eap_common/eap_pwd_common.c new file mode 100644 index 0000000000..7d6e6b8898 --- /dev/null +++ b/contrib/hostapd/src/eap_common/eap_pwd_common.c @@ -0,0 +1,345 @@ +/* + * EAP server/peer: EAP-pwd shared routines + * Copyright (c) 2010, Dan Harkins + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "common.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" +#include "eap_defs.h" +#include "eap_pwd_common.h" + +/* The random function H(x) = HMAC-SHA256(0^32, x) */ +struct crypto_hash * eap_pwd_h_init(void) +{ + u8 allzero[SHA256_MAC_LEN]; + os_memset(allzero, 0, SHA256_MAC_LEN); + return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero, + SHA256_MAC_LEN); +} + + +void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len) +{ + crypto_hash_update(hash, data, len); +} + + +void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest) +{ + size_t len = SHA256_MAC_LEN; + crypto_hash_finish(hash, digest, &len); +} + + +/* a counter-based KDF based on NIST SP800-108 */ +static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, + size_t labellen, u8 *result, size_t resultbitlen) +{ + struct crypto_hash *hash; + u8 digest[SHA256_MAC_LEN]; + u16 i, ctr, L; + size_t resultbytelen, len = 0, mdlen; + + resultbytelen = (resultbitlen + 7) / 8; + ctr = 0; + L = htons(resultbitlen); + while (len < resultbytelen) { + ctr++; + i = htons(ctr); + hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, + key, keylen); + if (hash == NULL) + return -1; + if (ctr > 1) + crypto_hash_update(hash, digest, SHA256_MAC_LEN); + crypto_hash_update(hash, (u8 *) &i, sizeof(u16)); + crypto_hash_update(hash, label, labellen); + crypto_hash_update(hash, (u8 *) &L, sizeof(u16)); + mdlen = SHA256_MAC_LEN; + if (crypto_hash_finish(hash, digest, &mdlen) < 0) + return -1; + if ((len + mdlen) > resultbytelen) + os_memcpy(result + len, digest, resultbytelen - len); + else + os_memcpy(result + len, digest, mdlen); + len += mdlen; + } + + /* since we're expanding to a bit length, mask off the excess */ + if (resultbitlen % 8) { + u8 mask = 0xff; + mask <<= (8 - (resultbitlen % 8)); + result[resultbytelen - 1] &= mask; + } + + return 0; +} + + +/* + * compute a "random" secret point on an elliptic curve based + * on the password and identities. + */ +int compute_password_element(EAP_PWD_group *grp, u16 num, + u8 *password, int password_len, + u8 *id_server, int id_server_len, + u8 *id_peer, int id_peer_len, u8 *token) +{ + BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; + struct crypto_hash *hash; + unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; + int nid, is_odd, ret = 0; + size_t primebytelen, primebitlen; + + switch (num) { /* from IANA registry for IKE D-H groups */ + case 19: + nid = NID_X9_62_prime256v1; + break; + case 20: + nid = NID_secp384r1; + break; + case 21: + nid = NID_secp521r1; + break; + case 25: + nid = NID_X9_62_prime192v1; + break; + case 26: + nid = NID_secp224r1; + break; + default: + wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num); + return -1; + } + + grp->pwe = NULL; + grp->order = NULL; + grp->prime = NULL; + + if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP"); + goto fail; + } + + if (((rnd = BN_new()) == NULL) || + ((cofactor = BN_new()) == NULL) || + ((grp->pwe = EC_POINT_new(grp->group)) == NULL) || + ((grp->order = BN_new()) == NULL) || + ((grp->prime = BN_new()) == NULL) || + ((x_candidate = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); + goto fail; + } + + if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL)) + { + wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp " + "curve"); + goto fail; + } + if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve"); + goto fail; + } + if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " + "curve"); + goto fail; + } + primebitlen = BN_num_bits(grp->prime); + primebytelen = BN_num_bytes(grp->prime); + if ((prfbuf = os_malloc(primebytelen)) == NULL) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf " + "buffer"); + goto fail; + } + os_memset(prfbuf, 0, primebytelen); + ctr = 0; + while (1) { + if (ctr > 30) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " + "point on curve for group %d, something's " + "fishy", num); + goto fail; + } + ctr++; + + /* + * compute counter-mode password value and stretch to prime + * pwd-seed = H(token | peer-id | server-id | password | + * counter) + */ + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fail; + eap_pwd_h_update(hash, token, sizeof(u32)); + eap_pwd_h_update(hash, id_peer, id_peer_len); + eap_pwd_h_update(hash, id_server, id_server_len); + eap_pwd_h_update(hash, password, password_len); + eap_pwd_h_update(hash, &ctr, sizeof(ctr)); + eap_pwd_h_final(hash, pwe_digest); + + BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd); + + if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, + (u8 *) "EAP-pwd Hunting And Pecking", + os_strlen("EAP-pwd Hunting And Pecking"), + prfbuf, primebitlen) < 0) + goto fail; + + BN_bin2bn(prfbuf, primebytelen, x_candidate); + + /* + * eap_pwd_kdf() returns a string of bits 0..primebitlen but + * BN_bin2bn will treat that string of bits as a big endian + * number. If the primebitlen is not an even multiple of 8 + * then excessive bits-- those _after_ primebitlen-- so now + * we have to shift right the amount we masked off. + */ + if (primebitlen % 8) + BN_rshift(x_candidate, x_candidate, + (8 - (primebitlen % 8))); + + if (BN_ucmp(x_candidate, grp->prime) >= 0) + continue; + + wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", + prfbuf, primebytelen); + + /* + * need to unambiguously identify the solution, if there is + * one... + */ + if (BN_is_odd(rnd)) + is_odd = 1; + else + is_odd = 0; + + /* + * solve the quadratic equation, if it's not solvable then we + * don't have a point + */ + if (!EC_POINT_set_compressed_coordinates_GFp(grp->group, + grp->pwe, + x_candidate, + is_odd, NULL)) + continue; + /* + * If there's a solution to the equation then the point must be + * on the curve so why check again explicitly? OpenSSL code + * says this is required by X9.62. We're not X9.62 but it can't + * hurt just to be sure. + */ + if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); + continue; + } + + if (BN_cmp(cofactor, BN_value_one())) { + /* make sure the point is not in a small sub-group */ + if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe, + cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd: cannot " + "multiply generator by order"); + continue; + } + if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) { + wpa_printf(MSG_INFO, "EAP-pwd: point is at " + "infinity"); + continue; + } + } + /* if we got here then we have a new generator. */ + break; + } + wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr); + grp->group_num = num; + if (0) { + fail: + EC_GROUP_free(grp->group); + grp->group = NULL; + EC_POINT_free(grp->pwe); + grp->pwe = NULL; + BN_free(grp->order); + grp->order = NULL; + BN_free(grp->prime); + grp->prime = NULL; + ret = 1; + } + /* cleanliness and order.... */ + BN_free(cofactor); + BN_free(x_candidate); + BN_free(rnd); + os_free(prfbuf); + + return ret; +} + + +int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k, + BIGNUM *peer_scalar, BIGNUM *server_scalar, + u8 *confirm_peer, u8 *confirm_server, + u32 *ciphersuite, u8 *msk, u8 *emsk) +{ + struct crypto_hash *hash; + u8 mk[SHA256_MAC_LEN], *cruft; + u8 session_id[SHA256_MAC_LEN + 1]; + u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN]; + int offset; + + if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL) + return -1; + + /* + * first compute the session-id = TypeCode | H(ciphersuite | scal_p | + * scal_s) + */ + session_id[0] = EAP_TYPE_PWD; + hash = eap_pwd_h_init(); + if (hash == NULL) { + os_free(cruft); + return -1; + } + eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32)); + offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); + os_memset(cruft, 0, BN_num_bytes(grp->prime)); + BN_bn2bin(peer_scalar, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); + offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar); + os_memset(cruft, 0, BN_num_bytes(grp->prime)); + BN_bn2bin(server_scalar, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); + eap_pwd_h_final(hash, &session_id[1]); + + /* then compute MK = H(k | confirm-peer | confirm-server) */ + hash = eap_pwd_h_init(); + if (hash == NULL) { + os_free(cruft); + return -1; + } + offset = BN_num_bytes(grp->prime) - BN_num_bytes(k); + os_memset(cruft, 0, BN_num_bytes(grp->prime)); + BN_bn2bin(k, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime)); + os_free(cruft); + eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN); + eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN); + eap_pwd_h_final(hash, mk); + + /* stretch the mk with the session-id to get MSK | EMSK */ + if (eap_pwd_kdf(mk, SHA256_MAC_LEN, + session_id, SHA256_MAC_LEN + 1, + msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) { + return -1; + } + + os_memcpy(msk, msk_emsk, EAP_MSK_LEN); + os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN); + + return 1; +} diff --git a/contrib/hostapd/src/eap_common/eap_pwd_common.h b/contrib/hostapd/src/eap_common/eap_pwd_common.h new file mode 100644 index 0000000000..816e58ccb3 --- /dev/null +++ b/contrib/hostapd/src/eap_common/eap_pwd_common.h @@ -0,0 +1,67 @@ +/* + * EAP server/peer: EAP-pwd shared definitions + * Copyright (c) 2009, Dan Harkins + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_PWD_COMMON_H +#define EAP_PWD_COMMON_H + +#include +#include +#include + +/* + * definition of a finite cyclic group + * TODO: support one based on a prime field + */ +typedef struct group_definition_ { + u16 group_num; + EC_GROUP *group; + EC_POINT *pwe; + BIGNUM *order; + BIGNUM *prime; +} EAP_PWD_group; + +/* + * EAP-pwd header, included on all payloads + * L(1 bit) | M(1 bit) | exch(6 bits) | total_length(if L is set) + */ +#define EAP_PWD_HDR_SIZE 1 + +#define EAP_PWD_OPCODE_ID_EXCH 1 +#define EAP_PWD_OPCODE_COMMIT_EXCH 2 +#define EAP_PWD_OPCODE_CONFIRM_EXCH 3 +#define EAP_PWD_GET_LENGTH_BIT(x) ((x) & 0x80) +#define EAP_PWD_SET_LENGTH_BIT(x) ((x) |= 0x80) +#define EAP_PWD_GET_MORE_BIT(x) ((x) & 0x40) +#define EAP_PWD_SET_MORE_BIT(x) ((x) |= 0x40) +#define EAP_PWD_GET_EXCHANGE(x) ((x) & 0x3f) +#define EAP_PWD_SET_EXCHANGE(x,y) ((x) |= (y)) + +/* EAP-pwd-ID payload */ +struct eap_pwd_id { + be16 group_num; + u8 random_function; +#define EAP_PWD_DEFAULT_RAND_FUNC 1 + u8 prf; +#define EAP_PWD_DEFAULT_PRF 1 + u8 token[4]; + u8 prep; +#define EAP_PWD_PREP_NONE 0 +#define EAP_PWD_PREP_MS 1 + u8 identity[0]; /* length inferred from payload */ +} STRUCT_PACKED; + +/* common routines */ +int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *, + int, u8 *); +int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *, + u8 *, u8 *, u32 *, u8 *, u8 *); +struct crypto_hash * eap_pwd_h_init(void); +void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len); +void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest); + +#endif /* EAP_PWD_COMMON_H */ diff --git a/contrib/hostapd/src/eap_common/eap_sake_common.c b/contrib/hostapd/src/eap_common/eap_sake_common.c index eafad1d117..a76253d00f 100644 --- a/contrib/hostapd/src/eap_common/eap_sake_common.c +++ b/contrib/hostapd/src/eap_common/eap_sake_common.c @@ -2,21 +2,15 @@ * EAP server/peer: EAP-SAKE shared routines * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "sha1.h" #include "wpabuf.h" +#include "crypto/sha1.h" #include "eap_defs.h" #include "eap_sake_common.h" diff --git a/contrib/hostapd/src/eap_common/eap_sake_common.h b/contrib/hostapd/src/eap_common/eap_sake_common.h index 201e20729c..9e1e75745a 100644 --- a/contrib/hostapd/src/eap_common/eap_sake_common.h +++ b/contrib/hostapd/src/eap_common/eap_sake_common.h @@ -2,14 +2,8 @@ * EAP server/peer: EAP-SAKE shared routines * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_SAKE_COMMON_H diff --git a/contrib/hostapd/src/eap_common/eap_sim_common.c b/contrib/hostapd/src/eap_common/eap_sim_common.c index fccda02417..e1773bf1ac 100644 --- a/contrib/hostapd/src/eap_common/eap_sim_common.c +++ b/contrib/hostapd/src/eap_common/eap_sim_common.c @@ -2,25 +2,20 @@ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "eap_common/eap_defs.h" -#include "sha1.h" -#include "sha256.h" -#include "crypto.h" -#include "aes_wrap.h" #include "wpabuf.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "eap_common/eap_defs.h" #include "eap_common/eap_sim_common.h" @@ -233,7 +228,7 @@ void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, } -#ifdef EAP_AKA_PRIME +#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, @@ -496,7 +491,7 @@ void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, 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 */ +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ int eap_sim_parse_attr(const u8 *start, const u8 *end, @@ -858,7 +853,7 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND"); attr->result_ind = 1; break; -#ifdef EAP_AKA_PRIME +#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 " @@ -913,7 +908,7 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, } attr->bidding = apos; break; -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ default: if (pos[0] < 128) { wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " @@ -1023,14 +1018,14 @@ struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, eap = wpabuf_mhead(msg->buf); eap->length = host_to_be16(wpabuf_len(msg->buf)); -#ifdef EAP_AKA_PRIME +#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 */ +#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), @@ -1121,8 +1116,8 @@ int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, 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)) { + if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv, + EAP_SIM_IV_LEN)) { msg->iv = 0; return -1; } diff --git a/contrib/hostapd/src/eap_common/eap_sim_common.h b/contrib/hostapd/src/eap_common/eap_sim_common.h index a8080e27a7..6021bd2e2b 100644 --- a/contrib/hostapd/src/eap_common/eap_sim_common.h +++ b/contrib/hostapd/src/eap_common/eap_sim_common.h @@ -2,14 +2,8 @@ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_SIM_COMMON_H @@ -94,7 +88,7 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, 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); -#ifdef EAP_AKA_PRIME +#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); @@ -110,7 +104,7 @@ void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_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 */ +#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, @@ -135,7 +129,7 @@ static inline int eap_sim_verify_mac_sha256(const u8 *k_aut, { return -1; } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ /* EAP-SIM/AKA Attributes (0..127 non-skippable) */ diff --git a/contrib/hostapd/src/eap_common/eap_tlv_common.h b/contrib/hostapd/src/eap_common/eap_tlv_common.h index f86015d179..3286055ab7 100644 --- a/contrib/hostapd/src/eap_common/eap_tlv_common.h +++ b/contrib/hostapd/src/eap_common/eap_tlv_common.h @@ -2,14 +2,8 @@ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_TLV_COMMON_H diff --git a/contrib/hostapd/src/eap_common/eap_ttls.h b/contrib/hostapd/src/eap_common/eap_ttls.h index 797d084786..17901d42db 100644 --- a/contrib/hostapd/src/eap_common/eap_ttls.h +++ b/contrib/hostapd/src/eap_common/eap_ttls.h @@ -2,14 +2,8 @@ * EAP server/peer: EAP-TTLS (RFC 5281) * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_TTLS_H diff --git a/contrib/hostapd/src/eap_common/eap_wsc_common.c b/contrib/hostapd/src/eap_common/eap_wsc_common.c index 5d4e8cc114..7c1496ec33 100644 --- a/contrib/hostapd/src/eap_common/eap_wsc_common.c +++ b/contrib/hostapd/src/eap_common/eap_wsc_common.c @@ -2,14 +2,8 @@ * EAP-WSC common routines for Wi-Fi Protected Setup * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/hostapd/src/eap_common/eap_wsc_common.h b/contrib/hostapd/src/eap_common/eap_wsc_common.h index fdf61d317d..0e7b653081 100644 --- a/contrib/hostapd/src/eap_common/eap_wsc_common.h +++ b/contrib/hostapd/src/eap_common/eap_wsc_common.h @@ -2,14 +2,8 @@ * EAP-WSC definitions for Wi-Fi Protected Setup * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_WSC_COMMON_H diff --git a/contrib/hostapd/src/eap_common/ikev2_common.c b/contrib/hostapd/src/eap_common/ikev2_common.c index 818b5bdbaa..f061866ae2 100644 --- a/contrib/hostapd/src/eap_common/ikev2_common.c +++ b/contrib/hostapd/src/eap_common/ikev2_common.c @@ -2,22 +2,17 @@ * IKEv2 common routines for initiator and responder * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "sha1.h" -#include "md5.h" -#include "crypto.h" +#include "crypto/crypto.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/random.h" #include "ikev2_common.h" @@ -26,7 +21,7 @@ static struct ikev2_integ_alg ikev2_integ_algs[] = { { AUTH_HMAC_MD5_96, 16, 12 } }; -#define NUM_INTEG_ALGS (sizeof(ikev2_integ_algs) / sizeof(ikev2_integ_algs[0])) +#define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs) static struct ikev2_prf_alg ikev2_prf_algs[] = { @@ -34,7 +29,7 @@ static struct ikev2_prf_alg ikev2_prf_algs[] = { { PRF_HMAC_MD5, 16, 16 } }; -#define NUM_PRF_ALGS (sizeof(ikev2_prf_algs) / sizeof(ikev2_prf_algs[0])) +#define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs) static struct ikev2_encr_alg ikev2_encr_algs[] = { @@ -42,7 +37,7 @@ static struct ikev2_encr_alg ikev2_encr_algs[] = { { ENCR_3DES, 24, 8 } }; -#define NUM_ENCR_ALGS (sizeof(ikev2_encr_algs) / sizeof(ikev2_encr_algs[0])) +#define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs) const struct ikev2_integ_alg * ikev2_get_integ(int id) @@ -639,7 +634,7 @@ int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, phdr->flags = 0; iv = wpabuf_put(msg, iv_len); - if (os_get_random(iv, iv_len)) { + if (random_get_bytes(iv, iv_len)) { wpa_printf(MSG_INFO, "IKEV2: Could not generate IV"); return -1; } diff --git a/contrib/hostapd/src/eap_common/ikev2_common.h b/contrib/hostapd/src/eap_common/ikev2_common.h index c96a070d5b..45c970b608 100644 --- a/contrib/hostapd/src/eap_common/ikev2_common.h +++ b/contrib/hostapd/src/eap_common/ikev2_common.h @@ -2,14 +2,8 @@ * IKEv2 definitions * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IKEV2_COMMON_H @@ -139,7 +133,7 @@ enum { IKEV2_TRANSFORM_ESN = 5 }; -/* IKEv2 Tranform Type 1 (Encryption Algorithm) */ +/* IKEv2 Transform Type 1 (Encryption Algorithm) */ enum { ENCR_DES_IV64 = 1, ENCR_DES = 2, diff --git a/contrib/hostapd/src/eap_peer/eap.c b/contrib/hostapd/src/eap_peer/eap.c index e8e504af54..47cbbeed13 100644 --- a/contrib/hostapd/src/eap_peer/eap.c +++ b/contrib/hostapd/src/eap_peer/eap.c @@ -1,15 +1,9 @@ /* * EAP peer state machines (RFC 4137) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -24,19 +18,21 @@ #include "includes.h" #include "common.h" -#include "eap_i.h" -#include "eap_config.h" -#include "tls.h" -#include "crypto.h" #include "pcsc_funcs.h" -#include "wpa_ctrl.h" #include "state_machine.h" +#include "ext_password.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 +#define EAP_CLIENT_TIMEOUT_DEFAULT 60 static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, @@ -86,8 +82,21 @@ static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm) } +static void eap_notify_status(struct eap_sm *sm, const char *status, + const char *parameter) +{ + wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)", + status, parameter); + if (sm->eapol_cb->notify_status) + sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter); +} + + static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) { + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + if (sm->m == NULL || sm->eap_method_priv == NULL) return; @@ -146,11 +155,14 @@ SM_STATE(EAP, INITIALIZE) sm->methodState = METHOD_NONE; sm->allowNotifications = TRUE; sm->decision = DECISION_FAIL; + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; 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; + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; sm->eapKeyAvailable = FALSE; eapol_set_bool(sm, EAPOL_eapRestart, FALSE); sm->lastId = -1; /* new session - make sure this does not match with @@ -167,6 +179,7 @@ SM_STATE(EAP, INITIALIZE) eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); sm->num_rounds = 0; sm->prev_failure = 0; + sm->expected_failure = 0; } @@ -179,6 +192,12 @@ SM_STATE(EAP, DISABLED) { SM_ENTRY(EAP, DISABLED); sm->num_rounds = 0; + /* + * RFC 4137 does not describe clearing of idleWhile here, but doing so + * allows the timer tick to be stopped more quickly when EAP is not in + * use. + */ + eapol_set_int(sm, EAPOL_idleWhile, 0); } @@ -217,6 +236,7 @@ SM_STATE(EAP, GET_METHOD) { int reinit; EapType method; + const struct eap_method *eap_method; SM_ENTRY(EAP, GET_METHOD); @@ -225,12 +245,24 @@ SM_STATE(EAP, GET_METHOD) else method = sm->reqMethod; + eap_method = eap_peer_get_eap_method(sm->reqVendor, method); + 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); + eap_notify_status(sm, "refuse proposed method", + eap_method ? eap_method->name : "unknown"); goto nak; } + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "vendor=%u method=%u", sm->reqVendor, method); + + eap_notify_status(sm, "accept proposed method", + eap_method ? eap_method->name : "unknown"); /* * RFC 4137 does not define specific operation for fast * re-authentication (session resumption). The design here is to allow @@ -254,7 +286,7 @@ SM_STATE(EAP, GET_METHOD) sm->selectedMethod = sm->reqMethod; if (sm->m == NULL) - sm->m = eap_peer_get_eap_method(sm->reqVendor, method); + sm->m = eap_method; if (!sm->m) { wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: " "vendor %d method %d", @@ -262,6 +294,8 @@ SM_STATE(EAP, GET_METHOD) goto nak; } + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; + wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: " "vendor %u method %u (%s)", sm->reqVendor, method, sm->m->name); @@ -317,6 +351,7 @@ SM_STATE(EAP, METHOD) { struct wpabuf *eapReqData; struct eap_method_ret ret; + int min_len = 1; SM_ENTRY(EAP, METHOD); if (sm->m == NULL) { @@ -325,6 +360,10 @@ SM_STATE(EAP, METHOD) } eapReqData = eapol_get_eapReqData(sm); + if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP) + min_len = 0; /* LEAP uses EAP-Success without payload */ + if (!eap_hdr_len_valid(eapReqData, min_len)) + return; /* * Get ignore, methodState, decision, allowNotifications, and @@ -350,10 +389,11 @@ SM_STATE(EAP, METHOD) 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", + "methodState=%s decision=%s eapRespData=%p", ret.ignore ? "TRUE" : "FALSE", eap_sm_method_state_txt(ret.methodState), - eap_sm_decision_txt(ret.decision)); + eap_sm_decision_txt(ret.decision), + sm->eapRespData); sm->ignore = ret.ignore; if (sm->ignore) @@ -367,6 +407,15 @@ SM_STATE(EAP, METHOD) os_free(sm->eapKeyData); sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); + os_free(sm->eapSessionId); + sm->eapSessionId = NULL; + if (sm->m->getSessionId) { + sm->eapSessionId = sm->m->getSessionId( + sm, sm->eap_method_priv, + &sm->eapSessionIdLen); + wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", + sm->eapSessionId, sm->eapSessionIdLen); + } } } @@ -385,8 +434,10 @@ SM_STATE(EAP, SEND_RESPONSE) sm->lastId = sm->reqId; sm->lastRespData = wpabuf_dup(sm->eapRespData); eapol_set_bool(sm, EAPOL_eapResp, TRUE); - } else + } else { + wpa_printf(MSG_DEBUG, "EAP: No eapRespData available"); sm->lastRespData = NULL; + } eapol_set_bool(sm, EAPOL_eapReq, FALSE); eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); } @@ -413,6 +464,8 @@ SM_STATE(EAP, IDENTITY) SM_ENTRY(EAP, IDENTITY); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processIdentity(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -429,6 +482,8 @@ SM_STATE(EAP, NOTIFICATION) SM_ENTRY(EAP, NOTIFICATION); eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; eap_sm_processNotify(sm, eapReqData); wpabuf_free(sm->eapRespData); sm->eapRespData = NULL; @@ -673,8 +728,19 @@ static void eap_peer_sm_step_local(struct eap_sm *sm) SM_ENTER(EAP, SEND_RESPONSE); break; case EAP_METHOD: + /* + * Note: RFC 4137 uses methodState == DONE && decision == FAIL + * as the condition. eapRespData == NULL here is used to allow + * final EAP method response to be sent without having to change + * all methods to either use methodState MAY_CONT or leaving + * decision to something else than FAIL in cases where the only + * expected response is EAP-Failure. + */ if (sm->ignore) SM_ENTER(EAP, DISCARD); + else if (sm->methodState == METHOD_DONE && + sm->decision == DECISION_FAIL && !sm->eapRespData) + SM_ENTER(EAP, FAILURE); else SM_ENTER(EAP, SEND_RESPONSE); break; @@ -846,12 +912,17 @@ 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) { - const struct eap_hdr *hdr = wpabuf_head(req); - const u8 *pos = (const u8 *) (hdr + 1); - pos++; + const u8 *pos; + size_t msg_len; wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED "EAP authentication started"); + eap_notify_status(sm, "started", ""); + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req, + &msg_len); + if (pos == NULL) + return; /* * RFC 3748 - 5.1: Identity @@ -863,15 +934,80 @@ static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) /* 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); + pos, msg_len); } #ifdef PCSC_FUNCS + +/* + * Rules for figuring out MNC length based on IMSI for SIM cards that do not + * include MNC length field. + */ +static int mnc_len_from_imsi(const char *imsi) +{ + char mcc_str[4]; + unsigned int mcc; + + os_memcpy(mcc_str, imsi, 3); + mcc_str[3] = '\0'; + mcc = atoi(mcc_str); + + if (mcc == 228) + return 2; /* Networks in Switzerland use 2-digit MNC */ + if (mcc == 244) + return 2; /* Networks in Finland use 2-digit MNC */ + + return -1; +} + + +static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, + size_t max_len, size_t *imsi_len) +{ + int mnc_len; + char *pos, mnc[4]; + + if (*imsi_len + 36 > max_len) { + wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); + return -1; + } + + /* MNC (2 or 3 digits) */ + mnc_len = scard_get_mnc_len(sm->scard_ctx); + if (mnc_len < 0) + mnc_len = mnc_len_from_imsi(imsi); + if (mnc_len < 0) { + wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " + "assuming 3"); + mnc_len = 3; + } + + if (mnc_len == 2) { + mnc[0] = '0'; + mnc[1] = imsi[3]; + mnc[2] = imsi[4]; + } else if (mnc_len == 3) { + mnc[0] = imsi[3]; + mnc[1] = imsi[4]; + mnc[2] = imsi[5]; + } + mnc[3] = '\0'; + + pos = imsi + *imsi_len; + pos += os_snprintf(pos, imsi + max_len - pos, + "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", + mnc, imsi[0], imsi[1], imsi[2]); + *imsi_len = pos - imsi; + + return 0; +} + + static int eap_sm_imsi_identity(struct eap_sm *sm, struct eap_peer_config *conf) { - int aka = 0; + enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM; char imsi[100]; size_t imsi_len; struct eap_method_type *m = conf->eap_methods; @@ -885,11 +1021,28 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); + if (imsi_len < 7) { + wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity"); + return -1; + } + + if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) { + wpa_printf(MSG_WARNING, "Could not add realm to SIM identity"); + return -1; + } + wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (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_PRIME) { + method = EAP_SM_AKA_PRIME; + break; + } + if (m[i].vendor == EAP_VENDOR_IETF && m[i].method == EAP_TYPE_AKA) { - aka = 1; + method = EAP_SM_AKA; break; } } @@ -902,12 +1055,23 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, return -1; } - conf->identity[0] = aka ? '0' : '1'; + switch (method) { + case EAP_SM_SIM: + conf->identity[0] = '1'; + break; + case EAP_SM_AKA: + conf->identity[0] = '0'; + break; + case EAP_SM_AKA_PRIME: + conf->identity[0] = '6'; + break; + } os_memcpy(conf->identity + 1, imsi, imsi_len); conf->identity_len = 1 + imsi_len; return 0; } + #endif /* PCSC_FUNCS */ @@ -1140,10 +1304,12 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) break; case EAP_CODE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); + eap_notify_status(sm, "completion", "success"); sm->rxSuccess = TRUE; break; case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); + eap_notify_status(sm, "completion", "failure"); sm->rxFailure = TRUE; break; default: @@ -1154,6 +1320,60 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) } +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; + + switch (ev) { + case TLS_CERT_CHAIN_SUCCESS: + eap_notify_status(sm, "remote certificate verification", + "success"); + break; + 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); + eap_notify_status(sm, "remote certificate verification", + data->cert_fail.reason_txt); + break; + case TLS_PEER_CERTIFICATE: + if (!sm->eapol_cb->notify_cert) + break; + + 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); + } + } + + sm->eapol_cb->notify_cert(sm->eapol_ctx, + data->peer_cert.depth, + data->peer_cert.subject, + hash_hex, data->peer_cert.cert); + break; + case TLS_ALERT: + if (data->alert.is_local) + eap_notify_status(sm, "local TLS alert", + data->alert.description); + else + eap_notify_status(sm, "remote TLS alert", + data->alert.description); + break; + } + + os_free(hash_hex); +} + + /** * eap_peer_sm_init - Allocate and initialize EAP peer state machine * @eapol_ctx: Context data to be used with eapol_cb calls @@ -1181,13 +1401,19 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, sm->eapol_ctx = eapol_ctx; sm->eapol_cb = eapol_cb; sm->msg_ctx = msg_ctx; - sm->ClientTimeout = 60; + sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; 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; + tlsconf.cert_in_cb = conf->cert_in_cb; sm->ssl_ctx = tls_init(&tlsconf); if (sm->ssl_ctx == NULL) { wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " @@ -1196,6 +1422,13 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, return NULL; } + sm->ssl_ctx2 = tls_init(&tlsconf); + if (sm->ssl_ctx2 == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS " + "context (2)."); + /* Run without separate TLS context within TLS tunnel */ + } + return sm; } @@ -1213,6 +1446,8 @@ void eap_peer_sm_deinit(struct eap_sm *sm) return; eap_deinit_prev_method(sm, "EAP deinit"); eap_sm_abort(sm); + if (sm->ssl_ctx2) + tls_deinit(sm->ssl_ctx2); tls_deinit(sm->ssl_ctx); os_free(sm); } @@ -1255,6 +1490,8 @@ void eap_sm_abort(struct eap_sm *sm) sm->eapRespData = NULL; os_free(sm->eapKeyData); sm->eapKeyData = NULL; + os_free(sm->eapSessionId); + sm->eapSessionId = 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 @@ -1412,16 +1649,12 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) #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, +static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, const char *msg, size_t msglen) { struct eap_peer_config *config; - char *field, *txt, *tmp; + const char *txt = NULL; + char *tmp; if (sm == NULL) return; @@ -1429,29 +1662,20 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, if (config == NULL) return; - switch (type) { - case TYPE_IDENTITY: - field = "IDENTITY"; - txt = "Identity"; + switch (field) { + case WPA_CTRL_REQ_EAP_IDENTITY: config->pending_req_identity++; break; - case TYPE_PASSWORD: - field = "PASSWORD"; - txt = "Password"; + case WPA_CTRL_REQ_EAP_PASSWORD: config->pending_req_password++; break; - case TYPE_NEW_PASSWORD: - field = "NEW_PASSWORD"; - txt = "New Password"; + case WPA_CTRL_REQ_EAP_NEW_PASSWORD: config->pending_req_new_password++; break; - case TYPE_PIN: - field = "PIN"; - txt = "PIN"; + case WPA_CTRL_REQ_EAP_PIN: config->pending_req_pin++; break; - case TYPE_OTP: - field = "OTP"; + case WPA_CTRL_REQ_EAP_OTP: if (msg) { tmp = os_malloc(msglen + 3); if (tmp == NULL) @@ -1470,11 +1694,12 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, txt = config->pending_req_otp; } break; - case TYPE_PASSPHRASE: - field = "PASSPHRASE"; - txt = "Private key passphrase"; + case WPA_CTRL_REQ_EAP_PASSPHRASE: config->pending_req_passphrase++; break; + case WPA_CTRL_REQ_SIM: + txt = msg; + break; default: return; } @@ -1486,6 +1711,13 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, #define eap_sm_request(sm, type, msg, msglen) do { } while (0) #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +const char * eap_sm_get_method_name(struct eap_sm *sm) +{ + if (sm->m == NULL) + return "UNKNOWN"; + return sm->m->name; +} + /** * eap_sm_request_identity - Request identity from user (ctrl_iface) @@ -1498,7 +1730,7 @@ static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type, */ void eap_sm_request_identity(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_IDENTITY, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0); } @@ -1513,7 +1745,7 @@ void eap_sm_request_identity(struct eap_sm *sm) */ void eap_sm_request_password(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PASSWORD, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0); } @@ -1528,7 +1760,7 @@ void eap_sm_request_password(struct eap_sm *sm) */ void eap_sm_request_new_password(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0); } @@ -1543,7 +1775,7 @@ void eap_sm_request_new_password(struct eap_sm *sm) */ void eap_sm_request_pin(struct eap_sm *sm) { - eap_sm_request(sm, TYPE_PIN, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0); } @@ -1559,7 +1791,7 @@ 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) { - eap_sm_request(sm, TYPE_OTP, msg, msg_len); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len); } @@ -1574,7 +1806,18 @@ 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) { - eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0); + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0); +} + + +/** + * eap_sm_request_sim - Request external SIM processing + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @req: EAP method specific request + */ +void eap_sm_request_sim(struct eap_sm *sm, const char *req) +{ + eap_sm_request(sm, WPA_CTRL_REQ_SIM, req, os_strlen(req)); } @@ -1741,6 +1984,27 @@ const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) } +static int eap_get_ext_password(struct eap_sm *sm, + struct eap_peer_config *config) +{ + char *name; + + if (config->password == NULL) + return -1; + + name = os_zalloc(config->password_len + 1); + if (name == NULL) + return -1; + os_memcpy(name, config->password, config->password_len); + + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = ext_password_get(sm->ext_pw, name); + os_free(name); + + return sm->ext_pw_buf == NULL ? -1 : 0; +} + + /** * eap_get_config_password - Get password from the network configuration * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -1752,6 +2016,14 @@ 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; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + *len = config->password_len; return config->password; } @@ -1771,6 +2043,16 @@ 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; + + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + if (eap_get_ext_password(sm, config) < 0) + return NULL; + if (hash) + *hash = 0; + *len = wpabuf_len(sm->ext_pw_buf); + return wpabuf_head(sm->ext_pw_buf); + } + *len = config->password_len; if (hash) *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); @@ -1858,6 +2140,15 @@ const char * eap_get_config_phase2(struct eap_sm *sm) } +int eap_get_config_fragment_size(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return -1; + return config->fragment_size; +} + + /** * eap_key_available - Get key availability (eapKeyAvailable variable) * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -1914,6 +2205,28 @@ void eap_notify_lower_layer_success(struct eap_sm *sm) } +/** + * eap_get_eapSessionId - Get Session-Id 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 session + * Returns: Pointer to the EAP Session-Id or %NULL on failure + * + * Fetch EAP Session-Id from the EAP state machine. The Session-Id is available + * only after a successful authentication. EAP state machine continues to manage + * the Session-Id and the caller must not change or free the returned data. + */ +const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapSessionId == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapSessionIdLen; + return sm->eapSessionId; +} + + /** * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() @@ -2023,6 +2336,17 @@ void eap_set_force_disabled(struct eap_sm *sm, int disabled) } +/** + * eap_set_external_sim - Set external_sim flag + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @external_sim: Whether external SIM/USIM processing is used + */ +void eap_set_external_sim(struct eap_sm *sm, int external_sim) +{ + sm->external_sim = external_sim; +} + + /** * 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() @@ -2073,3 +2397,30 @@ int eap_is_wps_pin_enrollee(struct eap_peer_config *conf) return 1; } + + +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext) +{ + ext_password_free(sm->ext_pw_buf); + sm->ext_pw_buf = NULL; + sm->ext_pw = ext; +} + + +/** + * eap_set_anon_id - Set or add anonymous identity + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len) +{ + if (sm->eapol_cb->set_anon_id) + sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len); +} + + +int eap_peer_was_failure_expected(struct eap_sm *sm) +{ + return sm->expected_failure; +} diff --git a/contrib/hostapd/src/eap_peer/eap.h b/contrib/hostapd/src/eap_peer/eap.h index d7a562812b..712e929dcb 100644 --- a/contrib/hostapd/src/eap_peer/eap.h +++ b/contrib/hostapd/src/eap_peer/eap.h @@ -1,21 +1,15 @@ /* * EAP peer state machine functions (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_H #define EAP_H -#include "defs.h" +#include "common/defs.h" #include "eap_common/eap_defs.h" #include "eap_peer/eap_methods.h" @@ -216,11 +210,39 @@ struct eapol_callbacks { /** * eap_param_needed - Notify that EAP parameter is needed * @ctx: eapol_ctx from eap_peer_sm_init() call - * @field: Field name (e.g., "IDENTITY") + * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) * @txt: User readable text describing the required parameter */ - void (*eap_param_needed)(void *ctx, const char *field, + void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field, const char *txt); + + /** + * notify_cert - Notification of a peer certificate + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @depth: Depth in certificate chain (0 = server) + * @subject: Subject of the peer certificate + * @cert_hash: SHA-256 hash of the certificate + * @cert: Peer certificate + */ + void (*notify_cert)(void *ctx, int depth, const char *subject, + const char *cert_hash, const struct wpabuf *cert); + + /** + * notify_status - Notification of the current EAP state + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @status: Step in the process of EAP authentication + * @parameter: Step-specific parameter, e.g., EAP method name + */ + void (*notify_status)(void *ctx, const char *status, + const char *parameter); + + /** + * set_anon_id - Set or add anonymous identity + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear + * @len: Length of anonymous identity in octets + */ + void (*set_anon_id)(void *ctx, const u8 *id, size_t len); }; /** @@ -251,6 +273,11 @@ struct eap_config { * This is only used by EAP-WSC and can be left %NULL if not available. */ struct wps_context *wps; + + /** + * cert_in_cb - Include server certificates in callback + */ + int cert_in_cb; }; struct eap_sm * eap_peer_sm_init(void *eapol_ctx, @@ -261,6 +288,7 @@ 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); +const char * eap_sm_get_method_name(struct eap_sm *sm); 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); @@ -268,6 +296,7 @@ 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_request_sim(struct eap_sm *sm, const char *req); 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, @@ -275,9 +304,11 @@ struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, 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); +void eap_set_external_sim(struct eap_sm *sm, int external_sim); 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_eapSessionId(struct eap_sm *sm, size_t *len); 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); @@ -286,6 +317,11 @@ 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); +struct ext_password_data; +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext); +void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len); +int eap_peer_was_failure_expected(struct eap_sm *sm); + #endif /* IEEE8021X_EAPOL */ #endif /* EAP_H */ diff --git a/contrib/hostapd/src/eap_peer/eap_aka.c b/contrib/hostapd/src/eap_peer/eap_aka.c index f237141425..d3cbaca6d5 100644 --- a/contrib/hostapd/src/eap_peer/eap_aka.c +++ b/contrib/hostapd/src/eap_peer/eap_aka.c @@ -1,30 +1,22 @@ /* - * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) - * Copyright (c) 2004-2008, Jouni Malinen + * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "eap_peer/eap_i.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 "sha1.h" -#include "sha256.h" -#include "crypto.h" -#include "eap_peer/eap_config.h" -#ifdef CONFIG_USIM_SIMULATOR -#include "hlr_auc_gw/milenage.h" -#endif /* CONFIG_USIM_SIMULATOR */ +#include "eap_config.h" +#include "eap_i.h" struct eap_aka_data { @@ -98,6 +90,7 @@ static void * eap_aka_init(struct eap_sm *sm) { struct eap_aka_data *data; const char *phase1 = eap_get_config_phase1(sm); + struct eap_peer_config *config = eap_get_config(sm); data = os_zalloc(sizeof(*data)); if (data == NULL) @@ -110,6 +103,15 @@ static void * eap_aka_init(struct eap_sm *sm) data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; + if (config && config->anonymous_identity) { + data->pseudonym = os_malloc(config->anonymous_identity_len); + if (data->pseudonym) { + os_memcpy(data->pseudonym, config->anonymous_identity, + config->anonymous_identity_len); + data->pseudonym_len = config->anonymous_identity_len; + } + } + return data; } @@ -140,6 +142,89 @@ static void eap_aka_deinit(struct eap_sm *sm, void *priv) } +static int eap_aka_ext_sim_req(struct eap_sm *sm, struct eap_aka_data *data) +{ + char req[200], *pos, *end; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Use external USIM processing"); + pos = req; + end = pos + sizeof(req); + pos += os_snprintf(pos, end - pos, "UMTS-AUTH"); + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->rand, EAP_AKA_RAND_LEN); + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->autn, EAP_AKA_AUTN_LEN); + + eap_sm_request_sim(sm, req); + return 1; +} + + +static int eap_aka_ext_sim_result(struct eap_sm *sm, struct eap_aka_data *data, + struct eap_peer_config *conf) +{ + char *resp, *pos; + + wpa_printf(MSG_DEBUG, + "EAP-AKA: Use result from external USIM processing"); + + resp = conf->external_sim_resp; + conf->external_sim_resp = NULL; + + if (os_strncmp(resp, "UMTS-AUTS:", 10) == 0) { + pos = resp + 10; + if (hexstr2bin(pos, data->auts, EAP_AKA_AUTS_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: AUTS", data->auts, + EAP_AKA_AUTS_LEN); + os_free(resp); + return -2; + } + + if (os_strncmp(resp, "UMTS-AUTH:", 10) != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized external USIM processing response"); + os_free(resp); + return -1; + } + + pos = resp + 10; + wpa_hexdump(MSG_DEBUG, "EAP-AKA: RAND", data->rand, EAP_AKA_RAND_LEN); + + if (hexstr2bin(pos, data->ik, EAP_AKA_IK_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, EAP_AKA_IK_LEN); + pos += EAP_AKA_IK_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + if (hexstr2bin(pos, data->ck, EAP_AKA_CK_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, EAP_AKA_CK_LEN); + pos += EAP_AKA_CK_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + data->res_len = os_strlen(pos) / 2; + if (data->res_len > EAP_AKA_RES_MAX_LEN) { + data->res_len = 0; + goto invalid; + } + if (hexstr2bin(pos, data->res, data->res_len) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: RES", data->res, data->res_len); + + os_free(resp); + return 0; + +invalid: + wpa_printf(MSG_DEBUG, "EAP-AKA: Invalid external USIM processing UMTS-AUTH response"); + os_free(resp); + return -1; +} + + static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) { struct eap_peer_config *conf; @@ -149,6 +234,14 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) conf = eap_get_config(sm); if (conf == NULL) return -1; + + if (sm->external_sim) { + if (conf->external_sim_resp) + return eap_aka_ext_sim_result(sm, data, conf); + else + return eap_aka_ext_sim_req(sm, data); + } + if (conf->pcsc) { return scard_umts_auth(sm->scard_ctx, data->rand, data->autn, data->res, &data->res_len, @@ -235,23 +328,24 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) #define CLEAR_REAUTH_ID 0x02 #define CLEAR_EAP_ID 0x04 -static void eap_aka_clear_identities(struct eap_aka_data *data, int id) +static void eap_aka_clear_identities(struct eap_sm *sm, + 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) { + if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { + wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; + eap_set_anon_id(sm, NULL, 0); } - if (id & CLEAR_REAUTH_ID) { + if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { + wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id"); os_free(data->reauth_id); data->reauth_id = NULL; data->reauth_id_len = 0; } - if (id & CLEAR_EAP_ID) { + if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { + wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id"); os_free(data->last_eap_identity); data->last_eap_identity = NULL; data->last_eap_identity_len = 0; @@ -259,24 +353,45 @@ static void eap_aka_clear_identities(struct eap_aka_data *data, int id) } -static int eap_aka_learn_ids(struct eap_aka_data *data, +static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data, struct eap_sim_attrs *attr) { if (attr->next_pseudonym) { + const u8 *identity = NULL; + size_t identity_len = 0; + const u8 *realm = NULL; + size_t realm_len = 0; + + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", + attr->next_pseudonym, + attr->next_pseudonym_len); os_free(data->pseudonym); - data->pseudonym = os_malloc(attr->next_pseudonym_len); + /* Look for the realm of the permanent identity */ + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + for (realm = identity, realm_len = identity_len; + realm_len > 0; realm_len--, realm++) { + if (*realm == '@') + break; + } + } + data->pseudonym = os_malloc(attr->next_pseudonym_len + + realm_len); if (data->pseudonym == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next pseudonym"); + data->pseudonym_len = 0; 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 (realm_len) { + os_memcpy(data->pseudonym + attr->next_pseudonym_len, + realm, realm_len); + } + data->pseudonym_len = attr->next_pseudonym_len + realm_len; + eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); } if (attr->next_reauth_id) { @@ -285,6 +400,7 @@ static int eap_aka_learn_ids(struct eap_aka_data *data, if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next reauth_id"); + data->reauth_id_len = 0; return -1; } os_memcpy(data->reauth_id, attr->next_reauth_id, @@ -413,6 +529,8 @@ static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, data->num_id_req = 0; data->num_notification = 0; + wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)", + err); 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); @@ -474,16 +592,16 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - eap_aka_clear_identities(data, CLEAR_REAUTH_ID); + eap_aka_clear_identities(sm, 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 | + eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); } } if (id_req != NO_ID_REQ) - eap_aka_clear_identities(data, CLEAR_EAP_ID); + eap_aka_clear_identities(sm, 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, @@ -834,6 +952,9 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " "failed (AUTN seq# -> AUTS)"); return eap_aka_synchronization_failure(data, id); + } else if (res > 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing"); + return NULL; } else if (res) { wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); return eap_aka_client_error(data, id, @@ -882,11 +1003,11 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, 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); + /* Old reauthentication identity must not be used anymore. In + * other words, if no new identities are received, full + * authentication will be used on next reauthentication (using + * pseudonym identity or permanent identity). */ + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; @@ -897,7 +1018,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, return eap_aka_client_error( data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); } - eap_aka_learn_ids(data, &eattr); + eap_aka_learn_ids(sm, data, &eattr); os_free(decrypted); } @@ -1114,8 +1235,8 @@ static struct wpabuf * eap_aka_process_reauthentication( 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); + eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_aka_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) data->use_result_ind = 1; @@ -1130,7 +1251,8 @@ static struct wpabuf * eap_aka_process_reauthentication( 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); + eap_aka_clear_identities(sm, data, + CLEAR_REAUTH_ID | CLEAR_EAP_ID); } os_free(decrypted); return eap_aka_response_reauth(data, id, 0, data->nonce_s); @@ -1248,7 +1370,7 @@ static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) 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); + eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); data->prev_id = -1; wpabuf_free(data->id_msgs); data->id_msgs = NULL; @@ -1312,6 +1434,28 @@ static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = data->eap_method; + os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); + os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_aka_data *data = priv; @@ -1346,6 +1490,7 @@ int eap_peer_aka_register(void) eap->process = eap_aka_process; eap->isKeyAvailable = eap_aka_isKeyAvailable; eap->getKey = eap_aka_getKey; + eap->getSessionId = eap_aka_get_session_id; 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; @@ -1376,6 +1521,7 @@ int eap_peer_aka_prime_register(void) eap->process = eap_aka_process; eap->isKeyAvailable = eap_aka_isKeyAvailable; eap->getKey = eap_aka_getKey; + eap->getSessionId = eap_aka_get_session_id; 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; diff --git a/contrib/hostapd/src/eap_peer/eap_config.h b/contrib/hostapd/src/eap_peer/eap_config.h index 94245c3d03..98ec1f7631 100644 --- a/contrib/hostapd/src/eap_peer/eap_config.h +++ b/contrib/hostapd/src/eap_peer/eap_config.h @@ -1,15 +1,9 @@ /* * EAP peer configuration data - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_CONFIG_H @@ -41,6 +35,9 @@ struct eap_peer_config { * * If not set, the identity field will be used for both unencrypted and * protected fields. + * + * This field can also be used with EAP-SIM/AKA/AKA' to store the + * pseudonym identity. */ u8 *anonymous_identity; @@ -85,6 +82,15 @@ struct eap_peer_config { * 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". @@ -201,6 +207,24 @@ struct eap_peer_config { */ u8 *altsubject_match; + /** + * domain_suffix_match - Constraint for server domain name + * + * If set, this FQDN is used as a suffix match requirement for the + * server certificate in SubjectAltName dNSName element(s). If a + * matching dNSName is found, this constraint is met. If no dNSName + * values are present, this constraint is matched against SubjetName CN + * using same suffix match comparison. Suffix match here means that the + * host/domain name is compared one label at a time starting from the + * top-level domain and all the labels in domain_suffix_match shall be + * included in the certificate. The certificate may include additional + * sub-level labels in addition to the required labels. + * + * For example, domain_suffix_match=example.com would match + * test.example.com but would not match test-example.com. + */ + char *domain_suffix_match; + /** * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2) * @@ -296,6 +320,14 @@ struct eap_peer_config { */ u8 *altsubject_match2; + /** + * domain_suffix_match2 - Constraint for server domain name + * + * This field is like domain_suffix_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *domain_suffix_match2; + /** * eap_methods - Allowed EAP methods * @@ -616,6 +648,7 @@ struct eap_peer_config { int fragment_size; #define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) +#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1) /** * flags - Network configuration flags (bitfield) * @@ -623,8 +656,28 @@ struct eap_peer_config { * for the network parameters. * bit 0 = password is represented as a 16-byte NtPasswordHash value * instead of plaintext password + * bit 1 = password is stored in external storage; the value in the + * password field is the name of that external entry */ u32 flags; + + /** + * ocsp - Whether to use/require OCSP to check server certificate + * + * 0 = do not use OCSP stapling (TLS certificate status extension) + * 1 = try to use OCSP stapling, but not require response + * 2 = require valid OCSP stapling response + */ + int ocsp; + + /** + * external_sim_resp - Response from external SIM processing + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request external + * SIM/USIM processing. + */ + char *external_sim_resp; }; diff --git a/contrib/hostapd/src/eap_peer/eap_eke.c b/contrib/hostapd/src/eap_peer/eap_eke.c new file mode 100644 index 0000000000..864ea1d970 --- /dev/null +++ b/contrib/hostapd/src/eap_peer/eap_eke.c @@ -0,0 +1,765 @@ +/* + * EAP peer method: EAP-EKE (RFC 6124) + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/random.h" +#include "eap_peer/eap_i.h" +#include "eap_common/eap_eke_common.h" + +struct eap_eke_data { + enum { + IDENTITY, COMMIT, CONFIRM, SUCCESS, FAILURE + } state; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 *peerid; + size_t peerid_len; + u8 *serverid; + size_t serverid_len; + u8 dh_priv[EAP_EKE_MAX_DH_LEN]; + struct eap_eke_session sess; + u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; + u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; + struct wpabuf *msgs; + u8 dhgroup; /* forced DH group or 0 to allow all supported */ + u8 encr; /* forced encryption algorithm or 0 to allow all supported */ + u8 prf; /* forced PRF or 0 to allow all supported */ + u8 mac; /* forced MAC or 0 to allow all supported */ +}; + + +static const char * eap_eke_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case COMMIT: + return "COMMIT"; + case CONFIRM: + return "CONFIRM"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_eke_state(struct eap_eke_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", + eap_eke_state_txt(data->state), eap_eke_state_txt(state)); + data->state = state; +} + + +static void eap_eke_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_eke_init(struct eap_sm *sm) +{ + struct eap_eke_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + const char *phase1; + + password = eap_get_config_password(sm, &password_len); + if (!password) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + eap_eke_state(data, IDENTITY); + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->peerid = os_malloc(identity_len); + if (data->peerid == NULL) { + eap_eke_deinit(sm, data); + return NULL; + } + os_memcpy(data->peerid, identity, identity_len); + data->peerid_len = identity_len; + } + + phase1 = eap_get_config_phase1(sm); + if (phase1) { + const char *pos; + + pos = os_strstr(phase1, "dhgroup="); + if (pos) { + data->dhgroup = atoi(pos + 8); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced dhgroup %u", + data->dhgroup); + } + + pos = os_strstr(phase1, "encr="); + if (pos) { + data->encr = atoi(pos + 5); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced encr %u", + data->encr); + } + + pos = os_strstr(phase1, "prf="); + if (pos) { + data->prf = atoi(pos + 4); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced prf %u", + data->prf); + } + + pos = os_strstr(phase1, "mac="); + if (pos) { + data->mac = atoi(pos + 4); + wpa_printf(MSG_DEBUG, "EAP-EKE: Forced mac %u", + data->mac); + } + } + + return data; +} + + +static void eap_eke_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + eap_eke_session_clean(&data->sess); + os_free(data->serverid); + os_free(data->peerid); + wpabuf_free(data->msgs); + os_free(data); +} + + +static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, int id, + size_t length, u8 eke_exch) +{ + struct wpabuf *msg; + size_t plen; + + plen = 1 + length; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, + EAP_CODE_RESPONSE, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); + return NULL; + } + + wpabuf_put_u8(msg, eke_exch); + + return msg; +} + + +static int eap_eke_supp_dhgroup(u8 dhgroup) +{ + return dhgroup == EAP_EKE_DHGROUP_EKE_2 || + dhgroup == EAP_EKE_DHGROUP_EKE_5 || + dhgroup == EAP_EKE_DHGROUP_EKE_14 || + dhgroup == EAP_EKE_DHGROUP_EKE_15 || + dhgroup == EAP_EKE_DHGROUP_EKE_16; +} + + +static int eap_eke_supp_encr(u8 encr) +{ + return encr == EAP_EKE_ENCR_AES128_CBC; +} + + +static int eap_eke_supp_prf(u8 prf) +{ + return prf == EAP_EKE_PRF_HMAC_SHA1 || + prf == EAP_EKE_PRF_HMAC_SHA2_256; +} + + +static int eap_eke_supp_mac(u8 mac) +{ + return mac == EAP_EKE_MAC_HMAC_SHA1 || + mac == EAP_EKE_MAC_HMAC_SHA2_256; +} + + +static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + u32 failure_code) +{ + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x", + failure_code); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE); + if (resp) + wpabuf_put_be32(resp, failure_code); + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, FAILURE); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + unsigned num_prop, i; + const u8 *pos, *end; + const u8 *prop = NULL; + u8 idtype; + + if (data->state != IDENTITY) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-ID/Request"); + + if (payload_len < 2 + 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + pos = payload; + end = payload + payload_len; + + num_prop = *pos++; + pos++; /* Ignore Reserved field */ + + if (pos + num_prop * 4 > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)", + num_prop); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + for (i = 0; i < num_prop; i++) { + const u8 *tmp = pos; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Proposal #%u: dh=%u encr=%u prf=%u mac=%u", + i, pos[0], pos[1], pos[2], pos[3]); + pos += 4; + + if ((data->dhgroup && data->dhgroup != *tmp) || + !eap_eke_supp_dhgroup(*tmp)) + continue; + tmp++; + if ((data->encr && data->encr != *tmp) || + !eap_eke_supp_encr(*tmp)) + continue; + tmp++; + if ((data->prf && data->prf != *tmp) || + !eap_eke_supp_prf(*tmp)) + continue; + tmp++; + if ((data->mac && data->mac != *tmp) || + !eap_eke_supp_mac(*tmp)) + continue; + + prop = tmp - 3; + if (eap_eke_session_init(&data->sess, prop[0], prop[1], prop[2], + prop[3]) < 0) { + prop = NULL; + continue; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Selected proposal"); + break; + } + + if (prop == NULL) { + wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN); + } + + pos += (num_prop - i - 1) * 4; + + if (pos == end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + idtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-EKE: Server IDType %u", idtype); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity", + pos, end - pos); + os_free(data->serverid); + data->serverid = os_malloc(end - pos); + if (data->serverid == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + os_memcpy(data->serverid, pos, end - pos); + data->serverid_len = end - pos; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + 2 + 4 + 1 + data->peerid_len, + EAP_EKE_ID); + if (resp == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + wpabuf_put_u8(resp, 1); /* NumProposals */ + wpabuf_put_u8(resp, 0); /* Reserved */ + wpabuf_put_data(resp, prop, 4); /* Selected Proposal */ + wpabuf_put_u8(resp, EAP_EKE_ID_NAI); + if (data->peerid) + wpabuf_put_data(resp, data->peerid, data->peerid_len); + + wpabuf_free(data->msgs); + data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp)); + if (data->msgs == NULL) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put_buf(data->msgs, reqData); + wpabuf_put_buf(data->msgs, resp); + + eap_eke_state(data, COMMIT); + + return resp; +} + + +static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm, + struct eap_eke_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, *dhcomp; + size_t prot_len; + u8 *rpos; + u8 key[EAP_EKE_MAX_KEY_LEN]; + u8 pub[EAP_EKE_MAX_DH_LEN]; + const u8 *password; + size_t password_len; + + if (data->state != COMMIT) { + wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Commit/Request"); + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured!"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PASSWD_NOT_FOUND); + } + + pos = payload; + end = payload + payload_len; + + if (pos + data->sess.dhcomp_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_S", + pos, data->sess.dhcomp_len); + dhcomp = pos; + pos += data->sess.dhcomp_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); + + /* + * temp = prf(0+, password) + * key = prf+(temp, ID_S | ID_P) + */ + if (eap_eke_derive_key(&data->sess, password, password_len, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, key) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + /* + * y_p = g ^ x_p (mod p) + * x_p = random number 2 .. p-1 + */ + if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_shared_secret(&data->sess, key, data->dh_priv, dhcomp) < 0) + { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_derive_ke_ki(&data->sess, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + data->sess.dhcomp_len + data->sess.pnonce_len, + EAP_EKE_COMMIT); + if (resp == NULL) { + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + /* DHComponent_P = Encr(key, y_p) */ + rpos = wpabuf_put(resp, data->sess.dhcomp_len); + if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S"); + os_memset(key, 0, sizeof(key)); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + os_memset(key, 0, sizeof(key)); + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", + rpos, data->sess.dhcomp_len); + + if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", + data->nonce_p, data->sess.nonce_len); + prot_len = wpabuf_tailroom(resp); + if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len, + wpabuf_put(resp, 0), &prot_len) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", + wpabuf_put(resp, 0), prot_len); + wpabuf_put(resp, prot_len); + + /* TODO: CBValue */ + + if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp)) + < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put_buf(data->msgs, reqData); + wpabuf_put_buf(data->msgs, resp); + + eap_eke_state(data, CONFIRM); + + return resp; +} + + +static struct wpabuf * eap_eke_process_confirm(struct eap_eke_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; + size_t prot_len; + u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; + u8 auth_s[EAP_EKE_MAX_HASH_LEN]; + size_t decrypt_len; + u8 *auth; + + if (data->state != CONFIRM) { + wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)", + data->state); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Confirm/Request"); + + pos = payload; + end = payload + payload_len; + + if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PROTO_ERROR); + } + + decrypt_len = sizeof(nonces); + if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len, + nonces, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + if (decrypt_len != (size_t) 2 * data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S", + nonces, 2 * data->sess.nonce_len); + if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match trnsmitted Nonce_P"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + + os_memcpy(data->nonce_s, nonces + data->sess.nonce_len, + data->sess.nonce_len); + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", + data->nonce_s, data->sess.nonce_len); + + if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len, + data->peerid, data->peerid_len, + data->nonce_p, data->nonce_s) < 0) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0) + { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len); + if (os_memcmp(auth_s, pos + data->sess.pnonce_ps_len, + data->sess.prf_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match"); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_AUTHENTICATION_FAIL); + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response"); + + resp = eap_eke_build_msg(data, eap_get_id(reqData), + data->sess.pnonce_len + data->sess.prf_len, + EAP_EKE_CONFIRM); + if (resp == NULL) { + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + prot_len = wpabuf_tailroom(resp); + if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len, + wpabuf_put(resp, 0), &prot_len) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpabuf_put(resp, prot_len); + + auth = wpabuf_put(resp, data->sess.prf_len); + if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) { + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len); + + if (eap_eke_derive_msk(&data->sess, data->serverid, data->serverid_len, + data->peerid, data->peerid_len, + data->nonce_s, data->nonce_p, + data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); + wpabuf_free(resp); + return eap_eke_build_fail(data, ret, reqData, + EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + } + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, SUCCESS); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Failure/Request"); + + if (payload_len < 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); + } else { + u32 code; + code = WPA_GET_BE32(payload); + wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code); + } + + return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR); +} + + +static struct wpabuf * eap_eke_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_eke_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *end; + size_t len; + u8 eke_exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, reqData, &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + + end = pos + len; + eke_exch = *pos++; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: exch %d", eke_exch); + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received Data", pos, end - pos); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (eke_exch) { + case EAP_EKE_ID: + resp = eap_eke_process_id(data, ret, reqData, pos, end - pos); + break; + case EAP_EKE_COMMIT: + resp = eap_eke_process_commit(sm, data, ret, reqData, + pos, end - pos); + break; + case EAP_EKE_CONFIRM: + resp = eap_eke_process_confirm(data, ret, reqData, + pos, end - pos); + break; + case EAP_EKE_FAILURE: + resp = eap_eke_process_failure(data, ret, reqData, + pos, end - pos); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-EKE: Ignoring message with unknown EKE-Exch %d", eke_exch); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) + ret->allowNotifications = FALSE; + + return resp; +} + + +static Boolean eap_eke_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_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_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_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_eke_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); + if (eap == NULL) + return -1; + + eap->init = eap_eke_init; + eap->deinit = eap_eke_deinit; + eap->process = eap_eke_process; + eap->isKeyAvailable = eap_eke_isKeyAvailable; + eap->getKey = eap_eke_getKey; + eap->get_emsk = eap_eke_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/src/eap_peer/eap_fast.c b/contrib/hostapd/src/eap_peer/eap_fast.c index d00867099a..1b0c562a60 100644 --- a/contrib/hostapd/src/eap_peer/eap_fast.c +++ b/contrib/hostapd/src/eap_peer/eap_fast.c @@ -2,25 +2,19 @@ * EAP peer method: EAP-FAST (RFC 4851) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 "tls.h" -#include "eap_common/eap_tlv_common.h" -#include "sha1.h" #include "eap_fast_pac.h" #ifdef EAP_FAST_DYNAMIC @@ -59,6 +53,8 @@ struct eap_fast_data { int session_ticket_used; u8 key_data[EAP_FAST_KEY_LEN]; + u8 *session_id; + size_t id_len; u8 emsk[EAP_EMSK_LEN]; int success; @@ -175,7 +171,7 @@ static void * eap_fast_init(struct eap_sm *sm) data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); eap_fast_deinit(sm, data); return NULL; @@ -200,14 +196,22 @@ static void * eap_fast_init(struct eap_sm *sm) "workarounds"); } + if (!config->pac_file) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC file configured"); + eap_fast_deinit(sm, data); + return NULL; + } + if (data->use_pac_binary_format && eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file"); eap_fast_deinit(sm, data); return NULL; } if (!data->use_pac_binary_format && eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file"); eap_fast_deinit(sm, data); return NULL; } @@ -244,6 +248,7 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv) pac = pac->next; eap_fast_free_pac(prev); } + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -444,8 +449,9 @@ static int eap_fast_phase2_request(struct eap_sm *sm, return 0; } - if (data->phase2_priv == NULL && - eap_fast_init_phase2_method(sm, data) < 0) { + if ((data->phase2_priv == NULL && + eap_fast_init_phase2_method(sm, data) < 0) || + data->phase2_method == NULL) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize " "Phase 2 EAP method %d", *pos); ret->methodState = METHOD_DONE; @@ -542,7 +548,7 @@ static struct wpabuf * eap_fast_tlv_pac_ack(void) 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, + struct eap_method_ret *ret, u8 *eap_payload_tlv, size_t eap_payload_tlv_len) { struct eap_hdr *hdr; @@ -790,6 +796,21 @@ static struct wpabuf * eap_fast_process_crypto_binding( return NULL; } + if (!data->anon_provisioning && data->phase2_success) { + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id( + sm, &data->ssl, EAP_TYPE_FAST, &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive " + "Session-Id"); + 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); @@ -1034,14 +1055,19 @@ static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm, } wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " "- Provisioning completed successfully"); + sm->expected_failure = 1; } else { /* * This is PAC refreshing, i.e., normal authentication that is - * expected to be completed with an EAP-Success. + * expected to be completed with an EAP-Success. However, + * RFC 5422, Section 3.5 allows EAP-Failure to be sent even + * after protected success exchange in case of EAP-Fast + * provisioning, so we better use DECISION_COND_SUCC here + * instead of DECISION_UNCOND_SUCC. */ wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " "- PAC refreshing completed successfully"); - ret->decision = DECISION_UNCOND_SUCC; + ret->decision = DECISION_COND_SUCC; } ret->methodState = METHOD_DONE; return eap_fast_tlv_pac_ack(); @@ -1184,7 +1210,7 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, if (tlv.eap_payload_tlv) { tmp = eap_fast_process_eap_payload_tlv( - sm, data, ret, req, tlv.eap_payload_tlv, + sm, data, ret, tlv.eap_payload_tlv, tlv.eap_payload_tlv_len); resp = wpabuf_concat(resp, tmp); } @@ -1227,6 +1253,7 @@ static int eap_fast_process_decrypted(struct eap_sm *sm, "provisioning completed successfully."); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; + sm->expected_failure = 1; } else { wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication " "completed successfully."); @@ -1445,9 +1472,9 @@ static int eap_fast_process_start(struct eap_sm *sm, /* EAP-FAST Version negotiation (section 3.1) */ wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)", - flags & EAP_PEAP_VERSION_MASK, data->fast_version); - if ((flags & EAP_PEAP_VERSION_MASK) < data->fast_version) - data->fast_version = flags & EAP_PEAP_VERSION_MASK; + 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); @@ -1605,6 +1632,8 @@ static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv) os_free(data); return NULL; } + os_free(data->session_id); + data->session_id = NULL; if (data->phase2_priv && data->phase2_method && data->phase2_method->init_for_reauth) data->phase2_method->init_for_reauth(sm, data->phase2_priv); @@ -1663,6 +1692,25 @@ static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *id; + + if (!data->success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_fast_data *data = priv; @@ -1697,6 +1745,7 @@ int eap_peer_fast_register(void) eap->process = eap_fast_process; eap->isKeyAvailable = eap_fast_isKeyAvailable; eap->getKey = eap_fast_getKey; + eap->getSessionId = eap_fast_get_session_id; eap->get_status = eap_fast_get_status; #if 0 eap->has_reauth_data = eap_fast_has_reauth_data; diff --git a/contrib/hostapd/src/eap_peer/eap_fast_pac.c b/contrib/hostapd/src/eap_peer/eap_fast_pac.c index 77893d6195..8c480b9679 100644 --- a/contrib/hostapd/src/eap_peer/eap_fast_pac.c +++ b/contrib/hostapd/src/eap_peer/eap_fast_pac.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-FAST PAC file processing * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -428,8 +422,12 @@ int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, 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) + if (eap_fast_read_line(&rc, &pos) < 0) { + /* empty file - assume it is fine to overwrite */ + eap_fast_deinit_pac_data(&rc); + return 0; + } + if (os_strcmp(pac_file_hdr, rc.buf) != 0) err = "Unrecognized header line"; while (!err && eap_fast_read_line(&rc, &pos) == 0) { @@ -480,8 +478,10 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len, { size_t i, need; int ret; + char *end; - if (data == NULL || *buf == NULL) + if (data == NULL || buf == NULL || *buf == NULL || + pos == NULL || *pos == NULL || *pos < *buf) return; need = os_strlen(field) + len * 2 + 30; @@ -495,35 +495,35 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len, *buf = NULL; return; } + *pos = nbuf + (*pos - *buf); *buf = nbuf; *buf_len += need; } + end = *buf + *buf_len; - ret = os_snprintf(*pos, *buf + *buf_len - *pos, "%s=", field); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + ret = os_snprintf(*pos, end - *pos, "%s=", field); + if (ret < 0 || ret >= end - *pos) return; *pos += ret; - *pos += wpa_snprintf_hex(*pos, *buf + *buf_len - *pos, data, len); - ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n"); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + *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, *buf + *buf_len - *pos, - "%s-txt=", field); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + 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, *buf + *buf_len - *pos, - "%c", data[i]); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + ret = os_snprintf(*pos, end - *pos, "%c", data[i]); + if (ret < 0 || ret >= end - *pos) return; *pos += ret; } - ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n"); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + ret = os_snprintf(*pos, end - *pos, "\n"); + if (ret < 0 || ret >= end - *pos) return; *pos += ret; } diff --git a/contrib/hostapd/src/eap_peer/eap_fast_pac.h b/contrib/hostapd/src/eap_peer/eap_fast_pac.h index 9483f96852..8815d9168d 100644 --- a/contrib/hostapd/src/eap_peer/eap_fast_pac.h +++ b/contrib/hostapd/src/eap_peer/eap_fast_pac.h @@ -2,14 +2,8 @@ * EAP peer method: EAP-FAST PAC file processing * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_FAST_PAC_H diff --git a/contrib/hostapd/src/eap_peer/eap_gpsk.c b/contrib/hostapd/src/eap_peer/eap_gpsk.c index f6a1955baf..5b023c7933 100644 --- a/contrib/hostapd/src/eap_peer/eap_gpsk.c +++ b/contrib/hostapd/src/eap_peer/eap_gpsk.c @@ -1,20 +1,15 @@ /* * EAP peer method: EAP-GPSK (RFC 5433) - * Copyright (c) 2006-2008, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_gpsk_common.h" @@ -28,8 +23,8 @@ struct eap_gpsk_data { size_t sk_len; u8 pk[EAP_GPSK_MAX_PK_LEN]; size_t pk_len; - u8 session_id; - int session_id_set; + u8 session_id[128]; + size_t id_len; u8 *id_peer; size_t id_peer_len; u8 *id_server; @@ -38,6 +33,7 @@ struct eap_gpsk_data { int specifier; /* CSuite/Specifier */ u8 *psk; size_t psk_len; + u16 forced_cipher; /* force cipher or 0 to allow all supported */ }; @@ -85,6 +81,7 @@ static void * eap_gpsk_init(struct eap_sm *sm) struct eap_gpsk_data *data; const u8 *identity, *password; size_t identity_len, password_len; + const char *phase1; password = eap_get_config_password(sm, &password_len); if (password == NULL) { @@ -108,6 +105,18 @@ static void * eap_gpsk_init(struct eap_sm *sm) data->id_peer_len = identity_len; } + phase1 = eap_get_config_phase1(sm); + if (phase1) { + const char *pos; + + pos = os_strstr(phase1, "cipher="); + if (pos) { + data->forced_cipher = atoi(pos + 7); + wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u", + data->forced_cipher); + } + } + data->psk = os_malloc(password_len); if (data->psk == NULL) { eap_gpsk_deinit(sm, data); @@ -200,7 +209,9 @@ static int eap_gpsk_select_csuite(struct eap_sm *sm, i, vendor, specifier); if (data->vendor == EAP_GPSK_VENDOR_IETF && data->specifier == EAP_GPSK_CIPHER_RESERVED && - eap_gpsk_supported_ciphersuite(vendor, specifier)) { + eap_gpsk_supported_ciphersuite(vendor, specifier) && + (!data->forced_cipher || data->forced_cipher == specifier)) + { data->vendor = vendor; data->specifier = specifier; } @@ -278,6 +289,7 @@ static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list, &csuite_list_len, pos, end); if (pos == NULL) { + ret->methodState = METHOD_DONE; eap_gpsk_state(data, FAILURE); return NULL; } @@ -326,7 +338,7 @@ static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, 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)) { + if (random_get_bytes(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); @@ -359,6 +371,21 @@ static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, return NULL; } + if (eap_gpsk_derive_session_id(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, + EAP_TYPE_GPSK, + data->session_id, &data->id_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id"); + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id", + data->session_id, data->id_len); + /* No PD_Payload_1 */ wpabuf_put_be16(resp, 0); @@ -713,6 +740,24 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *sid; + + if (data->state != SUCCESS) + return NULL; + + sid = os_malloc(data->id_len); + if (sid == NULL) + return NULL; + os_memcpy(sid, data->session_id, data->id_len); + *len = data->id_len; + + return sid; +} + + int eap_peer_gpsk_register(void) { struct eap_method *eap; @@ -729,6 +774,7 @@ int eap_peer_gpsk_register(void) eap->isKeyAvailable = eap_gpsk_isKeyAvailable; eap->getKey = eap_gpsk_getKey; eap->get_emsk = eap_gpsk_get_emsk; + eap->getSessionId = eap_gpsk_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/contrib/hostapd/src/eap_peer/eap_gtc.c b/contrib/hostapd/src/eap_peer/eap_gtc.c index b2b554b447..9f3cfbdaca 100644 --- a/contrib/hostapd/src/eap_peer/eap_gtc.c +++ b/contrib/hostapd/src/eap_peer/eap_gtc.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-GTC (RFC 3748) * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/hostapd/src/eap_peer/eap_i.h b/contrib/hostapd/src/eap_peer/eap_i.h index e7c826ee8f..8288ba5b51 100644 --- a/contrib/hostapd/src/eap_peer/eap_i.h +++ b/contrib/hostapd/src/eap_peer/eap_i.h @@ -2,14 +2,8 @@ * EAP peer state machines internal structures (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_I_H @@ -267,6 +261,19 @@ struct eap_method { * private data or this function may derive the key. */ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * getSessionId - Get EAP method specific Session-Id + * @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 Session-Id length + * Returns: Session-Id or %NULL if not available + * + * This function can be used to get the Session-Id from the EAP method. + * The Session-Id may already be stored in the method-specific private + * data or this function may derive the Session-Id. + */ + u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len); }; @@ -304,6 +311,8 @@ struct eap_sm { Boolean eapKeyAvailable; /* peer to lower layer */ u8 *eapKeyData; /* peer to lower layer */ size_t eapKeyDataLen; /* peer to lower layer */ + u8 *eapSessionId; /* peer to lower layer */ + size_t eapSessionIdLen; /* peer to lower layer */ const struct eap_method *m; /* selected EAP method */ /* not defined in RFC 4137 */ Boolean changed; @@ -323,6 +332,7 @@ struct eap_sm { void *msg_ctx; void *scard_ctx; void *ssl_ctx; + void *ssl_ctx2; unsigned int workaround; @@ -335,6 +345,13 @@ struct eap_sm { struct wps_context *wps; int prev_failure; + + struct ext_password_data *ext_pw; + struct wpabuf *ext_pw_buf; + + int external_sim; + + unsigned int expected_failure:1; }; const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len); @@ -345,6 +362,7 @@ 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); +int eap_get_config_fragment_size(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 * diff --git a/contrib/hostapd/src/eap_peer/eap_ikev2.c b/contrib/hostapd/src/eap_peer/eap_ikev2.c index bb49a662d7..2d7841dd01 100644 --- a/contrib/hostapd/src/eap_peer/eap_ikev2.c +++ b/contrib/hostapd/src/eap_peer/eap_ikev2.c @@ -1,15 +1,9 @@ /* * EAP-IKEv2 peer (RFC 5106) - * Copyright (c) 2007, Jouni Malinen + * Copyright (c) 2007-2014, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -66,6 +60,7 @@ static void * eap_ikev2_init(struct eap_sm *sm) struct eap_ikev2_data *data; const u8 *identity, *password; size_t identity_len, password_len; + int fragment_size; identity = eap_get_config_identity(sm, &identity_len); if (identity == NULL) { @@ -77,7 +72,11 @@ static void * eap_ikev2_init(struct eap_sm *sm) if (data == NULL) return NULL; data->state = WAIT_START; - data->fragment_size = IKEV2_FRAGMENT_SIZE; + fragment_size = eap_get_config_fragment_size(sm); + if (fragment_size <= 0) + data->fragment_size = IKEV2_FRAGMENT_SIZE; + else + data->fragment_size = 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"); @@ -481,6 +480,36 @@ static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *sid; + size_t sid_len; + size_t offset; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len; + sid = os_malloc(sid_len); + if (sid) { + offset = 0; + sid[offset] = EAP_TYPE_IKEV2; + offset++; + os_memcpy(sid + offset, data->ikev2.i_nonce, + data->ikev2.i_nonce_len); + offset += data->ikev2.i_nonce_len; + os_memcpy(sid + offset, data->ikev2.r_nonce, + data->ikev2.r_nonce_len); + *len = sid_len; + wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id", + sid, sid_len); + } + + return sid; +} + + int eap_peer_ikev2_register(void) { struct eap_method *eap; @@ -498,6 +527,7 @@ int eap_peer_ikev2_register(void) eap->isKeyAvailable = eap_ikev2_isKeyAvailable; eap->getKey = eap_ikev2_getKey; eap->get_emsk = eap_ikev2_get_emsk; + eap->getSessionId = eap_ikev2_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/contrib/hostapd/src/eap_peer/eap_leap.c b/contrib/hostapd/src/eap_peer/eap_leap.c index 01c1f160ab..df3401384c 100644 --- a/contrib/hostapd/src/eap_peer/eap_leap.c +++ b/contrib/hostapd/src/eap_peer/eap_leap.c @@ -2,22 +2,17 @@ * EAP peer method: LEAP * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/ms_funcs.h" +#include "crypto/crypto.h" +#include "crypto/random.h" #include "eap_i.h" -#include "ms_funcs.h" -#include "crypto.h" #define LEAP_VERSION 1 #define LEAP_CHALLENGE_LEN 8 @@ -167,7 +162,7 @@ static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv, 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)) { + if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) { wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " "for challenge"); wpabuf_free(resp); @@ -233,10 +228,16 @@ static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); if (pwhash) { - hash_nt_password_hash(password, pw_hash_hash); + if (hash_nt_password_hash(password, pw_hash_hash)) { + ret->ignore = TRUE; + return NULL; + } } else { - nt_password_hash(password, password_len, pw_hash); - hash_nt_password_hash(pw_hash, pw_hash_hash); + 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); @@ -345,11 +346,17 @@ static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) if (key == NULL) return NULL; - if (pwhash) - hash_nt_password_hash(password, pw_hash_hash); - else { - nt_password_hash(password, password_len, pw_hash); - hash_nt_password_hash(pw_hash, pw_hash_hash); + 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); diff --git a/contrib/hostapd/src/eap_peer/eap_md5.c b/contrib/hostapd/src/eap_peer/eap_md5.c index 7961143a00..d06befaeb1 100644 --- a/contrib/hostapd/src/eap_peer/eap_md5.c +++ b/contrib/hostapd/src/eap_peer/eap_md5.c @@ -1,15 +1,9 @@ /* * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994) - * Copyright (c) 2004-2006, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -76,7 +70,7 @@ static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response"); ret->methodState = METHOD_DONE; - ret->decision = DECISION_UNCOND_SUCC; + ret->decision = DECISION_COND_SUCC; ret->allowNotifications = TRUE; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN, @@ -92,7 +86,13 @@ static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, id = eap_get_id(resp); rpos = wpabuf_put(resp, CHAP_MD5_LEN); - chap_md5(id, password, password_len, challenge, challenge_len, rpos); + if (chap_md5(id, password, password_len, challenge, challenge_len, + rpos)) { + wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); + ret->ignore = TRUE; + wpabuf_free(resp); + return NULL; + } wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN); return resp; diff --git a/contrib/hostapd/src/eap_peer/eap_methods.c b/contrib/hostapd/src/eap_peer/eap_methods.c index 2374e5e422..83a1457964 100644 --- a/contrib/hostapd/src/eap_peer/eap_methods.c +++ b/contrib/hostapd/src/eap_peer/eap_methods.c @@ -2,14 +2,8 @@ * EAP peer: Method registration * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -77,6 +71,8 @@ EapType eap_peer_get_type(const char *name, int *vendor) const char * eap_get_name(int vendor, EapType type) { struct eap_method *m; + if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED) + return "expanded"; for (m = eap_methods; m; m = m->next) { if (m->vendor == vendor && m->method == type) return m->name; @@ -339,161 +335,6 @@ int eap_peer_method_register(struct eap_method *method) } -/** - * eap_peer_register_methods - Register statically linked EAP peer methods - * Returns: 0 on success, -1 on failure - * - * This function is called at program initialization to register all EAP peer - * methods that were linked in statically. - */ -int eap_peer_register_methods(void) -{ - int ret = 0; - -#ifdef EAP_MD5 - if (ret == 0) { - int eap_peer_md5_register(void); - ret = eap_peer_md5_register(); - } -#endif /* EAP_MD5 */ - -#ifdef EAP_TLS - if (ret == 0) { - int eap_peer_tls_register(void); - ret = eap_peer_tls_register(); - } -#endif /* EAP_TLS */ - -#ifdef EAP_MSCHAPv2 - if (ret == 0) { - int eap_peer_mschapv2_register(void); - ret = eap_peer_mschapv2_register(); - } -#endif /* EAP_MSCHAPv2 */ - -#ifdef EAP_PEAP - if (ret == 0) { - int eap_peer_peap_register(void); - ret = eap_peer_peap_register(); - } -#endif /* EAP_PEAP */ - -#ifdef EAP_TTLS - if (ret == 0) { - int eap_peer_ttls_register(void); - ret = eap_peer_ttls_register(); - } -#endif /* EAP_TTLS */ - -#ifdef EAP_GTC - if (ret == 0) { - int eap_peer_gtc_register(void); - ret = eap_peer_gtc_register(); - } -#endif /* EAP_GTC */ - -#ifdef EAP_OTP - if (ret == 0) { - int eap_peer_otp_register(void); - ret = eap_peer_otp_register(); - } -#endif /* EAP_OTP */ - -#ifdef EAP_SIM - if (ret == 0) { - int eap_peer_sim_register(void); - ret = eap_peer_sim_register(); - } -#endif /* EAP_SIM */ - -#ifdef EAP_LEAP - if (ret == 0) { - int eap_peer_leap_register(void); - ret = eap_peer_leap_register(); - } -#endif /* EAP_LEAP */ - -#ifdef EAP_PSK - if (ret == 0) { - int eap_peer_psk_register(void); - ret = eap_peer_psk_register(); - } -#endif /* EAP_PSK */ - -#ifdef EAP_AKA - if (ret == 0) { - int eap_peer_aka_register(void); - ret = eap_peer_aka_register(); - } -#endif /* EAP_AKA */ - -#ifdef EAP_AKA_PRIME - if (ret == 0) { - int eap_peer_aka_prime_register(void); - ret = eap_peer_aka_prime_register(); - } -#endif /* EAP_AKA_PRIME */ - -#ifdef EAP_FAST - if (ret == 0) { - int eap_peer_fast_register(void); - ret = eap_peer_fast_register(); - } -#endif /* EAP_FAST */ - -#ifdef EAP_PAX - if (ret == 0) { - int eap_peer_pax_register(void); - ret = eap_peer_pax_register(); - } -#endif /* EAP_PAX */ - -#ifdef EAP_SAKE - if (ret == 0) { - int eap_peer_sake_register(void); - ret = eap_peer_sake_register(); - } -#endif /* EAP_SAKE */ - -#ifdef EAP_GPSK - if (ret == 0) { - int eap_peer_gpsk_register(void); - ret = eap_peer_gpsk_register(); - } -#endif /* EAP_GPSK */ - -#ifdef EAP_WSC - if (ret == 0) { - int eap_peer_wsc_register(void); - ret = eap_peer_wsc_register(); - } -#endif /* EAP_WSC */ - -#ifdef EAP_IKEV2 - if (ret == 0) { - int eap_peer_ikev2_register(void); - ret = eap_peer_ikev2_register(); - } -#endif /* EAP_IKEV2 */ - -#ifdef EAP_VENDOR_TEST - if (ret == 0) { - int eap_peer_vendor_test_register(void); - ret = eap_peer_vendor_test_register(); - } -#endif /* EAP_VENDOR_TEST */ - -#ifdef EAP_TNC - if (ret == 0) { - int eap_peer_tnc_register(void); - ret = eap_peer_tnc_register(); - } -#endif /* EAP_TNC */ - - return ret; -} - - /** * eap_peer_unregister_methods - Unregister EAP peer methods * diff --git a/contrib/hostapd/src/eap_peer/eap_methods.h b/contrib/hostapd/src/eap_peer/eap_methods.h index 9fd9b517c7..a465fd2354 100644 --- a/contrib/hostapd/src/eap_peer/eap_methods.h +++ b/contrib/hostapd/src/eap_peer/eap_methods.h @@ -2,14 +2,8 @@ * EAP peer: Method registration * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_METHODS_H @@ -32,7 +26,6 @@ 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 */ @@ -89,4 +82,29 @@ static inline int eap_peer_method_unload(struct eap_method *method) #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_unauth_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); +int eap_peer_pwd_register(void); +int eap_peer_eke_register(void); + #endif /* EAP_METHODS_H */ diff --git a/contrib/hostapd/src/eap_peer/eap_mschapv2.c b/contrib/hostapd/src/eap_peer/eap_mschapv2.c index b0c3ab7763..f9aa74257d 100644 --- a/contrib/hostapd/src/eap_peer/eap_mschapv2.c +++ b/contrib/hostapd/src/eap_peer/eap_mschapv2.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -22,11 +16,12 @@ #include "includes.h" #include "common.h" +#include "crypto/ms_funcs.h" +#include "crypto/random.h" +#include "common/wpa_ctrl.h" +#include "mschapv2.h" #include "eap_i.h" #include "eap_config.h" -#include "ms_funcs.h" -#include "wpa_ctrl.h" -#include "mschapv2.h" #ifdef _MSC_VER @@ -199,7 +194,7 @@ static struct wpabuf * eap_mschapv2_challenge_reply( "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)) { + } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) { wpabuf_free(resp); return NULL; } @@ -209,10 +204,15 @@ static struct wpabuf * eap_mschapv2_challenge_reply( "in Phase 1"); auth_challenge = data->auth_challenge; } - mschapv2_derive_response(identity, identity_len, password, - password_len, pwhash, auth_challenge, - peer_challenge, r->nt_response, - data->auth_response, data->master_key); + 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; @@ -304,7 +304,9 @@ static void eap_mschapv2_password_changed(struct eap_sm *sm, "EAP-MSCHAPV2: Password changed successfully"); data->prev_error = 0; os_free(config->password); - if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { + if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) { + /* TODO: update external storage */ + } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) { config->password = os_malloc(16); config->password_len = 16; if (config->password) { @@ -559,7 +561,7 @@ static struct wpabuf * eap_mschapv2_change_password( } /* Peer-Challenge */ - if (os_get_random(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) + if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) goto fail; /* Reserved, must be zero */ @@ -642,10 +644,8 @@ static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm, * must allocate a large enough temporary buffer to create that since * the received message does not include nul termination. */ - buf = os_malloc(len + 1); + buf = dup_binstr(msdata, len); if (buf) { - os_memcpy(buf, msdata, len); - buf[len] = '\0'; retry = eap_mschapv2_failure_txt(sm, data, buf); os_free(buf); } diff --git a/contrib/hostapd/src/eap_peer/eap_otp.c b/contrib/hostapd/src/eap_peer/eap_otp.c index 556c22f9e2..9ac744a7dd 100644 --- a/contrib/hostapd/src/eap_peer/eap_otp.c +++ b/contrib/hostapd/src/eap_peer/eap_otp.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-OTP (RFC 3748) * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/hostapd/src/eap_peer/eap_pax.c b/contrib/hostapd/src/eap_peer/eap_pax.c index afd56dd49b..7f87052071 100644 --- a/contrib/hostapd/src/eap_peer/eap_pax.c +++ b/contrib/hostapd/src/eap_peer/eap_pax.c @@ -2,23 +2,16 @@ * EAP peer method: EAP-PAX (RFC 4746) * Copyright (c) 2005-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "eap_peer/eap_i.h" +#include "crypto/random.h" #include "eap_common/eap_pax_common.h" -#include "sha1.h" -#include "crypto.h" +#include "eap_i.h" /* * Note: only PAX_STD subprotocol is currently supported @@ -176,7 +169,7 @@ static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, pos, left); } - if (os_get_random(data->rand.r.y, EAP_PAX_RAND_LEN)) { + if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); ret->ignore = TRUE; return NULL; diff --git a/contrib/hostapd/src/eap_peer/eap_peap.c b/contrib/hostapd/src/eap_peer/eap_peap.c index 894fc632fd..8634f754c9 100644 --- a/contrib/hostapd/src/eap_peer/eap_peap.c +++ b/contrib/hostapd/src/eap_peer/eap_peap.c @@ -2,33 +2,26 @@ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 "tls.h" -#include "eap_common/eap_tlv_common.h" -#include "eap_common/eap_peap_common.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 @@ -62,6 +55,8 @@ struct eap_peap_data { int resuming; /* starting a resumed session */ int reauth; /* reauthentication */ u8 *key_data; + u8 *session_id; + size_t id_len; struct wpabuf *pending_phase2_req; enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; @@ -165,7 +160,7 @@ static void * eap_peap_init(struct eap_sm *sm) data->phase2_type.vendor = EAP_VENDOR_IETF; data->phase2_type.method = EAP_TYPE_NONE; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); eap_peap_deinit(sm, data); return NULL; @@ -185,6 +180,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) os_free(data->phase2_types); eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -196,7 +192,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv) * @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 + * This function 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) @@ -285,8 +281,10 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * 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)); + if (peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); @@ -316,8 +314,6 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm, 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); @@ -346,8 +342,8 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm, * @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. + * This function 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, @@ -581,33 +577,6 @@ static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data, } -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, @@ -838,49 +807,6 @@ continue_req: 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 " @@ -997,11 +923,6 @@ continue_req: 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) @@ -1048,10 +969,10 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, if (flags & EAP_TLS_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " - "ver=%d)", flags & EAP_PEAP_VERSION_MASK, + "ver=%d)", flags & EAP_TLS_VERSION_MASK, data->peap_version); - if ((flags & EAP_PEAP_VERSION_MASK) < data->peap_version) - data->peap_version = flags & EAP_PEAP_VERSION_MASK; + 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 " @@ -1092,7 +1013,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, * 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) + if (data->force_new_label) label = "client PEAP encryption"; else label = "client EAP encryption"; @@ -1111,6 +1032,20 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, "derive key"); } + os_free(data->session_id); + data->session_id = + eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_PEAP, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, + "EAP-PEAP: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to " + "derive Session-Id"); + } + if (sm->workaround && data->resuming) { /* * At least few RADIUS servers (Aegis v1.1.6; @@ -1182,6 +1117,8 @@ 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; + os_free(data->session_id); + data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; @@ -1247,9 +1184,12 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) * 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)); + if (peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "\00", 1, csk, sizeof(csk)) < 0) { + os_free(key); + return NULL; + } 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", @@ -1261,6 +1201,25 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *id; + + if (data->session_id == NULL || !data->phase2_success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + int eap_peer_peap_register(void) { struct eap_method *eap; @@ -1280,6 +1239,7 @@ int eap_peer_peap_register(void) 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; + eap->getSessionId = eap_peap_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/contrib/hostapd/src/eap_peer/eap_proxy.h b/contrib/hostapd/src/eap_peer/eap_proxy.h new file mode 100644 index 0000000000..23cdbe698b --- /dev/null +++ b/contrib/hostapd/src/eap_peer/eap_proxy.h @@ -0,0 +1,49 @@ +/* + * EAP proxy definitions + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_PROXY_H +#define EAP_PROXY_H + +struct eap_proxy_sm; +struct eapol_callbacks; +struct eap_sm; +struct eap_peer_config; + +enum eap_proxy_status { + EAP_PROXY_FAILURE = 0x00, + EAP_PROXY_SUCCESS +}; + +struct eap_proxy_sm * +eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx); + +void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy); + +int eap_proxy_key_available(struct eap_proxy_sm *sm); + +const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len); + +struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm); + +int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm); + +enum eap_proxy_status +eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData, + int eapReqDataLen); + +int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, + int verbose); + +int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, + size_t *imsi_len); + +int eap_proxy_notify_config(struct eap_proxy_sm *sm, + struct eap_peer_config *config); + +#endif /* EAP_PROXY_H */ diff --git a/contrib/hostapd/src/eap_peer/eap_proxy_dummy.c b/contrib/hostapd/src/eap_peer/eap_proxy_dummy.c new file mode 100644 index 0000000000..d84f01234e --- /dev/null +++ b/contrib/hostapd/src/eap_peer/eap_proxy_dummy.c @@ -0,0 +1,77 @@ +/* + * EAP proxy - dummy implementation for build testing + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_proxy.h" + +struct eap_proxy_sm * +eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, + void *msg_ctx) +{ + return NULL; +} + + +void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy) +{ +} + + +int eap_proxy_key_available(struct eap_proxy_sm *sm) +{ + return 0; +} + + +const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len) +{ + return NULL; +} + + +struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm) +{ + return NULL; +} + + +int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm) +{ + return 0; +} + + +enum eap_proxy_status +eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData, + int eapReqDataLen) +{ + return EAP_PROXY_FAILURE; +} + + +int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, + int verbose) +{ + return 0; +} + + +int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, + size_t *imsi_len) +{ + return -1; +} + + +int eap_proxy_notify_config(struct eap_proxy_sm *sm, + struct eap_peer_config *config) +{ + return -1; +} diff --git a/contrib/hostapd/src/eap_peer/eap_psk.c b/contrib/hostapd/src/eap_peer/eap_psk.c index 1ce6356639..cd0e3f9666 100644 --- a/contrib/hostapd/src/eap_peer/eap_psk.c +++ b/contrib/hostapd/src/eap_peer/eap_psk.c @@ -2,14 +2,8 @@ * EAP peer method: EAP-PSK (RFC 4764) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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. @@ -18,14 +12,16 @@ #include "includes.h" #include "common.h" -#include "eap_peer/eap_i.h" -#include "aes_wrap.h" +#include "crypto/aes_wrap.h" +#include "crypto/random.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 rand_s[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; @@ -117,6 +113,7 @@ static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, } wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, EAP_PSK_RAND_LEN); + os_memcpy(data->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); @@ -130,7 +127,7 @@ static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, 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)) { + if (random_get_bytes(data->rand_p, EAP_PSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); ret->ignore = TRUE; return NULL; @@ -439,6 +436,28 @@ static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *id; + + if (data->state != PSK_DONE) + return NULL; + + *len = 1 + 2 * EAP_PSK_RAND_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_PSK; + os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN); + os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_psk_data *data = priv; @@ -473,6 +492,7 @@ int eap_peer_psk_register(void) eap->process = eap_psk_process; eap->isKeyAvailable = eap_psk_isKeyAvailable; eap->getKey = eap_psk_getKey; + eap->getSessionId = eap_psk_get_session_id; eap->get_emsk = eap_psk_get_emsk; ret = eap_peer_method_register(eap); diff --git a/contrib/hostapd/src/eap_peer/eap_pwd.c b/contrib/hostapd/src/eap_peer/eap_pwd.c new file mode 100644 index 0000000000..fef478370c --- /dev/null +++ b/contrib/hostapd/src/eap_peer/eap_pwd.c @@ -0,0 +1,927 @@ +/* + * EAP peer method: EAP-pwd (RFC 5931) + * Copyright (c) 2010, Dan Harkins + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha256.h" +#include "eap_peer/eap_i.h" +#include "eap_common/eap_pwd_common.h" + + +struct eap_pwd_data { + enum { + PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE + } state; + u8 *id_peer; + size_t id_peer_len; + u8 *id_server; + size_t id_server_len; + u8 *password; + size_t password_len; + u16 group_num; + EAP_PWD_group *grp; + + struct wpabuf *inbuf; + size_t in_frag_pos; + struct wpabuf *outbuf; + size_t out_frag_pos; + size_t mtu; + + BIGNUM *k; + BIGNUM *private_value; + BIGNUM *server_scalar; + BIGNUM *my_scalar; + EC_POINT *my_element; + EC_POINT *server_element; + + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + + BN_CTX *bnctx; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * eap_pwd_state_txt(int state) +{ + switch (state) { + case PWD_ID_Req: + return "PWD-ID-Req"; + case PWD_Commit_Req: + return "PWD-Commit-Req"; + case PWD_Confirm_Req: + return "PWD-Confirm-Req"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "PWD-UNK"; + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void eap_pwd_state(struct eap_pwd_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s", + eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); + data->state = state; +} + + +static void * eap_pwd_init(struct eap_sm *sm) +{ + struct eap_pwd_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + int fragment_size; + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: No password configured!"); + return NULL; + } + + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!"); + return NULL; + } + + if ((data = os_zalloc(sizeof(*data))) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail"); + return NULL; + } + + if ((data->bnctx = BN_CTX_new()) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); + os_free(data); + return NULL; + } + + if ((data->id_peer = os_malloc(identity_len)) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); + BN_CTX_free(data->bnctx); + os_free(data); + return NULL; + } + + os_memcpy(data->id_peer, identity, identity_len); + data->id_peer_len = identity_len; + + if ((data->password = os_malloc(password_len)) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail"); + BN_CTX_free(data->bnctx); + os_free(data->id_peer); + os_free(data); + return NULL; + } + os_memcpy(data->password, password, password_len); + data->password_len = password_len; + + data->out_frag_pos = data->in_frag_pos = 0; + data->inbuf = data->outbuf = NULL; + fragment_size = eap_get_config_fragment_size(sm); + if (fragment_size <= 0) + data->mtu = 1020; /* default from RFC 5931 */ + else + data->mtu = fragment_size; + + data->state = PWD_ID_Req; + + return data; +} + + +static void eap_pwd_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + + BN_free(data->private_value); + BN_free(data->server_scalar); + BN_free(data->my_scalar); + BN_free(data->k); + BN_CTX_free(data->bnctx); + EC_POINT_free(data->my_element); + EC_POINT_free(data->server_element); + os_free(data->id_peer); + os_free(data->id_server); + os_free(data->password); + if (data->grp) { + EC_GROUP_free(data->grp->group); + EC_POINT_free(data->grp->pwe); + BN_free(data->grp->order); + BN_free(data->grp->prime); + os_free(data->grp); + } + os_free(data); +} + + +static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_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 void +eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + struct eap_pwd_id *id; + + if (data->state != PWD_ID_Req) { + ret->ignore = TRUE; + eap_pwd_state(data, FAILURE); + return; + } + + if (payload_len < sizeof(struct eap_pwd_id)) { + ret->ignore = TRUE; + eap_pwd_state(data, FAILURE); + return; + } + + id = (struct eap_pwd_id *) payload; + data->group_num = be_to_host16(id->group_num); + if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || + (id->prf != EAP_PWD_DEFAULT_PRF)) { + ret->ignore = TRUE; + eap_pwd_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", + data->group_num); + + data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id)); + if (data->id_server == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); + eap_pwd_state(data, FAILURE); + return; + } + data->id_server_len = payload_len - sizeof(struct eap_pwd_id); + os_memcpy(data->id_server, id->identity, data->id_server_len); + wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of", + data->id_server, data->id_server_len); + + if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) == + NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " + "group"); + eap_pwd_state(data, FAILURE); + return; + } + + /* compute PWE */ + if (compute_password_element(data->grp, data->group_num, + data->password, data->password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + id->token)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); + eap_pwd_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...", + BN_num_bits(data->grp->prime)); + + data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + + data->id_peer_len); + if (data->outbuf == NULL) { + eap_pwd_state(data, FAILURE); + return; + } + wpabuf_put_be16(data->outbuf, data->group_num); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); + wpabuf_put_data(data->outbuf, id->token, sizeof(id->token)); + wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); + wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len); + + eap_pwd_state(data, PWD_Commit_Req); +} + + +static void +eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + EC_POINT *K = NULL, *point = NULL; + BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL; + u16 offset; + u8 *ptr, *scalar = NULL, *element = NULL; + + if (((data->private_value = BN_new()) == NULL) || + ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || + ((cofactor = BN_new()) == NULL) || + ((data->my_scalar = BN_new()) == NULL) || + ((mask = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); + goto fin; + } + + if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " + "for curve"); + goto fin; + } + + BN_rand_range(data->private_value, data->grp->order); + BN_rand_range(mask, data->grp->order); + BN_add(data->my_scalar, data->private_value, mask); + BN_mod(data->my_scalar, data->my_scalar, data->grp->order, + data->bnctx); + + if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, + data->grp->pwe, mask, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation " + "fail"); + eap_pwd_state(data, FAILURE); + goto fin; + } + + if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) + { + wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); + goto fin; + } + BN_free(mask); + + if (((x = BN_new()) == NULL) || + ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail"); + goto fin; + } + + /* process the request */ + if (((data->server_scalar = BN_new()) == NULL) || + ((data->k = BN_new()) == NULL) || + ((K = EC_POINT_new(data->grp->group)) == NULL) || + ((point = EC_POINT_new(data->grp->group)) == NULL) || + ((data->server_element = EC_POINT_new(data->grp->group)) == NULL)) + { + wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " + "fail"); + goto fin; + } + + /* element, x then y, followed by scalar */ + ptr = (u8 *) payload; + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar); + if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, + data->server_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " + "fail"); + goto fin; + } + + /* check to ensure server's element is not in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, point, NULL, + data->server_element, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " + "server element by order!\n"); + goto fin; + } + if (EC_POINT_is_at_infinity(data->grp->group, point)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " + "is at infinity!\n"); + goto fin; + } + } + + /* compute the shared key, k */ + if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, + data->server_scalar, data->bnctx)) || + (!EC_POINT_add(data->grp->group, K, K, data->server_element, + data->bnctx)) || + (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, + data->bnctx))) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key " + "fail"); + goto fin; + } + + /* ensure that the shared key isn't in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " + "shared key point by order"); + goto fin; + } + } + + /* + * This check is strictly speaking just for the case above where + * co-factor > 1 but it was suggested that even though this is probably + * never going to happen it is a simple and safe check "just to be + * sure" so let's be safe. + */ + if (EC_POINT_is_at_infinity(data->grp->group, K)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at " + "infinity!\n"); + goto fin; + } + + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, + NULL, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract " + "shared secret from point"); + goto fin; + } + + /* now do the response */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); + goto fin; + } + + if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || + ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); + goto fin; + } + + /* + * bignums occupy as little memory as possible so one that is + * sufficiently smaller than the prime or order might need pre-pending + * with zeros. + */ + os_memset(scalar, 0, BN_num_bytes(data->grp->order)); + os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, scalar + offset); + + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, element + offset); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); + + data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) + + 2 * BN_num_bytes(data->grp->prime)); + if (data->outbuf == NULL) + goto fin; + + /* we send the element as (x,y) follwed by the scalar */ + wpabuf_put_data(data->outbuf, element, + 2 * BN_num_bytes(data->grp->prime)); + wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); + +fin: + os_free(scalar); + os_free(element); + BN_free(x); + BN_free(y); + BN_free(cofactor); + EC_POINT_free(K); + EC_POINT_free(point); + if (data->outbuf == NULL) + eap_pwd_state(data, FAILURE); + else + eap_pwd_state(data, PWD_Confirm_Req); +} + + +static void +eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + BIGNUM *x = NULL, *y = NULL; + struct crypto_hash *hash; + u32 cs; + u16 grp; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; + int offset; + + /* + * first build up the ciphersuite which is group | random_function | + * prf + */ + grp = htons(data->group_num); + ptr = (u8 *) &cs; + os_memcpy(ptr, &grp, sizeof(u16)); + ptr += sizeof(u16); + *ptr = EAP_PWD_DEFAULT_RAND_FUNC; + ptr += sizeof(u8); + *ptr = EAP_PWD_DEFAULT_PRF; + + /* each component of the cruft will be at most as big as the prime */ + if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || + ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation " + "fail"); + goto fin; + } + + /* + * server's commit is H(k | server_element | server_scalar | + * peer_element | peer_scalar | ciphersuite) + */ + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; + + /* + * zero the memory each time because this is mod prime math and some + * value may start with a few zeros and the previous one did not. + */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); + BN_bn2bin(data->k, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* server element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->server_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* server scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->server_scalar); + BN_bn2bin(data->server_scalar, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* my element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* my scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* the ciphersuite */ + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); + + /* random function fin */ + eap_pwd_h_final(hash, conf); + + ptr = (u8 *) payload; + if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify"); + goto fin; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified"); + + /* + * compute confirm: + * H(k | peer_element | peer_scalar | server_element | server_scalar | + * ciphersuite) + */ + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; + + /* k */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); + BN_bn2bin(data->k, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* my element */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* my scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* server element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->server_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* server scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->server_scalar); + BN_bn2bin(data->server_scalar, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* the ciphersuite */ + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); + + /* all done */ + eap_pwd_h_final(hash, conf); + + if (compute_keys(data->grp, data->bnctx, data->k, + data->my_scalar, data->server_scalar, conf, ptr, + &cs, data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | " + "EMSK"); + goto fin; + } + + data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); + if (data->outbuf == NULL) + goto fin; + + wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); + +fin: + os_free(cruft); + BN_free(x); + BN_free(y); + ret->methodState = METHOD_DONE; + if (data->outbuf == NULL) { + ret->decision = DECISION_FAIL; + eap_pwd_state(data, FAILURE); + } else { + ret->decision = DECISION_UNCOND_SUCC; + eap_pwd_state(data, SUCCESS); + } +} + + +static struct wpabuf * +eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_pwd_data *data = priv; + struct wpabuf *resp = NULL; + const u8 *pos, *buf; + size_t len; + u16 tot_len = 0; + u8 lm_exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len); + if ((pos == NULL) || (len < 1)) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and " + "len is %d", + pos == NULL ? "NULL" : "not NULL", (int) len); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + lm_exch = *pos; + pos++; /* skip over the bits and the exch */ + len--; + + /* + * we're fragmenting so send out the next fragment + */ + if (data->out_frag_pos) { + /* + * this should be an ACK + */ + if (len) + wpa_printf(MSG_INFO, "Bad Response! Fragmenting but " + "not an ACK"); + + wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment"); + /* + * check if there are going to be more fragments + */ + len = wpabuf_len(data->outbuf) - data->out_frag_pos; + if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { + len = data->mtu - EAP_PWD_HDR_SIZE; + EAP_PWD_SET_MORE_BIT(lm_exch); + } + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE + len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) { + wpa_printf(MSG_INFO, "Unable to allocate memory for " + "next fragment!"); + return NULL; + } + wpabuf_put_u8(resp, lm_exch); + buf = wpabuf_head_u8(data->outbuf); + wpabuf_put_data(resp, buf + data->out_frag_pos, len); + data->out_frag_pos += len; + /* + * this is the last fragment so get rid of the out buffer + */ + if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { + wpabuf_free(data->outbuf); + data->outbuf = NULL; + data->out_frag_pos = 0; + } + wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes", + data->out_frag_pos == 0 ? "last" : "next", + (int) len); + return resp; + } + + /* + * see if this is a fragment that needs buffering + * + * if it's the first fragment there'll be a length field + */ + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + tot_len = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose " + "total length = %d", tot_len); + data->inbuf = wpabuf_alloc(tot_len); + if (data->inbuf == NULL) { + wpa_printf(MSG_INFO, "Out of memory to buffer " + "fragments!"); + return NULL; + } + pos += sizeof(u16); + len -= sizeof(u16); + } + /* + * buffer and ACK the fragment + */ + if (EAP_PWD_GET_MORE_BIT(lm_exch)) { + data->in_frag_pos += len; + if (data->in_frag_pos > wpabuf_size(data->inbuf)) { + wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack " + "detected (%d vs. %d)!", + (int) data->in_frag_pos, + (int) wpabuf_len(data->inbuf)); + wpabuf_free(data->inbuf); + data->in_frag_pos = 0; + return NULL; + } + wpabuf_put_data(data->inbuf, pos, len); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp != NULL) + wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch))); + wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment", + (int) len); + return resp; + } + /* + * we're buffering and this is the last fragment + */ + if (data->in_frag_pos) { + wpabuf_put_data(data->inbuf, pos, len); + wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", + (int) len); + data->in_frag_pos += len; + pos = wpabuf_head_u8(data->inbuf); + len = data->in_frag_pos; + } + wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d", + EAP_PWD_GET_EXCHANGE(lm_exch), (int) len); + + switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { + case EAP_PWD_OPCODE_ID_EXCH: + eap_pwd_perform_id_exchange(sm, data, ret, reqData, + pos, len); + break; + case EAP_PWD_OPCODE_COMMIT_EXCH: + eap_pwd_perform_commit_exchange(sm, data, ret, reqData, + pos, len); + break; + case EAP_PWD_OPCODE_CONFIRM_EXCH: + eap_pwd_perform_confirm_exchange(sm, data, ret, reqData, + pos, len); + break; + default: + wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown " + "opcode %d", lm_exch); + break; + } + /* + * if we buffered the just processed input now's the time to free it + */ + if (data->in_frag_pos) { + wpabuf_free(data->inbuf); + data->in_frag_pos = 0; + } + + if (data->outbuf == NULL) + return NULL; /* generic failure */ + + /* + * we have output! Do we need to fragment it? + */ + len = wpabuf_len(data->outbuf); + if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + /* + * if so it's the first so include a length field + */ + EAP_PWD_SET_LENGTH_BIT(lm_exch); + EAP_PWD_SET_MORE_BIT(lm_exch); + tot_len = len; + /* + * keep the packet at the MTU + */ + len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16); + wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total " + "length = %d", tot_len); + } else { + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE + len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + } + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, lm_exch); + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + wpabuf_put_be16(resp, tot_len); + data->out_frag_pos += len; + } + buf = wpabuf_head_u8(data->outbuf); + wpabuf_put_data(resp, buf, len); + /* + * if we're not fragmenting then there's no need to carry this around + */ + if (data->out_frag_pos == 0) { + wpabuf_free(data->outbuf); + data->outbuf = NULL; + data->out_frag_pos = 0; + } + + return resp; +} + + +static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + if ((key = os_malloc(EAP_EMSK_LEN)) == NULL) + return NULL; + + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +int eap_peer_pwd_register(void) +{ + struct eap_method *eap; + int ret; + + EVP_add_digest(EVP_sha256()); + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); + if (eap == NULL) + return -1; + + eap->init = eap_pwd_init; + eap->deinit = eap_pwd_deinit; + eap->process = eap_pwd_process; + eap->isKeyAvailable = eap_pwd_key_available; + eap->getKey = eap_pwd_getkey; + eap->get_emsk = eap_pwd_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/src/eap_peer/eap_sake.c b/contrib/hostapd/src/eap_peer/eap_sake.c index bb06bb2f42..431519cae6 100644 --- a/contrib/hostapd/src/eap_peer/eap_sake.c +++ b/contrib/hostapd/src/eap_peer/eap_sake.c @@ -2,19 +2,14 @@ * EAP peer method: EAP-SAKE (RFC 4763) * Copyright (c) 2006-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_sake_common.h" @@ -223,7 +218,7 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, 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)) { + if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); return NULL; } @@ -457,6 +452,28 @@ static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + 2 * EAP_SAKE_RAND_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_SAKE; + os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN); + os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sake_data *data = priv; @@ -490,6 +507,7 @@ int eap_peer_sake_register(void) eap->process = eap_sake_process; eap->isKeyAvailable = eap_sake_isKeyAvailable; eap->getKey = eap_sake_getKey; + eap->getSessionId = eap_sake_get_session_id; eap->get_emsk = eap_sake_get_emsk; ret = eap_peer_method_register(eap); diff --git a/contrib/hostapd/src/eap_peer/eap_sim.c b/contrib/hostapd/src/eap_peer/eap_sim.c index 5e30d1f7ed..d8560543fa 100644 --- a/contrib/hostapd/src/eap_peer/eap_sim.c +++ b/contrib/hostapd/src/eap_peer/eap_sim.c @@ -1,27 +1,20 @@ /* * EAP peer method: EAP-SIM (RFC 4186) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "pcsc_funcs.h" +#include "crypto/milenage.h" +#include "crypto/random.h" #include "eap_peer/eap_i.h" #include "eap_config.h" -#include "pcsc_funcs.h" #include "eap_common/eap_sim_common.h" -#ifdef CONFIG_SIM_SIMULATOR -#include "hlr_auc_gw/milenage.h" -#endif /* CONFIG_SIM_SIMULATOR */ struct eap_sim_data { @@ -95,7 +88,7 @@ static void * eap_sim_init(struct eap_sm *sm) if (data == NULL) return NULL; - if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) { + if (random_get_bytes(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); @@ -124,6 +117,15 @@ static void * eap_sim_init(struct eap_sm *sm) NULL; } + if (config && config->anonymous_identity) { + data->pseudonym = os_malloc(config->anonymous_identity_len); + if (data->pseudonym) { + os_memcpy(data->pseudonym, config->anonymous_identity, + config->anonymous_identity_len); + data->pseudonym_len = config->anonymous_identity_len; + } + } + eap_sim_state(data, CONTINUE); return data; @@ -143,6 +145,80 @@ static void eap_sim_deinit(struct eap_sm *sm, void *priv) } +static int eap_sim_ext_sim_req(struct eap_sm *sm, struct eap_sim_data *data) +{ + char req[200], *pos, *end; + size_t i; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Use external SIM processing"); + pos = req; + end = pos + sizeof(req); + pos += os_snprintf(pos, end - pos, "GSM-AUTH"); + for (i = 0; i < data->num_chal; i++) { + pos += os_snprintf(pos, end - pos, ":"); + pos += wpa_snprintf_hex(pos, end - pos, data->rand[i], + GSM_RAND_LEN); + } + + eap_sm_request_sim(sm, req); + return 1; +} + + +static int eap_sim_ext_sim_result(struct eap_sm *sm, struct eap_sim_data *data, + struct eap_peer_config *conf) +{ + char *resp, *pos; + size_t i; + + wpa_printf(MSG_DEBUG, + "EAP-SIM: Use result from external SIM processing"); + + resp = conf->external_sim_resp; + conf->external_sim_resp = NULL; + + if (os_strncmp(resp, "GSM-AUTH:", 9) != 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized external SIM processing response"); + os_free(resp); + return -1; + } + + pos = resp + 9; + for (i = 0; i < data->num_chal; i++) { + wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", + data->rand[i], GSM_RAND_LEN); + + if (hexstr2bin(pos, data->kc[i], EAP_SIM_KC_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", + data->kc[i], EAP_SIM_KC_LEN); + pos += EAP_SIM_KC_LEN * 2; + if (*pos != ':') + goto invalid; + pos++; + + if (hexstr2bin(pos, data->sres[i], EAP_SIM_SRES_LEN) < 0) + goto invalid; + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", + data->sres[i], EAP_SIM_SRES_LEN); + pos += EAP_SIM_SRES_LEN * 2; + if (i + 1 < data->num_chal) { + if (*pos != ':') + goto invalid; + pos++; + } + } + + os_free(resp); + return 0; + +invalid: + wpa_printf(MSG_DEBUG, "EAP-SIM: Invalid external SIM processing GSM-AUTH response"); + os_free(resp); + return -1; +} + + static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) { struct eap_peer_config *conf; @@ -152,6 +228,14 @@ static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data) conf = eap_get_config(sm); if (conf == NULL) return -1; + + if (sm->external_sim) { + if (conf->external_sim_resp) + return eap_sim_ext_sim_result(sm, data, conf); + else + return eap_sim_ext_sim_req(sm, data); + } + if (conf->pcsc) { if (scard_gsm_auth(sm->scard_ctx, data->rand[0], data->sres[0], data->kc[0]) || @@ -265,23 +349,24 @@ static int eap_sim_supported_ver(int version) #define CLEAR_REAUTH_ID 0x02 #define CLEAR_EAP_ID 0x04 -static void eap_sim_clear_identities(struct eap_sim_data *data, int id) +static void eap_sim_clear_identities(struct eap_sm *sm, + 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) { + if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { + wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym"); os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; + eap_set_anon_id(sm, NULL, 0); } - if (id & CLEAR_REAUTH_ID) { + if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { + wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id"); os_free(data->reauth_id); data->reauth_id = NULL; data->reauth_id_len = 0; } - if (id & CLEAR_EAP_ID) { + if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { + wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old eap_id"); os_free(data->last_eap_identity); data->last_eap_identity = NULL; data->last_eap_identity_len = 0; @@ -289,24 +374,45 @@ static void eap_sim_clear_identities(struct eap_sim_data *data, int id) } -static int eap_sim_learn_ids(struct eap_sim_data *data, +static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data, struct eap_sim_attrs *attr) { if (attr->next_pseudonym) { + const u8 *identity = NULL; + size_t identity_len = 0; + const u8 *realm = NULL; + size_t realm_len = 0; + + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: (encr) AT_NEXT_PSEUDONYM", + attr->next_pseudonym, + attr->next_pseudonym_len); os_free(data->pseudonym); - data->pseudonym = os_malloc(attr->next_pseudonym_len); + /* Look for the realm of the permanent identity */ + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + for (realm = identity, realm_len = identity_len; + realm_len > 0; realm_len--, realm++) { + if (*realm == '@') + break; + } + } + data->pseudonym = os_malloc(attr->next_pseudonym_len + + realm_len); if (data->pseudonym == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " "next pseudonym"); + data->pseudonym_len = 0; 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 (realm_len) { + os_memcpy(data->pseudonym + attr->next_pseudonym_len, + realm, realm_len); + } + data->pseudonym_len = attr->next_pseudonym_len + realm_len; + eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); } if (attr->next_reauth_id) { @@ -315,6 +421,7 @@ static int eap_sim_learn_ids(struct eap_sim_data *data, if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " "next reauth_id"); + data->reauth_id_len = 0; return -1; } os_memcpy(data->reauth_id, attr->next_reauth_id, @@ -339,6 +446,8 @@ static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id, data->num_id_req = 0; data->num_notification = 0; + wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)", + err); 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); @@ -363,16 +472,16 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - eap_sim_clear_identities(data, CLEAR_REAUTH_ID); + eap_sim_clear_identities(sm, 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 | + eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID); } } if (id_req != NO_ID_REQ) - eap_sim_clear_identities(data, CLEAR_EAP_ID); + eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id); msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, @@ -419,7 +528,8 @@ static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data, static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, - u8 id, int counter_too_small) + u8 id, int counter_too_small, + const u8 *nonce_s) { struct eap_sim_msg *msg; unsigned int counter; @@ -454,7 +564,7 @@ static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, } 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, + return eap_sim_msg_finish(msg, data->k_aut, nonce_s, EAP_SIM_NONCE_S_LEN); } @@ -577,6 +687,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, const u8 *identity; size_t identity_len; struct eap_sim_attrs eattr; + int res; wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); data->reauth = 0; @@ -620,8 +731,13 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, 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)) { + + res = eap_sim_gsm_auth(sm, data); + if (res > 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Wait for external SIM processing"); + return NULL; + } + if (res) { wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); @@ -650,11 +766,11 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, 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); + /* Old reauthentication identity must not be used anymore. In + * other words, if no new reauth identity is received, full + * authentication will be used on next reauthentication (using + * pseudonym identity or permanent identity). */ + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); if (attr->encr_data) { u8 *decrypted; @@ -665,7 +781,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, return eap_sim_client_error( data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } - eap_sim_learn_ids(data, &eattr); + eap_sim_learn_ids(sm, data, &eattr); os_free(decrypted); } @@ -850,7 +966,7 @@ static struct wpabuf * eap_sim_process_reauthentication( data->reauth_id = NULL; data->reauth_id_len = 0; os_free(decrypted); - return eap_sim_response_reauth(data, id, 1); + return eap_sim_response_reauth(data, id, 1, eattr.nonce_s); } data->counter = eattr.counter; @@ -862,8 +978,8 @@ static struct wpabuf * eap_sim_process_reauthentication( 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); + eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); + eap_sim_learn_ids(sm, data, &eattr); if (data->result_ind && attr->result_ind) data->use_result_ind = 1; @@ -878,10 +994,11 @@ static struct wpabuf * eap_sim_process_reauthentication( 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); + eap_sim_clear_identities(sm, data, + CLEAR_REAUTH_ID | CLEAR_EAP_ID); } os_free(decrypted); - return eap_sim_response_reauth(data, id, 0); + return eap_sim_response_reauth(data, id, 0, data->nonce_s); } @@ -989,7 +1106,7 @@ static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv) 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); + eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); data->use_result_ind = 0; } @@ -997,7 +1114,7 @@ static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) 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)) { + if (random_get_bytes(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); @@ -1055,6 +1172,29 @@ static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_SIM; + os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); + os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt, + EAP_SIM_NONCE_MT_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); + + return id; +} + + static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_sim_data *data = priv; @@ -1089,6 +1229,7 @@ int eap_peer_sim_register(void) eap->process = eap_sim_process; eap->isKeyAvailable = eap_sim_isKeyAvailable; eap->getKey = eap_sim_getKey; + eap->getSessionId = eap_sim_get_session_id; 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; diff --git a/contrib/hostapd/src/eap_peer/eap_tls.c b/contrib/hostapd/src/eap_peer/eap_tls.c index 31344a9136..d2066cd852 100644 --- a/contrib/hostapd/src/eap_peer/eap_tls.c +++ b/contrib/hostapd/src/eap_peer/eap_tls.c @@ -1,24 +1,18 @@ /* * EAP peer method: EAP-TLS (RFC 2716) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2008, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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" -#include "tls.h" static void eap_tls_deinit(struct eap_sm *sm, void *priv); @@ -27,6 +21,10 @@ static void eap_tls_deinit(struct eap_sm *sm, void *priv); struct eap_tls_data { struct eap_ssl_data ssl; u8 *key_data; + u8 *session_id; + size_t id_len; + void *ssl_ctx; + u8 eap_type; }; @@ -46,7 +44,10 @@ static void * eap_tls_init(struct eap_sm *sm) if (data == NULL) return NULL; - if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) { + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TLS)) { wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); eap_tls_deinit(sm, data); if (config->engine) { @@ -64,10 +65,39 @@ static void * eap_tls_init(struct eap_sm *sm) return NULL; } + data->eap_type = EAP_TYPE_TLS; + return data; } +#ifdef EAP_UNAUTH_TLS +static void * eap_unauth_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, + EAP_UNAUTH_TLS_TYPE)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_deinit(sm, data); + return NULL; + } + + data->eap_type = EAP_UNAUTH_TLS_TYPE; + + return data; +} +#endif /* EAP_UNAUTH_TLS */ + + static void eap_tls_deinit(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; @@ -75,6 +105,7 @@ static void eap_tls_deinit(struct eap_sm *sm, void *priv) return; eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); + os_free(data->session_id); os_free(data); } @@ -111,7 +142,7 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm, return resp; } - return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_peer_tls_build_ack(id, data->eap_type, 0); } @@ -137,6 +168,17 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, } else { wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key"); } + + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_TLS, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id"); + } } @@ -151,7 +193,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, const u8 *pos; struct eap_tls_data *data = priv; - pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret, + pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, reqData, &left, &flags); if (pos == NULL) return NULL; @@ -164,19 +206,19 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, } resp = NULL; - res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id, - pos, left, &resp); + res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 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)) + if (tls_connection_established(data->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 eap_peer_tls_build_ack(id, data->eap_type, 0); } return resp; @@ -186,7 +228,7 @@ static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, 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); + return tls_connection_established(data->ssl_ctx, data->ssl.conn); } @@ -200,6 +242,8 @@ 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; + os_free(data->session_id); + data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; @@ -261,6 +305,25 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *id; + + if (data->session_id == NULL) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + int eap_peer_tls_register(void) { struct eap_method *eap; @@ -276,6 +339,37 @@ int eap_peer_tls_register(void) eap->process = eap_tls_process; eap->isKeyAvailable = eap_tls_isKeyAvailable; eap->getKey = eap_tls_getKey; + eap->getSessionId = eap_tls_get_session_id; + 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; +} + + +#ifdef EAP_UNAUTH_TLS +int eap_peer_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_unauth_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; @@ -287,3 +381,4 @@ int eap_peer_tls_register(void) eap_peer_method_free(eap); return ret; } +#endif /* EAP_UNAUTH_TLS */ diff --git a/contrib/hostapd/src/eap_peer/eap_tls_common.c b/contrib/hostapd/src/eap_peer/eap_tls_common.c index 186feaada4..008af37b17 100644 --- a/contrib/hostapd/src/eap_peer/eap_tls_common.c +++ b/contrib/hostapd/src/eap_peer/eap_tls_common.c @@ -1,25 +1,31 @@ /* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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" -#include "sha1.h" -#include "tls.h" + + +static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + if (type == EAP_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, + code, identifier); + return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, + identifier); +} static int eap_tls_check_blob(struct eap_sm *sm, const char **name, @@ -54,6 +60,10 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; if (os_strstr(txt, "tls_disable_time_checks=1")) params->flags |= TLS_CONN_DISABLE_TIME_CHECKS; + if (os_strstr(txt, "tls_disable_session_ticket=1")) + params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; + if (os_strstr(txt, "tls_disable_session_ticket=0")) + params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET; } @@ -68,6 +78,7 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params, params->dh_file = (char *) config->dh_file; params->subject_match = (char *) config->subject_match; params->altsubject_match = (char *) config->altsubject_match; + params->suffix_match = config->domain_suffix_match; params->engine = config->engine; params->engine_id = config->engine_id; params->pin = config->pin; @@ -89,6 +100,7 @@ static void eap_tls_params_from_conf2(struct tls_connection_params *params, params->dh_file = (char *) config->dh_file2; params->subject_match = (char *) config->subject_match2; params->altsubject_match = (char *) config->altsubject_match2; + params->suffix_match = config->domain_suffix_match2; params->engine = config->engine2; params->engine_id = config->engine2_id; params->pin = config->pin2; @@ -105,6 +117,18 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, struct eap_peer_config *config, int phase2) { os_memset(params, 0, sizeof(*params)); + if (sm->workaround && data->eap_type != EAP_TYPE_FAST) { + /* + * Some deployed authentication servers seem to be unable to + * handle the TLS Session Ticket extension (they are supposed + * to ignore unrecognized TLS extensions, but end up rejecting + * the ClientHello instead). As a workaround, disable use of + * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and + * EAP-TTLS (EAP-FAST uses session ticket, so any server that + * supports EAP-FAST does not need this workaround). + */ + params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; + } if (phase2) { wpa_printf(MSG_DEBUG, "TLS: using phase2 config options"); eap_tls_params_from_conf2(params, config); @@ -112,7 +136,6 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, 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 @@ -143,14 +166,18 @@ static int eap_tls_init_connection(struct eap_sm *sm, { int res; - data->conn = tls_connection_init(sm->ssl_ctx); + if (config->ocsp) + params->flags |= TLS_CONN_REQUEST_OCSP; + if (config->ocsp == 2) + params->flags |= TLS_CONN_REQUIRE_OCSP; + data->conn = tls_connection_init(data->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); + res = tls_connection_set_params(data->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. @@ -169,10 +196,14 @@ static int eap_tls_init_connection(struct eap_sm *sm, config->pin = NULL; eap_sm_request_pin(sm); sm->ignore = TRUE; + tls_connection_deinit(data->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(data->ssl_ctx, data->conn); + data->conn = NULL; return -1; } @@ -185,13 +216,14 @@ static int eap_tls_init_connection(struct eap_sm *sm, * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() * @data: Data for TLS processing * @config: Pointer to the network configuration + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) * 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 eap_peer_config *config, u8 eap_type) { struct tls_connection_params params; @@ -199,7 +231,10 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, return -1; data->eap = sm; + data->eap_type = eap_type; data->phase2 = sm->init_phase2; + data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 : + sm->ssl_ctx; if (eap_tls_params_from_conf(sm, data, ¶ms, config, data->phase2) < 0) return -1; @@ -237,7 +272,7 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, */ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) { - tls_connection_deinit(sm->ssl_ctx, data->conn); + tls_connection_deinit(data->ssl_ctx, data->conn); eap_peer_tls_reset_input(data); eap_peer_tls_reset_output(data); } @@ -260,7 +295,9 @@ 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) { +#ifndef CONFIG_FIPS struct tls_keys keys; +#endif /* CONFIG_FIPS */ u8 *rnd = NULL, *out; out = os_malloc(len); @@ -268,16 +305,17 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, 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) + if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len) + == 0) return out; +#ifndef CONFIG_FIPS /* * 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)) + if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys)) goto fail; if (keys.client_random == NULL || keys.server_random == NULL || @@ -291,46 +329,95 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, 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)) + if (tls_prf_sha1_md5(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: +#endif /* CONFIG_FIPS */ os_free(out); os_free(rnd); return NULL; } +/** + * eap_peer_tls_derive_session_id - Derive a Session-Id based on TLS data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + * @len: Pointer to length of the session ID generated + * Returns: Pointer to allocated Session-Id on success or %NULL on failure + * + * This function derive the Session-Id based on the TLS session data + * (client/server random and method type). + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len) +{ + struct tls_keys keys; + u8 *out; + + /* + * TLS library did not support session ID generation, + * so get the needed TLS session parameters + */ + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + return NULL; + + if (keys.client_random == NULL || keys.server_random == NULL || + keys.master_key == NULL) + return NULL; + + *len = 1 + keys.client_random_len + keys.server_random_len; + out = os_malloc(*len); + if (out == NULL) + return NULL; + + /* Session-Id = EAP type || client.random || server.random */ + out[0] = eap_type; + os_memcpy(out + 1, keys.client_random, keys.client_random_len); + os_memcpy(out + 1 + keys.client_random_len, keys.server_random, + keys.server_random_len); + + return out; +} + + /** * eap_peer_tls_reassemble_fragment - Reassemble a received fragment * @data: Data for TLS processing * @in_data: Next incoming TLS segment - * @in_len: Length of in_data * 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 u8 *in_data, size_t in_len) + const struct wpabuf *in_data) { - u8 *buf; + 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 (data->tls_in_len + in_len == 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) data->tls_in_len, + (unsigned long) tls_in_len, (unsigned long) in_len); eap_peer_tls_reset_input(data); return -1; } - if (data->tls_in_len + in_len > 65536) { + if (tls_in_len + in_len > 65536) { /* * Limit length to avoid rogue servers from causing large * memory allocations. @@ -349,16 +436,14 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, return -1; } - buf = os_realloc(data->tls_in, data->tls_in_len + in_len); - if (buf == NULL) { + 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; } - os_memcpy(buf + data->tls_in_len, in_data, in_len); - data->tls_in = buf; - data->tls_in_len += in_len; + if (in_data) + wpabuf_put_buf(data->tls_in, in_data); data->tls_in_left -= in_len; if (data->tls_in_left > 0) { @@ -375,8 +460,6 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, * eap_peer_tls_data_reassemble - Reassemble TLS data * @data: Data for TLS processing * @in_data: Next incoming TLS segment - * @in_len: Length of in_data - * @out_len: Variable for returning length of the reassembled message * @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 @@ -385,16 +468,15 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, * This function reassembles TLS fragments. Caller must not free the returned * data buffer since an internal pointer to it is maintained. */ -const u8 * eap_peer_tls_data_reassemble( - struct eap_ssl_data *data, const u8 *in_data, size_t in_len, - size_t *out_len, int *need_more_input) +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 > in_len || data->tls_in) { + 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, - in_len); + int res = eap_peer_tls_reassemble_fragment(data, in_data); if (res) { if (res == 1) *need_more_input = 1; @@ -405,14 +487,11 @@ const u8 * eap_peer_tls_data_reassemble( } else { /* No fragments in this message, so just make a copy of it. */ data->tls_in_left = 0; - data->tls_in = os_malloc(in_len ? in_len : 1); + data->tls_in = wpabuf_dup(in_data); if (data->tls_in == NULL) return NULL; - os_memcpy(data->tls_in, in_data, in_len); - data->tls_in_len = in_len; } - *out_len = data->tls_in_len; return data->tls_in; } @@ -431,14 +510,13 @@ 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 u8 *msg; - size_t msg_len; + const struct wpabuf *msg; int need_more_input; - u8 *appl_data; - size_t appl_data_len; + struct wpabuf *appl_data; + struct wpabuf buf; - msg = eap_peer_tls_data_reassemble(data, in_data, in_len, - &msg_len, &need_more_input); + 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; @@ -447,31 +525,25 @@ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, /* This should not happen.. */ wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending " "tls_out data even though tls_out_len = 0"); - os_free(data->tls_out); + 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, msg_len, - &data->tls_out_len, - &appl_data, &appl_data_len); + data->tls_out = tls_connection_handshake(data->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_key(MSG_MSGDUMP, "SSL: Application data", - appl_data, appl_data_len); - *out_data = wpabuf_alloc_ext_data(appl_data, appl_data_len); - if (*out_data == NULL) { - os_free(appl_data); - return -1; - } + tls_connection_established(data->ssl_ctx, data->conn) && + !tls_connection_get_failed(data->ssl_ctx, data->conn)) { + wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data", + appl_data); + *out_data = appl_data; return 2; } - os_free(appl_data); + wpabuf_free(appl_data); return 0; } @@ -494,11 +566,14 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, size_t len; u8 *flags; int more_fragments, length_included; - - len = data->tls_out_len - data->tls_out_pos; + + 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) data->tls_out_len); + (unsigned long) len, + (unsigned long) wpabuf_len(data->tls_out)); /* * Limit outgoing message to the configured maximum size. Fragment @@ -513,7 +588,7 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, more_fragments = 0; length_included = data->tls_out_pos == 0 && - (data->tls_out_len > data->tls_out_limit || + (wpabuf_len(data->tls_out) > data->tls_out_limit || data->include_tls_length); if (!length_included && eap_type == EAP_TYPE_PEAP && peap_version == 0 && @@ -527,9 +602,8 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, length_included = 1; } - *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, - 1 + length_included * 4 + len, - EAP_CODE_RESPONSE, id); + *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len, + EAP_CODE_RESPONSE, id); if (*out_data == NULL) return -1; @@ -539,10 +613,12 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; if (length_included) { *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; - wpabuf_put_be32(*out_data, data->tls_out_len); + wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out)); } - wpabuf_put_data(*out_data, &data->tls_out[data->tls_out_pos], len); + wpabuf_put_data(*out_data, + wpabuf_head_u8(data->tls_out) + data->tls_out_pos, + len); data->tls_out_pos += len; if (!more_fragments) @@ -590,13 +666,13 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, *out_data = NULL; - if (data->tls_out_len > 0 && in_len > 0) { + 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_len == 0) { + 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. @@ -627,7 +703,7 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, return -1; } - if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) { + if (tls_connection_get_failed(data->ssl_ctx, data->conn)) { /* TLS processing has failed - return error */ wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " "report error"); @@ -635,14 +711,14 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, /* TODO: clean pin if engine used? */ } - if (data->tls_out_len == 0) { + 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"); - os_free(data->tls_out); + wpabuf_free(data->tls_out); data->tls_out = NULL; return 1; } @@ -665,8 +741,7 @@ struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, { struct wpabuf *resp; - resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE, - id); + resp = eap_tls_msg_alloc(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)", @@ -686,7 +761,7 @@ 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); + return tls_connection_shutdown(data->ssl_ctx, data->conn); } @@ -705,7 +780,8 @@ int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, char name[128]; int len = 0, ret; - if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) { + if (tls_get_cipher(data->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) @@ -752,13 +828,19 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, size_t left; unsigned int tls_msg_len; - if (tls_get_errors(sm->ssl_ctx)) { + if (tls_get_errors(data->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 (eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, reqData, + &left); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, + &left); if (pos == NULL) { ret->ignore = TRUE; return NULL; @@ -794,12 +876,19 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, if (data->tls_in_left == 0) { data->tls_in_total = tls_msg_len; data->tls_in_left = tls_msg_len; - os_free(data->tls_in); + wpabuf_free(data->tls_in); data->tls_in = NULL; - data->tls_in_len = 0; } pos += 4; left -= 4; + + if (left > tls_msg_len) { + wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d " + "bytes) smaller than this fragment (%d " + "bytes)", (int) tls_msg_len, (int) left); + ret->ignore = TRUE; + return NULL; + } } ret->ignore = FALSE; @@ -821,8 +910,8 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, */ void eap_peer_tls_reset_input(struct eap_ssl_data *data) { - data->tls_in_left = data->tls_in_total = data->tls_in_len = 0; - os_free(data->tls_in); + data->tls_in_left = data->tls_in_total = 0; + wpabuf_free(data->tls_in); data->tls_in = NULL; } @@ -836,9 +925,8 @@ void eap_peer_tls_reset_input(struct eap_ssl_data *data) */ void eap_peer_tls_reset_output(struct eap_ssl_data *data) { - data->tls_out_len = 0; data->tls_out_pos = 0; - os_free(data->tls_out); + wpabuf_free(data->tls_out); data->tls_out = NULL; } @@ -855,44 +943,19 @@ int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, const struct wpabuf *in_data, struct wpabuf **in_decrypted) { - int res; - const u8 *msg; - size_t msg_len, buf_len; + const struct wpabuf *msg; int need_more_input; - msg = eap_peer_tls_data_reassemble(data, wpabuf_head(in_data), - wpabuf_len(in_data), &msg_len, - &need_more_input); + msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; - buf_len = wpabuf_len(in_data); - if (data->tls_in_total > buf_len) - buf_len = data->tls_in_total; - /* - * 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_len += 500; - buf_len *= 3; - *in_decrypted = wpabuf_alloc(buf_len ? buf_len : 1); - if (*in_decrypted == NULL) { - eap_peer_tls_reset_input(data); - wpa_printf(MSG_WARNING, "SSL: Failed to allocate memory for " - "decryption"); - return -1; - } - - res = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg, msg_len, - wpabuf_mhead(*in_decrypted), buf_len); + *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg); eap_peer_tls_reset_input(data); - if (res < 0) { + if (*in_decrypted == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); return -1; } - wpabuf_put(*in_decrypted, res); return 0; } @@ -913,29 +976,17 @@ int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, const struct wpabuf *in_data, struct wpabuf **out_data) { - int res; - size_t len; - if (in_data) { eap_peer_tls_reset_output(data); - len = wpabuf_len(in_data) + 300; - data->tls_out = os_malloc(len); - if (data->tls_out == NULL) - return -1; - - res = tls_connection_encrypt(sm->ssl_ctx, data->conn, - wpabuf_head(in_data), - wpabuf_len(in_data), - data->tls_out, len); - if (res < 0) { + data->tls_out = tls_connection_encrypt(data->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; } - - data->tls_out_len = res; } return eap_tls_process_output(data, eap_type, peap_version, id, 0, @@ -993,8 +1044,8 @@ int eap_peer_select_phase2_methods(struct eap_peer_config *config, "method '%s'", start); } else { num_methods++; - _methods = os_realloc(methods, - num_methods * sizeof(*methods)); + _methods = os_realloc_array(methods, num_methods, + sizeof(*methods)); if (_methods == NULL) { os_free(methods); os_free(buf); diff --git a/contrib/hostapd/src/eap_peer/eap_tls_common.h b/contrib/hostapd/src/eap_peer/eap_tls_common.h index 2c87427c28..1a5e0f89e4 100644 --- a/contrib/hostapd/src/eap_peer/eap_tls_common.h +++ b/contrib/hostapd/src/eap_peer/eap_tls_common.h @@ -1,15 +1,9 @@ /* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2006, Jouni Malinen + * Copyright (c) 2004-2009, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_TLS_COMMON_H @@ -27,12 +21,7 @@ struct eap_ssl_data { /** * tls_out - TLS message to be sent out in fragments */ - u8 *tls_out; - - /** - * tls_out_len - Total length of the outgoing TLS message - */ - size_t tls_out_len; + struct wpabuf *tls_out; /** * tls_out_pos - The current position in the outgoing TLS message @@ -47,12 +36,7 @@ struct eap_ssl_data { /** * tls_in - Received TLS message buffer for re-assembly */ - u8 *tls_in; - - /** - * tls_in_len - Number of bytes of the received TLS message in tls_in - */ - size_t tls_in_len; + struct wpabuf *tls_in; /** * tls_in_left - Number of remaining bytes in the incoming TLS message @@ -76,14 +60,19 @@ struct eap_ssl_data { int include_tls_length; /** - * tls_ia - Whether TLS/IA is enabled for this TLS connection + * eap - EAP state machine allocated with eap_peer_sm_init() */ - int tls_ia; + struct eap_sm *eap; /** - * eap - Pointer to EAP state machine allocated with eap_peer_sm_init() + * ssl_ctx - TLS library context to use for the connection */ - struct eap_sm *eap; + void *ssl_ctx; + + /** + * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + */ + u8 eap_type; }; @@ -91,20 +80,23 @@ struct eap_ssl_data { #define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 #define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 #define EAP_TLS_FLAGS_START 0x20 -#define EAP_PEAP_VERSION_MASK 0x07 +#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 +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 + int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, - struct eap_peer_config *config); + struct eap_peer_config *config, u8 eap_type); 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); -const u8 * eap_peer_tls_data_reassemble( - struct eap_ssl_data *data, const u8 *in_data, size_t in_len, - size_t *out_len, int *need_more_input); +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + 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, diff --git a/contrib/hostapd/src/eap_peer/eap_tnc.c b/contrib/hostapd/src/eap_peer/eap_tnc.c index c560015281..bc136470b3 100644 --- a/contrib/hostapd/src/eap_peer/eap_tnc.c +++ b/contrib/hostapd/src/eap_peer/eap_tnc.c @@ -2,20 +2,13 @@ * EAP peer method: EAP-TNC (Trusted Network Connect) * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "base64.h" #include "eap_i.h" #include "tncc.h" @@ -73,12 +66,13 @@ 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, 0, code, id); + 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"); @@ -262,7 +256,7 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, "Message Length %u", flags, message_length); if (data->state == WAIT_FRAG_ACK) { - if (len != 0) { + if (len > 1) { wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload in " "WAIT_FRAG_ACK state"); ret->ignore = TRUE; diff --git a/contrib/hostapd/src/eap_peer/eap_ttls.c b/contrib/hostapd/src/eap_peer/eap_ttls.c index 0851f8bb4d..5091bf0844 100644 --- a/contrib/hostapd/src/eap_peer/eap_ttls.c +++ b/contrib/hostapd/src/eap_peer/eap_ttls.c @@ -1,42 +1,26 @@ /* * EAP peer method: EAP-TTLS (RFC 5281) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "eap_peer/eap_i.h" -#include "eap_peer/eap_tls_common.h" -#include "eap_peer/eap_config.h" -#include "ms_funcs.h" -#include "sha1.h" +#include "crypto/ms_funcs.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "eap_common/chap.h" -#include "tls.h" -#include "mschapv2.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 +#define EAP_TTLS_VERSION 0 static void eap_ttls_deinit(struct eap_sm *sm, void *priv); @@ -44,9 +28,8 @@ 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; + int ttls_version; const struct eap_method *phase2_method; void *phase2_priv; @@ -71,6 +54,8 @@ struct eap_ttls_data { int resuming; /* starting a resumed session */ int reauth; /* reauthentication */ u8 *key_data; + u8 *session_id; + size_t id_len; struct wpabuf *pending_phase2_req; @@ -91,22 +76,9 @@ static void * eap_ttls_init(struct eap_sm *sm) 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"; @@ -140,19 +112,11 @@ static void * eap_ttls_init(struct eap_sm *sm) 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; + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); + eap_ttls_deinit(sm, data); + return NULL; } -#endif /* EAP_TTLS_VERSION */ return data; } @@ -176,9 +140,9 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv) 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); + eap_peer_tls_ssl_deinit(sm, &data->ssl); os_free(data->key_data); + os_free(data->session_id); wpabuf_free(data->pending_phase2_req); os_free(data); } @@ -202,7 +166,7 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, } avp->avp_code = host_to_be32(avp_code); - avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); + avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len)); return avphdr + hdrlen; } @@ -246,39 +210,6 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, } -#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) { @@ -294,160 +225,25 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, 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; + os_free(data->session_id); + data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, + EAP_TYPE_TTLS, + &data->id_len); + if (data->session_id) { + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id", + data->session_id, data->id_len); + } else { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id"); } - 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 */ + return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); } @@ -494,7 +290,6 @@ static int eap_ttls_phase2_eap_process(struct eap_sm *sm, ret->methodState = iret.methodState; ret->decision = iret.decision; } - eap_ttlsv1_phase2_eap_finish(sm, data, ret); return 0; } @@ -615,31 +410,12 @@ static int eap_ttls_phase2_request_eap(struct eap_sm *sm, } -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) { +#ifdef EAP_MSCHAPv2 struct wpabuf *msg; u8 *buf, *pos, *challenge, *peer_challenge; const u8 *identity, *password; @@ -674,7 +450,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, "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, @@ -687,18 +462,29 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; *pos++ = data->ident; *pos++ = 0; /* Flags */ - os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) { + os_free(challenge); + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get " + "random data for peer challenge"); + return -1; + } + peer_challenge = pos; pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; os_memset(pos, 0, 8); /* Reserved, must be zero */ pos += 8; - mschapv2_derive_response(identity, identity_len, password, - password_len, pwhash, challenge, - peer_challenge, pos, data->auth_response, - data->master_key); + if (mschapv2_derive_response(identity, identity_len, password, + password_len, pwhash, challenge, + peer_challenge, pos, data->auth_response, + data->master_key)) { + os_free(challenge); + 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); @@ -706,7 +492,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, wpabuf_put(msg, pos - buf); *resp = msg; - if (sm->workaround && data->ttls_version == 0) { + if (sm->workaround) { /* At least FreeRADIUS seems to be terminating * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success * packet. */ @@ -717,6 +503,10 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, } return 0; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ } @@ -793,17 +583,10 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, 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; - } + /* 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; } @@ -854,17 +637,10 @@ static int eap_ttls_phase2_request_pap(struct eap_sm *sm, 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; - } + /* 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; } @@ -937,17 +713,10 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm, 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; - } + /* 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; } @@ -1022,38 +791,6 @@ static int eap_ttls_phase2_request(struct eap_sm *sm, } -#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) -{ - int len; - struct wpabuf *req; - u8 *pos; - const int max_len = 300; - - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1 + max_len, - EAP_CODE_RESPONSE, id); - if (req == NULL) - return NULL; - - wpabuf_put_u8(req, data->ttls_version); - - pos = wpabuf_put(req, 0); - len = tls_connection_ia_send_phase_finished(sm->ssl_ctx, - data->ssl.conn, - final, pos, max_len); - if (len < 0) { - wpabuf_free(req); - return NULL; - } - wpabuf_put(req, len); - eap_update_len(req); - - return req; -} -#endif /* EAP_TTLS_VERSION */ - - struct ttls_parse_avp { u8 *mschapv2; u8 *eapdata; @@ -1324,6 +1061,7 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, struct eap_method_ret *ret, struct ttls_parse_avp *parse) { +#ifdef EAP_MSCHAPv2 if (parse->mschapv2_error) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " "MS-CHAP-Error - failed"); @@ -1363,25 +1101,19 @@ static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, 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; - } + 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; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ } @@ -1490,24 +1222,6 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm, } -#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, @@ -1531,6 +1245,21 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm, "processing failed"); retval = -1; } else { + struct eap_peer_config *config = eap_get_config(sm); + if (resp == NULL && + (config->pending_req_identity || + config->pending_req_password || + config->pending_req_otp || + config->pending_req_new_password)) { + /* + * Use empty buffer to force implicit request + * processing when EAP request is re-processed after + * user input. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc(0); + } + retval = eap_ttls_encrypt_response(sm, data, resp, identifier, out_data); } @@ -1624,17 +1353,6 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, 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; @@ -1659,46 +1377,6 @@ done: } -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_PEAP_VERSION_MASK, data->ttls_version); -#if EAP_TTLS_VERSION > 0 - if ((flags & EAP_PEAP_VERSION_MASK) < data->ttls_version) - data->ttls_version = flags & EAP_PEAP_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, @@ -1722,8 +1400,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, ret->methodState = METHOD_MAY_CONT; } data->phase2_start = 1; - if (data->ttls_version == 0) - eap_ttls_v0_derive_key(sm, data); + eap_ttls_v0_derive_key(sm, data); if (*out_data == NULL || wpabuf_len(*out_data) == 0) { if (eap_ttls_decrypt(sm, data, ret, identifier, @@ -1758,7 +1435,7 @@ 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) { + if (ret->methodState == METHOD_DONE) { ret->allowNotifications = FALSE; if (ret->decision == DECISION_UNCOND_SUCC || ret->decision == DECISION_COND_SUCC) { @@ -1776,8 +1453,7 @@ static void eap_ttls_check_auth_status(struct eap_sm *sm, } #endif /* EAP_TNC */ } - } else if (data->ttls_version == 0 && - ret->methodState == METHOD_MAY_CONT && + } else if (ret->methodState == METHOD_MAY_CONT && (ret->decision == DECISION_UNCOND_SUCC || ret->decision == DECISION_COND_SUCC)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " @@ -1805,8 +1481,9 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, id = eap_get_id(reqData); if (flags & EAP_TLS_FLAGS_START) { - if (eap_ttls_process_start(sm, data, flags, ret) < 0) - return NULL; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own " + "ver=%d)", flags & EAP_TLS_VERSION_MASK, + data->ttls_version); /* RFC 5281, Ch. 9.2: * "This packet MAY contain additional information in the form @@ -1814,13 +1491,6 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, * 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; @@ -1872,6 +1542,8 @@ 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; + os_free(data->session_id); + data->session_id = NULL; if (eap_peer_tls_reauth_init(sm, &data->ssl)) { os_free(data); return NULL; @@ -1956,6 +1628,25 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *id; + + if (data->session_id == NULL || !data->phase2_success) + return NULL; + + id = os_malloc(data->id_len); + if (id == NULL) + return NULL; + + *len = data->id_len; + os_memcpy(id, data->session_id, data->id_len); + + return id; +} + + int eap_peer_ttls_register(void) { struct eap_method *eap; @@ -1971,6 +1662,7 @@ int eap_peer_ttls_register(void) eap->process = eap_ttls_process; eap->isKeyAvailable = eap_ttls_isKeyAvailable; eap->getKey = eap_ttls_getKey; + eap->getSessionId = eap_ttls_get_session_id; 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; diff --git a/contrib/hostapd/src/eap_peer/eap_vendor_test.c b/contrib/hostapd/src/eap_peer/eap_vendor_test.c index 3e114c142a..040d1e7f9a 100644 --- a/contrib/hostapd/src/eap_peer/eap_vendor_test.c +++ b/contrib/hostapd/src/eap_peer/eap_vendor_test.c @@ -2,14 +2,8 @@ * EAP peer method: Test method for vendor specific (expanded) EAP type * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -25,7 +19,7 @@ #endif /* TEST_PENDING_REQUEST */ -#define EAP_VENDOR_ID 0xfffefd +#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 diff --git a/contrib/hostapd/src/eap_peer/eap_wsc.c b/contrib/hostapd/src/eap_peer/eap_wsc.c index 7c8ad2fdad..6bdd341182 100644 --- a/contrib/hostapd/src/eap_peer/eap_wsc.c +++ b/contrib/hostapd/src/eap_peer/eap_wsc.c @@ -1,15 +1,9 @@ /* * EAP-WSC peer for Wi-Fi Protected Setup - * Copyright (c) 2007-2008, Jouni Malinen + * Copyright (c) 2007-2009, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -65,6 +59,84 @@ static void eap_wsc_state(struct eap_wsc_data *data, int 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)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid"); + return -1; + } + cred->ssid_len = len / 2; + + pos = os_strstr(params, "new_auth="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth"); + 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 { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth"); + return -1; + } + + pos = os_strstr(params, "new_encr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr"); + 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 { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr"); + 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)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key"); + return -1; + } + cred->key_len = len / 2; + + return 1; +} + + static void * eap_wsc_init(struct eap_sm *sm) { struct eap_wsc_data *data; @@ -72,9 +144,13 @@ static void * eap_wsc_init(struct eap_sm *sm) size_t identity_len; int registrar; struct wps_config cfg; - const char *pos; + const char *pos, *end; const char *phase1; struct wps_context *wps; + struct wps_credential new_ap_settings; + int res; + int nfc = 0; + u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN]; wps = sm->wps; if (wps == NULL) { @@ -122,31 +198,88 @@ static void * eap_wsc_init(struct eap_sm *sm) while (*pos != '\0' && *pos != ' ') pos++; cfg.pin_len = pos - (const char *) cfg.pin; + if (cfg.pin_len == 6 && + os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) { + cfg.pin = NULL; + cfg.pin_len = 0; + nfc = 1; + } } else { pos = os_strstr(phase1, "pbc=1"); if (pos) cfg.pbc = 1; } - if (cfg.pin == NULL && !cfg.pbc) { + pos = os_strstr(phase1, "dev_pw_id="); + if (pos) { + u16 id = atoi(pos + 10); + if (id == DEV_PW_NFC_CONNECTION_HANDOVER) + nfc = 1; + if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER) + cfg.dev_pw_id = id; + } + + if (cfg.pin == NULL && !cfg.pbc && !nfc) { wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 " "configuration data"); os_free(data); return NULL; } + pos = os_strstr(phase1, " pkhash="); + if (pos) { + size_t len; + pos += 8; + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN || + hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) { + wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash"); + os_free(data); + return NULL; + } + cfg.peer_pubkey_hash = pkhash; + } + + res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); + if (res < 0) { + os_free(data); + wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP " + "settings"); + 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); + wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed"); return NULL; } - data->fragment_size = WSC_FRAGMENT_SIZE; + res = eap_get_config_fragment_size(sm); + if (res > 0) + data->fragment_size = res; + else + data->fragment_size = WSC_FRAGMENT_SIZE; + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u", + (unsigned int) data->fragment_size); if (registrar && cfg.pin) { - wps_registrar_add_pin(data->wps_ctx->registrar, NULL, + wps_registrar_add_pin(data->wps_ctx->registrar, NULL, 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; } @@ -302,6 +435,7 @@ static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, 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); @@ -427,7 +561,13 @@ send_msg: } eap_wsc_state(data, MESG); - return eap_wsc_build_msg(data, ret, id); + 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; } diff --git a/contrib/hostapd/src/eap_peer/ikev2.c b/contrib/hostapd/src/eap_peer/ikev2.c index 9172e1f348..1ccc35230e 100644 --- a/contrib/hostapd/src/eap_peer/ikev2.c +++ b/contrib/hostapd/src/eap_peer/ikev2.c @@ -2,20 +2,15 @@ * IKEv2 responder (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "dh_groups.h" +#include "crypto/dh_groups.h" +#include "crypto/random.h" #include "ikev2.h" @@ -424,7 +419,7 @@ static int ikev2_process_kei(struct ikev2_responder_data *data, } /* RFC 4306, Section 3.4: - * The length of DH public value MUST be equal to the lenght of the + * The length of DH public value MUST be equal to the length of the * prime modulus. */ if (kei_len - 4 != data->dh->prime_len) { @@ -1133,7 +1128,7 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data) 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)) + if (random_get_bytes(data->r_nonce, data->r_nonce_len)) return NULL; #ifdef CCNS_PL /* Zeros are removed incorrectly from the beginning of the nonces in @@ -1262,6 +1257,7 @@ static struct wpabuf * ikev2_build_notify(struct ikev2_responder_data *data) wpabuf_free(msg); return NULL; } + wpabuf_free(plain); data->state = IKEV2_FAILED; } else { /* HDR, N */ diff --git a/contrib/hostapd/src/eap_peer/ikev2.h b/contrib/hostapd/src/eap_peer/ikev2.h index 9ca0ca5695..627a2cbbb0 100644 --- a/contrib/hostapd/src/eap_peer/ikev2.h +++ b/contrib/hostapd/src/eap_peer/ikev2.h @@ -2,14 +2,8 @@ * IKEv2 responder (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IKEV2_H diff --git a/contrib/hostapd/src/eap_peer/mschapv2.c b/contrib/hostapd/src/eap_peer/mschapv2.c index 01c22d8975..37e6735efb 100644 --- a/contrib/hostapd/src/eap_peer/mschapv2.c +++ b/contrib/hostapd/src/eap_peer/mschapv2.c @@ -2,20 +2,14 @@ * MSCHAPV2 (RFC 2759) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "ms_funcs.h" +#include "crypto/ms_funcs.h" #include "mschapv2.h" const u8 * mschapv2_remove_domain(const u8 *username, size_t *len) @@ -39,13 +33,13 @@ const u8 * mschapv2_remove_domain(const u8 *username, size_t *len) } -void 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) +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; @@ -69,22 +63,28 @@ void mschapv2_derive_response(const u8 *identity, size_t identity_len, 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); + if (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)) + return -1; } 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); + if (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)) + return -1; } wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response", nt_response, MSCHAPV2_NT_RESPONSE_LEN); @@ -93,14 +93,19 @@ void mschapv2_derive_response(const u8 *identity, size_t identity_len, /* Generate master_key here since we have the needed data available. */ if (pwhash) { - hash_nt_password_hash(password, password_hash_hash); + if (hash_nt_password_hash(password, password_hash_hash)) + return -1; } else { - nt_password_hash(password, password_len, password_hash); - hash_nt_password_hash(password_hash, password_hash_hash); + 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); + if (get_master_key(password_hash_hash, nt_response, master_key)) + return -1; wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key", master_key, MSCHAPV2_MASTER_KEY_LEN); + + return 0; } diff --git a/contrib/hostapd/src/eap_peer/mschapv2.h b/contrib/hostapd/src/eap_peer/mschapv2.h index c7c36f7721..edd458b402 100644 --- a/contrib/hostapd/src/eap_peer/mschapv2.h +++ b/contrib/hostapd/src/eap_peer/mschapv2.h @@ -2,14 +2,8 @@ * MSCHAPV2 (RFC 2759) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef MSCHAPV2_H @@ -21,13 +15,13 @@ #define MSCHAPV2_MASTER_KEY_LEN 16 const u8 * mschapv2_remove_domain(const u8 *username, size_t *len); -void 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_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); diff --git a/contrib/hostapd/src/eap_peer/tncc.c b/contrib/hostapd/src/eap_peer/tncc.c index eaaa1689b5..a3ec395140 100644 --- a/contrib/hostapd/src/eap_peer/tncc.c +++ b/contrib/hostapd/src/eap_peer/tncc.c @@ -2,14 +2,8 @@ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -180,11 +174,11 @@ TNC_Result TNC_TNCC_ReportMessageTypes( imc = tnc_imc[imcID]; os_free(imc->supported_types); imc->supported_types = - os_malloc(typeCount * sizeof(TNC_MessageTypeList)); + os_malloc(typeCount * sizeof(TNC_MessageType)); if (imc->supported_types == NULL) return TNC_RESULT_FATAL; os_memcpy(imc->supported_types, supportedTypes, - typeCount * sizeof(TNC_MessageTypeList)); + typeCount * sizeof(TNC_MessageType)); imc->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; @@ -747,12 +741,10 @@ enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION; int recommendation_msg = 0; - buf = os_malloc(len + 1); + buf = dup_binstr(msg, len); if (buf == NULL) return TNCCS_PROCESS_ERROR; - os_memcpy(buf, msg, len); - buf[len] = '\0'; start = os_strstr(buf, ""); if (start == NULL || end == NULL || start > end) { diff --git a/contrib/hostapd/src/eap_peer/tncc.h b/contrib/hostapd/src/eap_peer/tncc.h index 4d42a05b9a..df2a2870f9 100644 --- a/contrib/hostapd/src/eap_peer/tncc.h +++ b/contrib/hostapd/src/eap_peer/tncc.h @@ -2,14 +2,8 @@ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TNCC_H diff --git a/contrib/hostapd/src/eap_server/eap.h b/contrib/hostapd/src/eap_server/eap.h index 6a20da4f46..36b230b48c 100644 --- a/contrib/hostapd/src/eap_server/eap.h +++ b/contrib/hostapd/src/eap_server/eap.h @@ -2,28 +2,20 @@ * hostapd / EAP Full Authenticator state machine (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_H #define EAP_H -#include "defs.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 @@ -91,9 +83,11 @@ struct eapol_callbacks { struct eap_config { void *ssl_ctx; + void *msg_ctx; void *eap_sim_db_priv; Boolean backend_auth; int eap_server; + u16 pwd_group; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -105,6 +99,14 @@ struct eap_config { int tnc; struct wps_context *wps; const struct wpabuf *assoc_wps_ie; + const struct wpabuf *assoc_p2p_ie; + const u8 *peer_addr; + int fragment_size; + + int pbc_in_m1; + + const u8 *server_id; + size_t server_id_len; }; @@ -118,5 +120,6 @@ 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); +void eap_server_clear_identity(struct eap_sm *sm); #endif /* EAP_H */ diff --git a/contrib/hostapd/src/eap_server/eap_i.h b/contrib/hostapd/src/eap_server/eap_i.h index d52b86f955..003e20205f 100644 --- a/contrib/hostapd/src/eap_server/eap_i.h +++ b/contrib/hostapd/src/eap_server/eap_i.h @@ -2,14 +2,8 @@ * hostapd / EAP Authenticator state machine internal structures (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_I_H @@ -119,7 +113,7 @@ struct eap_sm { /* Full authenticator state machine local variables */ - /* Long-term (maintained betwen packets) */ + /* Long-term (maintained between packets) */ EapType currentMethod; int currentId; enum { @@ -157,7 +151,7 @@ struct eap_sm { int user_eap_method_index; int init_phase2; void *ssl_ctx; - void *eap_sim_db_priv; + struct eap_sim_db_data *eap_sim_db_priv; Boolean backend_auth; Boolean update_user; int eap_server; @@ -181,10 +175,22 @@ struct eap_sm { int pac_key_refresh_time; int eap_sim_aka_result_ind; int tnc; + u16 pwd_group; struct wps_context *wps; struct wpabuf *assoc_wps_ie; + struct wpabuf *assoc_p2p_ie; Boolean start_reauth; + + u8 peer_addr[ETH_ALEN]; + + /* Fragmentation size for EAP method init() handler */ + int fragment_size; + + int pbc_in_m1; + + const u8 *server_id; + size_t server_id_len; }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, diff --git a/contrib/hostapd/src/eap_server/eap_methods.h b/contrib/hostapd/src/eap_server/eap_methods.h index 0fd53909ff..429cb72b22 100644 --- a/contrib/hostapd/src/eap_server/eap_methods.h +++ b/contrib/hostapd/src/eap_server/eap_methods.h @@ -1,19 +1,15 @@ /* - * hostapd / EAP method registration - * Copyright (c) 2004-2006, Jouni Malinen + * EAP server method registration + * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#ifndef EAP_METHODS_H -#define EAP_METHODS_H +#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); @@ -23,7 +19,32 @@ 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); -int eap_server_register_methods(void); 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_unauth_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); +int eap_server_pwd_register(void); +int eap_server_eke_register(void); -#endif /* EAP_METHODS_H */ +#endif /* EAP_SERVER_METHODS_H */ diff --git a/contrib/hostapd/src/eap_server/eap.c b/contrib/hostapd/src/eap_server/eap_server.c similarity index 93% rename from contrib/hostapd/src/eap_server/eap.c rename to contrib/hostapd/src/eap_server/eap_server.c index 897adc3b1a..233e2726e1 100644 --- a/contrib/hostapd/src/eap_server/eap.c +++ b/contrib/hostapd/src/eap_server/eap_server.c @@ -2,14 +2,8 @@ * hostapd / EAP Full Authenticator state machine (RFC 4137) * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -23,6 +17,7 @@ #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" @@ -135,6 +130,14 @@ SM_STATE(EAP, INITIALIZE) { SM_ENTRY(EAP, INITIALIZE); + if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) { + /* + * Need to allow internal Identity method to be used instead + * of passthrough at the beginning of reauthentication. + */ + eap_server_clear_identity(sm); + } + sm->currentId = -1; sm->eap_if.eapSuccess = FALSE; sm->eap_if.eapFail = FALSE; @@ -167,6 +170,9 @@ SM_STATE(EAP, INITIALIZE) } 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)); } @@ -196,6 +202,9 @@ SM_STATE(EAP, PICK_UP_METHOD) sm->currentMethod = EAP_TYPE_NONE; } } + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "method=%u", sm->currentMethod); } @@ -266,6 +275,11 @@ SM_STATE(EAP, INTEGRITY_CHECK) { SM_ENTRY(EAP, INTEGRITY_CHECK); + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) { + sm->ignore = TRUE; + return; + } + if (sm->m->check) { sm->ignore = sm->m->check(sm, sm->eap_method_priv, sm->eap_if.eapRespData); @@ -300,6 +314,9 @@ SM_STATE(EAP, METHOD_RESPONSE) { SM_ENTRY(EAP, METHOD_RESPONSE); + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + 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); @@ -326,6 +343,7 @@ SM_STATE(EAP, PROPOSE_METHOD) SM_ENTRY(EAP, PROPOSE_METHOD); +try_another_method: type = eap_sm_Policy_getNextMethod(sm, &vendor); if (vendor == EAP_VENDOR_IETF) sm->currentMethod = type; @@ -343,13 +361,22 @@ SM_STATE(EAP, PROPOSE_METHOD) "method %d", sm->currentMethod); sm->m = NULL; sm->currentMethod = EAP_TYPE_NONE; + goto try_another_method; } } + if (sm->m == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method"); + sm->decision = DECISION_FAILURE; + return; + } 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); } @@ -368,6 +395,9 @@ SM_STATE(EAP, NAK) } sm->m = NULL; + if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) + return; + nak = wpabuf_head(sm->eap_if.eapRespData); if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) { len = be_to_host16(nak->length); @@ -410,6 +440,9 @@ SM_STATE(EAP, FAILURE) 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)); } @@ -424,6 +457,9 @@ SM_STATE(EAP, SUCCESS) 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)); } @@ -673,6 +709,15 @@ SM_STEP(EAP) SM_ENTER(EAP, METHOD_RESPONSE); break; case EAP_METHOD_REQUEST: + if (sm->m == NULL) { + /* + * This transition is not mentioned in RFC 4137, but it + * is needed to handle cleanly a case where EAP method + * initialization fails. + */ + SM_ENTER(EAP, FAILURE); + break; + } SM_ENTER(EAP, SEND_REQUEST); break; case EAP_METHOD_RESPONSE: @@ -1012,9 +1057,12 @@ void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len) 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])); + if (i + 1 < EAP_MAX_METHODS) { + 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; @@ -1210,6 +1258,7 @@ struct eap_sm * eap_server_sm_init(void *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; @@ -1238,6 +1287,15 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->wps = conf->wps; if (conf->assoc_wps_ie) sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie); + if (conf->assoc_p2p_ie) + sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie); + if (conf->peer_addr) + os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN); + sm->fragment_size = conf->fragment_size; + sm->pwd_group = conf->pwd_group; + sm->pbc_in_m1 = conf->pbc_in_m1; + sm->server_id = conf->server_id; + sm->server_id_len = conf->server_id_len; wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); @@ -1272,6 +1330,7 @@ void eap_server_sm_deinit(struct eap_sm *sm) os_free(sm->eap_if.aaaEapKeyData); eap_user_free(sm->user); wpabuf_free(sm->assoc_wps_ie); + wpabuf_free(sm->assoc_p2p_ie); os_free(sm); } @@ -1343,3 +1402,18 @@ struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm) { return &sm->eap_if; } + + +/** + * eap_server_clear_identity - Clear EAP identity information + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * + * This function can be used to clear the EAP identity information in the EAP + * server context. This allows the EAP/Identity method to be used again after + * EAPOL-Start or EAPOL-Logoff. + */ +void eap_server_clear_identity(struct eap_sm *sm) +{ + os_free(sm->identity); + sm->identity = NULL; +} diff --git a/contrib/hostapd/src/eap_server/eap_aka.c b/contrib/hostapd/src/eap_server/eap_server_aka.c similarity index 78% rename from contrib/hostapd/src/eap_server/eap_aka.c rename to contrib/hostapd/src/eap_server/eap_server_aka.c index aad52fd649..46fc45847f 100644 --- a/contrib/hostapd/src/eap_server/eap_aka.c +++ b/contrib/hostapd/src/eap_server/eap_server_aka.c @@ -1,26 +1,20 @@ /* - * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf) - * Copyright (c) 2005-2008, Jouni Malinen + * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) + * Copyright (c) 2005-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "eap_server/eap_i.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" +#include "crypto/random.h" #include "eap_common/eap_sim_common.h" +#include "eap_server/eap_i.h" #include "eap_server/eap_sim_db.h" -#include "sha1.h" -#include "sha256.h" -#include "crypto.h" struct eap_aka_data { @@ -55,12 +49,12 @@ struct eap_aka_data { u8 *network_name; size_t network_name_len; u16 kdf; + int identity_round; + char permanent[20]; /* Permanent username */ }; -static void eap_aka_determine_identity(struct eap_sm *sm, - struct eap_aka_data *data, - int before_identity, int after_reauth); +static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data); static const char * eap_aka_state_txt(int state) @@ -93,6 +87,96 @@ static void eap_aka_state(struct eap_aka_data *data, int state) } +static int eap_aka_check_identity_reauth(struct eap_sm *sm, + struct eap_aka_data *data, + const char *username) +{ + if (data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX) + return 0; + if (data->eap_method == EAP_TYPE_AKA && + username[0] != EAP_AKA_REAUTH_ID_PREFIX) + return 0; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username); + data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv, + username); + if (data->reauth == NULL) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - " + "request full auth identity"); + /* Remain in IDENTITY state for another round */ + return 0; + } + + wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication"); + os_strlcpy(data->permanent, data->reauth->permanent, + sizeof(data->permanent)); + 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); + } + + eap_aka_state(data, REAUTH); + return 1; +} + + +static void eap_aka_check_identity(struct eap_sm *sm, + struct eap_aka_data *data) +{ + char *username; + + /* Check if we already know the identity from EAP-Response/Identity */ + + username = sim_get_username(sm->identity, sm->identity_len); + if (username == NULL) + return; + + if (eap_aka_check_identity_reauth(sm, data, username) > 0) { + os_free(username); + /* + * Since re-auth username was recognized, skip AKA/Identity + * exchange. + */ + return; + } + + if ((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_PSEUDONYM_PREFIX)) { + const char *permanent; + wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'", + username); + permanent = eap_sim_db_get_permanent( + sm->eap_sim_db_priv, username); + if (permanent == NULL) { + os_free(username); + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym " + "identity - request permanent identity"); + /* Remain in IDENTITY state for another round */ + return; + } + os_strlcpy(data->permanent, permanent, + sizeof(data->permanent)); + /* + * Since pseudonym username was recognized, skip AKA/Identity + * exchange. + */ + eap_aka_fullauth(sm, data); + } + + os_free(username); +} + + static void * eap_aka_init(struct eap_sm *sm) { struct eap_aka_data *data; @@ -109,14 +193,14 @@ static void * eap_aka_init(struct eap_sm *sm) data->eap_method = EAP_TYPE_AKA; data->state = IDENTITY; - eap_aka_determine_identity(sm, data, 1, 0); data->pending_id = -1; + eap_aka_check_identity(sm, data); return data; } -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME static void * eap_aka_prime_init(struct eap_sm *sm) { struct eap_aka_data *data; @@ -133,22 +217,21 @@ static void * eap_aka_prime_init(struct eap_sm *sm) return NULL; data->eap_method = EAP_TYPE_AKA_PRIME; - data->network_name = os_malloc(os_strlen(network_name)); + data->network_name = (u8 *) os_strdup(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; + eap_aka_check_identity(sm, data); return data; } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ static void eap_aka_reset(struct eap_sm *sm, void *priv) @@ -271,11 +354,8 @@ static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, 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 { + data->identity_round++; + if (data->identity_round == 1) { /* * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is * ignored and the AKA/Identity is used to request the @@ -283,6 +363,19 @@ static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, */ wpa_printf(MSG_DEBUG, " AT_ANY_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0); + } else if (data->identity_round > 3) { + /* Cannot use more than three rounds of Identity messages */ + eap_sim_msg_free(msg); + return NULL; + } else if (sm->identity && sm->identity_len > 0 && + (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX || + sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) { + /* Reauth id may have expired - try fullauth */ + wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0); + } else { + wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); } buf = eap_sim_msg_finish(msg, NULL, NULL, 0); if (eap_aka_add_id_msg(data, buf) < 0) { @@ -299,12 +392,23 @@ static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, const u8 *nonce_s) { os_free(data->next_pseudonym); - data->next_pseudonym = - eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1); + if (nonce_s == NULL) { + data->next_pseudonym = + eap_sim_db_get_next_pseudonym( + sm->eap_sim_db_priv, + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA); + } else { + /* Do not update pseudonym during re-authentication */ + data->next_pseudonym = NULL; + } 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); + eap_sim_db_get_next_reauth_id( + sm->eap_sim_db_priv, + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA); } else { wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication " "count exceeded - force full authentication"); @@ -399,7 +503,7 @@ static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA) { u16 flags = 0; int i; @@ -426,7 +530,7 @@ static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, flags |= EAP_AKA_BIDDING_FLAG_D; eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0); } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); @@ -441,7 +545,7 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); - if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN)) + if (random_get_bytes(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); @@ -608,92 +712,83 @@ static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype) static void eap_aka_determine_identity(struct eap_sm *sm, - struct eap_aka_data *data, - int before_identity, int after_reauth) + struct eap_aka_data *data) { - const u8 *identity; - size_t identity_len; - int res; + char *username; - identity = NULL; - identity_len = 0; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", + sm->identity, sm->identity_len); - 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); - } - } - } + username = sim_get_username(sm->identity, sm->identity_len); + if (username == NULL) { + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; } - 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. */ - } + if (eap_aka_check_identity_reauth(sm, data, username) > 0) { + os_free(username); + return; } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", - identity, identity_len); + if (((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_REAUTH_ID_PREFIX)) && + data->identity_round == 1) { + /* Remain in IDENTITY state for another round to request full + * auth identity since we did not recognize reauth id */ + os_free(username); + return; + } - if (!after_reauth && data->reauth) { - eap_aka_state(data, REAUTH); + if ((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_PSEUDONYM_PREFIX)) { + const char *permanent; + wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'", + username); + permanent = eap_sim_db_get_permanent( + sm->eap_sim_db_priv, username); + os_free(username); + if (permanent == NULL) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym " + "identity - request permanent identity"); + /* Remain in IDENTITY state for another round */ + return; + } + os_strlcpy(data->permanent, permanent, + sizeof(data->permanent)); + } else if ((data->eap_method == EAP_TYPE_AKA_PRIME && + username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) || + (data->eap_method == EAP_TYPE_AKA && + username[0] == EAP_AKA_PERMANENT_PREFIX)) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'", + username); + os_strlcpy(data->permanent, username, sizeof(data->permanent)); + os_free(username); + } else { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'", + username); + os_free(username); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); 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); + eap_aka_fullauth(sm, data); +} + + +static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data) +{ + size_t identity_len; + int res; + + res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent, + 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"); @@ -701,7 +796,7 @@ static void eap_aka_determine_identity(struct eap_sm *sm, return; } -#ifdef EAP_AKA_PRIME +#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 */ @@ -710,7 +805,7 @@ static void eap_aka_determine_identity(struct eap_sm *sm, data->network_name, data->network_name_len); } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ data->reauth = NULL; data->counter = 0; /* reset re-auth counter since this is full auth */ @@ -738,7 +833,7 @@ static void eap_aka_determine_identity(struct eap_sm *sm, sm->identity, identity_len); if (data->eap_method == EAP_TYPE_AKA_PRIME) { - eap_aka_prime_derive_keys(identity, identity_len, data->ik, + eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik, data->ck, data->k_encr, data->k_aut, data->k_re, data->msk, data->emsk); } else { @@ -757,6 +852,8 @@ static void eap_aka_process_identity(struct eap_sm *sm, struct wpabuf *respData, struct eap_sim_attrs *attr) { + u8 *new_identity; + wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); if (attr->mac || attr->iv || attr->encr_data) { @@ -767,17 +864,30 @@ static void eap_aka_process_identity(struct eap_sm *sm, 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; - } + /* + * We always request identity with AKA/Identity, so the peer is + * required to have replied with one. + */ + if (!attr->identity || attr->identity_len == 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any " + "identity"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + new_identity = os_malloc(attr->identity_len); + if (new_identity == NULL) { + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; } + os_free(sm->identity); + sm->identity = new_identity; + os_memcpy(sm->identity, attr->identity, attr->identity_len); + sm->identity_len = attr->identity_len; - eap_aka_determine_identity(sm, data, 0, 0); + eap_aka_determine_identity(sm, data); if (eap_get_id(respData) == data->pending_id) { data->pending_id = -1; eap_aka_add_id_msg(data, respData); @@ -802,12 +912,9 @@ static void eap_aka_process_challenge(struct eap_sm *sm, 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_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME #if 0 /* KDF negotiation; to be enabled only after more than one KDF is * supported */ @@ -830,7 +937,7 @@ static void eap_aka_process_challenge(struct eap_sm *sm, return; } #endif -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ if (attr->checkcode && eap_aka_verify_checkcode(data, attr->checkcode, @@ -877,33 +984,24 @@ static void eap_aka_process_challenge(struct eap_sm *sm, } 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, + eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent, data->next_pseudonym); data->next_pseudonym = NULL; } if (data->next_reauth_id) { if (data->eap_method == EAP_TYPE_AKA_PRIME) { -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, - identity, - identity_len, + data->permanent, data->next_reauth_id, data->counter + 1, data->k_encr, data->k_aut, data->k_re); -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ } else { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, + data->permanent, data->next_reauth_id, data->counter + 1, data->mk); @@ -932,9 +1030,8 @@ static void eap_aka_process_sync_failure(struct eap_sm *sm, * 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)) { + eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent, + 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); @@ -942,8 +1039,8 @@ static void eap_aka_process_sync_failure(struct eap_sm *sm, } data->auts_reported = 1; - /* Try again after resynchronization */ - eap_aka_determine_identity(sm, data, 0, 0); + /* Remain in CHALLENGE state to re-try after resynchronization */ + eap_aka_fullauth(sm, data); } @@ -954,8 +1051,6 @@ static void eap_aka_process_reauth(struct eap_sm *sm, { 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"); @@ -998,7 +1093,7 @@ static void eap_aka_process_reauth(struct eap_sm *sm, 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); + eap_aka_fullauth(sm, data); return; } @@ -1009,40 +1104,19 @@ static void eap_aka_process_reauth(struct eap_sm *sm, } 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_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, - identity, - identity_len, + data->permanent, data->next_reauth_id, data->counter + 1, data->k_encr, data->k_aut, data->k_re); -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ } else { - eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, - identity_len, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, + data->permanent, data->next_reauth_id, data->counter + 1, data->mk); @@ -1247,7 +1321,7 @@ int eap_server_aka_register(void) } -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME int eap_server_aka_prime_register(void) { struct eap_method *eap; @@ -1275,4 +1349,4 @@ int eap_server_aka_prime_register(void) return ret; } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ diff --git a/contrib/hostapd/src/eap_server/eap_server_eke.c b/contrib/hostapd/src/eap_server/eap_server_eke.c new file mode 100644 index 0000000000..b19a321af4 --- /dev/null +++ b/contrib/hostapd/src/eap_server/eap_server_eke.c @@ -0,0 +1,793 @@ +/* + * hostapd / EAP-EKE (RFC 6124) server + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/random.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_eke_common.h" + + +struct eap_eke_data { + enum { + IDENTITY, COMMIT, CONFIRM, FAILURE_REPORT, SUCCESS, FAILURE + } state; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 *peerid; + size_t peerid_len; + u8 peerid_type; + u8 serverid_type; + u8 dh_priv[EAP_EKE_MAX_DH_LEN]; + u8 key[EAP_EKE_MAX_KEY_LEN]; + struct eap_eke_session sess; + u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; + u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; + struct wpabuf *msgs; + int phase2; + u32 failure_code; +}; + + +static const char * eap_eke_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case COMMIT: + return "COMMIT"; + case CONFIRM: + return "CONFIRM"; + case FAILURE_REPORT: + return "FAILURE_REPORT"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_eke_state(struct eap_eke_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", + eap_eke_state_txt(data->state), + eap_eke_state_txt(state)); + data->state = state; +} + + +static void eap_eke_fail(struct eap_eke_data *data, u32 code) +{ + wpa_printf(MSG_DEBUG, "EAP-EKE: Failure - code 0x%x", code); + data->failure_code = code; + eap_eke_state(data, FAILURE_REPORT); +} + + +static void * eap_eke_init(struct eap_sm *sm) +{ + struct eap_eke_data *data; + size_t i; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + eap_eke_state(data, IDENTITY); + + data->serverid_type = EAP_EKE_ID_OPAQUE; + for (i = 0; i < sm->server_id_len; i++) { + if (sm->server_id[i] == '.' && + data->serverid_type == EAP_EKE_ID_OPAQUE) + data->serverid_type = EAP_EKE_ID_FQDN; + if (sm->server_id[i] == '@') + data->serverid_type = EAP_EKE_ID_NAI; + } + + data->phase2 = sm->init_phase2; + + return data; +} + + +static void eap_eke_reset(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + eap_eke_session_clean(&data->sess); + os_free(data->peerid); + wpabuf_free(data->msgs); + os_free(data); +} + + +static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, + u8 id, size_t length, u8 eke_exch) +{ + struct wpabuf *msg; + size_t plen; + + plen = 1 + length; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, + EAP_CODE_REQUEST, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); + return NULL; + } + + wpabuf_put_u8(msg, eke_exch); + + return msg; +} + + +static int supported_proposal(const u8 *pos) +{ + if (pos[0] == EAP_EKE_DHGROUP_EKE_16 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_15 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && + pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) + return 1; + + if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && + pos[1] == EAP_EKE_ENCR_AES128_CBC && + pos[2] == EAP_EKE_PRF_HMAC_SHA1 && + pos[3] == EAP_EKE_MAC_HMAC_SHA1) + return 1; + + return 0; +} + + +static struct wpabuf * eap_eke_build_failure(struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Failure: Failure-Code=0x%x", + data->failure_code); + + msg = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE); + if (msg == NULL) { + eap_eke_state(data, FAILURE); + return NULL; + } + wpabuf_put_be32(msg, data->failure_code); + + return msg; +} + + +static struct wpabuf * eap_eke_build_identity(struct eap_sm *sm, + struct eap_eke_data *data, + u8 id) +{ + struct wpabuf *msg; + size_t plen; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity"); + + plen = 2 + 4 * 4 + 1 + sm->server_id_len; + msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID); + if (msg == NULL) + return NULL; + + wpabuf_put_u8(msg, 4); /* NumProposals */ + wpabuf_put_u8(msg, 0); /* Reserved */ + + /* Proposal - DH Group 16 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_16); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* Proposal - DH Group 15 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_15); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* Proposal - DH Group 14 with AES128-CBC and SHA256 */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ + + /* + * Proposal - DH Group 14 with AES128-CBC and SHA1 + * (mandatory to implement algorithms) + */ + wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ + wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ + wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA1); /* PRF */ + wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA1); /* MAC */ + + /* Server IDType + Identity */ + wpabuf_put_u8(msg, data->serverid_type); + wpabuf_put_data(msg, sm->server_id, sm->server_id_len); + + wpabuf_free(data->msgs); + data->msgs = wpabuf_dup(msg); + if (data->msgs == NULL) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +static struct wpabuf * eap_eke_build_commit(struct eap_sm *sm, + struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + u8 pub[EAP_EKE_MAX_DH_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Commit"); + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: Password with not configured"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return eap_eke_build_failure(data, id); + } + + if (eap_eke_derive_key(&data->sess, sm->user->password, + sm->user->password_len, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, data->key) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + msg = eap_eke_build_msg(data, id, data->sess.dhcomp_len, + EAP_EKE_COMMIT); + if (msg == NULL) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + /* + * y_s = g ^ x_s (mod p) + * x_s = random number 2 .. p-1 + * temp = prf(0+, password) + * key = prf+(temp, ID_S | ID_P) + * DHComponent_S = Encr(key, y_s) + */ + + if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (eap_eke_dhcomp(&data->sess, data->key, pub, + wpabuf_put(msg, data->sess.dhcomp_len)) + < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S"); + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (wpabuf_resize(&data->msgs, wpabuf_len(msg)) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpabuf_put_buf(data->msgs, msg); + + return msg; +} + + +static struct wpabuf * eap_eke_build_confirm(struct eap_sm *sm, + struct eap_eke_data *data, u8 id) +{ + struct wpabuf *msg; + size_t plen, prot_len; + u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; + u8 *auth; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Confirm"); + + plen = data->sess.pnonce_ps_len + data->sess.prf_len; + msg = eap_eke_build_msg(data, id, plen, EAP_EKE_CONFIRM); + if (msg == NULL) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + if (random_get_bytes(data->nonce_s, data->sess.nonce_len)) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", + data->nonce_s, data->sess.nonce_len); + + os_memcpy(nonces, data->nonce_p, data->sess.nonce_len); + os_memcpy(nonces + data->sess.nonce_len, data->nonce_s, + data->sess.nonce_len); + prot_len = wpabuf_tailroom(msg); + if (eap_eke_prot(&data->sess, nonces, 2 * data->sess.nonce_len, + wpabuf_put(msg, 0), &prot_len) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpabuf_put(msg, prot_len); + + if (eap_eke_derive_ka(&data->sess, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, + data->nonce_p, data->nonce_s) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + + auth = wpabuf_put(msg, data->sess.prf_len); + if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth) < 0) { + wpabuf_free(msg); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return eap_eke_build_failure(data, id); + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth, data->sess.prf_len); + + return msg; +} + + +static struct wpabuf * eap_eke_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_eke_data *data = priv; + + switch (data->state) { + case IDENTITY: + return eap_eke_build_identity(sm, data, id); + case COMMIT: + return eap_eke_build_commit(sm, data, id); + case CONFIRM: + return eap_eke_build_confirm(sm, data, id); + case FAILURE_REPORT: + return eap_eke_build_failure(data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-EKE: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_eke_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_eke_data *data = priv; + size_t len; + const u8 *pos; + u8 eke_exch; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-EKE: Invalid frame"); + return TRUE; + } + + eke_exch = *pos; + wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: EKE-Exch=%d", eke_exch); + + if (data->state == IDENTITY && eke_exch == EAP_EKE_ID) + return FALSE; + + if (data->state == COMMIT && eke_exch == EAP_EKE_COMMIT) + return FALSE; + + if (data->state == CONFIRM && eke_exch == EAP_EKE_CONFIRM) + return FALSE; + + if (eke_exch == EAP_EKE_FAILURE) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected EKE-Exch=%d in state=%d", + eke_exch, data->state); + + return TRUE; +} + + +static void eap_eke_process_identity(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end; + int i; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Identity"); + + if (data->state != IDENTITY) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos = payload; + end = payload + payloadlen; + + if (pos + 2 + 4 + 1 > end) { + wpa_printf(MSG_INFO, "EAP-EKE: Too short EAP-EKE-ID payload"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + if (*pos != 1) { + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected NumProposals %d (expected 1)", + *pos); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos += 2; + + if (!supported_proposal(pos)) { + wpa_printf(MSG_INFO, "EAP-EKE: Unexpected Proposal (%u:%u:%u:%u)", + pos[0], pos[1], pos[2], pos[3]); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Selected Proposal (%u:%u:%u:%u)", + pos[0], pos[1], pos[2], pos[3]); + if (eap_eke_session_init(&data->sess, pos[0], pos[1], pos[2], pos[3]) < + 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + pos += 4; + + data->peerid_type = *pos++; + os_free(data->peerid); + data->peerid = os_malloc(end - pos); + if (data->peerid == NULL) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + os_memcpy(data->peerid, pos, end - pos); + data->peerid_len = end - pos; + wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity", + data->peerid, data->peerid_len); + + if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) { + wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + for (i = 0; i < EAP_MAX_METHODS; i++) { + if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && + sm->user->methods[i].method == EAP_TYPE_EKE) + break; + } + if (i == EAP_MAX_METHODS) { + wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + if (sm->user->password == NULL || sm->user->password_len == 0) { + wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer"); + eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); + return; + } + + if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpabuf_put_buf(data->msgs, respData); + + eap_eke_state(data, COMMIT); +} + + +static void eap_eke_process_commit(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end, *dhcomp, *pnonce; + size_t decrypt_len; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit"); + + if (data->state != COMMIT) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + pos = payload; + end = payload + payloadlen; + + if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", + pos, data->sess.dhcomp_len); + dhcomp = pos; + pos += data->sess.dhcomp_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len); + pnonce = pos; + pos += data->sess.pnonce_len; + wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); + + if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp) + < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + if (eap_eke_derive_ke_ki(&data->sess, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + decrypt_len = sizeof(data->nonce_p); + if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len, + data->nonce_p, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + if (decrypt_len < (size_t) data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", + data->nonce_p, data->sess.nonce_len); + + if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpabuf_put_buf(data->msgs, respData); + + eap_eke_state(data, CONFIRM); +} + + +static void eap_eke_process_confirm(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + size_t decrypt_len; + u8 nonce[EAP_EKE_MAX_NONCE_LEN]; + u8 auth_p[EAP_EKE_MAX_HASH_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); + + if (data->state != CONFIRM) { + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); + + if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); + eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); + return; + } + + decrypt_len = sizeof(nonce); + if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len, + nonce, &decrypt_len) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + if (decrypt_len < (size_t) data->sess.nonce_len) { + wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S", + nonce, data->sess.nonce_len); + if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + + if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len); + if (os_memcmp(auth_p, payload + data->sess.pnonce_len, + data->sess.prf_len) != 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match"); + eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); + return; + } + + if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, + data->nonce_s, data->nonce_p, + data->msk, data->emsk) < 0) { + wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); + eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); + return; + } + + os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); + os_memset(data->key, 0, sizeof(data->key)); + eap_eke_session_clean(&data->sess); + + eap_eke_state(data, SUCCESS); +} + + +static void eap_eke_process_failure(struct eap_sm *sm, + struct eap_eke_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + u32 code; + + wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure"); + + if (payloadlen < 4) { + wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); + eap_eke_state(data, FAILURE); + return; + } + + code = WPA_GET_BE32(payload); + wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code); + + eap_eke_state(data, FAILURE); +} + + +static void eap_eke_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_eke_data *data = priv; + u8 eke_exch; + size_t len; + const u8 *pos, *end; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); + if (pos == NULL || len < 1) + return; + + eke_exch = *pos; + end = pos + len; + pos++; + + wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos); + + switch (eke_exch) { + case EAP_EKE_ID: + eap_eke_process_identity(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_COMMIT: + eap_eke_process_commit(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_CONFIRM: + eap_eke_process_confirm(sm, data, respData, pos, end - pos); + break; + case EAP_EKE_FAILURE: + eap_eke_process_failure(sm, data, respData, pos, end - pos); + break; + } +} + + +static Boolean eap_eke_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_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_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_eke_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_eke_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_eke_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_eke_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); + if (eap == NULL) + return -1; + + eap->init = eap_eke_init; + eap->reset = eap_eke_reset; + eap->buildReq = eap_eke_buildReq; + eap->check = eap_eke_check; + eap->process = eap_eke_process; + eap->isDone = eap_eke_isDone; + eap->getKey = eap_eke_getKey; + eap->isSuccess = eap_eke_isSuccess; + eap->get_emsk = eap_eke_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/src/eap_server/eap_fast.c b/contrib/hostapd/src/eap_server/eap_server_fast.c similarity index 94% rename from contrib/hostapd/src/eap_server/eap_fast.c rename to contrib/hostapd/src/eap_server/eap_server_fast.c index c06f396ffd..fcb80dc756 100644 --- a/contrib/hostapd/src/eap_server/eap_fast.c +++ b/contrib/hostapd/src/eap_server/eap_server_fast.c @@ -2,26 +2,21 @@ * EAP-FAST server (RFC 4851) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "aes_wrap.h" -#include "sha1.h" -#include "eap_i.h" -#include "eap_tls_common.h" -#include "tls.h" +#include "crypto/aes_wrap.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" +#include "crypto/random.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); @@ -642,7 +637,7 @@ static struct wpabuf * eap_fast_build_crypto_binding( 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) { + if (random_get_bytes(binding->nonce, sizeof(binding->nonce)) < 0) { wpabuf_free(buf); return NULL; } @@ -692,7 +687,7 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, struct eap_tlv_result_tlv *result; struct os_time now; - if (os_get_random(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 || + if (random_get_bytes(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", @@ -791,6 +786,11 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, /* 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); @@ -816,29 +816,28 @@ static int eap_fast_encrypt_phase2(struct eap_sm *sm, wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 TLVs", plain); - encr = eap_server_tls_encrypt(sm, &data->ssl, wpabuf_mhead(plain), - wpabuf_len(plain)); + encr = eap_server_tls_encrypt(sm, &data->ssl, plain); wpabuf_free(plain); - if (data->ssl.out_buf && piggyback) { + 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.out_buf), - (int) data->ssl.out_used); - if (wpabuf_resize(&data->ssl.out_buf, wpabuf_len(encr)) < 0) { + (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.out_buf, encr); + wpabuf_put_buf(data->ssl.tls_out, encr); wpabuf_free(encr); } else { - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = encr; + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = encr; } return 0; @@ -987,7 +986,7 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm, left = in_len - sizeof(*hdr); wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 type Nak'ed; " "allowed types", pos + 1, left - 1); -#ifdef EAP_TNC +#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 " @@ -996,7 +995,7 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm, eap_fast_phase2_init(sm, data, next_type); return; } -#endif /* EAP_TNC */ +#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 != @@ -1064,13 +1063,13 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm, eap_fast_state(data, CRYPTO_BINDING); data->eap_seq++; next_type = EAP_TYPE_NONE; -#ifdef EAP_TNC +#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_TNC */ +#endif /* EAP_SERVER_TNC */ break; case FAILURE: break; @@ -1121,7 +1120,7 @@ static void eap_fast_process_phase2_eap(struct eap_sm *sm, } -static int eap_fast_parse_tlvs(u8 *data, size_t data_len, +static int eap_fast_parse_tlvs(struct wpabuf *data, struct eap_fast_tlv_parse *tlv) { int mandatory, tlv_type, len, res; @@ -1129,8 +1128,8 @@ static int eap_fast_parse_tlvs(u8 *data, size_t data_len, os_memset(tlv, 0, sizeof(*tlv)); - pos = data; - end = data + data_len; + pos = wpabuf_mhead(data); + end = pos + wpabuf_len(data); while (pos + 4 < end) { mandatory = pos[0] & 0x80; tlv_type = WPA_GET_BE16(pos) & 0x3fff; @@ -1241,12 +1240,12 @@ static int eap_fast_pac_type(u8 *pac, size_t len, u16 type) static void eap_fast_process_phase2_tlvs(struct eap_sm *sm, struct eap_fast_data *data, - u8 *in_data, size_t in_len) + 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, in_len, &tlv) < 0) { + if (eap_fast_parse_tlvs(in_data, &tlv) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to parse received " "Phase 2 TLVs"); return; @@ -1373,70 +1372,44 @@ static void eap_fast_process_phase2(struct eap_sm *sm, struct eap_fast_data *data, struct wpabuf *in_buf) { - u8 *in_decrypted; - int len_decrypted; - size_t buf_len; - u8 *in_data; - size_t in_len; - - in_data = wpabuf_mhead(in_buf); - in_len = wpabuf_len(in_buf); + struct wpabuf *in_decrypted; wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" - " Phase 2", (unsigned long) in_len); + " 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, wpabuf_mhead(data->pending_phase2_resp), - wpabuf_len(data->pending_phase2_resp)); + eap_fast_process_phase2_tlvs(sm, data, + data->pending_phase2_resp); wpabuf_free(data->pending_phase2_resp); data->pending_phase2_resp = NULL; return; } - buf_len = in_len; - /* - * 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_len += 500; - buf_len *= 3; - in_decrypted = os_malloc(buf_len); + in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_buf); if (in_decrypted == NULL) { - wpa_printf(MSG_WARNING, "EAP-FAST: Failed to allocate memory " - "for decryption"); - return; - } - - len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, - in_decrypted, buf_len); - if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 " "data"); - os_free(in_decrypted); eap_fast_state(data, FAILURE); return; } - wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs", - in_decrypted, len_decrypted); + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs", + in_decrypted); - eap_fast_process_phase2_tlvs(sm, data, in_decrypted, len_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 = wpabuf_alloc_copy(in_decrypted, - len_decrypted); + data->pending_phase2_resp = in_decrypted; + return; } - os_free(in_decrypted); + wpabuf_free(in_decrypted); } @@ -1475,7 +1448,7 @@ static int eap_fast_process_phase1(struct eap_sm *sm, } if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || - wpabuf_len(data->ssl.out_buf) > 0) + wpabuf_len(data->ssl.tls_out) > 0) return 1; /* @@ -1541,7 +1514,7 @@ static void eap_fast_process_msg(struct eap_sm *sm, void *priv, case PHASE2_METHOD: case CRYPTO_BINDING: case REQUEST_PAC: - eap_fast_process_phase2(sm, data, data->ssl.in_buf); + eap_fast_process_phase2(sm, data, data->ssl.tls_in); break; default: wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected state %d in %s", diff --git a/contrib/hostapd/src/eap_server/eap_gpsk.c b/contrib/hostapd/src/eap_server/eap_server_gpsk.c similarity index 93% rename from contrib/hostapd/src/eap_server/eap_gpsk.c rename to contrib/hostapd/src/eap_server/eap_server_gpsk.c index d0c7559d75..66f4271583 100644 --- a/contrib/hostapd/src/eap_server/eap_gpsk.c +++ b/contrib/hostapd/src/eap_server/eap_server_gpsk.c @@ -2,19 +2,14 @@ * hostapd / EAP-GPSK (RFC 5433) server * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_gpsk_common.h" @@ -31,8 +26,6 @@ struct eap_gpsk_data { 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; @@ -76,11 +69,6 @@ static void * eap_gpsk_init(struct eap_sm *sm) 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)) { @@ -106,7 +94,6 @@ static void * eap_gpsk_init(struct eap_sm *sm) 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); } @@ -120,7 +107,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1"); - if (os_get_random(data->rand_server, EAP_GPSK_RAND_LEN)) { + if (random_get_bytes(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; @@ -128,7 +115,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, 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 + + len = 1 + 2 + sm->server_id_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); @@ -140,8 +127,8 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, } 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_be16(req, sm->server_id_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); wpabuf_put_be16(req, data->csuite_count * sizeof(struct eap_gpsk_csuite)); @@ -163,7 +150,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, 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 + + len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->server_id_len + sizeof(struct eap_gpsk_csuite) + 2 + miclen; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, EAP_CODE_REQUEST, id); @@ -179,8 +166,8 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, 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); + wpabuf_put_be16(req, sm->server_id_len); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); csuite = wpabuf_put(req, sizeof(*csuite)); WPA_PUT_BE32(csuite->vendor, data->vendor); WPA_PUT_BE16(csuite->specifier, data->specifier); @@ -306,8 +293,8 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, eap_gpsk_state(data, FAILURE); return; } - if (alen != data->id_server_len || - os_memcmp(pos, data->id_server, alen) != 0) { + if (alen != sm->server_id_len || + os_memcmp(pos, sm->server_id, alen) != 0) { wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and " "GPSK-2 did not match"); eap_gpsk_state(data, FAILURE); @@ -421,7 +408,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, data->vendor, data->specifier, data->rand_peer, data->rand_server, data->id_peer, data->id_peer_len, - data->id_server, data->id_server_len, + sm->server_id, sm->server_id_len, data->msk, data->emsk, data->sk, &data->sk_len, data->pk, &data->pk_len) < 0) { diff --git a/contrib/hostapd/src/eap_server/eap_gtc.c b/contrib/hostapd/src/eap_server/eap_server_gtc.c similarity index 92% rename from contrib/hostapd/src/eap_server/eap_gtc.c rename to contrib/hostapd/src/eap_server/eap_server_gtc.c index 97e328b83f..f423106bfc 100644 --- a/contrib/hostapd/src/eap_server/eap_gtc.c +++ b/contrib/hostapd/src/eap_server/eap_server_gtc.c @@ -2,14 +2,8 @@ * hostapd / EAP-GTC (RFC 3748) * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -33,14 +27,14 @@ static void * eap_gtc_init(struct eap_sm *sm) return NULL; data->state = CONTINUE; -#ifdef EAP_FAST +#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_FAST */ +#endif /* EAP_SERVER_FAST */ return data; } @@ -109,7 +103,7 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv, wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen); -#ifdef EAP_FAST +#ifdef EAP_SERVER_FAST if (data->prefix) { const u8 *pos2, *end; /* "RESPONSE=\0" */ @@ -170,7 +164,7 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv, "EAP-GTC: Response password", pos, rlen); } -#endif /* EAP_FAST */ +#endif /* EAP_SERVER_FAST */ if (sm->user == NULL || sm->user->password == NULL || sm->user->password_hash) { diff --git a/contrib/hostapd/src/eap_server/eap_identity.c b/contrib/hostapd/src/eap_server/eap_server_identity.c similarity index 91% rename from contrib/hostapd/src/eap_server/eap_identity.c rename to contrib/hostapd/src/eap_server/eap_server_identity.c index cd8da2a632..51dc4e8b4f 100644 --- a/contrib/hostapd/src/eap_server/eap_identity.c +++ b/contrib/hostapd/src/eap_server/eap_server_identity.c @@ -2,14 +2,8 @@ * hostapd / EAP-Identity * Copyright (c) 2004-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/hostapd/src/eap_server/eap_ikev2.c b/contrib/hostapd/src/eap_server/eap_server_ikev2.c similarity index 96% rename from contrib/hostapd/src/eap_server/eap_ikev2.c rename to contrib/hostapd/src/eap_server/eap_server_ikev2.c index 06074ee28f..1ada0c8a6d 100644 --- a/contrib/hostapd/src/eap_server/eap_ikev2.c +++ b/contrib/hostapd/src/eap_server/eap_server_ikev2.c @@ -2,14 +2,8 @@ * EAP-IKEv2 server (RFC 5106) * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -93,7 +87,8 @@ static void * eap_ikev2_init(struct eap_sm *sm) if (data == NULL) return NULL; data->state = MSG; - data->fragment_size = IKEV2_FRAGMENT_SIZE; + data->fragment_size = sm->fragment_size > 0 ? sm->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"); @@ -108,8 +103,11 @@ static void * eap_ikev2_init(struct eap_sm *sm) 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.IDi = os_malloc(sm->server_id_len); + if (data->ikev2.IDi == NULL) + goto failed; + os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len); + data->ikev2.IDi_len = sm->server_id_len; data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret; data->ikev2.cb_ctx = sm; diff --git a/contrib/hostapd/src/eap_server/eap_md5.c b/contrib/hostapd/src/eap_server/eap_server_md5.c similarity index 87% rename from contrib/hostapd/src/eap_server/eap_md5.c rename to contrib/hostapd/src/eap_server/eap_server_md5.c index dee2dc5a01..5a5e2907ef 100644 --- a/contrib/hostapd/src/eap_server/eap_md5.c +++ b/contrib/hostapd/src/eap_server/eap_server_md5.c @@ -1,20 +1,15 @@ /* * hostapd / EAP-MD5 server - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_i.h" #include "eap_common/chap.h" @@ -52,7 +47,7 @@ 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)) { + if (random_get_bytes(data->challenge, CHALLENGE_LEN)) { wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data"); data->state = FAILURE; return NULL; @@ -124,8 +119,12 @@ static void eap_md5_process(struct eap_sm *sm, void *priv, 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 (chap_md5(id, sm->user->password, sm->user->password_len, + data->challenge, CHALLENGE_LEN, hash)) { + wpa_printf(MSG_INFO, "EAP-MD5: CHAP MD5 operation failed"); + data->state = FAILURE; + return; + } if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success"); diff --git a/contrib/hostapd/src/eap_server/eap_methods.c b/contrib/hostapd/src/eap_server/eap_server_methods.c similarity index 50% rename from contrib/hostapd/src/eap_server/eap_methods.c rename to contrib/hostapd/src/eap_server/eap_server_methods.c index 4092d675b3..0209fad639 100644 --- a/contrib/hostapd/src/eap_server/eap_methods.c +++ b/contrib/hostapd/src/eap_server/eap_server_methods.c @@ -1,15 +1,9 @@ /* - * hostapd / EAP method registration - * Copyright (c) 2004-2006, Jouni Malinen + * EAP server method registration + * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -133,159 +127,6 @@ int eap_server_method_register(struct eap_method *method) } -/** - * eap_server_register_methods - Register statically linked EAP server methods - * Returns: 0 on success, -1 on failure - * - * This function is called at program initialization to register all EAP server - * methods that were linked in statically. - */ -int eap_server_register_methods(void) -{ - int ret = 0; - - if (ret == 0) { - int eap_server_identity_register(void); - ret = eap_server_identity_register(); - } - -#ifdef EAP_MD5 - if (ret == 0) { - int eap_server_md5_register(void); - ret = eap_server_md5_register(); - } -#endif /* EAP_MD5 */ - -#ifdef EAP_TLS - if (ret == 0) { - int eap_server_tls_register(void); - ret = eap_server_tls_register(); - } -#endif /* EAP_TLS */ - -#ifdef EAP_MSCHAPv2 - if (ret == 0) { - int eap_server_mschapv2_register(void); - ret = eap_server_mschapv2_register(); - } -#endif /* EAP_MSCHAPv2 */ - -#ifdef EAP_PEAP - if (ret == 0) { - int eap_server_peap_register(void); - ret = eap_server_peap_register(); - } -#endif /* EAP_PEAP */ - -#ifdef EAP_TLV - if (ret == 0) { - int eap_server_tlv_register(void); - ret = eap_server_tlv_register(); - } -#endif /* EAP_TLV */ - -#ifdef EAP_GTC - if (ret == 0) { - int eap_server_gtc_register(void); - ret = eap_server_gtc_register(); - } -#endif /* EAP_GTC */ - -#ifdef EAP_TTLS - if (ret == 0) { - int eap_server_ttls_register(void); - ret = eap_server_ttls_register(); - } -#endif /* EAP_TTLS */ - -#ifdef EAP_SIM - if (ret == 0) { - int eap_server_sim_register(void); - ret = eap_server_sim_register(); - } -#endif /* EAP_SIM */ - -#ifdef EAP_AKA - if (ret == 0) { - int eap_server_aka_register(void); - ret = eap_server_aka_register(); - } -#endif /* EAP_AKA */ - -#ifdef EAP_AKA_PRIME - if (ret == 0) { - int eap_server_aka_prime_register(void); - ret = eap_server_aka_prime_register(); - } -#endif /* EAP_AKA_PRIME */ - -#ifdef EAP_PAX - if (ret == 0) { - int eap_server_pax_register(void); - ret = eap_server_pax_register(); - } -#endif /* EAP_PAX */ - -#ifdef EAP_PSK - if (ret == 0) { - int eap_server_psk_register(void); - ret = eap_server_psk_register(); - } -#endif /* EAP_PSK */ - -#ifdef EAP_SAKE - if (ret == 0) { - int eap_server_sake_register(void); - ret = eap_server_sake_register(); - } -#endif /* EAP_SAKE */ - -#ifdef EAP_GPSK - if (ret == 0) { - int eap_server_gpsk_register(void); - ret = eap_server_gpsk_register(); - } -#endif /* EAP_GPSK */ - -#ifdef EAP_VENDOR_TEST - if (ret == 0) { - int eap_server_vendor_test_register(void); - ret = eap_server_vendor_test_register(); - } -#endif /* EAP_VENDOR_TEST */ - -#ifdef EAP_FAST - if (ret == 0) { - int eap_server_fast_register(void); - ret = eap_server_fast_register(); - } -#endif /* EAP_FAST */ - -#ifdef EAP_WSC - if (ret == 0) { - int eap_server_wsc_register(void); - ret = eap_server_wsc_register(); - } -#endif /* EAP_WSC */ - -#ifdef EAP_IKEV2 - if (ret == 0) { - int eap_server_ikev2_register(void); - ret = eap_server_ikev2_register(); - } -#endif /* EAP_IKEV2 */ - -#ifdef EAP_TNC - if (ret == 0) { - int eap_server_tnc_register(void); - ret = eap_server_tnc_register(); - } -#endif /* EAP_TNC */ - - return ret; -} - - /** * eap_server_unregister_methods - Unregister EAP server methods * @@ -306,3 +147,25 @@ void eap_server_unregister_methods(void) 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; + if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_EXPANDED) + return "expanded"; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == type) + return m->name; + } + return NULL; +} diff --git a/contrib/hostapd/src/eap_server/eap_mschapv2.c b/contrib/hostapd/src/eap_server/eap_server_mschapv2.c similarity index 93% rename from contrib/hostapd/src/eap_server/eap_mschapv2.c rename to contrib/hostapd/src/eap_server/eap_server_mschapv2.c index 20e7adee6f..3153d2ecfb 100644 --- a/contrib/hostapd/src/eap_server/eap_mschapv2.c +++ b/contrib/hostapd/src/eap_server/eap_server_mschapv2.c @@ -2,21 +2,16 @@ * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/ms_funcs.h" +#include "crypto/random.h" #include "eap_i.h" -#include "ms_funcs.h" struct eap_mschapv2_hdr { @@ -105,18 +100,17 @@ static struct wpabuf * eap_mschapv2_build_challenge( { 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)) { + random_get_bytes(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); + ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, EAP_CODE_REQUEST, id); if (req == NULL) { @@ -138,7 +132,7 @@ static struct wpabuf * eap_mschapv2_build_challenge( 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)); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); return req; } @@ -295,6 +289,7 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, 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); @@ -372,17 +367,22 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, username, username_len); if (sm->user->password_hash) { - generate_nt_response_pwhash(data->auth_challenge, - peer_challenge, - username, username_len, - sm->user->password, - expected); + res = generate_nt_response_pwhash(data->auth_challenge, + peer_challenge, + username, username_len, + sm->user->password, + expected); } else { - generate_nt_response(data->auth_challenge, peer_challenge, - username, username_len, - sm->user->password, - sm->user->password_len, - expected); + 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) { @@ -398,9 +398,12 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, if (sm->user->password_hash) { pw_hash = sm->user->password; } else { - nt_password_hash(sm->user->password, - sm->user->password_len, - pw_hash_buf); + if (nt_password_hash(sm->user->password, + sm->user->password_len, + pw_hash_buf) < 0) { + data->state = FAILURE; + return; + } pw_hash = pw_hash_buf; } generate_authenticator_response_pwhash( diff --git a/contrib/hostapd/src/eap_server/eap_pax.c b/contrib/hostapd/src/eap_server/eap_server_pax.c similarity index 97% rename from contrib/hostapd/src/eap_server/eap_pax.c rename to contrib/hostapd/src/eap_server/eap_server_pax.c index 1dc023b699..35a42ad107 100644 --- a/contrib/hostapd/src/eap_server/eap_pax.c +++ b/contrib/hostapd/src/eap_server/eap_server_pax.c @@ -2,19 +2,14 @@ * hostapd / EAP-PAX (RFC 4746) server * Copyright (c) 2005-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pax_common.h" @@ -82,7 +77,7 @@ static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); - if (os_get_random(data->rand.r.x, EAP_PAX_RAND_LEN)) { + if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); data->state = FAILURE; return NULL; diff --git a/contrib/hostapd/src/eap_server/eap_peap.c b/contrib/hostapd/src/eap_server/eap_server_peap.c similarity index 81% rename from contrib/hostapd/src/eap_server/eap_peap.c rename to contrib/hostapd/src/eap_server/eap_server_peap.c index 4b2d5a5c83..defcb3c054 100644 --- a/contrib/hostapd/src/eap_server/eap_peap.c +++ b/contrib/hostapd/src/eap_server/eap_server_peap.c @@ -2,32 +2,26 @@ * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "sha1.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" +#include "crypto/random.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 "tls.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 @@ -104,33 +98,6 @@ static void eap_peap_state(struct eap_peap_data *data, int 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) { @@ -235,7 +202,7 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, struct eap_peap_data *data, u8 id) { - struct wpabuf *buf, *encr_req; + struct wpabuf *buf, *encr_req, msgbuf; const u8 *req; size_t req_len; @@ -244,8 +211,6 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, 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; @@ -260,19 +225,20 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, req_len -= sizeof(struct eap_hdr); } - encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); + wpabuf_set(&msgbuf, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); wpabuf_free(buf); return encr_req; } -#ifdef EAP_TNC +#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; + struct wpabuf *buf1, *buf, *encr_req, msgbuf; const u8 *req; size_t req_len; @@ -297,13 +263,14 @@ static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm, 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, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); wpabuf_free(buf); return encr_req; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ static void eap_peap_get_isk(struct eap_peap_data *data, @@ -348,8 +315,12 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) * 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)); + if (peap_prfplus(data->peap_version, tk, 40, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)) < 0) { + os_free(tk); + return -1; + } wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)", imck, sizeof(imck)); @@ -370,17 +341,17 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, u8 id) { struct wpabuf *buf, *encr_req; - size_t len; + size_t mlen; - len = 6; /* Result TLV */ + mlen = 6; /* Result TLV */ if (data->crypto_binding != NO_BINDING) - len += 60; /* Cryptobinding TLV */ -#ifdef EAP_TNC + mlen += 60; /* Cryptobinding TLV */ +#ifdef EAP_SERVER_TNC if (data->soh_response) - len += wpabuf_len(data->soh_response); -#endif /* EAP_TNC */ + mlen += wpabuf_len(data->soh_response); +#endif /* EAP_SERVER_TNC */ - buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, + buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen, EAP_CODE_REQUEST, id); if (buf == NULL) return NULL; @@ -401,7 +372,7 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, size_t len[2]; u16 tlv_type; -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC if (data->soh_response) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH " "Response TLV"); @@ -409,10 +380,10 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, wpabuf_free(data->soh_response); data->soh_response = NULL; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ if (eap_peap_derive_cmk(sm, data) < 0 || - os_get_random(data->binding_nonce, 32)) { + random_get_bytes(data->binding_nonce, 32)) { wpabuf_free(buf); return NULL; } @@ -424,8 +395,6 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, 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); @@ -450,8 +419,7 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data", buf); - encr_req = eap_server_tls_encrypt(sm, &data->ssl, wpabuf_head(buf), - wpabuf_len(buf)); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf); wpabuf_free(buf); return encr_req; @@ -462,7 +430,7 @@ 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; + struct wpabuf *encr_req, msgbuf; size_t req_len; struct eap_hdr *hdr; @@ -478,7 +446,8 @@ static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm, wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", (u8 *) hdr, req_len); - encr_req = eap_server_tls_encrypt(sm, &data->ssl, (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; @@ -504,8 +473,7 @@ static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id) 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)) { + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, " "starting Phase2"); eap_peap_state(data, PHASE2_START); @@ -513,32 +481,32 @@ static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id) break; case PHASE2_ID: case PHASE2_METHOD: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_peap_build_phase2_req(sm, data, id); + 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_TNC +#ifdef EAP_SERVER_TNC case PHASE2_SOH: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_peap_build_phase2_soh(sm, data, id); + 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_TNC */ +#endif /* EAP_SERVER_TNC */ case PHASE2_TLV: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_peap_build_phase2_tlv(sm, data, id); + 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.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_peap_build_phase2_term(sm, data, id, + 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.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_peap_build_phase2_term(sm, data, id, + 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: @@ -757,7 +725,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, } -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC static void eap_peap_process_phase2_soh(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *in_data) @@ -885,7 +853,7 @@ auth_method: wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); eap_peap_phase2_init(sm, data, next_type); } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ static void eap_peap_process_phase2_response(struct eap_sm *sm, @@ -902,12 +870,12 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, return; } -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC if (data->state == PHASE2_SOH) { eap_peap_process_phase2_soh(sm, data, in_data); return; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ if (data->phase2_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not " @@ -991,7 +959,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, break; } -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC if (data->state != PHASE2_SOH && sm->tnc && data->peap_version == 0) { eap_peap_state(data, PHASE2_SOH); @@ -1000,7 +968,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, next_type = EAP_TYPE_NONE; break; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ eap_peap_state(data, PHASE2_METHOD); next_type = sm->user->methods[0].method; @@ -1029,17 +997,11 @@ static void eap_peap_process_phase2(struct eap_sm *sm, struct wpabuf *in_buf) { struct wpabuf *in_decrypted; - int len_decrypted; const struct eap_hdr *hdr; - size_t buf_len, len; - u8 *in_data; - size_t in_len; - - in_data = wpabuf_mhead(in_buf); - in_len = wpabuf_len(in_buf); + size_t len; wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" - " Phase 2", (unsigned long) in_len); + " Phase 2", (unsigned long) wpabuf_len(in_buf)); if (data->pending_phase2_resp) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " @@ -1051,40 +1013,18 @@ static void eap_peap_process_phase2(struct eap_sm *sm, return; } - buf_len = in_len; - /* - * 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_len += 500; - buf_len *= 3; - in_decrypted = wpabuf_alloc(buf_len); + in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_buf); if (in_decrypted == NULL) { - wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory " - "for decryption"); - return; - } - - len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, - wpabuf_mhead(in_decrypted), - buf_len); - if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " "data"); - wpabuf_free(in_decrypted); eap_peap_state(data, FAILURE); return; } - wpabuf_put(in_decrypted, len_decrypted); 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; @@ -1106,47 +1046,6 @@ static void eap_peap_process_phase2(struct eap_sm *sm, 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); @@ -1191,65 +1090,7 @@ static void eap_peap_process_phase2(struct eap_sm *sm, break; } - os_free(in_decrypted); -} - - -static int eap_peapv2_start_phase2(struct eap_sm *sm, - struct eap_peap_data *data) -{ - struct wpabuf *buf, *buf2; - int res; - - 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 = wpabuf_alloc(data->ssl.tls_out_limit); - if (buf == NULL) { - wpabuf_free(buf2); - return -1; - } - - res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - wpabuf_head(buf2), wpabuf_len(buf2), - wpabuf_put(buf, 0), - data->ssl.tls_out_limit); - wpabuf_free(buf2); - - if (res < 0) { - wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 " - "data"); - wpabuf_free(buf); - return -1; - } - - wpabuf_put(buf, res); - 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.out_buf, wpabuf_len(buf)) < 0) { - wpabuf_free(buf); - return -1; - } - wpabuf_put_buf(data->ssl.out_buf, buf); - wpabuf_free(buf); - - return 0; + wpabuf_free(in_decrypted); } @@ -1287,14 +1128,6 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv, 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); @@ -1305,7 +1138,7 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv, case PHASE2_METHOD: case PHASE2_SOH: case PHASE2_TLV: - eap_peap_process_phase2(sm, data, respData, data->ssl.in_buf); + eap_peap_process_phase2(sm, data, respData, data->ssl.tls_in); break; case SUCCESS_REQ: eap_peap_state(data, SUCCESS); @@ -1354,9 +1187,10 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) * 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)); + if (peap_prfplus(data->peap_version, data->ipmk, 40, + "Session Key Generating Function", + (u8 *) "\00", 1, csk, sizeof(csk)) < 0) + return NULL; wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk)); eapKeyData = os_malloc(EAP_TLS_KEY_LEN); if (eapKeyData) { diff --git a/contrib/hostapd/src/eap_server/eap_psk.c b/contrib/hostapd/src/eap_server/eap_server_psk.c similarity index 92% rename from contrib/hostapd/src/eap_server/eap_psk.c rename to contrib/hostapd/src/eap_server/eap_server_psk.c index c68d4c34d4..46bedd94bb 100644 --- a/contrib/hostapd/src/eap_server/eap_psk.c +++ b/contrib/hostapd/src/eap_server/eap_server_psk.c @@ -2,14 +2,8 @@ * hostapd / EAP-PSK (RFC 4764) server * Copyright (c) 2005-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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. @@ -18,17 +12,18 @@ #include "includes.h" #include "common.h" -#include "eap_server/eap_i.h" -#include "aes_wrap.h" +#include "crypto/aes_wrap.h" +#include "crypto/random.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 *id_p; + size_t id_p_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]; @@ -43,8 +38,6 @@ static void * eap_psk_init(struct eap_sm *sm) if (data == NULL) return NULL; data->state = PSK_1; - data->id_s = (u8 *) "hostapd"; - data->id_s_len = 7; return data; } @@ -66,7 +59,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)"); - if (os_get_random(data->rand_s, EAP_PSK_RAND_LEN)) { + if (random_get_bytes(data->rand_s, EAP_PSK_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); data->state = FAILURE; return NULL; @@ -75,7 +68,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, data->rand_s, EAP_PSK_RAND_LEN); req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, - sizeof(*psk) + data->id_s_len, + sizeof(*psk) + sm->server_id_len, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " @@ -87,7 +80,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, 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); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); return req; } @@ -117,15 +110,17 @@ static struct wpabuf * eap_psk_build_3(struct eap_sm *sm, 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; + buflen = sm->server_id_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)) + os_memcpy(buf, sm->server_id, sm->server_id_len); + os_memcpy(buf + sm->server_id_len, data->rand_p, EAP_PSK_RAND_LEN); + if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) { + os_free(buf); goto fail; + } os_free(buf); if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk, @@ -299,7 +294,7 @@ static void eap_psk_process_2(struct eap_sm *sm, 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; + buflen = data->id_p_len + sm->server_id_len + 2 * EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) { data->state = FAILURE; @@ -307,8 +302,8 @@ static void eap_psk_process_2(struct eap_sm *sm, } 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, sm->server_id, sm->server_id_len); + pos += sm->server_id_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); diff --git a/contrib/hostapd/src/eap_server/eap_server_pwd.c b/contrib/hostapd/src/eap_server/eap_server_pwd.c new file mode 100644 index 0000000000..b61061bce7 --- /dev/null +++ b/contrib/hostapd/src/eap_server/eap_server_pwd.c @@ -0,0 +1,1045 @@ +/* + * hostapd / EAP-pwd (RFC 5931) server + * Copyright (c) 2010, Dan Harkins + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/sha256.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_pwd_common.h" + + +struct eap_pwd_data { + enum { + PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE + } state; + u8 *id_peer; + size_t id_peer_len; + u8 *id_server; + size_t id_server_len; + u8 *password; + size_t password_len; + u32 token; + u16 group_num; + EAP_PWD_group *grp; + + struct wpabuf *inbuf; + size_t in_frag_pos; + struct wpabuf *outbuf; + size_t out_frag_pos; + size_t mtu; + + BIGNUM *k; + BIGNUM *private_value; + BIGNUM *peer_scalar; + BIGNUM *my_scalar; + EC_POINT *my_element; + EC_POINT *peer_element; + + u8 my_confirm[SHA256_MAC_LEN]; + + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + + BN_CTX *bnctx; +}; + + +static const char * eap_pwd_state_txt(int state) +{ + switch (state) { + case PWD_ID_Req: + return "PWD-ID-Req"; + case PWD_Commit_Req: + return "PWD-Commit-Req"; + case PWD_Confirm_Req: + return "PWD-Confirm-Req"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "PWD-Unk"; + } +} + + +static void eap_pwd_state(struct eap_pwd_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s", + eap_pwd_state_txt(data->state), eap_pwd_state_txt(state)); + data->state = state; +} + + +static void * eap_pwd_init(struct eap_sm *sm) +{ + struct eap_pwd_data *data; + + if (sm->user == NULL || sm->user->password == NULL || + sm->user->password_len == 0) { + wpa_printf(MSG_INFO, "EAP-PWD (server): Password is not " + "configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->group_num = sm->pwd_group; + wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d", + data->group_num); + data->state = PWD_ID_Req; + + data->id_server = (u8 *) os_strdup("server"); + if (data->id_server) + data->id_server_len = os_strlen((char *) data->id_server); + + data->password = os_malloc(sm->user->password_len); + if (data->password == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password " + "fail"); + os_free(data->id_server); + os_free(data); + return NULL; + } + data->password_len = sm->user->password_len; + os_memcpy(data->password, sm->user->password, data->password_len); + + data->bnctx = BN_CTX_new(); + if (data->bnctx == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); + os_free(data->password); + os_free(data->id_server); + os_free(data); + return NULL; + } + + data->in_frag_pos = data->out_frag_pos = 0; + data->inbuf = data->outbuf = NULL; + data->mtu = 1020; /* default from RFC 5931, make it configurable! */ + + return data; +} + + +static void eap_pwd_reset(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + + BN_free(data->private_value); + BN_free(data->peer_scalar); + BN_free(data->my_scalar); + BN_free(data->k); + BN_CTX_free(data->bnctx); + EC_POINT_free(data->my_element); + EC_POINT_free(data->peer_element); + os_free(data->id_peer); + os_free(data->id_server); + os_free(data->password); + if (data->grp) { + EC_GROUP_free(data->grp->group); + EC_POINT_free(data->grp->pwe); + BN_free(data->grp->order); + BN_free(data->grp->prime); + os_free(data->grp); + } + os_free(data); +} + + +static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, + u8 id) +{ + wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request"); + /* + * if we're fragmenting then we already have an id request, just return + */ + if (data->out_frag_pos) + return; + + data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + + data->id_server_len); + if (data->outbuf == NULL) { + eap_pwd_state(data, FAILURE); + return; + } + + /* an lfsr is good enough to generate unpredictable tokens */ + data->token = os_random(); + wpabuf_put_be16(data->outbuf, data->group_num); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); + wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token)); + wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); + wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); +} + + +static void eap_pwd_build_commit_req(struct eap_sm *sm, + struct eap_pwd_data *data, u8 id) +{ + BIGNUM *mask = NULL, *x = NULL, *y = NULL; + u8 *scalar = NULL, *element = NULL; + u16 offset; + + wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request"); + /* + * if we're fragmenting then we already have an commit request, just + * return + */ + if (data->out_frag_pos) + return; + + if (((data->private_value = BN_new()) == NULL) || + ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || + ((data->my_scalar = BN_new()) == NULL) || + ((mask = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation " + "fail"); + goto fin; + } + + BN_rand_range(data->private_value, data->grp->order); + BN_rand_range(mask, data->grp->order); + BN_add(data->my_scalar, data->private_value, mask); + BN_mod(data->my_scalar, data->my_scalar, data->grp->order, + data->bnctx); + + if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, + data->grp->pwe, mask, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation " + "fail"); + eap_pwd_state(data, FAILURE); + goto fin; + } + + if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) + { + wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion " + "fail"); + goto fin; + } + BN_free(mask); + + if (((x = BN_new()) == NULL) || + ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation " + "fail"); + goto fin; + } + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " + "fail"); + goto fin; + } + + if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || + ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); + goto fin; + } + + /* + * bignums occupy as little memory as possible so one that is + * sufficiently smaller than the prime or order might need pre-pending + * with zeros. + */ + os_memset(scalar, 0, BN_num_bytes(data->grp->order)); + os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, scalar + offset); + + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, element + offset); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); + + data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) + + BN_num_bytes(data->grp->order)); + if (data->outbuf == NULL) + goto fin; + + /* We send the element as (x,y) followed by the scalar */ + wpabuf_put_data(data->outbuf, element, + 2 * BN_num_bytes(data->grp->prime)); + wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); + +fin: + os_free(scalar); + os_free(element); + BN_free(x); + BN_free(y); + if (data->outbuf == NULL) + eap_pwd_state(data, FAILURE); +} + + +static void eap_pwd_build_confirm_req(struct eap_sm *sm, + struct eap_pwd_data *data, u8 id) +{ + BIGNUM *x = NULL, *y = NULL; + struct crypto_hash *hash; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; + u16 grp; + int offset; + + wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request"); + /* + * if we're fragmenting then we already have an confirm request, just + * return + */ + if (data->out_frag_pos) + return; + + /* Each component of the cruft will be at most as big as the prime */ + if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || + ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation " + "fail"); + goto fin; + } + + /* + * commit is H(k | server_element | server_scalar | peer_element | + * peer_scalar | ciphersuite) + */ + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; + + /* + * Zero the memory each time because this is mod prime math and some + * value may start with a few zeros and the previous one did not. + * + * First is k + */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); + BN_bn2bin(data->k, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* server element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* server scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* peer element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->peer_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* peer scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->peer_scalar); + BN_bn2bin(data->peer_scalar, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* ciphersuite */ + grp = htons(data->group_num); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + ptr = cruft; + os_memcpy(ptr, &grp, sizeof(u16)); + ptr += sizeof(u16); + *ptr = EAP_PWD_DEFAULT_RAND_FUNC; + ptr += sizeof(u8); + *ptr = EAP_PWD_DEFAULT_PRF; + ptr += sizeof(u8); + eap_pwd_h_update(hash, cruft, ptr - cruft); + + /* all done with the random function */ + eap_pwd_h_final(hash, conf); + os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN); + + data->outbuf = wpabuf_alloc(SHA256_MAC_LEN); + if (data->outbuf == NULL) + goto fin; + + wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); + +fin: + os_free(cruft); + BN_free(x); + BN_free(y); + if (data->outbuf == NULL) + eap_pwd_state(data, FAILURE); +} + + +static struct wpabuf * +eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_pwd_data *data = priv; + struct wpabuf *req; + u8 lm_exch; + const u8 *buf; + u16 totlen = 0; + size_t len; + + /* + * if we're buffering response fragments then just ACK + */ + if (data->in_frag_pos) { + wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a fragment!!"); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE, EAP_CODE_REQUEST, id); + if (req == NULL) { + eap_pwd_state(data, FAILURE); + return NULL; + } + switch (data->state) { + case PWD_ID_Req: + wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH); + break; + case PWD_Commit_Req: + wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH); + break; + case PWD_Confirm_Req: + wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH); + break; + default: + eap_pwd_state(data, FAILURE); /* just to be sure */ + wpabuf_free(req); + return NULL; + } + return req; + } + + /* + * build the data portion of a request + */ + switch (data->state) { + case PWD_ID_Req: + eap_pwd_build_id_req(sm, data, id); + lm_exch = EAP_PWD_OPCODE_ID_EXCH; + break; + case PWD_Commit_Req: + eap_pwd_build_commit_req(sm, data, id); + lm_exch = EAP_PWD_OPCODE_COMMIT_EXCH; + break; + case PWD_Confirm_Req: + eap_pwd_build_confirm_req(sm, data, id); + lm_exch = EAP_PWD_OPCODE_CONFIRM_EXCH; + break; + default: + wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req", + data->state); + eap_pwd_state(data, FAILURE); + lm_exch = 0; /* hush now, sweet compiler */ + break; + } + + if (data->state == FAILURE) + return NULL; + + /* + * determine whether that data needs to be fragmented + */ + len = wpabuf_len(data->outbuf) - data->out_frag_pos; + if ((len + EAP_PWD_HDR_SIZE) > data->mtu) { + len = data->mtu - EAP_PWD_HDR_SIZE; + EAP_PWD_SET_MORE_BIT(lm_exch); + /* + * if this is the first fragment, need to set the M bit + * and add the total length to the eap_pwd_hdr + */ + if (data->out_frag_pos == 0) { + EAP_PWD_SET_LENGTH_BIT(lm_exch); + totlen = wpabuf_len(data->outbuf) + + EAP_PWD_HDR_SIZE + sizeof(u16); + len -= sizeof(u16); + wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, " + "total length = %d", totlen); + } + wpa_printf(MSG_DEBUG, "EAP-pwd: Send a %d byte fragment", + (int) len); + } + + /* + * alloc an eap request and populate it with the data + */ + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, + EAP_PWD_HDR_SIZE + len + + (totlen ? sizeof(u16) : 0), + EAP_CODE_REQUEST, id); + if (req == NULL) { + eap_pwd_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, lm_exch); + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) + wpabuf_put_be16(req, totlen); + + buf = wpabuf_head_u8(data->outbuf); + wpabuf_put_data(req, buf + data->out_frag_pos, len); + data->out_frag_pos += len; + /* + * either not fragged or last fragment, either way free up the data + */ + if (data->out_frag_pos >= wpabuf_len(data->outbuf)) { + wpabuf_free(data->outbuf); + data->out_frag_pos = 0; + } + + return req; +} + + +static Boolean eap_pwd_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_pwd_data *data = priv; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid frame"); + return TRUE; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: exch = %d, len = %d", + EAP_PWD_GET_EXCHANGE(*pos), (int) len); + + if (data->state == PWD_ID_Req && + ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_ID_EXCH)) + return FALSE; + + if (data->state == PWD_Commit_Req && + ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_COMMIT_EXCH)) + return FALSE; + + if (data->state == PWD_Confirm_Req && + ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_CONFIRM_EXCH)) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d", + *pos, data->state); + + return TRUE; +} + + +static void eap_pwd_process_id_resp(struct eap_sm *sm, + struct eap_pwd_data *data, + const u8 *payload, size_t payload_len) +{ + struct eap_pwd_id *id; + + if (payload_len < sizeof(struct eap_pwd_id)) { + wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response"); + return; + } + + id = (struct eap_pwd_id *) payload; + if ((data->group_num != be_to_host16(id->group_num)) || + (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || + (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) || + (id->prf != EAP_PWD_DEFAULT_PRF)) { + wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters"); + eap_pwd_state(data, FAILURE); + return; + } + data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id)); + if (data->id_peer == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); + return; + } + data->id_peer_len = payload_len - sizeof(struct eap_pwd_id); + os_memcpy(data->id_peer, id->identity, data->id_peer_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of", + data->id_peer, data->id_peer_len); + + if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) { + wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " + "group"); + return; + } + if (compute_password_element(data->grp, data->group_num, + data->password, data->password_len, + data->id_server, data->id_server_len, + data->id_peer, data->id_peer_len, + (u8 *) &data->token)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute " + "PWE"); + return; + } + wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...", + BN_num_bits(data->grp->prime)); + + eap_pwd_state(data, PWD_Commit_Req); +} + + +static void +eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, + const u8 *payload, size_t payload_len) +{ + u8 *ptr; + BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; + EC_POINT *K = NULL, *point = NULL; + int res = 0; + + wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response"); + + if (((data->peer_scalar = BN_new()) == NULL) || + ((data->k = BN_new()) == NULL) || + ((cofactor = BN_new()) == NULL) || + ((x = BN_new()) == NULL) || + ((y = BN_new()) == NULL) || + ((point = EC_POINT_new(data->grp->group)) == NULL) || + ((K = EC_POINT_new(data->grp->group)) == NULL) || + ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " + "fail"); + goto fin; + } + + if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get " + "cofactor for curve"); + goto fin; + } + + /* element, x then y, followed by scalar */ + ptr = (u8 *) payload; + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); + ptr += BN_num_bytes(data->grp->prime); + BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar); + if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, + data->peer_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element " + "fail"); + goto fin; + } + + /* check to ensure peer's element is not in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, point, NULL, + data->peer_element, cofactor, NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " + "multiply peer element by order"); + goto fin; + } + if (EC_POINT_is_at_infinity(data->grp->group, point)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): peer element " + "is at infinity!\n"); + goto fin; + } + } + + /* compute the shared key, k */ + if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, + data->peer_scalar, data->bnctx)) || + (!EC_POINT_add(data->grp->group, K, K, data->peer_element, + data->bnctx)) || + (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, + data->bnctx))) { + wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key " + "fail"); + goto fin; + } + + /* ensure that the shared key isn't in a small sub-group */ + if (BN_cmp(cofactor, BN_value_one())) { + if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, + NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " + "multiply shared key point by order!\n"); + goto fin; + } + } + + /* + * This check is strictly speaking just for the case above where + * co-factor > 1 but it was suggested that even though this is probably + * never going to happen it is a simple and safe check "just to be + * sure" so let's be safe. + */ + if (EC_POINT_is_at_infinity(data->grp->group, K)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is " + "at infinity"); + goto fin; + } + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, + NULL, data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract " + "shared secret from secret point"); + goto fin; + } + res = 1; + +fin: + EC_POINT_free(K); + EC_POINT_free(point); + BN_free(cofactor); + BN_free(x); + BN_free(y); + + if (res) + eap_pwd_state(data, PWD_Confirm_Req); + else + eap_pwd_state(data, FAILURE); +} + + +static void +eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, + const u8 *payload, size_t payload_len) +{ + BIGNUM *x = NULL, *y = NULL; + struct crypto_hash *hash; + u32 cs; + u16 grp; + u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; + int offset; + + /* build up the ciphersuite: group | random_function | prf */ + grp = htons(data->group_num); + ptr = (u8 *) &cs; + os_memcpy(ptr, &grp, sizeof(u16)); + ptr += sizeof(u16); + *ptr = EAP_PWD_DEFAULT_RAND_FUNC; + ptr += sizeof(u8); + *ptr = EAP_PWD_DEFAULT_PRF; + + /* each component of the cruft will be at most as big as the prime */ + if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || + ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail"); + goto fin; + } + + /* + * commit is H(k | peer_element | peer_scalar | server_element | + * server_scalar | ciphersuite) + */ + hash = eap_pwd_h_init(); + if (hash == NULL) + goto fin; + + /* k */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); + BN_bn2bin(data->k, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* peer element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->peer_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* peer scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->peer_scalar); + BN_bn2bin(data->peer_scalar, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* server element: x, y */ + if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, + data->my_element, x, y, + data->bnctx)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " + "assignment fail"); + goto fin; + } + + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); + BN_bn2bin(x, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); + BN_bn2bin(y, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + + /* server scalar */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + offset = BN_num_bytes(data->grp->order) - + BN_num_bytes(data->my_scalar); + BN_bn2bin(data->my_scalar, cruft + offset); + eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + + /* ciphersuite */ + os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); + + /* all done */ + eap_pwd_h_final(hash, conf); + + ptr = (u8 *) payload; + if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) { + wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not " + "verify"); + goto fin; + } + + wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified"); + if (compute_keys(data->grp, data->bnctx, data->k, + data->peer_scalar, data->my_scalar, conf, + data->my_confirm, &cs, data->msk, data->emsk) < 0) + eap_pwd_state(data, FAILURE); + else + eap_pwd_state(data, SUCCESS); + +fin: + os_free(cruft); + BN_free(x); + BN_free(y); +} + + +static void eap_pwd_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_pwd_data *data = priv; + const u8 *pos; + size_t len; + u8 lm_exch; + u16 tot_len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len); + if ((pos == NULL) || (len < 1)) { + wpa_printf(MSG_INFO, "Bad EAP header! pos %s and len = %d", + (pos == NULL) ? "is NULL" : "is not NULL", + (int) len); + return; + } + + lm_exch = *pos; + pos++; /* skip over the bits and the exch */ + len--; + + /* + * if we're fragmenting then this should be an ACK with no data, + * just return and continue fragmenting in the "build" section above + */ + if (data->out_frag_pos) { + if (len > 1) + wpa_printf(MSG_INFO, "EAP-pwd: Bad response! " + "Fragmenting but not an ACK"); + else + wpa_printf(MSG_DEBUG, "EAP-pwd: received ACK from " + "peer"); + return; + } + /* + * if we're receiving fragmented packets then we need to buffer... + * + * the first fragment has a total length + */ + if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) { + tot_len = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total " + "length = %d", tot_len); + data->inbuf = wpabuf_alloc(tot_len); + if (data->inbuf == NULL) { + wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to " + "buffer fragments!"); + return; + } + pos += sizeof(u16); + len -= sizeof(u16); + } + /* + * the first and all intermediate fragments have the M bit set + */ + if (EAP_PWD_GET_MORE_BIT(lm_exch)) { + if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow " + "attack detected! (%d+%d > %d)", + (int) data->in_frag_pos, (int) len, + (int) wpabuf_size(data->inbuf)); + eap_pwd_state(data, FAILURE); + return; + } + wpabuf_put_data(data->inbuf, pos, len); + data->in_frag_pos += len; + wpa_printf(MSG_DEBUG, "EAP-pwd: Got a %d byte fragment", + (int) len); + return; + } + /* + * last fragment won't have the M bit set (but we're obviously + * buffering fragments so that's how we know it's the last) + */ + if (data->in_frag_pos) { + wpabuf_put_data(data->inbuf, pos, len); + data->in_frag_pos += len; + pos = wpabuf_head_u8(data->inbuf); + len = data->in_frag_pos; + wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes", + (int) len); + } + switch (EAP_PWD_GET_EXCHANGE(lm_exch)) { + case EAP_PWD_OPCODE_ID_EXCH: + eap_pwd_process_id_resp(sm, data, pos, len); + break; + case EAP_PWD_OPCODE_COMMIT_EXCH: + eap_pwd_process_commit_resp(sm, data, pos, len); + break; + case EAP_PWD_OPCODE_CONFIRM_EXCH: + eap_pwd_process_confirm_resp(sm, data, pos, len); + break; + } + /* + * if we had been buffering fragments, here's a great place + * to clean up + */ + if (data->in_frag_pos) { + wpabuf_free(data->inbuf); + data->in_frag_pos = 0; + } +} + + +static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_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_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_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_pwd_is_success(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + return data->state == SUCCESS; +} + + +static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv) +{ + struct eap_pwd_data *data = priv; + return (data->state == SUCCESS) || (data->state == FAILURE); +} + + +int eap_server_pwd_register(void) +{ + struct eap_method *eap; + int ret; + struct timeval tp; + struct timezone tz; + u32 sr; + + EVP_add_digest(EVP_sha256()); + + sr = 0xdeaddada; + (void) gettimeofday(&tp, &tz); + sr ^= (tp.tv_sec ^ tp.tv_usec); + srandom(sr); + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PWD, + "PWD"); + if (eap == NULL) + return -1; + + eap->init = eap_pwd_init; + eap->reset = eap_pwd_reset; + eap->buildReq = eap_pwd_build_req; + eap->check = eap_pwd_check; + eap->process = eap_pwd_process; + eap->isDone = eap_pwd_is_done; + eap->getKey = eap_pwd_getkey; + eap->get_emsk = eap_pwd_get_emsk; + eap->isSuccess = eap_pwd_is_success; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} + diff --git a/contrib/hostapd/src/eap_server/eap_sake.c b/contrib/hostapd/src/eap_server/eap_server_sake.c similarity index 90% rename from contrib/hostapd/src/eap_server/eap_sake.c rename to contrib/hostapd/src/eap_server/eap_server_sake.c index ce4848f85e..68dd76b18e 100644 --- a/contrib/hostapd/src/eap_server/eap_sake.c +++ b/contrib/hostapd/src/eap_server/eap_server_sake.c @@ -2,19 +2,14 @@ * hostapd / EAP-SAKE (RFC 4763) server * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_sake_common.h" @@ -32,8 +27,6 @@ struct eap_sake_data { u8 session_id; u8 *peerid; size_t peerid_len; - u8 *serverid; - size_t serverid_len; }; @@ -82,11 +75,6 @@ static void * eap_sake_init(struct eap_sm *sm) 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; } @@ -94,7 +82,6 @@ static void * eap_sake_init(struct eap_sm *sm) 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); } @@ -136,8 +123,7 @@ static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity"); plen = 4; - if (data->serverid) - plen += 2 + data->serverid_len; + plen += 2 + sm->server_id_len; msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY); if (msg == NULL) { data->state = FAILURE; @@ -147,11 +133,9 @@ static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm, 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); - } + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); + eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, + sm->server_id, sm->server_id_len); return msg; } @@ -166,7 +150,7 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge"); - if (os_get_random(data->rand_s, EAP_SAKE_RAND_LEN)) { + if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) { wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); data->state = FAILURE; return NULL; @@ -174,9 +158,7 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, 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; + plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len; msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE); if (msg == NULL) { data->state = FAILURE; @@ -187,11 +169,9 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, 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); - } + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID"); + eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID, + sm->server_id, sm->server_id_len); return msg; } @@ -218,7 +198,7 @@ static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm, 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, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 0, wpabuf_head(msg), wpabuf_len(msg), mic, mic)) { @@ -367,7 +347,7 @@ static void eap_sake_process_challenge(struct eap_sm *sm, (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, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 1, wpabuf_head(respData), wpabuf_len(respData), attr.mic_p, mic_p); @@ -404,7 +384,7 @@ static void eap_sake_process_confirm(struct eap_sm *sm, } eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, - data->serverid, data->serverid_len, + sm->server_id, sm->server_id_len, data->peerid, data->peerid_len, 1, wpabuf_head(respData), wpabuf_len(respData), attr.mic_p, mic_p); diff --git a/contrib/hostapd/src/eap_server/eap_sim.c b/contrib/hostapd/src/eap_server/eap_server_sim.c similarity index 77% rename from contrib/hostapd/src/eap_server/eap_sim.c rename to contrib/hostapd/src/eap_server/eap_server_sim.c index 436c65591f..b531241e84 100644 --- a/contrib/hostapd/src/eap_server/eap_sim.c +++ b/contrib/hostapd/src/eap_server/eap_server_sim.c @@ -1,20 +1,15 @@ /* * hostapd / EAP-SIM (RFC 4186) - * Copyright (c) 2005-2008, Jouni Malinen + * Copyright (c) 2005-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/random.h" #include "eap_server/eap_i.h" #include "eap_common/eap_sim_common.h" #include "eap_server/eap_sim_db.h" @@ -41,6 +36,8 @@ struct eap_sim_data { struct eap_sim_reauth *reauth; u16 notification; int use_result_ind; + int start_round; + char permanent[20]; /* Permanent username */ }; @@ -110,17 +107,33 @@ static struct wpabuf * eap_sim_build_start(struct eap_sm *sm, 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 { + data->start_round++; + if (data->start_round == 1) { /* * 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); + } else if (data->start_round > 3) { + /* Cannot use more than three rounds of Start messages */ + eap_sim_msg_free(msg); + return NULL; + } else if (data->start_round == 0) { + /* + * This is a special case that is used to recover from + * AT_COUNTER_TOO_SMALL during re-authentication. Since we + * already know the identity of the peer, there is no need to + * request any identity in this case. + */ + } else if (sm->identity && sm->identity_len > 0 && + sm->identity[0] == EAP_SIM_REAUTH_ID_PREFIX) { + /* Reauth id may have expired - try fullauth */ + wpa_printf(MSG_DEBUG, " AT_FULLAUTH_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0); + } else { + wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); } wpa_printf(MSG_DEBUG, " AT_VERSION_LIST"); ver[0] = 0; @@ -136,12 +149,19 @@ static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, const u8 *nonce_s) { os_free(data->next_pseudonym); - data->next_pseudonym = - eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0); + if (nonce_s == NULL) { + data->next_pseudonym = + eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, + EAP_SIM_DB_SIM); + } else { + /* Do not update pseudonym during re-authentication */ + data->next_pseudonym = NULL; + } 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); + eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, + EAP_SIM_DB_SIM); } else { wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication " "count exceeded - force full authentication"); @@ -232,7 +252,7 @@ static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication"); - if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN)) + if (random_get_bytes(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); @@ -326,18 +346,22 @@ static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id) 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; + return FALSE; +} + + +static Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data, + u8 subtype) +{ if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) return FALSE; @@ -391,85 +415,113 @@ static void eap_sim_process_start(struct eap_sm *sm, struct wpabuf *respData, struct eap_sim_attrs *attr) { - const u8 *identity; size_t identity_len; u8 ver_list[2]; + u8 *new_identity; + char *username; 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; - } + if (data->start_round == 0) { + /* + * Special case for AT_COUNTER_TOO_SMALL recovery - no identity + * was requested since we already know it. + */ + goto skip_id_update; } - 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); - } - } + /* + * We always request identity in SIM/Start, so the peer is required to + * have replied with one. + */ + if (!attr->identity || attr->identity_len == 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Peer did not provide any " + "identity"); + goto failed; } - if (identity == NULL) { - wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent" - " user name"); - eap_sim_state(data, FAILURE); - return; - } + new_identity = os_malloc(attr->identity_len); + if (new_identity == NULL) + goto failed; + os_free(sm->identity); + sm->identity = new_identity; + os_memcpy(sm->identity, attr->identity, attr->identity_len); + sm->identity_len = attr->identity_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", - identity, identity_len); - - if (data->reauth) { + sm->identity, sm->identity_len); + username = sim_get_username(sm->identity, sm->identity_len); + if (username == NULL) + goto failed; + + if (username[0] == EAP_SIM_REAUTH_ID_PREFIX) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Reauth username '%s'", + username); + data->reauth = eap_sim_db_get_reauth_entry( + sm->eap_sim_db_priv, username); + os_free(username); + if (data->reauth == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown reauth " + "identity - request full auth identity"); + /* Remain in START state for another round */ + return; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast re-authentication"); + os_strlcpy(data->permanent, data->reauth->permanent, + sizeof(data->permanent)); + data->counter = data->reauth->counter; + os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN); eap_sim_state(data, REAUTH); return; } + if (username[0] == EAP_SIM_PSEUDONYM_PREFIX) { + const char *permanent; + wpa_printf(MSG_DEBUG, "EAP-SIM: Pseudonym username '%s'", + username); + permanent = eap_sim_db_get_permanent( + sm->eap_sim_db_priv, username); + os_free(username); + if (permanent == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown pseudonym " + "identity - request permanent identity"); + /* Remain in START state for another round */ + return; + } + os_strlcpy(data->permanent, permanent, + sizeof(data->permanent)); + } else if (username[0] == EAP_SIM_PERMANENT_PREFIX) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Permanent username '%s'", + username); + os_strlcpy(data->permanent, username, sizeof(data->permanent)); + os_free(username); + } else { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized username '%s'", + username); + os_free(username); + goto failed; + } + +skip_id_update: + /* Full authentication */ + 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; + goto failed; } 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; + goto failed; } 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, + sm->eap_sim_db_priv, data->permanent, 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 " @@ -480,8 +532,7 @@ static void eap_sim_process_start(struct eap_sm *sm, 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; + goto failed; } identity_len = sm->identity_len; @@ -502,6 +553,11 @@ static void eap_sim_process_start(struct eap_sm *sm, data->emsk); eap_sim_state(data, CHALLENGE); + return; + +failed: + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); } @@ -510,16 +566,14 @@ static void eap_sim_process_challenge(struct eap_sm *sm, 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); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); return; } @@ -532,22 +586,13 @@ static void eap_sim_process_challenge(struct eap_sm *sm, } 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, + eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent, 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, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, data->next_reauth_id, data->counter + 1, data->mk); data->next_reauth_id = NULL; @@ -562,8 +607,6 @@ static void eap_sim_process_reauth(struct eap_sm *sm, { 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, @@ -599,6 +642,16 @@ static void eap_sim_process_reauth(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-SIM: 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"); + data->start_round = -1; + eap_sim_state(data, START); + return; + } + if (sm->eap_sim_aka_result_ind && attr->result_ind) { data->use_result_ind = 1; data->notification = EAP_SIM_SUCCESS; @@ -606,29 +659,9 @@ static void eap_sim_process_reauth(struct eap_sm *sm, } 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, + eap_sim_db_add_reauth(sm->eap_sim_db_priv, data->permanent, + data->next_reauth_id, data->counter + 1, data->mk); data->next_reauth_id = NULL; } else { @@ -639,7 +672,8 @@ static void eap_sim_process_reauth(struct eap_sm *sm, return; fail: - eap_sim_state(data, FAILURE); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); data->reauth = NULL; os_free(decrypted); @@ -690,8 +724,24 @@ static void eap_sim_process(struct eap_sm *sm, void *priv, subtype = *pos; pos += 3; + if (eap_sim_unexpected_subtype(data, subtype)) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized or unexpected " + "EAP-SIM Subtype in EAP Response"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); + return; + } + if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) { wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes"); + if (subtype != EAP_SIM_SUBTYPE_CLIENT_ERROR && + (data->state == START || data->state == CHALLENGE || + data->state == REAUTH)) { + data->notification = + EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); + return; + } eap_sim_state(data, FAILURE); return; } diff --git a/contrib/hostapd/src/eap_server/eap_tls.c b/contrib/hostapd/src/eap_server/eap_server_tls.c similarity index 73% rename from contrib/hostapd/src/eap_server/eap_tls.c rename to contrib/hostapd/src/eap_server/eap_server_tls.c index 5747940f78..447f47cfa0 100644 --- a/contrib/hostapd/src/eap_server/eap_tls.c +++ b/contrib/hostapd/src/eap_server/eap_server_tls.c @@ -2,14 +2,8 @@ * hostapd / EAP-TLS (RFC 2716) * Copyright (c) 2004-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,7 +11,7 @@ #include "common.h" #include "eap_i.h" #include "eap_tls_common.h" -#include "tls.h" +#include "crypto/tls.h" static void eap_tls_reset(struct eap_sm *sm, void *priv); @@ -27,6 +21,7 @@ struct eap_tls_data { struct eap_ssl_data ssl; enum { START, CONTINUE, SUCCESS, FAILURE } state; int established; + u8 eap_type; }; @@ -71,10 +66,34 @@ static void * eap_tls_init(struct eap_sm *sm) return NULL; } + data->eap_type = EAP_TYPE_TLS; + return data; } +#ifdef EAP_SERVER_UNAUTH_TLS +static void * eap_unauth_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, 0)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_reset(sm, data); + return NULL; + } + + data->eap_type = EAP_UNAUTH_TLS_TYPE; + return data; +} +#endif /* EAP_SERVER_UNAUTH_TLS */ + + static void eap_tls_reset(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; @@ -90,8 +109,7 @@ static struct wpabuf * eap_tls_build_start(struct eap_sm *sm, { struct wpabuf *req; - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST, - id); + req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for " "request"); @@ -113,11 +131,11 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) struct wpabuf *res; if (data->ssl.state == FRAG_ACK) { - return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0); + return eap_server_tls_build_ack(id, data->eap_type, 0); } if (data->ssl.state == WAIT_FRAG_ACK) { - res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, + res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); goto check_established; } @@ -135,7 +153,7 @@ static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) return NULL; } - res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id); + res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); check_established: if (data->established && data->ssl.state != WAIT_FRAG_ACK) { @@ -152,10 +170,17 @@ check_established: static Boolean eap_tls_check(struct eap_sm *sm, void *priv, struct wpabuf *respData) { + struct eap_tls_data *data = priv; const u8 *pos; size_t len; - pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len); + if (data->eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, respData, + &len); + else + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type, + respData, &len); if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); return TRUE; @@ -169,7 +194,7 @@ 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.in_buf) == 0) { + if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) { wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS " "handshake message"); return; @@ -184,7 +209,7 @@ static void eap_tls_process(struct eap_sm *sm, void *priv, { struct eap_tls_data *data = priv; if (eap_server_tls_process(sm, &data->ssl, respData, data, - EAP_TYPE_TLS, NULL, eap_tls_process_msg) < + data->eap_type, NULL, eap_tls_process_msg) < 0) eap_tls_state(data, FAILURE); } @@ -284,3 +309,34 @@ int eap_server_tls_register(void) eap_server_method_free(eap); return ret; } + + +#ifdef EAP_SERVER_UNAUTH_TLS +int eap_server_unauth_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, + "UNAUTH-TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_unauth_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; +} +#endif /* EAP_SERVER_UNAUTH_TLS */ diff --git a/contrib/hostapd/src/eap_server/eap_tls_common.c b/contrib/hostapd/src/eap_server/eap_server_tls_common.c similarity index 67% rename from contrib/hostapd/src/eap_server/eap_tls_common.c rename to contrib/hostapd/src/eap_server/eap_server_tls_common.c index bda1184c02..526e1bcc9c 100644 --- a/contrib/hostapd/src/eap_server/eap_tls_common.c +++ b/contrib/hostapd/src/eap_server/eap_server_tls_common.c @@ -1,29 +1,43 @@ /* - * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2008, Jouni Malinen + * EAP-TLS/PEAP/TTLS/FAST server common functions + * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 "sha1.h" -#include "tls.h" + + +static void eap_server_tls_free_in_buf(struct eap_ssl_data *data); + + +struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + if (type == EAP_UNAUTH_TLS_TYPE) + return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len, + code, identifier); + return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code, + identifier); +} int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer) { + if (sm->ssl_ctx == NULL) { + wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method"); + return -1; + } + data->eap = sm; data->phase2 = sm->init_phase2; @@ -42,8 +56,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, return -1; } - /* TODO: make this configurable */ - data->tls_out_limit = 1398; + data->tls_out_limit = sm->fragment_size > 0 ? sm->fragment_size : 1398; if (data->phase2) { /* Limit the fragment size in the inner TLS authentication * since the outer authentication with EAP-PEAP does not yet @@ -58,8 +71,9 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) { tls_connection_deinit(sm->ssl_ctx, data->conn); - os_free(data->in_buf); - os_free(data->out_buf); + eap_server_tls_free_in_buf(data); + wpabuf_free(data->tls_out); + data->tls_out = NULL; } @@ -91,9 +105,9 @@ u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, 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)) + if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len, + label, rnd, keys.client_random_len + + keys.server_random_len, out, len)) goto fail; os_free(rnd); @@ -114,17 +128,17 @@ struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, size_t send_len, plen; wpa_printf(MSG_DEBUG, "SSL: Generating Request"); - if (data->out_buf == NULL) { - wpa_printf(MSG_ERROR, "SSL: out_buf NULL in %s", __func__); + 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->out_buf) - data->out_used; + 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->out_used == 0) { + if (data->tls_out_pos == 0) { flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; send_len -= 4; } @@ -134,32 +148,31 @@ struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) plen += 4; - req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen, - EAP_CODE_REQUEST, id); + req = eap_tls_msg_alloc(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->out_buf)); + wpabuf_put_be32(req, wpabuf_len(data->tls_out)); - wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + wpabuf_put_data(req, wpabuf_head_u8(data->tls_out) + data->tls_out_pos, send_len); - data->out_used += send_len; + data->tls_out_pos += send_len; - if (data->out_used == wpabuf_len(data->out_buf)) { + 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->out_buf); - data->out_buf = NULL; - data->out_used = 0; + 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->out_buf) - - data->out_used); + (unsigned long) wpabuf_len(data->tls_out) - + data->tls_out_pos); data->state = WAIT_FRAG_ACK; } @@ -171,8 +184,7 @@ 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); + req = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_REQUEST, id); if (req == NULL) return NULL; wpa_printf(MSG_DEBUG, "SSL: Building ACK"); @@ -185,15 +197,15 @@ 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->in_buf)) { + if (len > wpabuf_tailroom(data->tls_in)) { wpa_printf(MSG_DEBUG, "SSL: Fragment overflow"); return -1; } - wpabuf_put_data(data->in_buf, buf, len); + 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->in_buf)); + (unsigned long) wpabuf_tailroom(data->tls_in)); return 0; } @@ -204,13 +216,13 @@ static int eap_server_tls_process_fragment(struct eap_ssl_data *data, 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_TLS_FLAGS_LENGTH_INCLUDED)) { + 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->in_buf == NULL) { + if (data->tls_in == NULL) { /* First fragment of the message */ /* Limit length to avoid rogue peers from causing large @@ -221,16 +233,24 @@ static int eap_server_tls_process_fragment(struct eap_ssl_data *data, return -1; } - data->in_buf = wpabuf_alloc(message_length); - if (data->in_buf == NULL) { + if (len > message_length) { + wpa_printf(MSG_INFO, "SSL: Too much data (%d bytes) in " + "first fragment of frame (TLS Message " + "Length %d bytes)", + (int) len, (int) message_length); + 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->in_buf, buf, len); + 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->in_buf)); + (unsigned long) wpabuf_tailroom(data->tls_in)); } return 0; @@ -239,30 +259,28 @@ static int eap_server_tls_process_fragment(struct eap_ssl_data *data, int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) { - u8 *next; - size_t next_len; - - next = tls_connection_server_handshake( - sm->ssl_ctx, data->conn, - wpabuf_mhead(data->in_buf), - wpabuf_len(data->in_buf), - &next_len); - if (next == NULL) { - wpa_printf(MSG_INFO, "SSL: TLS processing failed"); - return -1; - } - if (data->out_buf) { + if (data->tls_out) { /* This should not happen.. */ wpa_printf(MSG_INFO, "SSL: pending tls_out data when " "processing new message"); - os_free(data->out_buf); - WPA_ASSERT(data->out_buf == NULL); + 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; } - data->out_buf = wpabuf_alloc_ext_data(next, next_len); - if (data->out_buf == NULL) { - os_free(next); + 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; } @@ -284,6 +302,13 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, tls_msg_len); *pos += 4; *left -= 4; + + if (*left > tls_msg_len) { + wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d " + "bytes) smaller than this fragment (%d " + "bytes)", (int) tls_msg_len, (int) *left); + return -1; + } } wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x " @@ -299,7 +324,7 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, return 1; } - if (data->in_buf && + if (data->tls_in && eap_server_tls_process_cont(data, *pos, end - *pos) < 0) return -1; @@ -317,10 +342,10 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, data->state = MSG; } - if (data->in_buf == NULL) { + if (data->tls_in == NULL) { /* Wrap unfragmented messages as wpabuf without extra copy */ wpabuf_set(&data->tmpbuf, *pos, end - *pos); - data->in_buf = &data->tmpbuf; + data->tls_in = &data->tmpbuf; } return 0; @@ -329,36 +354,25 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, static void eap_server_tls_free_in_buf(struct eap_ssl_data *data) { - if (data->in_buf != &data->tmpbuf) - wpabuf_free(data->in_buf); - data->in_buf = NULL; + 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 u8 *plain, size_t plain_len) + const struct wpabuf *plain) { - int res; struct wpabuf *buf; - size_t buf_len; - /* reserve some extra room for encryption overhead */ - buf_len = plain_len + 300; - buf = wpabuf_alloc(buf_len); - if (buf == NULL) - return NULL; - res = tls_connection_encrypt(sm->ssl_ctx, data->conn, - plain, plain_len, wpabuf_put(buf, 0), - buf_len); - if (res < 0) { + buf = tls_connection_encrypt(sm->ssl_ctx, data->conn, + plain); + if (buf == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data"); - wpabuf_free(buf); return NULL; } - wpabuf_put(buf, res); - return buf; } @@ -375,7 +389,13 @@ int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, size_t left; int ret, res = 0; - pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left); + if (eap_type == EAP_UNAUTH_TLS_TYPE) + pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS, + EAP_VENDOR_TYPE_UNAUTH_TLS, respData, + &left); + else + 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++; diff --git a/contrib/hostapd/src/eap_server/eap_tnc.c b/contrib/hostapd/src/eap_server/eap_server_tnc.c similarity index 84% rename from contrib/hostapd/src/eap_server/eap_tnc.c rename to contrib/hostapd/src/eap_server/eap_server_tnc.c index 4cb3ecfb05..67a3dfa306 100644 --- a/contrib/hostapd/src/eap_server/eap_tnc.c +++ b/contrib/hostapd/src/eap_server/eap_server_tnc.c @@ -1,34 +1,31 @@ /* * EAP server method: EAP-TNC (Trusted Network Connect) - * Copyright (c) 2007-2008, Jouni Malinen + * Copyright (c) 2007-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "base64.h" #include "eap_i.h" #include "tncs.h" struct eap_tnc_data { - enum { START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE, - FAIL } state; + 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; }; @@ -41,6 +38,38 @@ struct eap_tnc_data { #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; @@ -48,14 +77,15 @@ static void * eap_tnc_init(struct eap_sm *sm) data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; - data->state = START; + eap_tnc_set_state(data, START); data->tncs = tncs_init(); if (data->tncs == NULL) { os_free(data); return NULL; } - data->fragment_size = 1300; + data->fragment_size = sm->fragment_size > 100 ? + sm->fragment_size - 98 : 1300; return data; } @@ -81,13 +111,13 @@ static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm, if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for " "request"); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return NULL; } wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION); - data->state = CONTINUE; + eap_tnc_set_state(data, CONTINUE); return req; } @@ -146,17 +176,17 @@ static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm, { switch (data->recommendation) { case ALLOW: - data->state = DONE; + eap_tnc_set_state(data, DONE); break; case ISOLATE: - data->state = FAIL; + eap_tnc_set_state(data, FAIL); /* TODO: support assignment to a different VLAN */ break; case NO_ACCESS: - data->state = FAIL; + eap_tnc_set_state(data, FAIL); break; case NO_RECOMMENDATION: - data->state = DONE; + eap_tnc_set_state(data, DONE); break; default: wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation"); @@ -171,12 +201,13 @@ 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, 0, code, id); + 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"); @@ -226,12 +257,20 @@ static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id) 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); - data->state = WAIT_FRAG_ACK; + 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; @@ -327,27 +366,27 @@ static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf) switch (res) { case TNCCS_RECOMMENDATION_ALLOW: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access"); - data->state = RECOMMENDATION; + 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"); - data->state = 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"); - data->state = RECOMMENDATION; + eap_tnc_set_state(data, RECOMMENDATION); data->recommendation = ISOLATE; break; case TNCCS_RECOMMENDATION_NO_ACCESS: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access"); - data->state = RECOMMENDATION; + eap_tnc_set_state(data, RECOMMENDATION); data->recommendation = NO_ACCESS; break; case TNCCS_PROCESS_ERROR: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error"); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); break; default: break; @@ -361,7 +400,7 @@ static int eap_tnc_process_cont(struct eap_tnc_data *data, /* Process continuation of a pending message */ if (len > wpabuf_tailroom(data->in_buf)) { wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return -1; } @@ -435,7 +474,7 @@ static void eap_tnc_process(struct eap_sm *sm, void *priv, if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { if (end - pos < 4) { wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return; } message_length = WPA_GET_BE32(pos); @@ -445,7 +484,7 @@ static void eap_tnc_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " "Length (%d; %ld remaining in this msg)", message_length, (long) (end - pos)); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return; } } @@ -453,32 +492,32 @@ static void eap_tnc_process(struct eap_sm *sm, void *priv, "Message Length %u", flags, message_length); if (data->state == WAIT_FRAG_ACK) { - if (len != 0) { + if (len > 1) { wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload " "in WAIT_FRAG_ACK state"); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return; } wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); - data->state = CONTINUE; + eap_tnc_set_state(data, CONTINUE); return; } if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { - data->state = FAIL; + 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) - data->state = FAIL; + eap_tnc_set_state(data, FAIL); else - data->state = FRAG_ACK; + eap_tnc_set_state(data, FRAG_ACK); return; } else if (data->state == FRAG_ACK) { wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); - data->state = CONTINUE; + eap_tnc_set_state(data, CONTINUE); } if (data->in_buf == NULL) { diff --git a/contrib/hostapd/src/eap_server/eap_ttls.c b/contrib/hostapd/src/eap_server/eap_server_ttls.c similarity index 75% rename from contrib/hostapd/src/eap_server/eap_ttls.c rename to contrib/hostapd/src/eap_server/eap_server_ttls.c index 21e4b21b15..647bd2fad9 100644 --- a/contrib/hostapd/src/eap_server/eap_ttls.c +++ b/contrib/hostapd/src/eap_server/eap_server_ttls.c @@ -1,39 +1,24 @@ /* * hostapd / EAP-TTLS (RFC 5281) - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 "ms_funcs.h" -#include "sha1.h" #include "eap_common/chap.h" -#include "tls.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 +#define EAP_TTLS_VERSION 0 static void eap_ttls_reset(struct eap_sm *sm, void *priv); @@ -43,17 +28,15 @@ struct eap_ttls_data { struct eap_ssl_data ssl; enum { START, PHASE1, PHASE2_START, PHASE2_METHOD, - PHASE2_MSCHAPV2_RESP, PHASE_FINISHED, SUCCESS, FAILURE + PHASE2_MSCHAPV2_RESP, 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; }; @@ -72,8 +55,6 @@ static const char * eap_ttls_state_txt(int state) return "PHASE2_METHOD"; case PHASE2_MSCHAPV2_RESP: return "PHASE2_MSCHAPV2_RESP"; - case PHASE_FINISHED: - return "PHASE_FINISHED"; case SUCCESS: return "SUCCESS"; case FAILURE: @@ -111,7 +92,8 @@ static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id, } avp->avp_code = host_to_be32(avp_code); - avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len)); + avp->avp_length = host_to_be32(((u32) flags << 24) | + ((u32) (hdrlen + len))); return avphdr + hdrlen; } @@ -163,14 +145,14 @@ struct eap_ttls_avp { }; -static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) +static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse) { struct ttls_avp *avp; u8 *pos; int left; - pos = buf; - left = len; + pos = wpabuf_mhead(buf); + left = wpabuf_len(buf); os_memset(parse, 0, sizeof(*parse)); while (left > 0) { @@ -320,54 +302,8 @@ fail: 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; + return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge", + len); } @@ -379,27 +315,8 @@ static void * eap_ttls_init(struct eap_sm *sm) 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); @@ -449,8 +366,6 @@ 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; - u8 *req; - size_t req_len; buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); @@ -467,12 +382,10 @@ static struct wpabuf * eap_ttls_build_phase2_eap_req( return NULL; } - req = wpabuf_mhead(buf); - req_len = wpabuf_len(buf); - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated Phase " - "2 data", req, req_len); + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated " + "Phase 2 data", buf); - encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf); wpabuf_free(buf); return encr_req; @@ -482,10 +395,9 @@ static struct wpabuf * eap_ttls_build_phase2_eap_req( static struct wpabuf * eap_ttls_build_phase2_mschapv2( struct eap_sm *sm, struct eap_ttls_data *data) { - struct wpabuf *encr_req; + struct wpabuf *encr_req, msgbuf; u8 *req, *pos, *end; int ret; - size_t req_len; pos = req = os_malloc(100); if (req == NULL) @@ -510,42 +422,17 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2( AVP_PAD(req, pos); } - req_len = pos - req; - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 " - "data", req, req_len); + 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, req, req_len); + 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) -{ - int len; - struct wpabuf *req; - const int max_len = 300; - - req = wpabuf_alloc(max_len); - if (req == NULL) - return NULL; - - len = tls_connection_ia_send_phase_finished(sm->ssl_ctx, - data->ssl.conn, final, - wpabuf_mhead(req), - max_len); - if (len < 0) { - wpabuf_free(req); - return NULL; - } - wpabuf_put(req, len); - - return req; -} - - static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) { struct eap_ttls_data *data = priv; @@ -571,20 +458,15 @@ static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) } break; case PHASE2_METHOD: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_ttls_build_phase2_eap_req(sm, data, + 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.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_ttls_build_phase2_mschapv2(sm, data); - break; - case PHASE_FINISHED: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_ttls_build_phase_finished(sm, data, 1); + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data); break; default: wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", @@ -613,37 +495,6 @@ static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, } -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, @@ -666,8 +517,7 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, } wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); - eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : - SUCCESS); + eap_ttls_state(data, SUCCESS); } @@ -723,8 +573,7 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, 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); + eap_ttls_state(data, SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); eap_ttls_state(data, FAILURE); @@ -784,8 +633,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, 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); + eap_ttls_state(data, SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", @@ -826,6 +674,13 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, return; } + if (sm->identity == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user identity " + "known"); + eap_ttls_state(data, FAILURE); + return; + } + /* MSCHAPv2 does not include optional domain name in the * challenge-response calculation, so remove domain prefix * (if present). */ @@ -885,30 +740,6 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, 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( @@ -1052,17 +883,7 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, } 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); + eap_ttls_state(data, SUCCESS); break; case FAILURE: break; @@ -1126,18 +947,11 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, struct eap_ttls_data *data, struct wpabuf *in_buf) { - u8 *in_decrypted; - int len_decrypted; + struct wpabuf *in_decrypted; struct eap_ttls_avp parse; - size_t buf_len; - u8 *in_data; - size_t in_len; - - in_data = wpabuf_mhead(in_buf); - in_len = wpabuf_len(in_buf); wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" - " Phase 2", (unsigned long) in_len); + " 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 " @@ -1150,56 +964,21 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, return; } - buf_len = in_len; - /* - * 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_len += 500; - buf_len *= 3; - in_decrypted = os_malloc(buf_len); + in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_buf); if (in_decrypted == NULL) { - wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate memory " - "for decryption"); - return; - } - - len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, - in_decrypted, buf_len); - if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " "data"); - os_free(in_decrypted); eap_ttls_state(data, FAILURE); return; } - if (data->state == PHASE_FINISHED) { - if (len_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); - } + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", + in_decrypted); - os_free(in_decrypted); - return; - } - - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", - in_decrypted, len_decrypted); - - if (eap_ttls_avp_parse(in_decrypted, len_decrypted, &parse) < 0) { + if (eap_ttls_avp_parse(in_decrypted, &parse) < 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs"); - os_free(in_decrypted); + wpabuf_free(in_decrypted); eap_ttls_state(data, FAILURE); return; } @@ -1207,11 +986,12 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, 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 (sm->identity == NULL) { + eap_ttls_state(data, FAILURE); + goto done; } + 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 " @@ -1221,14 +1001,14 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, } } -#ifdef EAP_TNC +#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_TNC */ +#endif /* EAP_SERVER_TNC */ if (parse.eap) { eap_ttls_process_phase2_eap(sm, data, parse.eap, @@ -1257,14 +1037,14 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, } done: - os_free(in_decrypted); + 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_TNC +#ifdef EAP_SERVER_TNC if (!sm->tnc || data->state != SUCCESS || data->tnc_started) return; @@ -1277,7 +1057,7 @@ static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data) data->tnc_started = 1; eap_ttls_state(data, PHASE2_METHOD); -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ } @@ -1292,15 +1072,6 @@ static int eap_ttls_process_version(struct eap_sm *sm, void *priv, 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; } @@ -1317,17 +1088,15 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, break; case PHASE2_START: case PHASE2_METHOD: - case PHASE_FINISHED: - eap_ttls_process_phase2(sm, data, data->ssl.in_buf); + 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.in_buf) == + 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); + eap_ttls_state(data, SUCCESS); } else if (!data->mschapv2_resp_ok) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged error"); @@ -1337,7 +1106,7 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, "frame from peer (payload len %lu, " "expected empty frame)", (unsigned long) - wpabuf_len(data->ssl.in_buf)); + wpabuf_len(data->ssl.tls_in)); eap_ttls_state(data, FAILURE); } eap_ttls_start_tnc(sm, data); @@ -1368,54 +1137,6 @@ static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv) } -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; @@ -1424,14 +1145,9 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) 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); - } - + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", diff --git a/contrib/hostapd/src/eap_server/eap_vendor_test.c b/contrib/hostapd/src/eap_server/eap_server_vendor_test.c similarity index 91% rename from contrib/hostapd/src/eap_server/eap_vendor_test.c rename to contrib/hostapd/src/eap_server/eap_server_vendor_test.c index 0dd0aca911..30f600d3ba 100644 --- a/contrib/hostapd/src/eap_server/eap_vendor_test.c +++ b/contrib/hostapd/src/eap_server/eap_server_vendor_test.c @@ -2,14 +2,8 @@ * hostapd / Test method for vendor specific (expanded) EAP type * Copyright (c) 2005-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,7 +12,7 @@ #include "eap_i.h" -#define EAP_VENDOR_ID 0xfffefd +#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP #define EAP_VENDOR_TYPE 0xfcfbfaf9 diff --git a/contrib/hostapd/src/eap_server/eap_wsc.c b/contrib/hostapd/src/eap_server/eap_server_wsc.c similarity index 89% rename from contrib/hostapd/src/eap_server/eap_wsc.c rename to contrib/hostapd/src/eap_server/eap_server_wsc.c index 3c17577889..97ec0c0eaa 100644 --- a/contrib/hostapd/src/eap_server/eap_wsc.c +++ b/contrib/hostapd/src/eap_server/eap_server_wsc.c @@ -2,14 +2,8 @@ * EAP-WSC server for Wi-Fi Protected Setup * Copyright (c) 2007-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,11 +12,12 @@ #include "eloop.h" #include "eap_i.h" #include "eap_common/eap_wsc_common.h" +#include "p2p/p2p.h" #include "wps/wps.h" struct eap_wsc_data { - enum { START, MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; + enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; int registrar; struct wpabuf *in_buf; struct wpabuf *out_buf; @@ -34,13 +29,14 @@ struct eap_wsc_data { }; +#ifndef CONFIG_NO_STDOUT_DEBUG static const char * eap_wsc_state_txt(int state) { switch (state) { case START: return "START"; - case MSG: - return "MSG"; + case MESG: + return "MESG"; case FRAG_ACK: return "FRAG_ACK"; case WAIT_FRAG_ACK: @@ -53,6 +49,7 @@ static const char * eap_wsc_state_txt(int state) return "?"; } } +#endif /* CONFIG_NO_STDOUT_DEBUG */ static void eap_wsc_state(struct eap_wsc_data *data, int state) @@ -102,7 +99,7 @@ static void * eap_wsc_init(struct eap_sm *sm) data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; - data->state = registrar ? START : MSG; + data->state = registrar ? START : MESG; data->registrar = registrar; os_memset(&cfg, 0, sizeof(cfg)); @@ -117,21 +114,38 @@ static void * eap_wsc_init(struct eap_sm *sm) } } else { if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-WSC: No AP PIN (password) " - "configured for Enrollee functionality"); - os_free(data); - return 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.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; +#ifdef CONFIG_P2P + if (sm->assoc_p2p_ie) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Prefer PSK format for P2P " + "client"); + cfg.use_psk_key = 1; + cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie); + } +#endif /* CONFIG_P2P */ + cfg.pbc_in_m1 = sm->pbc_in_m1; data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); return NULL; } - data->fragment_size = WSC_FRAGMENT_SIZE; + data->fragment_size = sm->fragment_size > 0 ? sm->fragment_size : + WSC_FRAGMENT_SIZE; return data; } @@ -212,7 +226,7 @@ static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id) wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; - eap_wsc_state(data, MSG); + eap_wsc_state(data, MESG); } else { wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, @@ -232,7 +246,7 @@ static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id) switch (data->state) { case START: return eap_wsc_build_start(sm, data, id); - case MSG: + case MESG: if (data->out_buf == NULL) { data->out_buf = wps_get_msg(data->wps, &data->out_op_code); @@ -385,7 +399,7 @@ static void eap_wsc_process(struct eap_sm *sm, void *priv, return; } wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); - eap_wsc_state(data, MSG); + eap_wsc_state(data, MESG); return; } @@ -427,14 +441,14 @@ static void eap_wsc_process(struct eap_sm *sm, void *priv, eap_wsc_state(data, FAIL); break; case WPS_CONTINUE: - eap_wsc_state(data, MSG); + 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, MSG); + 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, diff --git a/contrib/hostapd/src/eap_server/eap_sim_db.c b/contrib/hostapd/src/eap_server/eap_sim_db.c index ed0bd3cd13..45660ed7d2 100644 --- a/contrib/hostapd/src/eap_server/eap_sim_db.c +++ b/contrib/hostapd/src/eap_server/eap_sim_db.c @@ -1,15 +1,9 @@ /* * hostapd / EAP-SIM database/authenticator gateway - * Copyright (c) 2005-2007, Jouni Malinen + * Copyright (c) 2005-2010, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -23,26 +17,27 @@ #include "includes.h" #include +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ #include "common.h" +#include "crypto/random.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; + char *permanent; /* permanent username */ + char *pseudonym; /* pseudonym username */ }; struct eap_sim_db_pending { struct eap_sim_db_pending *next; - u8 imsi[20]; - size_t imsi_len; + char imsi[20]; enum { PENDING, SUCCESS, FAILURE } state; void *cb_session_ctx; - struct os_time timestamp; int aka; union { struct { @@ -71,19 +66,316 @@ struct eap_sim_db_data { struct eap_sim_pseudonym *pseudonyms; struct eap_sim_reauth *reauths; struct eap_sim_db_pending *pending; +#ifdef CONFIG_SQLITE + sqlite3 *sqlite_db; + char db_tmp_identity[100]; + char db_tmp_pseudonym_str[100]; + struct eap_sim_pseudonym db_tmp_pseudonym; + struct eap_sim_reauth db_tmp_reauth; +#endif /* CONFIG_SQLITE */ }; +#ifdef CONFIG_SQLITE + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_pseudonym(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE pseudonyms(" + " permanent CHAR(21) PRIMARY KEY," + " pseudonym CHAR(21) NOT NULL" + ");"; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " + "pseudonym information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static int db_table_create_reauth(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE reauth(" + " permanent CHAR(21) PRIMARY KEY," + " reauth_id CHAR(21) NOT NULL," + " counter INTEGER," + " mk CHAR(40)," + " k_encr CHAR(32)," + " k_aut CHAR(64)," + " k_re CHAR(64)" + ");"; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Adding database table for " + "reauth information"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static sqlite3 * db_open(const char *db_file) +{ + sqlite3 *db; + + if (sqlite3_open(db_file, &db)) { + wpa_printf(MSG_ERROR, "EAP-SIM DB: Failed to open database " + "%s: %s", db_file, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + if (!db_table_exists(db, "pseudonyms") && + db_table_create_pseudonym(db) < 0) { + sqlite3_close(db); + return NULL; + } + + if (!db_table_exists(db, "reauth") && + db_table_create_reauth(db) < 0) { + sqlite3_close(db); + return NULL; + } + + return db; +} + + +static int valid_db_string(const char *str) +{ + const char *pos = str; + while (*pos) { + if ((*pos < '0' || *pos > '9') && + (*pos < 'a' || *pos > 'f')) + return 0; + pos++; + } + return 1; +} + + +static int db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, char *pseudonym) +{ + char cmd[128]; + char *err = NULL; + + if (!valid_db_string(permanent) || !valid_db_string(pseudonym)) { + os_free(pseudonym); + return -1; + } + + os_snprintf(cmd, sizeof(cmd), "INSERT OR REPLACE INTO pseudonyms " + "(permanent, pseudonym) VALUES ('%s', '%s');", + permanent, pseudonym); + os_free(pseudonym); + if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) + { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static int get_pseudonym_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct eap_sim_db_data *data = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { + os_strlcpy(data->db_tmp_identity, argv[i], + sizeof(data->db_tmp_identity)); + } + } + + return 0; +} + + +static char * +db_get_pseudonym(struct eap_sim_db_data *data, const char *pseudonym) +{ + char cmd[128]; + + if (!valid_db_string(pseudonym)) + return NULL; + os_memset(&data->db_tmp_identity, 0, sizeof(data->db_tmp_identity)); + os_snprintf(cmd, sizeof(cmd), + "SELECT permanent FROM pseudonyms WHERE pseudonym='%s';", + pseudonym); + if (sqlite3_exec(data->sqlite_db, cmd, get_pseudonym_cb, data, NULL) != + SQLITE_OK) + return NULL; + if (data->db_tmp_identity[0] == '\0') + return NULL; + return data->db_tmp_identity; +} + + +static int db_add_reauth(struct eap_sim_db_data *data, const char *permanent, + char *reauth_id, u16 counter, const u8 *mk, + const u8 *k_encr, const u8 *k_aut, const u8 *k_re) +{ + char cmd[2000], *pos, *end; + char *err = NULL; + + if (!valid_db_string(permanent) || !valid_db_string(reauth_id)) { + os_free(reauth_id); + return -1; + } + + pos = cmd; + end = pos + sizeof(cmd); + pos += os_snprintf(pos, end - pos, "INSERT OR REPLACE INTO reauth " + "(permanent, reauth_id, counter%s%s%s%s) " + "VALUES ('%s', '%s', %u", + mk ? ", mk" : "", + k_encr ? ", k_encr" : "", + k_aut ? ", k_aut" : "", + k_re ? ", k_re" : "", + permanent, reauth_id, counter); + os_free(reauth_id); + + if (mk) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, mk, EAP_SIM_MK_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + if (k_encr) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, k_encr, + EAP_SIM_K_ENCR_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + if (k_aut) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, k_aut, + EAP_AKA_PRIME_K_AUT_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + if (k_re) { + pos += os_snprintf(pos, end - pos, ", '"); + pos += wpa_snprintf_hex(pos, end - pos, k_re, + EAP_AKA_PRIME_K_RE_LEN); + pos += os_snprintf(pos, end - pos, "'"); + } + + os_snprintf(pos, end - pos, ");"); + + if (sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, &err) != SQLITE_OK) + { + wpa_printf(MSG_ERROR, "EAP-SIM DB: SQLite error: %s", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static int get_reauth_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct eap_sim_db_data *data = ctx; + int i; + struct eap_sim_reauth *reauth = &data->db_tmp_reauth; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "permanent") == 0 && argv[i]) { + os_strlcpy(data->db_tmp_identity, argv[i], + sizeof(data->db_tmp_identity)); + reauth->permanent = data->db_tmp_identity; + } else if (os_strcmp(col[i], "counter") == 0 && argv[i]) { + reauth->counter = atoi(argv[i]); + } else if (os_strcmp(col[i], "mk") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->mk, sizeof(reauth->mk)); + } else if (os_strcmp(col[i], "k_encr") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->k_encr, + sizeof(reauth->k_encr)); + } else if (os_strcmp(col[i], "k_aut") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->k_aut, + sizeof(reauth->k_aut)); + } else if (os_strcmp(col[i], "k_re") == 0 && argv[i]) { + hexstr2bin(argv[i], reauth->k_re, + sizeof(reauth->k_re)); + } + } + + return 0; +} + + +static struct eap_sim_reauth * +db_get_reauth(struct eap_sim_db_data *data, const char *reauth_id) +{ + char cmd[256]; + + if (!valid_db_string(reauth_id)) + return NULL; + os_memset(&data->db_tmp_reauth, 0, sizeof(data->db_tmp_reauth)); + os_strlcpy(data->db_tmp_pseudonym_str, reauth_id, + sizeof(data->db_tmp_pseudonym_str)); + data->db_tmp_reauth.reauth_id = data->db_tmp_pseudonym_str; + os_snprintf(cmd, sizeof(cmd), + "SELECT * FROM reauth WHERE reauth_id='%s';", reauth_id); + if (sqlite3_exec(data->sqlite_db, cmd, get_reauth_cb, data, NULL) != + SQLITE_OK) + return NULL; + if (data->db_tmp_reauth.permanent == NULL) + return NULL; + return &data->db_tmp_reauth; +} + + +static void db_remove_reauth(struct eap_sim_db_data *data, + struct eap_sim_reauth *reauth) +{ + char cmd[256]; + + if (!valid_db_string(reauth->permanent)) + return; + os_snprintf(cmd, sizeof(cmd), + "DELETE FROM reauth WHERE permanent='%s';", + reauth->permanent); + sqlite3_exec(data->sqlite_db, cmd, NULL, NULL, NULL); +} + +#endif /* CONFIG_SQLITE */ + + 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) +eap_sim_db_get_pending(struct eap_sim_db_data *data, const char *imsi, 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 (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) { if (prev) prev->next = entry->next; else @@ -118,7 +410,7 @@ static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, * (IMSI = ASCII string, Kc/SRES/RAND = hex string) */ - entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0); + entry = eap_sim_db_get_pending(data, imsi, 0); if (entry == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " "received message found"); @@ -196,7 +488,7 @@ static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string) */ - entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1); + entry = eap_sim_db_get_pending(data, imsi, 1); if (entry == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " "received message found"); @@ -337,7 +629,7 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) data->sock = socket(PF_UNIX, SOCK_DGRAM, 0); if (data->sock < 0) { - perror("socket(eap_sim_db)"); + wpa_printf(MSG_INFO, "socket(eap_sim_db): %s", strerror(errno)); return -1; } @@ -345,9 +637,10 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) addr.sun_family = AF_UNIX; os_snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/eap_sim_db_%d-%d", getpid(), counter++); + os_free(data->local_sock); data->local_sock = os_strdup(addr.sun_path); if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(eap_sim_db)"); + wpa_printf(MSG_INFO, "bind(eap_sim_db): %s", strerror(errno)); close(data->sock); data->sock = -1; return -1; @@ -357,7 +650,8 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data) 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_printf(MSG_INFO, "connect(eap_sim_db): %s", + strerror(errno)); wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket", (u8 *) addr.sun_path, os_strlen(addr.sun_path)); @@ -394,11 +688,13 @@ static void eap_sim_db_close_socket(struct eap_sim_db_data *data) * @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 * +eap_sim_db_init(const char *config, + void (*get_complete_cb)(void *ctx, void *session_ctx), + void *ctx) { struct eap_sim_db_data *data; + char *pos; data = os_zalloc(sizeof(*data)); if (data == NULL) @@ -410,10 +706,23 @@ void * eap_sim_db_init(const char *config, data->fname = os_strdup(config); if (data->fname == NULL) goto fail; + pos = os_strstr(data->fname, " db="); + if (pos) { + *pos = '\0'; +#ifdef CONFIG_SQLITE + pos += 4; + data->sqlite_db = db_open(pos); + if (data->sqlite_db == NULL) + goto fail; +#endif /* CONFIG_SQLITE */ + } if (os_strncmp(data->fname, "unix:", 5) == 0) { - if (eap_sim_db_open_socket(data)) - goto fail; + if (eap_sim_db_open_socket(data)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database " + "connection not available - will retry " + "later"); + } } return data; @@ -428,7 +737,7 @@ fail: static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) { - os_free(p->identity); + os_free(p->permanent); os_free(p->pseudonym); os_free(p); } @@ -436,7 +745,7 @@ static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) static void eap_sim_db_free_reauth(struct eap_sim_reauth *r) { - os_free(r->identity); + os_free(r->permanent); os_free(r->reauth_id); os_free(r); } @@ -453,6 +762,13 @@ void eap_sim_db_deinit(void *priv) struct eap_sim_reauth *r, *prevr; struct eap_sim_db_pending *pending, *prev_pending; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) { + sqlite3_close(data->sqlite_db); + data->sqlite_db = NULL; + } +#endif /* CONFIG_SQLITE */ + eap_sim_db_close_socket(data); os_free(data->fname); @@ -488,7 +804,8 @@ static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, if (send(data->sock, msg, len, 0) < 0) { _errno = errno; - perror("send[EAP-SIM DB UNIX]"); + wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s", + strerror(errno)); } if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || @@ -500,7 +817,8 @@ static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, 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]"); + wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s", + strerror(errno)); return -1; } } @@ -519,9 +837,8 @@ static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) /** * 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 + * @data: Private data pointer from eap_sim_db_init() + * @username: Permanent username (prefix | IMSI) * @max_chal: Maximum number of triplets * @_rand: Buffer for RAND values * @kc: Buffer for Kc values @@ -533,9 +850,6 @@ static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) * 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 @@ -544,39 +858,28 @@ static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) * 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, +int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, + const char *username, 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]; + const char *imsi; + size_t imsi_len; - 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); + if (username == NULL || username[0] != EAP_SIM_PERMANENT_PREFIX || + username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", + username); return EAP_SIM_DB_FAILURE; } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI", - identity, identity_len); + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'", + imsi); - entry = eap_sim_db_get_pending(data, identity, identity_len, 0); + entry = eap_sim_db_get_pending(data, imsi, 0); if (entry) { int num_chal; if (entry->state == FAILURE) { @@ -611,18 +914,19 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, return EAP_SIM_DB_FAILURE; } + imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); - if (len < 0 || len + identity_len >= sizeof(msg)) + if (len < 0 || len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; - os_memcpy(msg + len, identity, identity_len); - len += identity_len; + os_memcpy(msg + len, imsi, imsi_len); + len += imsi_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); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " + "data for IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; @@ -630,9 +934,7 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, 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; + os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); @@ -642,195 +944,12 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, } -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))) + if (random_get_bytes(buf, sizeof(buf))) return NULL; id = os_malloc(sizeof(buf) * 2 + 2); if (id == NULL) @@ -847,8 +966,8 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) /** * 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 + * @data: Private data pointer from eap_sim_db_init() + * @method: EAP method (SIM/AKA/AKA') * Returns: Next pseudonym (allocated string) or %NULL on failure * * This function is used to generate a pseudonym for EAP-SIM. The returned @@ -856,18 +975,31 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) * 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) +char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data, + enum eap_sim_db_method method) { - struct eap_sim_db_data *data = priv; - return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX : - EAP_SIM_PSEUDONYM_PREFIX); + char prefix = EAP_SIM_REAUTH_ID_PREFIX; + + switch (method) { + case EAP_SIM_DB_SIM: + prefix = EAP_SIM_PSEUDONYM_PREFIX; + break; + case EAP_SIM_DB_AKA: + prefix = EAP_AKA_PSEUDONYM_PREFIX; + break; + case EAP_SIM_DB_AKA_PRIME: + prefix = EAP_AKA_PRIME_PSEUDONYM_PREFIX; + break; + } + + return eap_sim_db_get_next(data, 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 + * @data: Private data pointer from eap_sim_db_init() + * @method: EAP method (SIM/AKA/AKA') * Returns: Next reauth_id (allocated string) or %NULL on failure * * This function is used to generate a fast re-authentication identity for @@ -876,19 +1008,31 @@ char * eap_sim_db_get_next_pseudonym(void *priv, int aka) * has been completed successfully. Caller is responsible for freeing the * returned buffer. */ -char * eap_sim_db_get_next_reauth_id(void *priv, int aka) +char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data, + enum eap_sim_db_method method) { - 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); + char prefix = EAP_SIM_REAUTH_ID_PREFIX; + + switch (method) { + case EAP_SIM_DB_SIM: + prefix = EAP_SIM_REAUTH_ID_PREFIX; + break; + case EAP_SIM_DB_AKA: + prefix = EAP_AKA_REAUTH_ID_PREFIX; + break; + case EAP_SIM_DB_AKA_PRIME: + prefix = EAP_AKA_PRIME_REAUTH_ID_PREFIX; + break; + } + + return eap_sim_db_get_next(data, 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 + * @data: Private data pointer from eap_sim_db_init() + * @permanent: Permanent username * @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. @@ -897,20 +1041,22 @@ char * eap_sim_db_get_next_reauth_id(void *priv, int aka) * 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) +int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, 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); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent " + "username '%s'", pseudonym, permanent); /* 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); - +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_add_pseudonym(data, permanent, pseudonym); +#endif /* CONFIG_SQLITE */ + for (p = data->pseudonyms; p; p = p->next) { + if (os_strcmp(permanent, p->permanent) == 0) + break; + } if (p) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " "pseudonym: %s", p->pseudonym); @@ -926,14 +1072,12 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, } p->next = data->pseudonyms; - p->identity = os_malloc(identity_len); - if (p->identity == NULL) { + p->permanent = os_strdup(permanent); + if (p->permanent == 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; @@ -943,18 +1087,16 @@ int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, 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) +eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, + const char *permanent, + 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); + for (r = data->reauths; r; r = r->next) { + if (os_strcmp(r->permanent, permanent) == 0) + break; + } if (r) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " @@ -969,14 +1111,12 @@ eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, } r->next = data->reauths; - r->identity = os_malloc(identity_len); - if (r->identity == NULL) { + r->permanent = os_strdup(permanent); + if (r->permanent == 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"); @@ -991,7 +1131,7 @@ eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, /** * 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) + * @permanent: Permanent username * @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 @@ -1004,31 +1144,34 @@ eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity, * 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) +int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, + 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); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " + "identity '%s'", reauth_id, permanent); + +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_add_reauth(data, permanent, reauth_id, counter, mk, + NULL, NULL, NULL); +#endif /* CONFIG_SQLITE */ + r = eap_sim_db_add_reauth_data(data, permanent, 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_AKA_PRIME +#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 + * @data: Private data pointer from eap_sim_db_init() + * @permanent: Permanent username * @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. @@ -1042,91 +1185,105 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity, * 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) +int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data, + const char *permanent, 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); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add reauth_id '%s' for permanent " + "identity '%s'", reauth_id, permanent); + +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_add_reauth(data, permanent, reauth_id, counter, NULL, + k_encr, k_aut, k_re); +#endif /* CONFIG_SQLITE */ + r = eap_sim_db_add_reauth_data(data, permanent, 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_AKA_PRIME */ +#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 + * @data: Private data pointer from eap_sim_db_init() + * @pseudonym: Pseudonym username + * Returns: Pointer to permanent username or %NULL if not found */ -const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, - size_t identity_len, size_t *len) +const char * +eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym) { - struct eap_sim_db_data *data = priv; struct eap_sim_pseudonym *p; - if (identity == NULL) - return NULL; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_get_pseudonym(data, pseudonym); +#endif /* CONFIG_SQLITE */ - 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; + p = data->pseudonyms; + while (p) { + if (os_strcmp(p->pseudonym, pseudonym) == 0) + return p->permanent; + p = p->next; + } - *len = p->identity_len; - return p->identity; + return NULL; } /** * 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 + * @data: Private data pointer from eap_sim_db_init() + * @reauth_id: Fast re-authentication username * 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) +eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, + const char *reauth_id) { - 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); +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_get_reauth(data, reauth_id); +#endif /* CONFIG_SQLITE */ + + r = data->reauths; + while (r) { + if (os_strcmp(r->reauth_id, reauth_id) == 0) + break; + r = r->next; + } + return r; } /** * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry - * @priv: Private data pointer from eap_sim_db_init() + * @data: 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) +void eap_sim_db_remove_reauth(struct eap_sim_db_data *data, + struct eap_sim_reauth *reauth) { - struct eap_sim_db_data *data = priv; struct eap_sim_reauth *r, *prev = NULL; +#ifdef CONFIG_SQLITE + if (data->sqlite_db) { + db_remove_reauth(data, reauth); + return; + } +#endif /* CONFIG_SQLITE */ r = data->reauths; while (r) { if (r == reauth) { @@ -1145,9 +1302,8 @@ void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) /** * 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 + * @data: Private data pointer from eap_sim_db_init() + * @username: Permanent username (prefix | IMSI) * @_rand: Buffer for RAND value * @autn: Buffer for AUTN value * @ik: Buffer for IK value @@ -1160,9 +1316,6 @@ void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) * 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 @@ -1171,40 +1324,29 @@ void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) * 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) +int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, + 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]; + const char *imsi; + size_t imsi_len; - 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); + if (username == NULL || + (username[0] != EAP_AKA_PERMANENT_PREFIX && + username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || + username[1] == '\0' || os_strlen(username) > sizeof(entry->imsi)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", + username); return EAP_SIM_DB_FAILURE; } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI", - identity, identity_len); + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", + imsi); - entry = eap_sim_db_get_pending(data, identity, identity_len, 1); + entry = eap_sim_db_get_pending(data, imsi, 1); if (entry) { if (entry->state == FAILURE) { os_free(entry); @@ -1235,14 +1377,15 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, return EAP_SIM_DB_FAILURE; } + imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); - if (len < 0 || len + identity_len >= sizeof(msg)) + if (len < 0 || len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; - os_memcpy(msg + len, identity, identity_len); - len += identity_len; + os_memcpy(msg + len, imsi, imsi_len); + len += imsi_len; - wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " - "data for IMSI", identity, identity_len); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " + "data for IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return EAP_SIM_DB_FAILURE; @@ -1250,10 +1393,8 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, 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; + os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi)); entry->cb_session_ctx = cb_session_ctx; entry->state = PENDING; eap_sim_db_add_pending(data, entry); @@ -1265,9 +1406,8 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, /** * 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 + * @data: Private data pointer from eap_sim_db_init() + * @username: Permanent username * @auts: AUTS value from the peer * @_rand: RAND value used in the rejected message * Returns: 0 on success, -1 on failure @@ -1278,42 +1418,35 @@ int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, * 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) +int eap_sim_db_resynchronize(struct eap_sim_db_data *data, + const char *username, + const u8 *auts, const u8 *_rand) { - struct eap_sim_db_data *data = priv; - size_t i; + const char *imsi; + size_t imsi_len; - 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); + if (username == NULL || + (username[0] != EAP_AKA_PERMANENT_PREFIX && + username[0] != EAP_AKA_PRIME_PERMANENT_PREFIX) || + username[1] == '\0' || os_strlen(username) > 20) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: unexpected username '%s'", + username); return -1; } + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", + imsi); if (data->sock >= 0) { char msg[100]; int len, ret; + imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); - if (len < 0 || len + identity_len >= sizeof(msg)) + if (len < 0 || len + imsi_len >= sizeof(msg)) return -1; - os_memcpy(msg + len, identity, identity_len); - len += identity_len; + os_memcpy(msg + len, imsi, imsi_len); + len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " "); if (ret < 0 || (size_t) ret >= sizeof(msg) - len) @@ -1327,11 +1460,35 @@ int eap_sim_db_resynchronize(void *priv, const u8 *identity, 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); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " + "IMSI '%s'", imsi); if (eap_sim_db_send(data, msg, len) < 0) return -1; } return 0; } + + +/** + * sim_get_username - Extract username from SIM identity + * @identity: Identity + * @identity_len: Identity length + * Returns: Allocated buffer with the username part of the identity + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +char * sim_get_username(const u8 *identity, size_t identity_len) +{ + size_t pos; + + if (identity == NULL) + return NULL; + + for (pos = 0; pos < identity_len; pos++) { + if (identity[pos] == '@' || identity[pos] == '\0') + break; + } + + return dup_binstr(identity, pos); +} diff --git a/contrib/hostapd/src/eap_server/eap_sim_db.h b/contrib/hostapd/src/eap_server/eap_sim_db.h index 66221817b2..53a1a7c3b4 100644 --- a/contrib/hostapd/src/eap_server/eap_sim_db.h +++ b/contrib/hostapd/src/eap_server/eap_sim_db.h @@ -1,22 +1,14 @@ /* * hostapd / EAP-SIM database/authenticator gateway - * Copyright (c) 2005-2007, Jouni Malinen + * Copyright (c) 2005-2008, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAP_SIM_DB_H #define EAP_SIM_DB_H -#ifdef EAP_SIM - #include "eap_common/eap_sim_common.h" /* Identity prefixes */ @@ -26,49 +18,57 @@ #define EAP_AKA_PERMANENT_PREFIX '0' #define EAP_AKA_PSEUDONYM_PREFIX '2' #define EAP_AKA_REAUTH_ID_PREFIX '4' +#define EAP_AKA_PRIME_PERMANENT_PREFIX '6' +#define EAP_AKA_PRIME_PSEUDONYM_PREFIX '7' +#define EAP_AKA_PRIME_REAUTH_ID_PREFIX '8' + +enum eap_sim_db_method { + EAP_SIM_DB_SIM, + EAP_SIM_DB_AKA, + EAP_SIM_DB_AKA_PRIME +}; + +struct eap_sim_db_data; -void * eap_sim_db_init(const char *config, - void (*get_complete_cb)(void *ctx, void *session_ctx), - void *ctx); +struct eap_sim_db_data * +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, +int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, + const char *username, 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_pseudonym(struct eap_sim_db_data *data, + enum eap_sim_db_method method); -char * eap_sim_db_get_next_reauth_id(void *priv, int aka); +char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data, + enum eap_sim_db_method method); -int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, - size_t identity_len, char *pseudonym); +int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, 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); +int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, + char *reauth_id, u16 counter, const u8 *mk); +int eap_sim_db_add_reauth_prime(struct eap_sim_db_data *data, + const char *permanent, + 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); +const char * eap_sim_db_get_permanent(struct eap_sim_db_data *data, + const char *pseudonym); struct eap_sim_reauth { struct eap_sim_reauth *next; - u8 *identity; - size_t identity_len; - char *reauth_id; + char *permanent; /* Permanent username */ + char *reauth_id; /* Fast re-authentication username */ 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]; @@ -76,32 +76,20 @@ struct eap_sim_reauth { }; struct eap_sim_reauth * -eap_sim_db_get_reauth_entry(void *priv, const u8 *identity, - size_t identity_len); +eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, + const char *reauth_id); -void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth); +void eap_sim_db_remove_reauth(struct eap_sim_db_data *data, + 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_get_aka_auth(struct eap_sim_db_data *data, const char *username, + 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, +int eap_sim_db_resynchronize(struct eap_sim_db_data *data, + const char *username, const u8 *auts, const u8 *_rand); -#else /* EAP_SIM */ -static inline void * -eap_sim_db_init(const char *config, - void (*get_complete_cb)(void *ctx, void *session_ctx), - void *ctx) -{ - return (void *) 1; -} - -static inline void eap_sim_db_deinit(void *priv) -{ -} -#endif /* EAP_SIM */ +char * sim_get_username(const u8 *identity, size_t identity_len); #endif /* EAP_SIM_DB_H */ diff --git a/contrib/hostapd/src/eap_server/eap_tls_common.h b/contrib/hostapd/src/eap_server/eap_tls_common.h index ce8dd252bc..11f5827513 100644 --- a/contrib/hostapd/src/eap_server/eap_tls_common.h +++ b/contrib/hostapd/src/eap_server/eap_tls_common.h @@ -1,33 +1,54 @@ /* - * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2008, Jouni Malinen + * EAP-TLS/PEAP/TTLS/FAST server common functions + * Copyright (c) 2004-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 *in_buf; - struct wpabuf *out_buf; - size_t out_used; struct wpabuf tmpbuf; }; @@ -41,7 +62,12 @@ struct eap_ssl_data { /* could be up to 128 bytes, but only the first 64 bytes are used */ #define EAP_TLS_KEY_LEN 64 +/* dummy type used as a flag for UNAUTH-TLS */ +#define EAP_UNAUTH_TLS_TYPE 255 + +struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len, + u8 code, u8 identifier); 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); @@ -53,7 +79,7 @@ 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 u8 *plain, size_t plain_len); + 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, diff --git a/contrib/hostapd/src/eap_server/ikev2.c b/contrib/hostapd/src/eap_server/ikev2.c index 46767c5014..512ba30747 100644 --- a/contrib/hostapd/src/eap_server/ikev2.c +++ b/contrib/hostapd/src/eap_server/ikev2.c @@ -2,20 +2,15 @@ * IKEv2 initiator (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "dh_groups.h" +#include "crypto/dh_groups.h" +#include "crypto/random.h" #include "ikev2.h" @@ -403,7 +398,7 @@ static int ikev2_process_ker(struct ikev2_initiator_data *data, } /* RFC 4306, Section 3.4: - * The length of DH public value MUST be equal to the lenght of the + * The length of DH public value MUST be equal to the length of the * prime modulus. */ if (ker_len - 4 != data->dh->prime_len) { @@ -995,7 +990,7 @@ static int ikev2_build_kei(struct ikev2_initiator_data *data, */ wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); wpabuf_put_buf(msg, pv); - os_free(pv); + wpabuf_free(pv); plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; WPA_PUT_BE16(phdr->payload_length, plen); @@ -1100,7 +1095,7 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data) 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)) + if (random_get_bytes(data->i_nonce, data->i_nonce_len)) return NULL; wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len); @@ -1148,7 +1143,7 @@ static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data) if (data->shared_secret == NULL) return NULL; data->shared_secret_len = 16; - if (os_get_random(data->shared_secret, 16)) + if (random_get_bytes(data->shared_secret, 16)) return NULL; } else { os_free(data->shared_secret); diff --git a/contrib/hostapd/src/eap_server/ikev2.h b/contrib/hostapd/src/eap_server/ikev2.h index 8349fbe62d..051a93869c 100644 --- a/contrib/hostapd/src/eap_server/ikev2.h +++ b/contrib/hostapd/src/eap_server/ikev2.h @@ -2,14 +2,8 @@ * IKEv2 initiator (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef IKEV2_H diff --git a/contrib/hostapd/src/eap_server/tncs.c b/contrib/hostapd/src/eap_server/tncs.c index 21d83b3bd9..e429f1e67f 100644 --- a/contrib/hostapd/src/eap_server/tncs.c +++ b/contrib/hostapd/src/eap_server/tncs.c @@ -2,14 +2,8 @@ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) * Copyright (c) 2007-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -234,11 +228,11 @@ TNC_Result TNC_TNCS_ReportMessageTypes( return TNC_RESULT_INVALID_PARAMETER; os_free(imv->supported_types); imv->supported_types = - os_malloc(typeCount * sizeof(TNC_MessageTypeList)); + os_malloc(typeCount * sizeof(TNC_MessageType)); if (imv->supported_types == NULL) return TNC_RESULT_FATAL; os_memcpy(imv->supported_types, supportedTypes, - typeCount * sizeof(TNC_MessageTypeList)); + typeCount * sizeof(TNC_MessageType)); imv->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; @@ -857,12 +851,10 @@ enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs, unsigned char *decoded; size_t decoded_len; - buf = os_malloc(len + 1); + buf = dup_binstr(msg, len); if (buf == NULL) return TNCCS_PROCESS_ERROR; - os_memcpy(buf, msg, len); - buf[len] = '\0'; start = os_strstr(buf, ""); if (start == NULL || end == NULL || start > end) { @@ -1231,6 +1223,7 @@ void tncs_global_deinit(void) } os_free(tncs_global_data); + tncs_global_data = NULL; } diff --git a/contrib/hostapd/src/eap_server/tncs.h b/contrib/hostapd/src/eap_server/tncs.h index 18a3a1fa3c..ac7251bf8c 100644 --- a/contrib/hostapd/src/eap_server/tncs.h +++ b/contrib/hostapd/src/eap_server/tncs.h @@ -2,14 +2,8 @@ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) * Copyright (c) 2007-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TNCS_H diff --git a/contrib/hostapd/src/eapol_auth/eapol_auth_dump.c b/contrib/hostapd/src/eapol_auth/eapol_auth_dump.c new file mode 100644 index 0000000000..6c6969b5c9 --- /dev/null +++ b/contrib/hostapd/src/eapol_auth/eapol_auth_dump.c @@ -0,0 +1,289 @@ +/* + * IEEE 802.1X-2004 Authenticator - State dump + * Copyright (c) 2002-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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"; + } +} + + +int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, + size_t buflen) +{ + char *pos, *end; + int ret; + + pos = buf; + end = pos + buflen; + + ret = os_snprintf(pos, end - pos, "aWhile=%d\nquietWhile=%d\n" + "reAuthWhen=%d\n", + sm->aWhile, sm->quietWhile, sm->reAuthWhen); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + +#define _SB(b) ((b) ? "TRUE" : "FALSE") + ret = os_snprintf(pos, end - pos, + "authAbort=%s\n" + "authFail=%s\n" + "authPortStatus=%s\n" + "authStart=%s\n" + "authTimeout=%s\n" + "authSuccess=%s\n" + "eapFail=%s\n" + "eapolEap=%s\n" + "eapSuccess=%s\n" + "eapTimeout=%s\n" + "initialize=%s\n" + "keyAvailable=%s\n" + "keyDone=%s\n" + "keyRun=%s\n" + "keyTxEnabled=%s\n" + "portControl=%s\n" + "portEnabled=%s\n" + "portValid=%s\n" + "reAuthenticate=%s\n", + _SB(sm->authAbort), + _SB(sm->authFail), + port_state_txt(sm->authPortStatus), + _SB(sm->authStart), + _SB(sm->authTimeout), + _SB(sm->authSuccess), + _SB(sm->eap_if->eapFail), + _SB(sm->eapolEap), + _SB(sm->eap_if->eapSuccess), + _SB(sm->eap_if->eapTimeout), + _SB(sm->initialize), + _SB(sm->eap_if->eapKeyAvailable), + _SB(sm->keyDone), _SB(sm->keyRun), + _SB(sm->keyTxEnabled), + port_type_txt(sm->portControl), + _SB(sm->eap_if->portEnabled), + _SB(sm->portValid), + _SB(sm->reAuthenticate)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "auth_pae_state=%s\n" + "eapolLogoff=%s\n" + "eapolStart=%s\n" + "eapRestart=%s\n" + "portMode=%s\n" + "reAuthCount=%d\n" + "quietPeriod=%d\n" + "reAuthMax=%d\n" + "authEntersConnecting=%d\n" + "authEapLogoffsWhileConnecting=%d\n" + "authEntersAuthenticating=%d\n" + "authAuthSuccessesWhileAuthenticating=%d\n" + "authAuthTimeoutsWhileAuthenticating=%d\n" + "authAuthFailWhileAuthenticating=%d\n" + "authAuthEapStartsWhileAuthenticating=%d\n" + "authAuthEapLogoffWhileAuthenticating=%d\n" + "authAuthReauthsWhileAuthenticated=%d\n" + "authAuthEapStartsWhileAuthenticated=%d\n" + "authAuthEapLogoffWhileAuthenticated=%d\n", + auth_pae_state_txt(sm->auth_pae_state), + _SB(sm->eapolLogoff), + _SB(sm->eapolStart), + _SB(sm->eap_if->eapRestart), + port_type_txt(sm->portMode), + sm->reAuthCount, + sm->quietPeriod, sm->reAuthMax, + sm->authEntersConnecting, + sm->authEapLogoffsWhileConnecting, + sm->authEntersAuthenticating, + sm->authAuthSuccessesWhileAuthenticating, + sm->authAuthTimeoutsWhileAuthenticating, + sm->authAuthFailWhileAuthenticating, + sm->authAuthEapStartsWhileAuthenticating, + sm->authAuthEapLogoffWhileAuthenticating, + sm->authAuthReauthsWhileAuthenticated, + sm->authAuthEapStartsWhileAuthenticated, + sm->authAuthEapLogoffWhileAuthenticated); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "be_auth_state=%s\n" + "eapNoReq=%s\n" + "eapReq=%s\n" + "eapResp=%s\n" + "serverTimeout=%d\n" + "backendResponses=%d\n" + "backendAccessChallenges=%d\n" + "backendOtherRequestsToSupplicant=%d\n" + "backendAuthSuccesses=%d\n" + "backendAuthFails=%d\n", + be_auth_state_txt(sm->be_auth_state), + _SB(sm->eap_if->eapNoReq), + _SB(sm->eap_if->eapReq), + _SB(sm->eap_if->eapResp), + sm->serverTimeout, + sm->backendResponses, + sm->backendAccessChallenges, + sm->backendOtherRequestsToSupplicant, + sm->backendAuthSuccesses, + sm->backendAuthFails); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "reauth_timer_state=%s\n" + "reAuthPeriod=%d\n" + "reAuthEnabled=%s\n", + reauth_timer_state_txt(sm->reauth_timer_state), + sm->reAuthPeriod, + _SB(sm->reAuthEnabled)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "auth_key_tx_state=%s\n", + auth_key_tx_state_txt(sm->auth_key_tx_state)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "key_rx_state=%s\n" + "rxKey=%s\n", + key_rx_state_txt(sm->key_rx_state), + _SB(sm->rxKey)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + "ctrl_dir_state=%s\n" + "adminControlledDirections=%s\n" + "operControlledDirections=%s\n" + "operEdge=%s\n", + ctrl_dir_state_txt(sm->ctrl_dir_state), + ctrl_dir_txt(sm->adminControlledDirections), + ctrl_dir_txt(sm->operControlledDirections), + _SB(sm->operEdge)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; +#undef _SB + + return pos - buf; +} diff --git a/contrib/hostapd/hostapd/eapol_sm.c b/contrib/hostapd/src/eapol_auth/eapol_auth_sm.c similarity index 74% rename from contrib/hostapd/hostapd/eapol_sm.c rename to contrib/hostapd/src/eapol_auth/eapol_auth_sm.c index 8e9d56c796..a2577814ec 100644 --- a/contrib/hostapd/hostapd/eapol_sm.c +++ b/contrib/hostapd/src/eapol_auth/eapol_auth_sm.c @@ -1,29 +1,22 @@ /* - * hostapd / IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2008, Jouni Malinen + * IEEE 802.1X-2004 Authenticator - EAPOL state machine + * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" -#include "hostapd.h" -#include "ieee802_1x.h" -#include "eapol_sm.h" +#include "common.h" #include "eloop.h" -#include "wpa.h" -#include "preauth.h" -#include "sta_info.h" -#include "eap_server/eap.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" @@ -34,35 +27,36 @@ static struct eapol_callbacks eapol_cb; /* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ #define setPortAuthorized() \ -sm->eapol->cb.set_port_authorized(sm->hapd, sm->sta, 1) +sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 1) #define setPortUnauthorized() \ -sm->eapol->cb.set_port_authorized(sm->hapd, sm->sta, 0) +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->hapd, sm->sta) -#define txKey() sm->eapol->cb.tx_key(sm->hapd, sm->sta) +#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, logger_level level, + const u8 *addr, eapol_logger_level level, const char *txt) { if (eapol->cb.logger == NULL) return; - eapol->cb.logger(eapol->conf.hapd, addr, level, txt); + eapol->cb.logger(eapol->conf.ctx, addr, level, txt); } static void eapol_auth_vlogger(struct eapol_authenticator *eapol, - const u8 *addr, logger_level level, + const u8 *addr, eapol_logger_level level, const char *fmt, ...) { char *format; @@ -101,7 +95,8 @@ static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm, eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, "Sending canned EAP packet %s (identifier %d)", success ? "SUCCESS" : "FAILURE", eap.identifier); - sm->eapol->cb.eapol_send(sm->hapd, sm->sta, IEEE802_1X_TYPE_EAP_PACKET, + sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, + IEEE802_1X_TYPE_EAP_PACKET, (u8 *) &eap, sizeof(eap)); sm->dot1xAuthEapolFramesTx++; } @@ -129,7 +124,8 @@ static void eapol_auth_tx_req(struct eapol_state_machine *sm) eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, "Sending EAP Packet (identifier %d)", sm->last_eap_id); - sm->eapol->cb.eapol_send(sm->hapd, sm->sta, IEEE802_1X_TYPE_EAP_PACKET, + 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++; @@ -222,7 +218,7 @@ SM_STATE(AUTH_PAE, DISCONNECTED) sm->reAuthCount = 0; sm->eapolLogoff = FALSE; if (!from_initialize) { - sm->eapol->cb.finished(sm->hapd, sm->sta, 0, + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, sm->flags & EAPOL_SM_PREAUTH); } } @@ -272,14 +268,14 @@ SM_STATE(AUTH_PAE, HELD) eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING, "authentication failed - EAP type: %d (%s)", sm->eap_type_authsrv, - eap_type_text(sm->eap_type_authsrv)); + 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_type_text(sm->eap_type_supp)); + eap_server_get_name(0, sm->eap_type_supp)); } - sm->eapol->cb.finished(sm->hapd, sm->sta, 0, + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, sm->flags & EAPOL_SM_PREAUTH); } @@ -298,13 +294,14 @@ SM_STATE(AUTH_PAE, AUTHENTICATED) sm->reAuthCount = 0; if (sm->flags & EAPOL_SM_PREAUTH) extra = " (pre-authentication)"; - else if (wpa_auth_sta_get_pmksa(sm->sta->wpa_sm)) + 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_type_text(sm->eap_type_authsrv), extra); - sm->eapol->cb.finished(sm->hapd, sm->sta, 1, + 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); } @@ -611,7 +608,8 @@ SM_STATE(REAUTH_TIMER, REAUTHENTICATE) SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); sm->reAuthenticate = TRUE; - wpa_auth_sm_event(sm->sta->wpa_sm, WPA_REAUTH_EAPOL); + sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, + EAPOL_AUTH_REAUTHENTICATE); } @@ -664,7 +662,7 @@ SM_STEP(AUTH_KEY_TX) switch (sm->auth_key_tx_state) { case AUTH_KEY_TX_NO_KEY_TRANSMIT: if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable && - sm->keyRun && !wpa_auth_sta_wpa_version(sm->sta->wpa_sm)) + sm->keyRun && !(sm->flags & EAPOL_SM_USES_WPA)) SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); break; case AUTH_KEY_TX_KEY_TRANSMIT: @@ -758,15 +756,15 @@ SM_STEP(CTRL_DIR) struct eapol_state_machine * eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, - int preauth, struct sta_info *sta) + int flags, const struct wpabuf *assoc_wps_ie, + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui) { struct eapol_state_machine *sm; - struct hostapd_data *hapd; /* TODO: to be removed */ struct eap_config eap_conf; if (eapol == NULL) return NULL; - hapd = eapol->conf.hapd; sm = os_zalloc(sizeof(*sm)); if (sm == NULL) { @@ -776,12 +774,10 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, } sm->radius_identifier = -1; os_memcpy(sm->addr, addr, ETH_ALEN); - if (preauth) - sm->flags |= EAPOL_SM_PREAUTH; + sm->flags = flags; - sm->hapd = hapd; sm->eapol = eapol; - sm->sta = sta; + sm->sta = sta_ctx; /* Set default values for state machine constants */ sm->auth_pae_state = AUTH_PAE_INITIALIZE; @@ -804,7 +800,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, sm->portControl = Auto; if (!eapol->conf.wpa && - (hapd->default_wep_key || eapol->conf.individual_wep_key_len > 0)) + (eapol->default_wep_key || eapol->conf.individual_wep_key_len > 0)) sm->keyTxEnabled = TRUE; else sm->keyTxEnabled = FALSE; @@ -816,6 +812,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, 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; @@ -827,7 +824,14 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind; eap_conf.tnc = eapol->conf.tnc; eap_conf.wps = eapol->conf.wps; - eap_conf.assoc_wps_ie = sta->wps_ie; + eap_conf.assoc_wps_ie = assoc_wps_ie; + eap_conf.assoc_p2p_ie = assoc_p2p_ie; + eap_conf.peer_addr = addr; + eap_conf.fragment_size = eapol->conf.fragment_size; + eap_conf.pwd_group = eapol->conf.pwd_group; + eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1; + eap_conf.server_id = eapol->conf.server_id; + eap_conf.server_id_len = eapol->conf.server_id_len; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -837,6 +841,15 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eapol_auth_initialize(sm); + if (identity) { + sm->identity = (u8 *) os_strdup(identity); + if (sm->identity) + sm->identity_len = os_strlen(identity); + } + if (radius_cui) + sm->radius_cui = wpabuf_alloc_copy(radius_cui, + os_strlen(radius_cui)); + return sm; } @@ -857,7 +870,7 @@ void eapol_auth_free(struct eapol_state_machine *sm) static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol, const u8 *addr) { - return eapol->cb.sta_entry_alive(eapol->conf.hapd, addr); + return eapol->cb.sta_entry_alive(eapol->conf.ctx, addr); } @@ -928,14 +941,15 @@ restart: return; } sm->eapol->cb.aaa_send( - sm->hapd, sm->sta, + 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)) - wpa_auth_sm_notify(sm->sta->wpa_sm); + sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, + EAPOL_AUTH_SM_CHANGE); } @@ -965,7 +979,7 @@ void eapol_auth_step(struct eapol_state_machine *sm) } -void eapol_auth_initialize(struct eapol_state_machine *sm) +static void eapol_auth_initialize(struct eapol_state_machine *sm) { sm->initializing = TRUE; /* Initialize the state machines by asserting initialize and then @@ -982,227 +996,13 @@ void eapol_auth_initialize(struct eapol_state_machine *sm) } -#ifdef HOSTAPD_DUMP_STATE -static inline const char * port_type_txt(PortTypes pt) -{ - switch (pt) { - case ForceUnauthorized: return "ForceUnauthorized"; - case ForceAuthorized: return "ForceAuthorized"; - case Auto: return "Auto"; - default: return "Unknown"; - } -} - - -static inline const char * port_state_txt(PortState ps) -{ - switch (ps) { - case Unauthorized: return "Unauthorized"; - case Authorized: return "Authorized"; - default: return "Unknown"; - } -} - - -static inline const char * ctrl_dir_txt(ControlledDirection dir) -{ - switch (dir) { - case Both: return "Both"; - case In: return "In"; - default: return "Unknown"; - } -} - - -static inline const char * auth_pae_state_txt(int s) -{ - switch (s) { - case AUTH_PAE_INITIALIZE: return "INITIALIZE"; - case AUTH_PAE_DISCONNECTED: return "DISCONNECTED"; - case AUTH_PAE_CONNECTING: return "CONNECTING"; - case AUTH_PAE_AUTHENTICATING: return "AUTHENTICATING"; - case AUTH_PAE_AUTHENTICATED: return "AUTHENTICATED"; - case AUTH_PAE_ABORTING: return "ABORTING"; - case AUTH_PAE_HELD: return "HELD"; - case AUTH_PAE_FORCE_AUTH: return "FORCE_AUTH"; - case AUTH_PAE_FORCE_UNAUTH: return "FORCE_UNAUTH"; - case AUTH_PAE_RESTART: return "RESTART"; - default: return "Unknown"; - } -} - - -static inline const char * be_auth_state_txt(int s) -{ - switch (s) { - case BE_AUTH_REQUEST: return "REQUEST"; - case BE_AUTH_RESPONSE: return "RESPONSE"; - case BE_AUTH_SUCCESS: return "SUCCESS"; - case BE_AUTH_FAIL: return "FAIL"; - case BE_AUTH_TIMEOUT: return "TIMEOUT"; - case BE_AUTH_IDLE: return "IDLE"; - case BE_AUTH_INITIALIZE: return "INITIALIZE"; - case BE_AUTH_IGNORE: return "IGNORE"; - default: return "Unknown"; - } -} - - -static inline const char * reauth_timer_state_txt(int s) -{ - switch (s) { - case REAUTH_TIMER_INITIALIZE: return "INITIALIZE"; - case REAUTH_TIMER_REAUTHENTICATE: return "REAUTHENTICATE"; - default: return "Unknown"; - } -} - - -static inline const char * auth_key_tx_state_txt(int s) -{ - switch (s) { - case AUTH_KEY_TX_NO_KEY_TRANSMIT: return "NO_KEY_TRANSMIT"; - case AUTH_KEY_TX_KEY_TRANSMIT: return "KEY_TRANSMIT"; - default: return "Unknown"; - } -} - - -static inline const char * key_rx_state_txt(int s) -{ - switch (s) { - case KEY_RX_NO_KEY_RECEIVE: return "NO_KEY_RECEIVE"; - case KEY_RX_KEY_RECEIVE: return "KEY_RECEIVE"; - default: return "Unknown"; - } -} - - -static inline const char * ctrl_dir_state_txt(int s) -{ - switch (s) { - case CTRL_DIR_FORCE_BOTH: return "FORCE_BOTH"; - case CTRL_DIR_IN_OR_BOTH: return "IN_OR_BOTH"; - default: return "Unknown"; - } -} - - -void eapol_auth_dump_state(FILE *f, const char *prefix, - struct eapol_state_machine *sm) -{ - fprintf(f, "%sEAPOL state machine:\n", prefix); - fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix, - sm->aWhile, sm->quietWhile, sm->reAuthWhen); -#define _SB(b) ((b) ? "TRUE" : "FALSE") - fprintf(f, - "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n" - "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n" - "%s eapSuccess=%s eapTimeout=%s initialize=%s " - "keyAvailable=%s\n" - "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n" - "%s portEnabled=%s portValid=%s reAuthenticate=%s\n", - prefix, _SB(sm->authAbort), _SB(sm->authFail), - port_state_txt(sm->authPortStatus), _SB(sm->authStart), - prefix, _SB(sm->authTimeout), _SB(sm->authSuccess), - _SB(sm->eap_if->eapFail), _SB(sm->eapolEap), - prefix, _SB(sm->eap_if->eapSuccess), - _SB(sm->eap_if->eapTimeout), - _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable), - prefix, _SB(sm->keyDone), _SB(sm->keyRun), - _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), - prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid), - _SB(sm->reAuthenticate)); - - fprintf(f, "%s Authenticator PAE:\n" - "%s state=%s\n" - "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n" - "%s portMode=%s reAuthCount=%d\n" - "%s quietPeriod=%d reAuthMax=%d\n" - "%s authEntersConnecting=%d\n" - "%s authEapLogoffsWhileConnecting=%d\n" - "%s authEntersAuthenticating=%d\n" - "%s authAuthSuccessesWhileAuthenticating=%d\n" - "%s authAuthTimeoutsWhileAuthenticating=%d\n" - "%s authAuthFailWhileAuthenticating=%d\n" - "%s authAuthEapStartsWhileAuthenticating=%d\n" - "%s authAuthEapLogoffWhileAuthenticating=%d\n" - "%s authAuthReauthsWhileAuthenticated=%d\n" - "%s authAuthEapStartsWhileAuthenticated=%d\n" - "%s authAuthEapLogoffWhileAuthenticated=%d\n", - prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix, - _SB(sm->eapolLogoff), _SB(sm->eapolStart), - _SB(sm->eap_if->eapRestart), - prefix, port_type_txt(sm->portMode), sm->reAuthCount, - prefix, sm->quietPeriod, sm->reAuthMax, - prefix, sm->authEntersConnecting, - prefix, sm->authEapLogoffsWhileConnecting, - prefix, sm->authEntersAuthenticating, - prefix, sm->authAuthSuccessesWhileAuthenticating, - prefix, sm->authAuthTimeoutsWhileAuthenticating, - prefix, sm->authAuthFailWhileAuthenticating, - prefix, sm->authAuthEapStartsWhileAuthenticating, - prefix, sm->authAuthEapLogoffWhileAuthenticating, - prefix, sm->authAuthReauthsWhileAuthenticated, - prefix, sm->authAuthEapStartsWhileAuthenticated, - prefix, sm->authAuthEapLogoffWhileAuthenticated); - - fprintf(f, "%s Backend Authentication:\n" - "%s state=%s\n" - "%s eapNoReq=%s eapReq=%s eapResp=%s\n" - "%s serverTimeout=%d\n" - "%s backendResponses=%d\n" - "%s backendAccessChallenges=%d\n" - "%s backendOtherRequestsToSupplicant=%d\n" - "%s backendAuthSuccesses=%d\n" - "%s backendAuthFails=%d\n", - prefix, prefix, - be_auth_state_txt(sm->be_auth_state), - prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq), - _SB(sm->eap_if->eapResp), - prefix, sm->serverTimeout, - prefix, sm->backendResponses, - prefix, sm->backendAccessChallenges, - prefix, sm->backendOtherRequestsToSupplicant, - prefix, sm->backendAuthSuccesses, - prefix, sm->backendAuthFails); - - fprintf(f, "%s Reauthentication Timer:\n" - "%s state=%s\n" - "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix, - reauth_timer_state_txt(sm->reauth_timer_state), prefix, - sm->reAuthPeriod, _SB(sm->reAuthEnabled)); - - fprintf(f, "%s Authenticator Key Transmit:\n" - "%s state=%s\n", prefix, prefix, - auth_key_tx_state_txt(sm->auth_key_tx_state)); - - fprintf(f, "%s Key Receive:\n" - "%s state=%s\n" - "%s rxKey=%s\n", prefix, prefix, - key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey)); - - fprintf(f, "%s Controlled Directions:\n" - "%s state=%s\n" - "%s adminControlledDirections=%s " - "operControlledDirections=%s\n" - "%s operEdge=%s\n", prefix, prefix, - ctrl_dir_state_txt(sm->ctrl_dir_state), - prefix, ctrl_dir_txt(sm->adminControlledDirections), - ctrl_dir_txt(sm->operControlledDirections), - prefix, _SB(sm->operEdge)); -#undef _SB -} -#endif /* HOSTAPD_DUMP_STATE */ - - static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user) { struct eapol_state_machine *sm = ctx; - return sm->eapol->cb.get_eap_user(sm->hapd, identity, identity_len, - phase2, user); + return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, + identity_len, phase2, user); } @@ -1216,14 +1016,14 @@ static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) static struct eapol_callbacks eapol_cb = { - .get_eap_user = eapol_sm_get_eap_user, - .get_eap_req_id_text = eapol_sm_get_eap_req_id_text, + 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) + if (sm == NULL || ctx == NULL || ctx != sm->eap) return -1; eap_sm_pending_cb(sm->eap); @@ -1236,14 +1036,19 @@ int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) static int eapol_auth_conf_clone(struct eapol_auth_config *dst, struct eapol_auth_config *src) { - dst->hapd = src->hapd; + 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); + dst->pwd_group = src->pwd_group; + dst->pbc_in_m1 = src->pbc_in_m1; + dst->server_id = src->server_id; + dst->server_id_len = src->server_id_len; 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) @@ -1257,6 +1062,10 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, } if (src->pac_opaque_encr_key) { dst->pac_opaque_encr_key = os_malloc(16); + if (dst->pac_opaque_encr_key == NULL) { + os_free(dst->eap_req_id_text); + return -1; + } os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, 16); } else @@ -1265,6 +1074,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, 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); + os_free(dst->pac_opaque_encr_key); return -1; } os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, @@ -1276,6 +1086,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, 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->pac_opaque_encr_key); os_free(dst->eap_fast_a_id); return -1; } @@ -1287,6 +1098,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; dst->tnc = src->tnc; dst->wps = src->wps; + dst->fragment_size = src->fragment_size; return 0; } @@ -1318,6 +1130,11 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, 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; @@ -1327,6 +1144,7 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, 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; } @@ -1338,5 +1156,6 @@ void eapol_auth_deinit(struct eapol_authenticator *eapol) return; eapol_auth_conf_free(&eapol->conf); + os_free(eapol->default_wep_key); os_free(eapol); } diff --git a/contrib/hostapd/src/eapol_auth/eapol_auth_sm.h b/contrib/hostapd/src/eapol_auth/eapol_auth_sm.h new file mode 100644 index 0000000000..f0ff4644f5 --- /dev/null +++ b/contrib/hostapd/src/eapol_auth/eapol_auth_sm.h @@ -0,0 +1,90 @@ +/* + * IEEE 802.1X-2004 Authenticator - EAPOL state machine + * Copyright (c) 2002-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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; + int fragment_size; + u16 pwd_group; + int pbc_in_m1; + const u8 *server_id; + size_t server_id_len; + + /* 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, + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui); +void eapol_auth_free(struct eapol_state_machine *sm); +void eapol_auth_step(struct eapol_state_machine *sm); +int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, + size_t buflen); +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); + +#endif /* EAPOL_AUTH_SM_H */ diff --git a/contrib/hostapd/hostapd/eapol_sm.h b/contrib/hostapd/src/eapol_auth/eapol_auth_sm_i.h similarity index 60% rename from contrib/hostapd/hostapd/eapol_sm.h rename to contrib/hostapd/src/eapol_auth/eapol_auth_sm_i.h index 7a13e8e7c0..d7f893a1d6 100644 --- a/contrib/hostapd/hostapd/eapol_sm.h +++ b/contrib/hostapd/src/eapol_auth/eapol_auth_sm_i.h @@ -1,21 +1,16 @@ /* - * hostapd / IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2007, Jouni Malinen + * IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions) + * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#ifndef EAPOL_SM_H -#define EAPOL_SM_H +#ifndef EAPOL_AUTH_SM_I_H +#define EAPOL_AUTH_SM_I_H -#include "defs.h" +#include "common/defs.h" +#include "radius/radius.h" /* IEEE Std 802.1X-2004, Ch. 8.2 */ @@ -25,68 +20,6 @@ typedef enum { Unauthorized = 2, Authorized = 1 } PortState; typedef enum { Both = 0, In = 1 } ControlledDirection; typedef unsigned int Counter; -struct eap_sm; - -struct radius_attr_data { - u8 *data; - size_t len; -}; - -struct radius_class_data { - struct radius_attr_data *attr; - size_t count; -}; - - -struct eapol_auth_config { - int eap_reauth_period; - int wpa; - int individual_wep_key_len; - int eap_server; - void *ssl_ctx; - void *eap_sim_db_priv; - char *eap_req_id_text; /* a copy of this will be allocated */ - size_t eap_req_id_text_len; - u8 *pac_opaque_encr_key; - u8 *eap_fast_a_id; - size_t eap_fast_a_id_len; - char *eap_fast_a_id_info; - int eap_fast_prov; - int pac_key_lifetime; - int pac_key_refresh_time; - int eap_sim_aka_result_ind; - int tnc; - struct wps_context *wps; - - /* - * Pointer to hostapd data. This is a temporary workaround for - * transition phase and will be removed once IEEE 802.1X/EAPOL code is - * separated more cleanly from rest of hostapd. - */ - struct hostapd_data *hapd; -}; - -struct eap_user; - -typedef enum { - EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING -} eapol_logger_level; - -struct eapol_auth_cb { - void (*eapol_send)(void *ctx, void *sta_ctx, u8 type, const u8 *data, - size_t datalen); - void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data, - size_t datalen); - void (*finished)(void *ctx, void *sta_ctx, int success, int preauth); - int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, - int phase2, struct eap_user *user); - int (*sta_entry_alive)(void *ctx, const u8 *addr); - void (*logger)(void *ctx, const u8 *addr, eapol_logger_level level, - const char *txt); - void (*set_port_authorized)(void *ctx, void *sta_ctx, int authorized); - void (*abort_auth)(void *ctx, void *sta_ctx); - void (*tx_key)(void *ctx, void *sta_ctx); -}; /** * struct eapol_authenticator - Global EAPOL authenticator data @@ -94,6 +27,9 @@ struct eapol_auth_cb { struct eapol_authenticator { struct eapol_auth_config conf; struct eapol_auth_cb cb; + + u8 *default_wep_key; + u8 default_wep_key_idx; }; @@ -206,8 +142,6 @@ struct eapol_state_machine { /* Other variables - not defined in IEEE 802.1X */ u8 addr[ETH_ALEN]; /* Supplicant address */ -#define EAPOL_SM_PREAUTH BIT(0) -#define EAPOL_SM_WAIT_START BIT(1) int flags; /* EAPOL_SM_* */ /* EAPOL/AAA <-> EAP full authenticator interface */ @@ -223,6 +157,7 @@ struct eapol_state_machine { * Authentication server */ u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */ struct radius_class_data radius_class; + struct wpabuf *radius_cui; /* Chargeable-User-Identity */ /* Keys for encrypting and signing EAPOL-Key frames */ u8 *eapol_key_sign; @@ -237,24 +172,7 @@ struct eapol_state_machine { struct eapol_authenticator *eapol; - /* Somewhat nasty pointers to global hostapd and STA data to avoid - * passing these to every function */ - struct hostapd_data *hapd; - struct sta_info *sta; + void *sta; /* station context pointer to use in callbacks */ }; - -struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, - struct eapol_auth_cb *cb); -void eapol_auth_deinit(struct eapol_authenticator *eapol); -struct eapol_state_machine * -eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, - int preauth, struct sta_info *sta); -void eapol_auth_free(struct eapol_state_machine *sm); -void eapol_auth_step(struct eapol_state_machine *sm); -void eapol_auth_initialize(struct eapol_state_machine *sm); -void eapol_auth_dump_state(FILE *f, const char *prefix, - struct eapol_state_machine *sm); -int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); - -#endif /* EAPOL_SM_H */ +#endif /* EAPOL_AUTH_SM_I_H */ diff --git a/contrib/hostapd/src/eapol_supp/eapol_supp_sm.c b/contrib/hostapd/src/eapol_supp/eapol_supp_sm.c index d163049cce..cbcde7ec95 100644 --- a/contrib/hostapd/src/eapol_supp/eapol_supp_sm.c +++ b/contrib/hostapd/src/eapol_supp/eapol_supp_sm.c @@ -1,28 +1,23 @@ /* * EAPOL supplicant state machines - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "eapol_supp_sm.h" -#include "eap_peer/eap.h" -#include "eloop.h" -#include "eapol_common.h" -#include "md5.h" -#include "rc4.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 "eap_peer/eap_proxy.h" +#include "eapol_supp_sm.h" #define STATE_MACHINE_DATA struct eapol_sm #define STATE_MACHINE_DEBUG_PREFIX "EAPOL" @@ -142,47 +137,14 @@ struct eapol_sm { Boolean cached_pmk; Boolean unicast_key_received, broadcast_key_received; -}; + Boolean force_authorized_update; -#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 */ +#ifdef CONFIG_EAP_PROXY + Boolean use_eap_proxy; + struct eap_proxy_sm *eap_proxy; +#endif /* CONFIG_EAP_PROXY */ +}; static void eapol_sm_txLogoff(struct eapol_sm *sm); @@ -193,6 +155,8 @@ 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 @@ -249,7 +213,7 @@ 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); } @@ -259,11 +223,20 @@ SM_STATE(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; + + /* + * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so + * allows the timer tick to be stopped more quickly when the port is + * not enabled. Since this variable is used only within HELD state, + * clearing it on initialization does not change actual state machine + * behavior. + */ + sm->heldWhile = 0; } @@ -314,7 +287,7 @@ 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; } @@ -322,7 +295,7 @@ SM_STATE(SUPP_PAE, HELD) 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; } @@ -337,7 +310,7 @@ SM_STATE(SUPP_PAE, RESTART) 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; } @@ -345,7 +318,7 @@ SM_STATE(SUPP_PAE, S_FORCE_AUTH) 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); } @@ -492,6 +465,17 @@ SM_STATE(SUPP_BE, SUCCESS) sm->keyRun = TRUE; sm->suppSuccess = TRUE; +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + if (eap_proxy_key_available(sm->eap_proxy)) { + /* New key received - clear IEEE 802.1X EAPOL-Key replay + * counter */ + sm->replay_counter_valid = FALSE; + } + return; + } +#endif /* CONFIG_EAP_PROXY */ + if (eap_key_available(sm->eap)) { /* New key received - clear IEEE 802.1X EAPOL-Key replay * counter */ @@ -527,6 +511,15 @@ SM_STATE(SUPP_BE, INITIALIZE) SM_ENTRY(SUPP_BE, INITIALIZE); eapol_sm_abortSupp(sm); sm->suppAbort = FALSE; + + /* + * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so + * allows the timer tick to be stopped more quickly when the port is + * not enabled. Since this variable is used only within RECEIVE state, + * clearing it on initialization does not change actual state machine + * behavior. + */ + sm->authWhile = 0; } @@ -553,7 +546,7 @@ SM_STEP(SUPP_BE) * 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 + * eapNoResp should be set in conjunction 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 @@ -644,6 +637,7 @@ struct eap_key_data { static void eapol_sm_processKey(struct eapol_sm *sm) { +#ifndef CONFIG_FIPS struct ieee802_1x_hdr *hdr; struct ieee802_1x_eapol_key *key; struct eap_key_data keydata; @@ -651,6 +645,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; int key_len, res, sign_key_len, encr_key_len; u16 rx_key_length; + size_t plen; wpa_printf(MSG_DEBUG, "EAPOL: processKey"); if (sm->last_rx_key == NULL) @@ -663,9 +658,12 @@ static void eapol_sm_processKey(struct eapol_sm *sm) return; } + if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key)) + 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) { + plen = be_to_host16(hdr->length); + if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) { wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame"); return; } @@ -731,7 +729,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) } wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); - key_len = be_to_host16(hdr->length) - sizeof(*key); + key_len = plen - 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); @@ -802,6 +800,7 @@ static void eapol_sm_processKey(struct eapol_sm *sm) sm->ctx->eapol_done_cb(sm->ctx->ctx); } } +#endif /* CONFIG_FIPS */ } @@ -820,6 +819,19 @@ static void eapol_sm_txSuppRsp(struct eapol_sm *sm) struct wpabuf *resp; wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp"); + +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Get EAP Response from EAP Proxy */ + resp = eap_proxy_get_eapRespData(sm->eap_proxy); + if (resp == NULL) { + wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP Proxy " + "response data not available"); + return; + } + } else +#endif /* CONFIG_EAP_PROXY */ + resp = eap_get_eapRespData(sm->eap); if (resp == NULL) { wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data " @@ -862,6 +874,30 @@ static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) } +static void eapol_sm_set_port_authorized(struct eapol_sm *sm) +{ + int cb; + + cb = sm->suppPortStatus != Authorized || sm->force_authorized_update; + sm->force_authorized_update = FALSE; + sm->suppPortStatus = Authorized; + if (cb && sm->ctx->port_cb) + sm->ctx->port_cb(sm->ctx->ctx, 1); +} + + +static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm) +{ + int cb; + + cb = sm->suppPortStatus != Unauthorized || sm->force_authorized_update; + sm->force_authorized_update = FALSE; + sm->suppPortStatus = Unauthorized; + if (cb && 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() @@ -883,6 +919,13 @@ void eapol_sm_step(struct eapol_sm *sm) SM_STEP_RUN(SUPP_PAE); SM_STEP_RUN(KEY_RX); SM_STEP_RUN(SUPP_BE); +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Drive the EAP proxy state machine */ + if (eap_proxy_sm_step(sm->eap_proxy, sm->eap)) + sm->changed = TRUE; + } else +#endif /* CONFIG_EAP_PROXY */ if (eap_peer_sm_step(sm->eap)) sm->changed = TRUE; if (!sm->changed) @@ -897,9 +940,15 @@ void eapol_sm_step(struct eapol_sm *sm) } if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) { - int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0; + enum eapol_supp_result result; + if (sm->cb_status == EAPOL_CB_SUCCESS) + result = EAPOL_SUPP_RESULT_SUCCESS; + else if (eap_peer_was_failure_expected(sm->eap)) + result = EAPOL_SUPP_RESULT_EXPECTED_FAILURE; + else + result = EAPOL_SUPP_RESULT_FAILURE; sm->cb_status = EAPOL_CB_IN_PROGRESS; - sm->ctx->cb(sm, success, sm->ctx->cb_ctx); + sm->ctx->cb(sm, result, sm->ctx->cb_ctx); } } @@ -1007,6 +1056,21 @@ void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, } +/** + * eapol_sm_get_method_name - Get EAPOL method name + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * Returns: Static string containing name of current eap method or NULL + */ +const char * eapol_sm_get_method_name(struct eapol_sm *sm) +{ + if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED || + sm->suppPortStatus != Authorized) + return NULL; + + return eap_sm_get_method_name(sm->eap); +} + + #ifdef CONFIG_CTRL_IFACE /** * eapol_sm_get_status - Get EAPOL state machine status @@ -1055,6 +1119,13 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, len += ret; } +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) + len += eap_proxy_sm_get_status(sm->eap_proxy, + buf + len, buflen - len, + verbose); + else +#endif /* CONFIG_EAP_PROXY */ len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose); return len; @@ -1199,6 +1270,24 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, switch (hdr->type) { case IEEE802_1X_TYPE_EAP_PACKET: + if (sm->conf.workaround) { + /* + * An AP has been reported to send out EAP message with + * undocumented code 10 at some point near the + * completion of EAP authentication. This can result in + * issues with the unexpected EAP message triggering + * restart of EAPOL authentication. Avoid this by + * skipping the message without advancing the state + * machine. + */ + const struct eap_hdr *ehdr = + (const struct eap_hdr *) (hdr + 1); + if (plen >= sizeof(*ehdr) && ehdr->code == 10) { + wpa_printf(MSG_DEBUG, "EAPOL: Ignore EAP packet with unknown code 10"); + break; + } + } + if (sm->cached_pmk) { /* Trying to use PMKSA caching, but Authenticator did * not seem to have a matching entry. Need to restart @@ -1212,6 +1301,16 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " "frame"); sm->eapolEap = TRUE; +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + eap_proxy_packet_update( + sm->eap_proxy, + wpabuf_mhead_u8(sm->eapReqData), + wpabuf_len(sm->eapReqData)); + wpa_printf(MSG_DEBUG, "EAPOL: eap_proxy " + "EAP Req updated"); + } +#endif /* CONFIG_EAP_PROXY */ eapol_sm_step(sm); } break; @@ -1284,6 +1383,8 @@ void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) return; wpa_printf(MSG_DEBUG, "EAPOL: External notification - " "portEnabled=%d", enabled); + if (sm->portEnabled != enabled) + sm->force_authorized_update = TRUE; sm->portEnabled = enabled; eapol_sm_step(sm); } @@ -1372,6 +1473,9 @@ void eapol_sm_notify_config(struct eapol_sm *sm, return; sm->config = config; +#ifdef CONFIG_EAP_PROXY + sm->use_eap_proxy = eap_proxy_notify_config(sm->eap_proxy, config) > 0; +#endif /* CONFIG_EAP_PROXY */ if (conf == NULL) return; @@ -1380,10 +1484,17 @@ void eapol_sm_notify_config(struct eapol_sm *sm, sm->conf.required_keys = conf->required_keys; sm->conf.fast_reauth = conf->fast_reauth; sm->conf.workaround = conf->workaround; +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Using EAP Proxy, so skip EAP state machine update */ + return; + } +#endif /* CONFIG_EAP_PROXY */ 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); + eap_set_external_sim(sm->eap, conf->external_sim); } } @@ -1404,6 +1515,22 @@ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) const u8 *eap_key; size_t eap_len; +#ifdef CONFIG_EAP_PROXY + if (sm->use_eap_proxy) { + /* Get key from EAP proxy */ + if (sm == NULL || !eap_proxy_key_available(sm->eap_proxy)) { + wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); + return -1; + } + eap_key = eap_proxy_get_eapKeyData(sm->eap_proxy, &eap_len); + if (eap_key == NULL) { + wpa_printf(MSG_DEBUG, "EAPOL: Failed to get " + "eapKeyData"); + return -1; + } + goto key_fetched; + } +#endif /* CONFIG_EAP_PROXY */ if (sm == NULL || !eap_key_available(sm->eap)) { wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available"); return -1; @@ -1413,6 +1540,9 @@ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData"); return -1; } +#ifdef CONFIG_EAP_PROXY +key_fetched: +#endif /* CONFIG_EAP_PROXY */ if (len > eap_len) { wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not " "available (len=%lu)", @@ -1437,6 +1567,10 @@ void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) { if (sm) { sm->userLogoff = logoff; + if (!logoff) { + /* If there is a delayed txStart queued, start now. */ + sm->startWhen = 0; + } eapol_sm_step(sm); } } @@ -1454,9 +1588,7 @@ 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; - sm->portValid = TRUE; + sm->eapSuccess = TRUE; eap_notify_success(sm->eap); eapol_sm_step(sm); } @@ -1491,7 +1623,7 @@ static void eapol_sm_abort_cached(struct eapol_sm *sm) 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. */ @@ -1727,7 +1859,8 @@ static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, switch (variable) { case EAPOL_idleWhile: sm->idleWhile = value; - eapol_enable_timer_tick(sm); + if (sm->idleWhile > 0) + eapol_enable_timer_tick(sm); break; } } @@ -1774,7 +1907,7 @@ static void eapol_sm_notify_pending(void *ctx) #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) -static void eapol_sm_eap_param_needed(void *ctx, const char *field, +static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, const char *txt) { struct eapol_sm *sm = ctx; @@ -1786,6 +1919,35 @@ static void eapol_sm_eap_param_needed(void *ctx, const char *field, #define eapol_sm_eap_param_needed NULL #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject, + const char *cert_hash, + const struct wpabuf *cert) +{ + struct eapol_sm *sm = ctx; + if (sm->ctx->cert_cb) + sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, + cert_hash, cert); +} + + +static void eapol_sm_notify_status(void *ctx, const char *status, + const char *parameter) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->status_cb) + sm->ctx->status_cb(sm->ctx->ctx, status, parameter); +} + + +static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->set_anon_id) + sm->ctx->set_anon_id(sm->ctx->ctx, id, len); +} + static struct eapol_callbacks eapol_cb = { @@ -1798,7 +1960,10 @@ static struct eapol_callbacks eapol_cb = eapol_sm_set_config_blob, eapol_sm_get_config_blob, eapol_sm_notify_pending, - eapol_sm_eap_param_needed + eapol_sm_eap_param_needed, + eapol_sm_notify_cert, + eapol_sm_notify_status, + eapol_sm_set_anon_id }; @@ -1830,12 +1995,11 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) sm->authPeriod = 30; os_memset(&conf, 0, sizeof(conf)); -#ifdef EAP_TLS_OPENSSL conf.opensc_engine_path = ctx->opensc_engine_path; conf.pkcs11_engine_path = ctx->pkcs11_engine_path; conf.pkcs11_module_path = ctx->pkcs11_module_path; -#endif /* EAP_TLS_OPENSSL */ conf.wps = ctx->wps; + conf.cert_in_cb = ctx->cert_in_cb; sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); if (sm->eap == NULL) { @@ -1843,7 +2007,16 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) return NULL; } +#ifdef CONFIG_EAP_PROXY + sm->use_eap_proxy = FALSE; + sm->eap_proxy = eap_proxy_init(sm, &eapol_cb, sm->ctx->msg_ctx); + if (sm->eap_proxy == NULL) { + wpa_printf(MSG_ERROR, "Unable to initialize EAP Proxy"); + } +#endif /* CONFIG_EAP_PROXY */ + /* Initialize EAPOL state machines */ + sm->force_authorized_update = TRUE; sm->initialize = TRUE; eapol_sm_step(sm); sm->initialize = FALSE; @@ -1869,8 +2042,39 @@ void eapol_sm_deinit(struct eapol_sm *sm) eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); eap_peer_sm_deinit(sm->eap); +#ifdef CONFIG_EAP_PROXY + eap_proxy_deinit(sm->eap_proxy); +#endif /* CONFIG_EAP_PROXY */ os_free(sm->last_rx_key); wpabuf_free(sm->eapReqData); os_free(sm->ctx); os_free(sm); } + + +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext) +{ + if (sm && sm->eap) + eap_sm_set_ext_pw_ctx(sm->eap, ext); +} + + +int eapol_sm_failed(struct eapol_sm *sm) +{ + if (sm == NULL) + return 0; + return !sm->eapSuccess && sm->eapFail; +} + + +int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len) +{ +#ifdef CONFIG_EAP_PROXY + if (sm->eap_proxy == NULL) + return -1; + return eap_proxy_get_imsi(sm->eap_proxy, imsi, len); +#else /* CONFIG_EAP_PROXY */ + return -1; +#endif /* CONFIG_EAP_PROXY */ +} diff --git a/contrib/hostapd/src/eapol_supp/eapol_supp_sm.h b/contrib/hostapd/src/eapol_supp/eapol_supp_sm.h index 57d7bc1ab1..934eda0109 100644 --- a/contrib/hostapd/src/eapol_supp/eapol_supp_sm.h +++ b/contrib/hostapd/src/eapol_supp/eapol_supp_sm.h @@ -1,21 +1,15 @@ /* * EAPOL supplicant state machines - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef EAPOL_SUPP_SM_H #define EAPOL_SUPP_SM_H -#include "defs.h" +#include "common/defs.h" typedef enum { Unauthorized, Authorized } PortStatus; typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl; @@ -59,11 +53,22 @@ struct eapol_config { * eap_disabled - Whether EAP is disabled */ int eap_disabled; + + /** + * external_sim - Use external processing for SIM/USIM operations + */ + int external_sim; }; struct eapol_sm; struct wpa_config_blob; +enum eapol_supp_result { + EAPOL_SUPP_RESULT_FAILURE, + EAPOL_SUPP_RESULT_SUCCESS, + EAPOL_SUPP_RESULT_EXPECTED_FAILURE +}; + /** * struct eapol_ctx - Global (for all networks) EAPOL state machine context */ @@ -84,7 +89,7 @@ struct eapol_ctx { /** * 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 + * @result: Whether the authentication was completed successfully * @ctx: Pointer to context data (cb_ctx) * * This optional callback function will be called when the EAPOL @@ -92,7 +97,8 @@ struct eapol_ctx { * 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); + void (*cb)(struct eapol_sm *eapol, enum eapol_supp_result result, + void *ctx); /** * cb_ctx - Callback context for cb() @@ -173,7 +179,6 @@ struct eapol_ctx { */ void (*aborted_cached)(void *ctx); -#ifdef EAP_TLS_OPENSSL /** * opensc_engine_path - Path to the OpenSSL engine for opensc * @@ -198,7 +203,6 @@ struct eapol_ctx { * module is not loaded. */ const char *pkcs11_module_path; -#endif /* EAP_TLS_OPENSSL */ /** * wps - WPS context data @@ -210,15 +214,56 @@ struct eapol_ctx { /** * eap_param_needed - Notify that EAP parameter is needed * @ctx: Callback context (ctx) - * @field: Field name (e.g., "IDENTITY") + * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) * @txt: User readable text describing the required parameter */ - void (*eap_param_needed)(void *ctx, const char *field, + void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type 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); + + /** + * cert_cb - Notification of a peer certificate + * @ctx: Callback context (ctx) + * @depth: Depth in certificate chain (0 = server) + * @subject: Subject of the peer certificate + * @cert_hash: SHA-256 hash of the certificate + * @cert: Peer certificate + */ + void (*cert_cb)(void *ctx, int depth, const char *subject, + const char *cert_hash, const struct wpabuf *cert); + + /** + * cert_in_cb - Include server certificates in callback + */ + int cert_in_cb; + + /** + * status_cb - Notification of a change in EAP status + * @ctx: Callback context (ctx) + * @status: Step in the process of EAP authentication + * @parameter: Step-specific parameter, e.g., EAP method name + */ + void (*status_cb)(void *ctx, const char *status, + const char *parameter); + + /** + * set_anon_id - Set or add anonymous identity + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) + * @len: Length of anonymous identity in octets + */ + void (*set_anon_id)(void *ctx, const u8 *id, size_t len); }; struct eap_peer_config; +struct ext_password_data; #ifdef IEEE8021X_EAPOL struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx); @@ -250,6 +295,11 @@ 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); +const char * eapol_sm_get_method_name(struct eapol_sm *sm); +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext); +int eapol_sm_failed(struct eapol_sm *sm); +int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len); #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { @@ -337,6 +387,18 @@ static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm) { } +static inline const char * eapol_sm_get_method_name(struct eapol_sm *sm) +{ + return NULL; +} +static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, + struct ext_password_data *ext) +{ +} +static inline int eapol_sm_failed(struct eapol_sm *sm) +{ + return 0; +} #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SUPP_SM_H */ diff --git a/contrib/hostapd/src/hlr_auc_gw/hlr_auc_gw.c b/contrib/hostapd/src/hlr_auc_gw/hlr_auc_gw.c deleted file mode 100644 index e318903543..0000000000 --- a/contrib/hostapd/src/hlr_auc_gw/hlr_auc_gw.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator - * Copyright (c) 2005-2007, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * This is an example implementation of the EAP-SIM/AKA database/authentication - * gateway interface to HLR/AuC. It is expected to be replaced with an - * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or - * a local implementation of SIM triplet and AKA authentication data generator. - * - * hostapd will send SIM/AKA authentication queries over a UNIX domain socket - * to and external program, e.g., this hlr_auc_gw. This interface uses simple - * text-based format: - * - * EAP-SIM / GSM triplet query/response: - * SIM-REQ-AUTH - * SIM-RESP-AUTH Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3] - * SIM-RESP-AUTH FAILURE - * - * EAP-AKA / UMTS query/response: - * AKA-REQ-AUTH - * AKA-RESP-AUTH - * AKA-RESP-AUTH FAILURE - * - * EAP-AKA / UMTS AUTS (re-synchronization): - * AKA-AUTS - * - * IMSI and max_chal are sent as an ASCII string, - * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings. - * - * The example implementation here reads GSM authentication triplets from a - * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex - * strings. This is used to simulate an HLR/AuC. As such, it is not very useful - * for real life authentication, but it is useful both as an example - * implementation and for EAP-SIM testing. - */ - -#include "includes.h" -#include - -#include "common.h" -#include "milenage.h" - -static const char *default_socket_path = "/tmp/hlr_auc_gw.sock"; -static const char *socket_path; -static int serv_sock = -1; - -/* GSM triplets */ -struct gsm_triplet { - struct gsm_triplet *next; - char imsi[20]; - u8 kc[8]; - u8 sres[4]; - u8 _rand[16]; -}; - -static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL; - -/* OPc and AMF parameters for Milenage (Example algorithms for AKA). */ -struct milenage_parameters { - struct milenage_parameters *next; - char imsi[20]; - u8 ki[16]; - u8 opc[16]; - u8 amf[2]; - u8 sqn[6]; -}; - -static struct milenage_parameters *milenage_db = NULL; - -#define EAP_SIM_MAX_CHAL 3 - -#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 - - -static int open_socket(const char *path) -{ - struct sockaddr_un addr; - int s; - - s = socket(PF_UNIX, SOCK_DGRAM, 0); - if (s < 0) { - perror("socket(PF_UNIX)"); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); - if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); - close(s); - return -1; - } - - return s; -} - - -static int read_gsm_triplets(const char *fname) -{ - FILE *f; - char buf[200], *pos, *pos2; - struct gsm_triplet *g = NULL; - int line, ret = 0; - - if (fname == NULL) - return -1; - - f = fopen(fname, "r"); - if (f == NULL) { - printf("Could not open GSM tripler data file '%s'\n", fname); - return -1; - } - - line = 0; - while (fgets(buf, sizeof(buf), f)) { - line++; - - /* Parse IMSI:Kc:SRES:RAND */ - buf[sizeof(buf) - 1] = '\0'; - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0' && *pos != '\n') - pos++; - if (*pos == '\n') - *pos = '\0'; - pos = buf; - if (*pos == '\0') - continue; - - g = os_zalloc(sizeof(*g)); - if (g == NULL) { - ret = -1; - break; - } - - /* IMSI */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) >= sizeof(g->imsi)) { - printf("%s:%d - Too long IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } - os_strlcpy(g->imsi, pos, sizeof(g->imsi)); - pos = pos2 + 1; - - /* Kc */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { - printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; - - /* SRES */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid SRES (%s)\n", fname, line, - pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) { - printf("%s:%d - Invalid SRES (%s)\n", fname, line, - pos); - ret = -1; - break; - } - pos = pos2 + 1; - - /* RAND */ - pos2 = strchr(pos, ':'); - if (pos2) - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) { - printf("%s:%d - Invalid RAND (%s)\n", fname, line, - pos); - ret = -1; - break; - } - pos = pos2 + 1; - - g->next = gsm_db; - gsm_db = g; - g = NULL; - } - free(g); - - fclose(f); - - return ret; -} - - -static struct gsm_triplet * get_gsm_triplet(const char *imsi) -{ - struct gsm_triplet *g = gsm_db_pos; - - while (g) { - if (strcmp(g->imsi, imsi) == 0) { - gsm_db_pos = g->next; - return g; - } - g = g->next; - } - - g = gsm_db; - while (g && g != gsm_db_pos) { - if (strcmp(g->imsi, imsi) == 0) { - gsm_db_pos = g->next; - return g; - } - g = g->next; - } - - return NULL; -} - - -static int read_milenage(const char *fname) -{ - FILE *f; - char buf[200], *pos, *pos2; - struct milenage_parameters *m = NULL; - int line, ret = 0; - - if (fname == NULL) - return -1; - - f = fopen(fname, "r"); - if (f == NULL) { - printf("Could not open Milenage data file '%s'\n", fname); - return -1; - } - - line = 0; - while (fgets(buf, sizeof(buf), f)) { - line++; - - /* Parse IMSI Ki OPc AMF SQN */ - buf[sizeof(buf) - 1] = '\0'; - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0' && *pos != '\n') - pos++; - if (*pos == '\n') - *pos = '\0'; - pos = buf; - if (*pos == '\0') - continue; - - m = os_zalloc(sizeof(*m)); - if (m == NULL) { - ret = -1; - break; - } - - /* IMSI */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) >= sizeof(m->imsi)) { - printf("%s:%d - Too long IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } - os_strlcpy(m->imsi, pos, sizeof(m->imsi)); - pos = pos2 + 1; - - /* Ki */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) { - printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; - - /* OPc */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) { - printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; - - /* AMF */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { - printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; - - /* SQN */ - pos2 = strchr(pos, ' '); - if (pos2) - *pos2 = '\0'; - if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) { - printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; - - m->next = milenage_db; - milenage_db = m; - m = NULL; - } - free(m); - - fclose(f); - - return ret; -} - - -static struct milenage_parameters * get_milenage(const char *imsi) -{ - struct milenage_parameters *m = milenage_db; - - while (m) { - if (strcmp(m->imsi, imsi) == 0) - break; - m = m->next; - } - - return m; -} - - -static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, - char *imsi) -{ - int count, max_chal, ret; - char *pos; - char reply[1000], *rpos, *rend; - struct milenage_parameters *m; - struct gsm_triplet *g; - - reply[0] = '\0'; - - pos = strchr(imsi, ' '); - if (pos) { - *pos++ = '\0'; - max_chal = atoi(pos); - if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL) - max_chal = EAP_SIM_MAX_CHAL; - } else - max_chal = EAP_SIM_MAX_CHAL; - - rend = &reply[sizeof(reply)]; - rpos = reply; - ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi); - if (ret < 0 || ret >= rend - rpos) - return; - rpos += ret; - - m = get_milenage(imsi); - if (m) { - u8 _rand[16], sres[4], kc[8]; - for (count = 0; count < max_chal; count++) { - if (os_get_random(_rand, 16) < 0) - return; - gsm_milenage(m->opc, m->ki, _rand, sres, kc); - *rpos++ = ' '; - rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); - *rpos++ = ':'; - rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4); - *rpos++ = ':'; - rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16); - } - *rpos = '\0'; - goto send; - } - - count = 0; - while (count < max_chal && (g = get_gsm_triplet(imsi))) { - if (strcmp(g->imsi, imsi) != 0) - continue; - - if (rpos < rend) - *rpos++ = ' '; - rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8); - if (rpos < rend) - *rpos++ = ':'; - rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4); - if (rpos < rend) - *rpos++ = ':'; - rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16); - count++; - } - - if (count == 0) { - printf("No GSM triplets found for %s\n", imsi); - ret = snprintf(rpos, rend - rpos, " FAILURE"); - if (ret < 0 || ret >= rend - rpos) - return; - rpos += ret; - } - -send: - printf("Send: %s\n", reply); - if (sendto(s, reply, rpos - reply, 0, - (struct sockaddr *) from, fromlen) < 0) - perror("send"); -} - - -static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, - char *imsi) -{ - /* AKA-RESP-AUTH */ - char reply[1000], *pos, *end; - 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; - int ret; - struct milenage_parameters *m; - - m = get_milenage(imsi); - if (m) { - if (os_get_random(_rand, EAP_AKA_RAND_LEN) < 0) - return; - res_len = EAP_AKA_RES_MAX_LEN; - inc_byte_array(m->sqn, 6); - printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n", - m->sqn[0], m->sqn[1], m->sqn[2], - m->sqn[3], m->sqn[4], m->sqn[5]); - milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand, - autn, ik, ck, res, &res_len); - } else { - printf("Unknown IMSI: %s\n", imsi); -#ifdef AKA_USE_FIXED_TEST_VALUES - printf("Using fixed test values for AKA\n"); - memset(_rand, '0', EAP_AKA_RAND_LEN); - memset(autn, '1', EAP_AKA_AUTN_LEN); - memset(ik, '3', EAP_AKA_IK_LEN); - memset(ck, '4', EAP_AKA_CK_LEN); - memset(res, '2', EAP_AKA_RES_MAX_LEN); - res_len = EAP_AKA_RES_MAX_LEN; -#else /* AKA_USE_FIXED_TEST_VALUES */ - return; -#endif /* AKA_USE_FIXED_TEST_VALUES */ - } - - pos = reply; - end = &reply[sizeof(reply)]; - ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN); - *pos++ = ' '; - pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN); - *pos++ = ' '; - pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN); - *pos++ = ' '; - pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN); - *pos++ = ' '; - pos += wpa_snprintf_hex(pos, end - pos, res, res_len); - - printf("Send: %s\n", reply); - - if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from, - fromlen) < 0) - perror("send"); -} - - -static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen, - char *imsi) -{ - char *auts, *__rand; - u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6]; - struct milenage_parameters *m; - - /* AKA-AUTS */ - - auts = strchr(imsi, ' '); - if (auts == NULL) - return; - *auts++ = '\0'; - - __rand = strchr(auts, ' '); - if (__rand == NULL) - return; - *__rand++ = '\0'; - - printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand); - if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) || - hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) { - printf("Could not parse AUTS/RAND\n"); - return; - } - - m = get_milenage(imsi); - if (m == NULL) { - printf("Unknown IMSI: %s\n", imsi); - return; - } - - if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) { - printf("AKA-AUTS: Incorrect MAC-S\n"); - } else { - memcpy(m->sqn, sqn, 6); - printf("AKA-AUTS: Re-synchronized: " - "SQN=%02x%02x%02x%02x%02x%02x\n", - sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]); - } -} - - -static int process(int s) -{ - char buf[1000]; - struct sockaddr_un from; - socklen_t fromlen; - ssize_t res; - - fromlen = sizeof(from); - res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from, - &fromlen); - if (res < 0) { - perror("recvfrom"); - return -1; - } - - if (res == 0) - return 0; - - if ((size_t) res >= sizeof(buf)) - res = sizeof(buf) - 1; - buf[res] = '\0'; - - printf("Received: %s\n", buf); - - if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0) - sim_req_auth(s, &from, fromlen, buf + 13); - else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0) - aka_req_auth(s, &from, fromlen, buf + 13); - else if (strncmp(buf, "AKA-AUTS ", 9) == 0) - aka_auts(s, &from, fromlen, buf + 9); - else - printf("Unknown request: %s\n", buf); - - return 0; -} - - -static void cleanup(void) -{ - struct gsm_triplet *g, *gprev; - struct milenage_parameters *m, *prev; - - g = gsm_db; - while (g) { - gprev = g; - g = g->next; - free(gprev); - } - - m = milenage_db; - while (m) { - prev = m; - m = m->next; - free(prev); - } - - close(serv_sock); - unlink(socket_path); -} - - -static void handle_term(int sig) -{ - printf("Signal %d - terminate\n", sig); - exit(0); -} - - -static void usage(void) -{ - printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " - "database/authenticator\n" - "Copyright (c) 2005-2007, Jouni Malinen \n" - "\n" - "usage:\n" - "hlr_auc_gw [-h] [-s] [-g] " - "[-m]\n" - "\n" - "options:\n" - " -h = show this usage help\n" - " -s = path for UNIX domain socket\n" - " (default: %s)\n" - " -g = path for GSM authentication triplets\n" - " -m = path for Milenage keys\n", - default_socket_path); -} - - -int main(int argc, char *argv[]) -{ - int c; - char *milenage_file = NULL; - char *gsm_triplet_file = NULL; - - socket_path = default_socket_path; - - for (;;) { - c = getopt(argc, argv, "g:hm:s:"); - if (c < 0) - break; - switch (c) { - case 'g': - gsm_triplet_file = optarg; - break; - case 'h': - usage(); - return 0; - case 'm': - milenage_file = optarg; - break; - case 's': - socket_path = optarg; - break; - default: - usage(); - return -1; - } - } - - if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0) - return -1; - - if (milenage_file && read_milenage(milenage_file) < 0) - return -1; - - serv_sock = open_socket(socket_path); - if (serv_sock < 0) - return -1; - - printf("Listening for requests on %s\n", socket_path); - - atexit(cleanup); - signal(SIGTERM, handle_term); - signal(SIGINT, handle_term); - - for (;;) - process(serv_sock); - - return 0; -} diff --git a/contrib/hostapd/src/hlr_auc_gw/milenage.c b/contrib/hostapd/src/hlr_auc_gw/milenage.c deleted file mode 100644 index 0ce5ef3ff6..0000000000 --- a/contrib/hostapd/src/hlr_auc_gw/milenage.c +++ /dev/null @@ -1,1142 +0,0 @@ -/* - * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) - * Copyright (c) 2006-2007 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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 "milenage.h" -#include "aes_wrap.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 - */ -static 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 - */ -static 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; -} - - -#ifdef TEST_MAIN_MILENAGE - -extern int wpa_debug_level; - - -/** - * milenage_opc - Determine OPc from OP and K - * @op: OP = 128-bit operator variant algorithm configuration field - * @k: K = 128-bit subscriber key - * @opc: Buffer for OPc = 128-bit value derived from OP and K - */ -static void milenage_opc(const u8 *op, const u8 *k, u8 *opc) -{ - int i; - /* OP_C = OP XOR E_K(OP) */ - aes_128_encrypt_block(k, op, opc); - for (i = 0; i < 16; i++) - opc[i] ^= op[i]; -} - - -struct gsm_milenage_test_set { - u8 ki[16]; - u8 rand[16]; - u8 opc[16]; - u8 sres1[4]; - u8 sres2[4]; - u8 kc[8]; -}; - -static const struct gsm_milenage_test_set gsm_test_sets[] = -{ - { - /* 3GPP TS 55.205 v6.0.0 - Test Set 1 */ - { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, - 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc }, - { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, - 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 }, - { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e, - 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf }, - { 0x46, 0xf8, 0x41, 0x6a }, - { 0xa5, 0x42, 0x11, 0xd5 }, - { 0xea, 0xe4, 0xbe, 0x82, 0x3a, 0xf9, 0xa0, 0x8b } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 2 */ - { 0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0, - 0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f }, - { 0x9f, 0x7c, 0x8d, 0x02, 0x1a, 0xcc, 0xf4, 0xdb, - 0x21, 0x3c, 0xcf, 0xf0, 0xc7, 0xf7, 0x1a, 0x6a }, - { 0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6, - 0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3 }, - { 0x8c, 0x30, 0x8a, 0x5e }, - { 0x80, 0x11, 0xc4, 0x8c }, - { 0xaa, 0x01, 0x73, 0x9b, 0x8c, 0xaa, 0x97, 0x6d } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 3 */ - { 0x9e, 0x59, 0x44, 0xae, 0xa9, 0x4b, 0x81, 0x16, - 0x5c, 0x82, 0xfb, 0xf9, 0xf3, 0x2d, 0xb7, 0x51 }, - { 0xce, 0x83, 0xdb, 0xc5, 0x4a, 0xc0, 0x27, 0x4a, - 0x15, 0x7c, 0x17, 0xf8, 0x0d, 0x01, 0x7b, 0xd6 }, - { 0xa6, 0x4a, 0x50, 0x7a, 0xe1, 0xa2, 0xa9, 0x8b, - 0xb8, 0x8e, 0xb4, 0x21, 0x01, 0x35, 0xdc, 0x87 }, - { 0xcf, 0xbc, 0xe3, 0xfe }, - { 0xf3, 0x65, 0xcd, 0x68 }, - { 0x9a, 0x8e, 0xc9, 0x5f, 0x40, 0x8c, 0xc5, 0x07 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 4 */ - { 0x4a, 0xb1, 0xde, 0xb0, 0x5c, 0xa6, 0xce, 0xb0, - 0x51, 0xfc, 0x98, 0xe7, 0x7d, 0x02, 0x6a, 0x84 }, - { 0x74, 0xb0, 0xcd, 0x60, 0x31, 0xa1, 0xc8, 0x33, - 0x9b, 0x2b, 0x6c, 0xe2, 0xb8, 0xc4, 0xa1, 0x86 }, - { 0xdc, 0xf0, 0x7c, 0xbd, 0x51, 0x85, 0x52, 0x90, - 0xb9, 0x2a, 0x07, 0xa9, 0x89, 0x1e, 0x52, 0x3e }, - { 0x96, 0x55, 0xe2, 0x65 }, - { 0x58, 0x60, 0xfc, 0x1b }, - { 0xcd, 0xc1, 0xdc, 0x08, 0x41, 0xb8, 0x1a, 0x22 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 5 */ - { 0x6c, 0x38, 0xa1, 0x16, 0xac, 0x28, 0x0c, 0x45, - 0x4f, 0x59, 0x33, 0x2e, 0xe3, 0x5c, 0x8c, 0x4f }, - { 0xee, 0x64, 0x66, 0xbc, 0x96, 0x20, 0x2c, 0x5a, - 0x55, 0x7a, 0xbb, 0xef, 0xf8, 0xba, 0xbf, 0x63 }, - { 0x38, 0x03, 0xef, 0x53, 0x63, 0xb9, 0x47, 0xc6, - 0xaa, 0xa2, 0x25, 0xe5, 0x8f, 0xae, 0x39, 0x34 }, - { 0x13, 0x68, 0x8f, 0x17 }, - { 0x16, 0xc8, 0x23, 0x3f }, - { 0xdf, 0x75, 0xbc, 0x5e, 0xa8, 0x99, 0x87, 0x9f } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 6 */ - { 0x2d, 0x60, 0x9d, 0x4d, 0xb0, 0xac, 0x5b, 0xf0, - 0xd2, 0xc0, 0xde, 0x26, 0x70, 0x14, 0xde, 0x0d }, - { 0x19, 0x4a, 0xa7, 0x56, 0x01, 0x38, 0x96, 0xb7, - 0x4b, 0x4a, 0x2a, 0x3b, 0x0a, 0xf4, 0x53, 0x9e }, - { 0xc3, 0x5a, 0x0a, 0xb0, 0xbc, 0xbf, 0xc9, 0x25, - 0x2c, 0xaf, 0xf1, 0x5f, 0x24, 0xef, 0xbd, 0xe0 }, - { 0x55, 0x3d, 0x00, 0xb3 }, - { 0x8c, 0x25, 0xa1, 0x6c }, - { 0x84, 0xb4, 0x17, 0xae, 0x3a, 0xea, 0xb4, 0xf3 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 7 */ - { 0xa5, 0x30, 0xa7, 0xfe, 0x42, 0x8f, 0xad, 0x10, - 0x82, 0xc4, 0x5e, 0xdd, 0xfc, 0xe1, 0x38, 0x84 }, - { 0x3a, 0x4c, 0x2b, 0x32, 0x45, 0xc5, 0x0e, 0xb5, - 0xc7, 0x1d, 0x08, 0x63, 0x93, 0x95, 0x76, 0x4d }, - { 0x27, 0x95, 0x3e, 0x49, 0xbc, 0x8a, 0xf6, 0xdc, - 0xc6, 0xe7, 0x30, 0xeb, 0x80, 0x28, 0x6b, 0xe3 }, - { 0x59, 0xf1, 0xa4, 0x4a }, - { 0xa6, 0x32, 0x41, 0xe1 }, - { 0x3b, 0x4e, 0x24, 0x4c, 0xdc, 0x60, 0xce, 0x03 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 8 */ - { 0xd9, 0x15, 0x1c, 0xf0, 0x48, 0x96, 0xe2, 0x58, - 0x30, 0xbf, 0x2e, 0x08, 0x26, 0x7b, 0x83, 0x60 }, - { 0xf7, 0x61, 0xe5, 0xe9, 0x3d, 0x60, 0x3f, 0xeb, - 0x73, 0x0e, 0x27, 0x55, 0x6c, 0xb8, 0xa2, 0xca }, - { 0xc4, 0xc9, 0x3e, 0xff, 0xe8, 0xa0, 0x81, 0x38, - 0xc2, 0x03, 0xd4, 0xc2, 0x7c, 0xe4, 0xe3, 0xd9 }, - { 0x50, 0x58, 0x88, 0x61 }, - { 0x4a, 0x90, 0xb2, 0x17 }, - { 0x8d, 0x4e, 0xc0, 0x1d, 0xe5, 0x97, 0xac, 0xfe } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 9 */ - { 0xa0, 0xe2, 0x97, 0x1b, 0x68, 0x22, 0xe8, 0xd3, - 0x54, 0xa1, 0x8c, 0xc2, 0x35, 0x62, 0x4e, 0xcb }, - { 0x08, 0xef, 0xf8, 0x28, 0xb1, 0x3f, 0xdb, 0x56, - 0x27, 0x22, 0xc6, 0x5c, 0x7f, 0x30, 0xa9, 0xb2 }, - { 0x82, 0xa2, 0x6f, 0x22, 0xbb, 0xa9, 0xe9, 0x48, - 0x8f, 0x94, 0x9a, 0x10, 0xd9, 0x8e, 0x9c, 0xc4 }, - { 0xcd, 0xe6, 0xb0, 0x27 }, - { 0x4b, 0xc2, 0x21, 0x2d }, - { 0xd8, 0xde, 0xbc, 0x4f, 0xfb, 0xcd, 0x60, 0xaa } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 10 */ - { 0x0d, 0xa6, 0xf7, 0xba, 0x86, 0xd5, 0xea, 0xc8, - 0xa1, 0x9c, 0xf5, 0x63, 0xac, 0x58, 0x64, 0x2d }, - { 0x67, 0x9a, 0xc4, 0xdb, 0xac, 0xd7, 0xd2, 0x33, - 0xff, 0x9d, 0x68, 0x06, 0xf4, 0x14, 0x9c, 0xe3 }, - { 0x0d, 0xb1, 0x07, 0x1f, 0x87, 0x67, 0x56, 0x2c, - 0xa4, 0x3a, 0x0a, 0x64, 0xc4, 0x1e, 0x8d, 0x08 }, - { 0x02, 0xd1, 0x3a, 0xcd }, - { 0x6f, 0xc3, 0x0f, 0xee }, - { 0xf0, 0xea, 0xa5, 0x0a, 0x1e, 0xdc, 0xeb, 0xb7 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 11 */ - { 0x77, 0xb4, 0x58, 0x43, 0xc8, 0x8e, 0x58, 0xc1, - 0x0d, 0x20, 0x26, 0x84, 0x51, 0x5e, 0xd4, 0x30 }, - { 0x4c, 0x47, 0xeb, 0x30, 0x76, 0xdc, 0x55, 0xfe, - 0x51, 0x06, 0xcb, 0x20, 0x34, 0xb8, 0xcd, 0x78 }, - { 0xd4, 0x83, 0xaf, 0xae, 0x56, 0x24, 0x09, 0xa3, - 0x26, 0xb5, 0xbb, 0x0b, 0x20, 0xc4, 0xd7, 0x62 }, - { 0x44, 0x38, 0x9d, 0x01 }, - { 0xae, 0xfa, 0x35, 0x7b }, - { 0x82, 0xdb, 0xab, 0x7f, 0x83, 0xf0, 0x63, 0xda } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 12 */ - { 0x72, 0x9b, 0x17, 0x72, 0x92, 0x70, 0xdd, 0x87, - 0xcc, 0xdf, 0x1b, 0xfe, 0x29, 0xb4, 0xe9, 0xbb }, - { 0x31, 0x1c, 0x4c, 0x92, 0x97, 0x44, 0xd6, 0x75, - 0xb7, 0x20, 0xf3, 0xb7, 0xe9, 0xb1, 0xcb, 0xd0 }, - { 0x22, 0x8c, 0x2f, 0x2f, 0x06, 0xac, 0x32, 0x68, - 0xa9, 0xe6, 0x16, 0xee, 0x16, 0xdb, 0x4b, 0xa1 }, - { 0x03, 0xe0, 0xfd, 0x84 }, - { 0x98, 0xdb, 0xbd, 0x09 }, - { 0x3c, 0x66, 0xcb, 0x98, 0xca, 0xb2, 0xd3, 0x3d } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 13 */ - { 0xd3, 0x2d, 0xd2, 0x3e, 0x89, 0xdc, 0x66, 0x23, - 0x54, 0xca, 0x12, 0xeb, 0x79, 0xdd, 0x32, 0xfa }, - { 0xcf, 0x7d, 0x0a, 0xb1, 0xd9, 0x43, 0x06, 0x95, - 0x0b, 0xf1, 0x20, 0x18, 0xfb, 0xd4, 0x68, 0x87 }, - { 0xd2, 0x2a, 0x4b, 0x41, 0x80, 0xa5, 0x32, 0x57, - 0x08, 0xa5, 0xff, 0x70, 0xd9, 0xf6, 0x7e, 0xc7 }, - { 0xbe, 0x73, 0xb3, 0xdc }, - { 0xaf, 0x4a, 0x41, 0x1e }, - { 0x96, 0x12, 0xb5, 0xd8, 0x8a, 0x41, 0x30, 0xbb } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 14 */ - { 0xaf, 0x7c, 0x65, 0xe1, 0x92, 0x72, 0x21, 0xde, - 0x59, 0x11, 0x87, 0xa2, 0xc5, 0x98, 0x7a, 0x53 }, - { 0x1f, 0x0f, 0x85, 0x78, 0x46, 0x4f, 0xd5, 0x9b, - 0x64, 0xbe, 0xd2, 0xd0, 0x94, 0x36, 0xb5, 0x7a }, - { 0xa4, 0xcf, 0x5c, 0x81, 0x55, 0xc0, 0x8a, 0x7e, - 0xff, 0x41, 0x8e, 0x54, 0x43, 0xb9, 0x8e, 0x55 }, - { 0x8f, 0xe0, 0x19, 0xc7 }, - { 0x7b, 0xff, 0xa5, 0xc2 }, - { 0x75, 0xa1, 0x50, 0xdf, 0x3c, 0x6a, 0xed, 0x08 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 15 */ - { 0x5b, 0xd7, 0xec, 0xd3, 0xd3, 0x12, 0x7a, 0x41, - 0xd1, 0x25, 0x39, 0xbe, 0xd4, 0xe7, 0xcf, 0x71 }, - { 0x59, 0xb7, 0x5f, 0x14, 0x25, 0x1c, 0x75, 0x03, - 0x1d, 0x0b, 0xcb, 0xac, 0x1c, 0x2c, 0x04, 0xc7 }, - { 0x76, 0x08, 0x9d, 0x3c, 0x0f, 0xf3, 0xef, 0xdc, - 0x6e, 0x36, 0x72, 0x1d, 0x4f, 0xce, 0xb7, 0x47 }, - { 0x27, 0x20, 0x2b, 0x82 }, - { 0x7e, 0x3f, 0x44, 0xc7 }, - { 0xb7, 0xf9, 0x2e, 0x42, 0x6a, 0x36, 0xfe, 0xc5 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 16 */ - { 0x6c, 0xd1, 0xc6, 0xce, 0xb1, 0xe0, 0x1e, 0x14, - 0xf1, 0xb8, 0x23, 0x16, 0xa9, 0x0b, 0x7f, 0x3d }, - { 0xf6, 0x9b, 0x78, 0xf3, 0x00, 0xa0, 0x56, 0x8b, - 0xce, 0x9f, 0x0c, 0xb9, 0x3c, 0x4b, 0xe4, 0xc9 }, - { 0xa2, 0x19, 0xdc, 0x37, 0xf1, 0xdc, 0x7d, 0x66, - 0x73, 0x8b, 0x58, 0x43, 0xc7, 0x99, 0xf2, 0x06 }, - { 0xdd, 0xd7, 0xef, 0xe6 }, - { 0x70, 0xf6, 0xbd, 0xb9 }, - { 0x88, 0xd9, 0xde, 0x10, 0xa2, 0x20, 0x04, 0xc5 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 17 */ - { 0xb7, 0x3a, 0x90, 0xcb, 0xcf, 0x3a, 0xfb, 0x62, - 0x2d, 0xba, 0x83, 0xc5, 0x8a, 0x84, 0x15, 0xdf }, - { 0xb1, 0x20, 0xf1, 0xc1, 0xa0, 0x10, 0x2a, 0x2f, - 0x50, 0x7d, 0xd5, 0x43, 0xde, 0x68, 0x28, 0x1f }, - { 0xdf, 0x0c, 0x67, 0x86, 0x8f, 0xa2, 0x5f, 0x74, - 0x8b, 0x70, 0x44, 0xc6, 0xe7, 0xc2, 0x45, 0xb8 }, - { 0x67, 0xe4, 0xff, 0x3f }, - { 0x47, 0x9d, 0xd2, 0x5c }, - { 0xa8, 0x19, 0xe5, 0x77, 0xa8, 0xd6, 0x17, 0x5b } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 18 */ - { 0x51, 0x22, 0x25, 0x02, 0x14, 0xc3, 0x3e, 0x72, - 0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0 }, - { 0x81, 0xe9, 0x2b, 0x6c, 0x0e, 0xe0, 0xe1, 0x2e, - 0xbc, 0xeb, 0xa8, 0xd9, 0x2a, 0x99, 0xdf, 0xa5 }, - { 0x98, 0x1d, 0x46, 0x4c, 0x7c, 0x52, 0xeb, 0x6e, - 0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf }, - { 0x8a, 0x3b, 0x8d, 0x17 }, - { 0x28, 0xd7, 0xb0, 0xf2 }, - { 0x9a, 0x8d, 0x0e, 0x88, 0x3f, 0xf0, 0x88, 0x7a } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 19 */ - { 0x90, 0xdc, 0xa4, 0xed, 0xa4, 0x5b, 0x53, 0xcf, - 0x0f, 0x12, 0xd7, 0xc9, 0xc3, 0xbc, 0x6a, 0x89 }, - { 0x9f, 0xdd, 0xc7, 0x20, 0x92, 0xc6, 0xad, 0x03, - 0x6b, 0x6e, 0x46, 0x47, 0x89, 0x31, 0x5b, 0x78 }, - { 0xcb, 0x9c, 0xcc, 0xc4, 0xb9, 0x25, 0x8e, 0x6d, - 0xca, 0x47, 0x60, 0x37, 0x9f, 0xb8, 0x25, 0x81 }, - { 0xdf, 0x58, 0x52, 0x2f }, - { 0xa9, 0x51, 0x00, 0xe2 }, - { 0xed, 0x29, 0xb2, 0xf1, 0xc2, 0x7f, 0x9f, 0x34 } - } -}; - -#define NUM_GSM_TESTS (sizeof(gsm_test_sets) / sizeof(gsm_test_sets[0])) - - -struct milenage_test_set { - u8 k[16]; - u8 rand[16]; - u8 sqn[6]; - u8 amf[2]; - u8 op[16]; - u8 opc[16]; - u8 f1[8]; - u8 f1star[8]; - u8 f2[8]; - u8 f3[16]; - u8 f4[16]; - u8 f5[6]; - u8 f5star[6]; -}; - -static const struct milenage_test_set test_sets[] = -{ - { - /* 3GPP TS 35.208 v6.0.0 - 4.3.1 Test Set 1 */ - { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, - 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc }, - { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, - 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 }, - { 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 }, - { 0xb9, 0xb9 }, - { 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6, - 0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18 }, - { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e, - 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf }, - { 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 }, - { 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 }, - { 0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf }, - { 0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05, - 0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb }, - { 0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04, - 0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41 }, - { 0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70 }, - { 0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.2 Test Set 2 */ - { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, - 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc }, - { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, - 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 }, - { 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 }, - { 0xb9, 0xb9 }, - { 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6, - 0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18 }, - { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e, - 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf }, - { 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 }, - { 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 }, - { 0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf }, - { 0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05, - 0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb }, - { 0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04, - 0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41 }, - { 0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70 }, - { 0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.3 Test Set 3 */ - { 0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0, - 0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f }, - { 0x9f, 0x7c, 0x8d, 0x02, 0x1a, 0xcc, 0xf4, 0xdb, - 0x21, 0x3c, 0xcf, 0xf0, 0xc7, 0xf7, 0x1a, 0x6a }, - { 0x9d, 0x02, 0x77, 0x59, 0x5f, 0xfc }, - { 0x72, 0x5c }, - { 0xdb, 0xc5, 0x9a, 0xdc, 0xb6, 0xf9, 0xa0, 0xef, - 0x73, 0x54, 0x77, 0xb7, 0xfa, 0xdf, 0x83, 0x74 }, - { 0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6, - 0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3 }, - { 0x9c, 0xab, 0xc3, 0xe9, 0x9b, 0xaf, 0x72, 0x81 }, - { 0x95, 0x81, 0x4b, 0xa2, 0xb3, 0x04, 0x43, 0x24 }, - { 0x80, 0x11, 0xc4, 0x8c, 0x0c, 0x21, 0x4e, 0xd2 }, - { 0x5d, 0xbd, 0xbb, 0x29, 0x54, 0xe8, 0xf3, 0xcd, - 0xe6, 0x65, 0xb0, 0x46, 0x17, 0x9a, 0x50, 0x98 }, - { 0x59, 0xa9, 0x2d, 0x3b, 0x47, 0x6a, 0x04, 0x43, - 0x48, 0x70, 0x55, 0xcf, 0x88, 0xb2, 0x30, 0x7b }, - { 0x33, 0x48, 0x4d, 0xc2, 0x13, 0x6b }, - { 0xde, 0xac, 0xdd, 0x84, 0x8c, 0xc6 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.4 Test Set 4 */ - { 0x9e, 0x59, 0x44, 0xae, 0xa9, 0x4b, 0x81, 0x16, - 0x5c, 0x82, 0xfb, 0xf9, 0xf3, 0x2d, 0xb7, 0x51 }, - { 0xce, 0x83, 0xdb, 0xc5, 0x4a, 0xc0, 0x27, 0x4a, - 0x15, 0x7c, 0x17, 0xf8, 0x0d, 0x01, 0x7b, 0xd6 }, - { 0x0b, 0x60, 0x4a, 0x81, 0xec, 0xa8 }, - { 0x9e, 0x09 }, - { 0x22, 0x30, 0x14, 0xc5, 0x80, 0x66, 0x94, 0xc0, - 0x07, 0xca, 0x1e, 0xee, 0xf5, 0x7f, 0x00, 0x4f }, - { 0xa6, 0x4a, 0x50, 0x7a, 0xe1, 0xa2, 0xa9, 0x8b, - 0xb8, 0x8e, 0xb4, 0x21, 0x01, 0x35, 0xdc, 0x87 }, - { 0x74, 0xa5, 0x82, 0x20, 0xcb, 0xa8, 0x4c, 0x49 }, - { 0xac, 0x2c, 0xc7, 0x4a, 0x96, 0x87, 0x18, 0x37 }, - { 0xf3, 0x65, 0xcd, 0x68, 0x3c, 0xd9, 0x2e, 0x96 }, - { 0xe2, 0x03, 0xed, 0xb3, 0x97, 0x15, 0x74, 0xf5, - 0xa9, 0x4b, 0x0d, 0x61, 0xb8, 0x16, 0x34, 0x5d }, - { 0x0c, 0x45, 0x24, 0xad, 0xea, 0xc0, 0x41, 0xc4, - 0xdd, 0x83, 0x0d, 0x20, 0x85, 0x4f, 0xc4, 0x6b }, - { 0xf0, 0xb9, 0xc0, 0x8a, 0xd0, 0x2e }, - { 0x60, 0x85, 0xa8, 0x6c, 0x6f, 0x63 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.5 Test Set 5 */ - { 0x4a, 0xb1, 0xde, 0xb0, 0x5c, 0xa6, 0xce, 0xb0, - 0x51, 0xfc, 0x98, 0xe7, 0x7d, 0x02, 0x6a, 0x84 }, - { 0x74, 0xb0, 0xcd, 0x60, 0x31, 0xa1, 0xc8, 0x33, - 0x9b, 0x2b, 0x6c, 0xe2, 0xb8, 0xc4, 0xa1, 0x86 }, - { 0xe8, 0x80, 0xa1, 0xb5, 0x80, 0xb6 }, - { 0x9f, 0x07 }, - { 0x2d, 0x16, 0xc5, 0xcd, 0x1f, 0xdf, 0x6b, 0x22, - 0x38, 0x35, 0x84, 0xe3, 0xbe, 0xf2, 0xa8, 0xd8 }, - { 0xdc, 0xf0, 0x7c, 0xbd, 0x51, 0x85, 0x52, 0x90, - 0xb9, 0x2a, 0x07, 0xa9, 0x89, 0x1e, 0x52, 0x3e }, - { 0x49, 0xe7, 0x85, 0xdd, 0x12, 0x62, 0x6e, 0xf2 }, - { 0x9e, 0x85, 0x79, 0x03, 0x36, 0xbb, 0x3f, 0xa2 }, - { 0x58, 0x60, 0xfc, 0x1b, 0xce, 0x35, 0x1e, 0x7e }, - { 0x76, 0x57, 0x76, 0x6b, 0x37, 0x3d, 0x1c, 0x21, - 0x38, 0xf3, 0x07, 0xe3, 0xde, 0x92, 0x42, 0xf9 }, - { 0x1c, 0x42, 0xe9, 0x60, 0xd8, 0x9b, 0x8f, 0xa9, - 0x9f, 0x27, 0x44, 0xe0, 0x70, 0x8c, 0xcb, 0x53 }, - { 0x31, 0xe1, 0x1a, 0x60, 0x91, 0x18 }, - { 0xfe, 0x25, 0x55, 0xe5, 0x4a, 0xa9 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.6 Test Set 6 */ - { 0x6c, 0x38, 0xa1, 0x16, 0xac, 0x28, 0x0c, 0x45, - 0x4f, 0x59, 0x33, 0x2e, 0xe3, 0x5c, 0x8c, 0x4f }, - { 0xee, 0x64, 0x66, 0xbc, 0x96, 0x20, 0x2c, 0x5a, - 0x55, 0x7a, 0xbb, 0xef, 0xf8, 0xba, 0xbf, 0x63 }, - { 0x41, 0x4b, 0x98, 0x22, 0x21, 0x81 }, - { 0x44, 0x64 }, - { 0x1b, 0xa0, 0x0a, 0x1a, 0x7c, 0x67, 0x00, 0xac, - 0x8c, 0x3f, 0xf3, 0xe9, 0x6a, 0xd0, 0x87, 0x25 }, - { 0x38, 0x03, 0xef, 0x53, 0x63, 0xb9, 0x47, 0xc6, - 0xaa, 0xa2, 0x25, 0xe5, 0x8f, 0xae, 0x39, 0x34 }, - { 0x07, 0x8a, 0xdf, 0xb4, 0x88, 0x24, 0x1a, 0x57 }, - { 0x80, 0x24, 0x6b, 0x8d, 0x01, 0x86, 0xbc, 0xf1 }, - { 0x16, 0xc8, 0x23, 0x3f, 0x05, 0xa0, 0xac, 0x28 }, - { 0x3f, 0x8c, 0x75, 0x87, 0xfe, 0x8e, 0x4b, 0x23, - 0x3a, 0xf6, 0x76, 0xae, 0xde, 0x30, 0xba, 0x3b }, - { 0xa7, 0x46, 0x6c, 0xc1, 0xe6, 0xb2, 0xa1, 0x33, - 0x7d, 0x49, 0xd3, 0xb6, 0x6e, 0x95, 0xd7, 0xb4 }, - { 0x45, 0xb0, 0xf6, 0x9a, 0xb0, 0x6c }, - { 0x1f, 0x53, 0xcd, 0x2b, 0x11, 0x13 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.7 Test Set 7 */ - { 0x2d, 0x60, 0x9d, 0x4d, 0xb0, 0xac, 0x5b, 0xf0, - 0xd2, 0xc0, 0xde, 0x26, 0x70, 0x14, 0xde, 0x0d }, - { 0x19, 0x4a, 0xa7, 0x56, 0x01, 0x38, 0x96, 0xb7, - 0x4b, 0x4a, 0x2a, 0x3b, 0x0a, 0xf4, 0x53, 0x9e }, - { 0x6b, 0xf6, 0x94, 0x38, 0xc2, 0xe4 }, - { 0x5f, 0x67 }, - { 0x46, 0x0a, 0x48, 0x38, 0x54, 0x27, 0xaa, 0x39, - 0x26, 0x4a, 0xac, 0x8e, 0xfc, 0x9e, 0x73, 0xe8 }, - { 0xc3, 0x5a, 0x0a, 0xb0, 0xbc, 0xbf, 0xc9, 0x25, - 0x2c, 0xaf, 0xf1, 0x5f, 0x24, 0xef, 0xbd, 0xe0 }, - { 0xbd, 0x07, 0xd3, 0x00, 0x3b, 0x9e, 0x5c, 0xc3 }, - { 0xbc, 0xb6, 0xc2, 0xfc, 0xad, 0x15, 0x22, 0x50 }, - { 0x8c, 0x25, 0xa1, 0x6c, 0xd9, 0x18, 0xa1, 0xdf }, - { 0x4c, 0xd0, 0x84, 0x60, 0x20, 0xf8, 0xfa, 0x07, - 0x31, 0xdd, 0x47, 0xcb, 0xdc, 0x6b, 0xe4, 0x11 }, - { 0x88, 0xab, 0x80, 0xa4, 0x15, 0xf1, 0x5c, 0x73, - 0x71, 0x12, 0x54, 0xa1, 0xd3, 0x88, 0xf6, 0x96 }, - { 0x7e, 0x64, 0x55, 0xf3, 0x4c, 0xf3 }, - { 0xdc, 0x6d, 0xd0, 0x1e, 0x8f, 0x15 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.8 Test Set 8 */ - { 0xa5, 0x30, 0xa7, 0xfe, 0x42, 0x8f, 0xad, 0x10, - 0x82, 0xc4, 0x5e, 0xdd, 0xfc, 0xe1, 0x38, 0x84 }, - { 0x3a, 0x4c, 0x2b, 0x32, 0x45, 0xc5, 0x0e, 0xb5, - 0xc7, 0x1d, 0x08, 0x63, 0x93, 0x95, 0x76, 0x4d }, - { 0xf6, 0x3f, 0x5d, 0x76, 0x87, 0x84 }, - { 0xb9, 0x0e }, - { 0x51, 0x1c, 0x6c, 0x4e, 0x83, 0xe3, 0x8c, 0x89, - 0xb1, 0xc5, 0xd8, 0xdd, 0xe6, 0x24, 0x26, 0xfa }, - { 0x27, 0x95, 0x3e, 0x49, 0xbc, 0x8a, 0xf6, 0xdc, - 0xc6, 0xe7, 0x30, 0xeb, 0x80, 0x28, 0x6b, 0xe3 }, - { 0x53, 0x76, 0x1f, 0xbd, 0x67, 0x9b, 0x0b, 0xad }, - { 0x21, 0xad, 0xfd, 0x33, 0x4a, 0x10, 0xe7, 0xce }, - { 0xa6, 0x32, 0x41, 0xe1, 0xff, 0xc3, 0xe5, 0xab }, - { 0x10, 0xf0, 0x5b, 0xab, 0x75, 0xa9, 0x9a, 0x5f, - 0xbb, 0x98, 0xa9, 0xc2, 0x87, 0x67, 0x9c, 0x3b }, - { 0xf9, 0xec, 0x08, 0x65, 0xeb, 0x32, 0xf2, 0x23, - 0x69, 0xca, 0xde, 0x40, 0xc5, 0x9c, 0x3a, 0x44 }, - { 0x88, 0x19, 0x6c, 0x47, 0x98, 0x6f }, - { 0xc9, 0x87, 0xa3, 0xd2, 0x31, 0x15 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.9 Test Set 9 */ - { 0xd9, 0x15, 0x1c, 0xf0, 0x48, 0x96, 0xe2, 0x58, - 0x30, 0xbf, 0x2e, 0x08, 0x26, 0x7b, 0x83, 0x60 }, - { 0xf7, 0x61, 0xe5, 0xe9, 0x3d, 0x60, 0x3f, 0xeb, - 0x73, 0x0e, 0x27, 0x55, 0x6c, 0xb8, 0xa2, 0xca }, - { 0x47, 0xee, 0x01, 0x99, 0x82, 0x0a }, - { 0x91, 0x13 }, - { 0x75, 0xfc, 0x22, 0x33, 0xa4, 0x42, 0x94, 0xee, - 0x8e, 0x6d, 0xe2, 0x5c, 0x43, 0x53, 0xd2, 0x6b }, - { 0xc4, 0xc9, 0x3e, 0xff, 0xe8, 0xa0, 0x81, 0x38, - 0xc2, 0x03, 0xd4, 0xc2, 0x7c, 0xe4, 0xe3, 0xd9 }, - { 0x66, 0xcc, 0x4b, 0xe4, 0x48, 0x62, 0xaf, 0x1f }, - { 0x7a, 0x4b, 0x8d, 0x7a, 0x87, 0x53, 0xf2, 0x46 }, - { 0x4a, 0x90, 0xb2, 0x17, 0x1a, 0xc8, 0x3a, 0x76 }, - { 0x71, 0x23, 0x6b, 0x71, 0x29, 0xf9, 0xb2, 0x2a, - 0xb7, 0x7e, 0xa7, 0xa5, 0x4c, 0x96, 0xda, 0x22 }, - { 0x90, 0x52, 0x7e, 0xba, 0xa5, 0x58, 0x89, 0x68, - 0xdb, 0x41, 0x72, 0x73, 0x25, 0xa0, 0x4d, 0x9e }, - { 0x82, 0xa0, 0xf5, 0x28, 0x7a, 0x71 }, - { 0x52, 0x7d, 0xbf, 0x41, 0xf3, 0x5f } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.10 Test Set 10 */ - { 0xa0, 0xe2, 0x97, 0x1b, 0x68, 0x22, 0xe8, 0xd3, - 0x54, 0xa1, 0x8c, 0xc2, 0x35, 0x62, 0x4e, 0xcb }, - { 0x08, 0xef, 0xf8, 0x28, 0xb1, 0x3f, 0xdb, 0x56, - 0x27, 0x22, 0xc6, 0x5c, 0x7f, 0x30, 0xa9, 0xb2 }, - { 0xdb, 0x5c, 0x06, 0x64, 0x81, 0xe0 }, - { 0x71, 0x6b }, - { 0x32, 0x37, 0x92, 0xfa, 0xca, 0x21, 0xfb, 0x4d, - 0x5d, 0x6f, 0x13, 0xc1, 0x45, 0xa9, 0xd2, 0xc1 }, - { 0x82, 0xa2, 0x6f, 0x22, 0xbb, 0xa9, 0xe9, 0x48, - 0x8f, 0x94, 0x9a, 0x10, 0xd9, 0x8e, 0x9c, 0xc4 }, - { 0x94, 0x85, 0xfe, 0x24, 0x62, 0x1c, 0xb9, 0xf6 }, - { 0xbc, 0xe3, 0x25, 0xce, 0x03, 0xe2, 0xe9, 0xb9 }, - { 0x4b, 0xc2, 0x21, 0x2d, 0x86, 0x24, 0x91, 0x0a }, - { 0x08, 0xce, 0xf6, 0xd0, 0x04, 0xec, 0x61, 0x47, - 0x1a, 0x3c, 0x3c, 0xda, 0x04, 0x81, 0x37, 0xfa }, - { 0xed, 0x03, 0x18, 0xca, 0x5d, 0xeb, 0x92, 0x06, - 0x27, 0x2f, 0x6e, 0x8f, 0xa6, 0x4b, 0xa4, 0x11 }, - { 0xa2, 0xf8, 0x58, 0xaa, 0x9e, 0x5d }, - { 0x74, 0xe7, 0x6f, 0xbb, 0xec, 0x38 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.11 Test Set 11 */ - { 0x0d, 0xa6, 0xf7, 0xba, 0x86, 0xd5, 0xea, 0xc8, - 0xa1, 0x9c, 0xf5, 0x63, 0xac, 0x58, 0x64, 0x2d }, - { 0x67, 0x9a, 0xc4, 0xdb, 0xac, 0xd7, 0xd2, 0x33, - 0xff, 0x9d, 0x68, 0x06, 0xf4, 0x14, 0x9c, 0xe3 }, - { 0x6e, 0x23, 0x31, 0xd6, 0x92, 0xad }, - { 0x22, 0x4a }, - { 0x4b, 0x9a, 0x26, 0xfa, 0x45, 0x9e, 0x3a, 0xcb, - 0xff, 0x36, 0xf4, 0x01, 0x5d, 0xe3, 0xbd, 0xc1 }, - { 0x0d, 0xb1, 0x07, 0x1f, 0x87, 0x67, 0x56, 0x2c, - 0xa4, 0x3a, 0x0a, 0x64, 0xc4, 0x1e, 0x8d, 0x08 }, - { 0x28, 0x31, 0xd7, 0xae, 0x90, 0x88, 0xe4, 0x92 }, - { 0x9b, 0x2e, 0x16, 0x95, 0x11, 0x35, 0xd5, 0x23 }, - { 0x6f, 0xc3, 0x0f, 0xee, 0x6d, 0x12, 0x35, 0x23 }, - { 0x69, 0xb1, 0xca, 0xe7, 0xc7, 0x42, 0x9d, 0x97, - 0x5e, 0x24, 0x5c, 0xac, 0xb0, 0x5a, 0x51, 0x7c }, - { 0x74, 0xf2, 0x4e, 0x8c, 0x26, 0xdf, 0x58, 0xe1, - 0xb3, 0x8d, 0x7d, 0xcd, 0x4f, 0x1b, 0x7f, 0xbd }, - { 0x4c, 0x53, 0x9a, 0x26, 0xe1, 0xfa }, - { 0x07, 0x86, 0x1e, 0x12, 0x69, 0x28 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.12 Test Set 12 */ - { 0x77, 0xb4, 0x58, 0x43, 0xc8, 0x8e, 0x58, 0xc1, - 0x0d, 0x20, 0x26, 0x84, 0x51, 0x5e, 0xd4, 0x30 }, - { 0x4c, 0x47, 0xeb, 0x30, 0x76, 0xdc, 0x55, 0xfe, - 0x51, 0x06, 0xcb, 0x20, 0x34, 0xb8, 0xcd, 0x78 }, - { 0xfe, 0x1a, 0x87, 0x31, 0x00, 0x5d }, - { 0xad, 0x25 }, - { 0xbf, 0x32, 0x86, 0xc7, 0xa5, 0x14, 0x09, 0xce, - 0x95, 0x72, 0x4d, 0x50, 0x3b, 0xfe, 0x6e, 0x70 }, - { 0xd4, 0x83, 0xaf, 0xae, 0x56, 0x24, 0x09, 0xa3, - 0x26, 0xb5, 0xbb, 0x0b, 0x20, 0xc4, 0xd7, 0x62 }, - { 0x08, 0x33, 0x2d, 0x7e, 0x9f, 0x48, 0x45, 0x70 }, - { 0xed, 0x41, 0xb7, 0x34, 0x48, 0x9d, 0x52, 0x07 }, - { 0xae, 0xfa, 0x35, 0x7b, 0xea, 0xc2, 0xa8, 0x7a }, - { 0x90, 0x8c, 0x43, 0xf0, 0x56, 0x9c, 0xb8, 0xf7, - 0x4b, 0xc9, 0x71, 0xe7, 0x06, 0xc3, 0x6c, 0x5f }, - { 0xc2, 0x51, 0xdf, 0x0d, 0x88, 0x8d, 0xd9, 0x32, - 0x9b, 0xcf, 0x46, 0x65, 0x5b, 0x22, 0x6e, 0x40 }, - { 0x30, 0xff, 0x25, 0xcd, 0xad, 0xf6 }, - { 0xe8, 0x4e, 0xd0, 0xd4, 0x67, 0x7e } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.13 Test Set 13 */ - { 0x72, 0x9b, 0x17, 0x72, 0x92, 0x70, 0xdd, 0x87, - 0xcc, 0xdf, 0x1b, 0xfe, 0x29, 0xb4, 0xe9, 0xbb }, - { 0x31, 0x1c, 0x4c, 0x92, 0x97, 0x44, 0xd6, 0x75, - 0xb7, 0x20, 0xf3, 0xb7, 0xe9, 0xb1, 0xcb, 0xd0 }, - { 0xc8, 0x5c, 0x4c, 0xf6, 0x59, 0x16 }, - { 0x5b, 0xb2 }, - { 0xd0, 0x4c, 0x9c, 0x35, 0xbd, 0x22, 0x62, 0xfa, - 0x81, 0x0d, 0x29, 0x24, 0xd0, 0x36, 0xfd, 0x13 }, - { 0x22, 0x8c, 0x2f, 0x2f, 0x06, 0xac, 0x32, 0x68, - 0xa9, 0xe6, 0x16, 0xee, 0x16, 0xdb, 0x4b, 0xa1 }, - { 0xff, 0x79, 0x4f, 0xe2, 0xf8, 0x27, 0xeb, 0xf8 }, - { 0x24, 0xfe, 0x4d, 0xc6, 0x1e, 0x87, 0x4b, 0x52 }, - { 0x98, 0xdb, 0xbd, 0x09, 0x9b, 0x3b, 0x40, 0x8d }, - { 0x44, 0xc0, 0xf2, 0x3c, 0x54, 0x93, 0xcf, 0xd2, - 0x41, 0xe4, 0x8f, 0x19, 0x7e, 0x1d, 0x10, 0x12 }, - { 0x0c, 0x9f, 0xb8, 0x16, 0x13, 0x88, 0x4c, 0x25, - 0x35, 0xdd, 0x0e, 0xab, 0xf3, 0xb4, 0x40, 0xd8 }, - { 0x53, 0x80, 0xd1, 0x58, 0xcf, 0xe3 }, - { 0x87, 0xac, 0x3b, 0x55, 0x9f, 0xb6 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.14 Test Set 14 */ - { 0xd3, 0x2d, 0xd2, 0x3e, 0x89, 0xdc, 0x66, 0x23, - 0x54, 0xca, 0x12, 0xeb, 0x79, 0xdd, 0x32, 0xfa }, - { 0xcf, 0x7d, 0x0a, 0xb1, 0xd9, 0x43, 0x06, 0x95, - 0x0b, 0xf1, 0x20, 0x18, 0xfb, 0xd4, 0x68, 0x87 }, - { 0x48, 0x41, 0x07, 0xe5, 0x6a, 0x43 }, - { 0xb5, 0xe6 }, - { 0xfe, 0x75, 0x90, 0x5b, 0x9d, 0xa4, 0x7d, 0x35, - 0x62, 0x36, 0xd0, 0x31, 0x4e, 0x09, 0xc3, 0x2e }, - { 0xd2, 0x2a, 0x4b, 0x41, 0x80, 0xa5, 0x32, 0x57, - 0x08, 0xa5, 0xff, 0x70, 0xd9, 0xf6, 0x7e, 0xc7 }, - { 0xcf, 0x19, 0xd6, 0x2b, 0x6a, 0x80, 0x98, 0x66 }, - { 0x5d, 0x26, 0x95, 0x37, 0xe4, 0x5e, 0x2c, 0xe6 }, - { 0xaf, 0x4a, 0x41, 0x1e, 0x11, 0x39, 0xf2, 0xc2 }, - { 0x5a, 0xf8, 0x6b, 0x80, 0xed, 0xb7, 0x0d, 0xf5, - 0x29, 0x2c, 0xc1, 0x12, 0x1c, 0xba, 0xd5, 0x0c }, - { 0x7f, 0x4d, 0x6a, 0xe7, 0x44, 0x0e, 0x18, 0x78, - 0x9a, 0x8b, 0x75, 0xad, 0x3f, 0x42, 0xf0, 0x3a }, - { 0x21, 0x7a, 0xf4, 0x92, 0x72, 0xad }, - { 0x90, 0x0e, 0x10, 0x1c, 0x67, 0x7e } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.15 Test Set 15 */ - { 0xaf, 0x7c, 0x65, 0xe1, 0x92, 0x72, 0x21, 0xde, - 0x59, 0x11, 0x87, 0xa2, 0xc5, 0x98, 0x7a, 0x53 }, - { 0x1f, 0x0f, 0x85, 0x78, 0x46, 0x4f, 0xd5, 0x9b, - 0x64, 0xbe, 0xd2, 0xd0, 0x94, 0x36, 0xb5, 0x7a }, - { 0x3d, 0x62, 0x7b, 0x01, 0x41, 0x8d }, - { 0x84, 0xf6 }, - { 0x0c, 0x7a, 0xcb, 0x8d, 0x95, 0xb7, 0xd4, 0xa3, - 0x1c, 0x5a, 0xca, 0x6d, 0x26, 0x34, 0x5a, 0x88 }, - { 0xa4, 0xcf, 0x5c, 0x81, 0x55, 0xc0, 0x8a, 0x7e, - 0xff, 0x41, 0x8e, 0x54, 0x43, 0xb9, 0x8e, 0x55 }, - { 0xc3, 0x7c, 0xae, 0x78, 0x05, 0x64, 0x20, 0x32 }, - { 0x68, 0xcd, 0x09, 0xa4, 0x52, 0xd8, 0xdb, 0x7c }, - { 0x7b, 0xff, 0xa5, 0xc2, 0xf4, 0x1f, 0xbc, 0x05 }, - { 0x3f, 0x8c, 0x3f, 0x3c, 0xcf, 0x76, 0x25, 0xbf, - 0x77, 0xfc, 0x94, 0xbc, 0xfd, 0x22, 0xfd, 0x26 }, - { 0xab, 0xcb, 0xae, 0x8f, 0xd4, 0x61, 0x15, 0xe9, - 0x96, 0x1a, 0x55, 0xd0, 0xda, 0x5f, 0x20, 0x78 }, - { 0x83, 0x7f, 0xd7, 0xb7, 0x44, 0x19 }, - { 0x56, 0xe9, 0x7a, 0x60, 0x90, 0xb1 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.16 Test Set 16 */ - { 0x5b, 0xd7, 0xec, 0xd3, 0xd3, 0x12, 0x7a, 0x41, - 0xd1, 0x25, 0x39, 0xbe, 0xd4, 0xe7, 0xcf, 0x71 }, - { 0x59, 0xb7, 0x5f, 0x14, 0x25, 0x1c, 0x75, 0x03, - 0x1d, 0x0b, 0xcb, 0xac, 0x1c, 0x2c, 0x04, 0xc7 }, - { 0xa2, 0x98, 0xae, 0x89, 0x29, 0xdc }, - { 0xd0, 0x56 }, - { 0xf9, 0x67, 0xf7, 0x60, 0x38, 0xb9, 0x20, 0xa9, - 0xcd, 0x25, 0xe1, 0x0c, 0x08, 0xb4, 0x99, 0x24 }, - { 0x76, 0x08, 0x9d, 0x3c, 0x0f, 0xf3, 0xef, 0xdc, - 0x6e, 0x36, 0x72, 0x1d, 0x4f, 0xce, 0xb7, 0x47 }, - { 0xc3, 0xf2, 0x5c, 0xd9, 0x43, 0x09, 0x10, 0x7e }, - { 0xb0, 0xc8, 0xba, 0x34, 0x36, 0x65, 0xaf, 0xcc }, - { 0x7e, 0x3f, 0x44, 0xc7, 0x59, 0x1f, 0x6f, 0x45 }, - { 0xd4, 0x2b, 0x2d, 0x61, 0x5e, 0x49, 0xa0, 0x3a, - 0xc2, 0x75, 0xa5, 0xae, 0xf9, 0x7a, 0xf8, 0x92 }, - { 0x0b, 0x3f, 0x8d, 0x02, 0x4f, 0xe6, 0xbf, 0xaf, - 0xaa, 0x98, 0x2b, 0x8f, 0x82, 0xe3, 0x19, 0xc2 }, - { 0x5b, 0xe1, 0x14, 0x95, 0x52, 0x5d }, - { 0x4d, 0x6a, 0x34, 0xa1, 0xe4, 0xeb } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.17 Test Set 17 */ - { 0x6c, 0xd1, 0xc6, 0xce, 0xb1, 0xe0, 0x1e, 0x14, - 0xf1, 0xb8, 0x23, 0x16, 0xa9, 0x0b, 0x7f, 0x3d }, - { 0xf6, 0x9b, 0x78, 0xf3, 0x00, 0xa0, 0x56, 0x8b, - 0xce, 0x9f, 0x0c, 0xb9, 0x3c, 0x4b, 0xe4, 0xc9 }, - { 0xb4, 0xfc, 0xe5, 0xfe, 0xb0, 0x59 }, - { 0xe4, 0xbb }, - { 0x07, 0x8b, 0xfc, 0xa9, 0x56, 0x46, 0x59, 0xec, - 0xd8, 0x85, 0x1e, 0x84, 0xe6, 0xc5, 0x9b, 0x48 }, - { 0xa2, 0x19, 0xdc, 0x37, 0xf1, 0xdc, 0x7d, 0x66, - 0x73, 0x8b, 0x58, 0x43, 0xc7, 0x99, 0xf2, 0x06 }, - { 0x69, 0xa9, 0x08, 0x69, 0xc2, 0x68, 0xcb, 0x7b }, - { 0x2e, 0x0f, 0xdc, 0xf9, 0xfd, 0x1c, 0xfa, 0x6a }, - { 0x70, 0xf6, 0xbd, 0xb9, 0xad, 0x21, 0x52, 0x5f }, - { 0x6e, 0xda, 0xf9, 0x9e, 0x5b, 0xd9, 0xf8, 0x5d, - 0x5f, 0x36, 0xd9, 0x1c, 0x12, 0x72, 0xfb, 0x4b }, - { 0xd6, 0x1c, 0x85, 0x3c, 0x28, 0x0d, 0xd9, 0xc4, - 0x6f, 0x29, 0x7b, 0xae, 0xc3, 0x86, 0xde, 0x17 }, - { 0x1c, 0x40, 0x8a, 0x85, 0x8b, 0x3e }, - { 0xaa, 0x4a, 0xe5, 0x2d, 0xaa, 0x30 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.18 Test Set 18 */ - { 0xb7, 0x3a, 0x90, 0xcb, 0xcf, 0x3a, 0xfb, 0x62, - 0x2d, 0xba, 0x83, 0xc5, 0x8a, 0x84, 0x15, 0xdf }, - { 0xb1, 0x20, 0xf1, 0xc1, 0xa0, 0x10, 0x2a, 0x2f, - 0x50, 0x7d, 0xd5, 0x43, 0xde, 0x68, 0x28, 0x1f }, - { 0xf1, 0xe8, 0xa5, 0x23, 0xa3, 0x6d }, - { 0x47, 0x1b }, - { 0xb6, 0x72, 0x04, 0x7e, 0x00, 0x3b, 0xb9, 0x52, - 0xdc, 0xa6, 0xcb, 0x8a, 0xf0, 0xe5, 0xb7, 0x79 }, - { 0xdf, 0x0c, 0x67, 0x86, 0x8f, 0xa2, 0x5f, 0x74, - 0x8b, 0x70, 0x44, 0xc6, 0xe7, 0xc2, 0x45, 0xb8 }, - { 0xeb, 0xd7, 0x03, 0x41, 0xbc, 0xd4, 0x15, 0xb0 }, - { 0x12, 0x35, 0x9f, 0x5d, 0x82, 0x22, 0x0c, 0x14 }, - { 0x47, 0x9d, 0xd2, 0x5c, 0x20, 0x79, 0x2d, 0x63 }, - { 0x66, 0x19, 0x5d, 0xbe, 0xd0, 0x31, 0x32, 0x74, - 0xc5, 0xca, 0x77, 0x66, 0x61, 0x5f, 0xa2, 0x5e }, - { 0x66, 0xbe, 0xc7, 0x07, 0xeb, 0x2a, 0xfc, 0x47, - 0x6d, 0x74, 0x08, 0xa8, 0xf2, 0x92, 0x7b, 0x36 }, - { 0xae, 0xfd, 0xaa, 0x5d, 0xdd, 0x99 }, - { 0x12, 0xec, 0x2b, 0x87, 0xfb, 0xb1 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.19 Test Set 19 */ - { 0x51, 0x22, 0x25, 0x02, 0x14, 0xc3, 0x3e, 0x72, - 0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0 }, - { 0x81, 0xe9, 0x2b, 0x6c, 0x0e, 0xe0, 0xe1, 0x2e, - 0xbc, 0xeb, 0xa8, 0xd9, 0x2a, 0x99, 0xdf, 0xa5 }, - { 0x16, 0xf3, 0xb3, 0xf7, 0x0f, 0xc2 }, - { 0xc3, 0xab }, - { 0xc9, 0xe8, 0x76, 0x32, 0x86, 0xb5, 0xb9, 0xff, - 0xbd, 0xf5, 0x6e, 0x12, 0x97, 0xd0, 0x88, 0x7b }, - { 0x98, 0x1d, 0x46, 0x4c, 0x7c, 0x52, 0xeb, 0x6e, - 0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf }, - { 0x2a, 0x5c, 0x23, 0xd1, 0x5e, 0xe3, 0x51, 0xd5 }, - { 0x62, 0xda, 0xe3, 0x85, 0x3f, 0x3a, 0xf9, 0xd2 }, - { 0x28, 0xd7, 0xb0, 0xf2, 0xa2, 0xec, 0x3d, 0xe5 }, - { 0x53, 0x49, 0xfb, 0xe0, 0x98, 0x64, 0x9f, 0x94, - 0x8f, 0x5d, 0x2e, 0x97, 0x3a, 0x81, 0xc0, 0x0f }, - { 0x97, 0x44, 0x87, 0x1a, 0xd3, 0x2b, 0xf9, 0xbb, - 0xd1, 0xdd, 0x5c, 0xe5, 0x4e, 0x3e, 0x2e, 0x5a }, - { 0xad, 0xa1, 0x5a, 0xeb, 0x7b, 0xb8 }, - { 0xd4, 0x61, 0xbc, 0x15, 0x47, 0x5d } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.20 Test Set 20 */ - { 0x90, 0xdc, 0xa4, 0xed, 0xa4, 0x5b, 0x53, 0xcf, - 0x0f, 0x12, 0xd7, 0xc9, 0xc3, 0xbc, 0x6a, 0x89 }, - { 0x9f, 0xdd, 0xc7, 0x20, 0x92, 0xc6, 0xad, 0x03, - 0x6b, 0x6e, 0x46, 0x47, 0x89, 0x31, 0x5b, 0x78 }, - { 0x20, 0xf8, 0x13, 0xbd, 0x41, 0x41 }, - { 0x61, 0xdf }, - { 0x3f, 0xfc, 0xfe, 0x5b, 0x7b, 0x11, 0x11, 0x58, - 0x99, 0x20, 0xd3, 0x52, 0x8e, 0x84, 0xe6, 0x55 }, - { 0xcb, 0x9c, 0xcc, 0xc4, 0xb9, 0x25, 0x8e, 0x6d, - 0xca, 0x47, 0x60, 0x37, 0x9f, 0xb8, 0x25, 0x81 }, - { 0x09, 0xdb, 0x94, 0xea, 0xb4, 0xf8, 0x14, 0x9e }, - { 0xa2, 0x94, 0x68, 0xaa, 0x97, 0x75, 0xb5, 0x27 }, - { 0xa9, 0x51, 0x00, 0xe2, 0x76, 0x09, 0x52, 0xcd }, - { 0xb5, 0xf2, 0xda, 0x03, 0x88, 0x3b, 0x69, 0xf9, - 0x6b, 0xf5, 0x2e, 0x02, 0x9e, 0xd9, 0xac, 0x45 }, - { 0xb4, 0x72, 0x13, 0x68, 0xbc, 0x16, 0xea, 0x67, - 0x87, 0x5c, 0x55, 0x98, 0x68, 0x8b, 0xb0, 0xef }, - { 0x83, 0xcf, 0xd5, 0x4d, 0xb9, 0x13 }, - { 0x4f, 0x20, 0x39, 0x39, 0x2d, 0xdc } - } -}; - -#define NUM_TESTS (sizeof(test_sets) / sizeof(test_sets[0])) - - -int main(int argc, char *argv[]) -{ - u8 buf[16], buf2[16], buf3[16], buf4[16], buf5[16], opc[16]; - u8 auts[14], sqn[6], _rand[16]; - int ret = 0, res, i; - const struct milenage_test_set *t; - size_t res_len; - - wpa_debug_level = 0; - - printf("Milenage test sets\n"); - for (i = 0; i < NUM_TESTS; i++) { - t = &test_sets[i]; - printf("Test Set %d\n", i + 1); - - milenage_opc(t->op, t->k, opc); - if (memcmp(opc, t->opc, 16) != 0) { - printf("- milenage_opc failed\n"); - ret++; - } - - if (milenage_f1(opc, t->k, t->rand, t->sqn, t->amf, buf, buf2) - || memcmp(buf, t->f1, 8) != 0) { - printf("- milenage_f1 failed\n"); - ret++; - } - if (memcmp(buf2, t->f1star, 8) != 0) { - printf("- milenage_f1* failed\n"); - ret++; - } - - if (milenage_f2345(opc, t->k, t->rand, buf, buf2, buf3, buf4, - buf5) || - memcmp(buf, t->f2, 8) != 0) { - printf("- milenage_f2 failed\n"); - ret++; - } - if (memcmp(buf2, t->f3, 16) != 0) { - printf("- milenage_f3 failed\n"); - ret++; - } - if (memcmp(buf3, t->f4, 16) != 0) { - printf("- milenage_f4 failed\n"); - ret++; - } - if (memcmp(buf4, t->f5, 6) != 0) { - printf("- milenage_f5 failed\n"); - ret++; - } - if (memcmp(buf5, t->f5star, 6) != 0) { - printf("- milenage_f5* failed\n"); - ret++; - } - } - - printf("milenage_auts test:\n"); - os_memcpy(auts, "\x4f\x20\x39\x39\x2d\xdd", 6); - os_memcpy(auts + 6, "\x4b\xb4\x31\x6e\xd4\xa1\x46\x88", 8); - res = milenage_auts(t->opc, t->k, t->rand, auts, buf); - printf("AUTS for test set %d: %d / SQN=%02x%02x%02x%02x%02x%02x\n", - i, res, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - if (res) - ret++; - - os_memset(_rand, 0xaa, sizeof(_rand)); - os_memcpy(auts, - "\x43\x68\x1a\xd3\xda\xf0\x06\xbc\xde\x40\x5a\x20\x72\x67", - 14); - res = milenage_auts(t->opc, t->k, _rand, auts, buf); - printf("AUTS from a test USIM: %d / SQN=%02x%02x%02x%02x%02x%02x\n", - res, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - if (res) - ret++; - - printf("milenage_generate test:\n"); - os_memcpy(sqn, "\x00\x00\x00\x00\x40\x44", 6); - os_memcpy(_rand, "\x12\x69\xb8\x23\x41\x39\x35\x66\xfb\x99\x41\xe9\x84" - "\x4f\xe6\x2f", 16); - res_len = 8; - milenage_generate(t->opc, t->amf, t->k, sqn, _rand, buf, buf2, buf3, - buf4, &res_len); - wpa_hexdump(MSG_DEBUG, "SQN", sqn, 6); - wpa_hexdump(MSG_DEBUG, "RAND", _rand, 16); - wpa_hexdump(MSG_DEBUG, "AUTN", buf, 16); - wpa_hexdump(MSG_DEBUG, "IK", buf2, 16); - wpa_hexdump(MSG_DEBUG, "CK", buf3, 16); - wpa_hexdump(MSG_DEBUG, "RES", buf4, res_len); - - printf("GSM-Milenage test sets\n"); - for (i = 0; i < NUM_GSM_TESTS; i++) { - const struct gsm_milenage_test_set *g; - u8 sres[4], kc[8]; - g = &gsm_test_sets[i]; - printf("Test Set %d\n", i + 1); - gsm_milenage(g->opc, g->ki, g->rand, sres, kc); - if (memcmp(g->kc, kc, 8) != 0) { - printf("- gsm_milenage Kc failed\n"); - ret++; - } -#ifdef GSM_MILENAGE_ALT_SRES - if (memcmp(g->sres2, sres, 4) != 0) { - printf("- gsm_milenage SRES#2 failed\n"); - ret++; - } -#else /* GSM_MILENAGE_ALT_SRES */ - if (memcmp(g->sres1, sres, 4) != 0) { - printf("- gsm_milenage SRES#1 failed\n"); - ret++; - } -#endif /* GSM_MILENAGE_ALT_SRES */ - } - - if (ret) - printf("Something failed\n"); - else - printf("OK\n"); - - return ret; -} -#endif /* TEST_MAIN_MILENAGE */ diff --git a/contrib/hostapd/src/l2_packet/l2_packet.h b/contrib/hostapd/src/l2_packet/l2_packet.h index c7b50141e9..dd825b5689 100644 --- a/contrib/hostapd/src/l2_packet/l2_packet.h +++ b/contrib/hostapd/src/l2_packet/l2_packet.h @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet interface definition * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 diff --git a/contrib/hostapd/src/l2_packet/l2_packet_freebsd.c b/contrib/hostapd/src/l2_packet/l2_packet_freebsd.c index d1034aa762..2e9a04c894 100644 --- a/contrib/hostapd/src/l2_packet/l2_packet_freebsd.c +++ b/contrib/hostapd/src/l2_packet/l2_packet_freebsd.c @@ -3,24 +3,22 @@ * Copyright (c) 2003-2005, Jouni Malinen * Copyright (c) 2005, Sam Leffler * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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 software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__GLIBC__) #include #endif /* __APPLE__ */ #include #include +#ifdef __sun__ +#include +#else /* __sun__ */ #include +#endif /* __sun__ */ #include #include @@ -139,6 +137,7 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2, } pcap_freecode(&pcap_fp); +#ifndef __sun__ /* * When libpcap uses BPF we must enable "immediate mode" to * receive frames right away; otherwise the system may @@ -153,6 +152,7 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2, /* XXX should we fail? */ } } +#endif /* __sun__ */ eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), l2_packet_receive, l2, l2->pcap); @@ -163,6 +163,30 @@ static int l2_packet_init_libpcap(struct l2_packet_data *l2, static int eth_get(const char *device, u8 ea[ETH_ALEN]) { +#ifdef __sun__ + dlpi_handle_t dh; + u32 physaddrlen = DLPI_PHYSADDR_MAX; + u8 physaddr[DLPI_PHYSADDR_MAX]; + int retval; + + retval = dlpi_open(device, &dh, 0); + if (retval != DLPI_SUCCESS) { + wpa_printf(MSG_ERROR, "dlpi_open error: %s", + dlpi_strerror(retval)); + return -1; + } + + retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, physaddr, + &physaddrlen); + if (retval != DLPI_SUCCESS) { + wpa_printf(MSG_ERROR, "dlpi_get_physaddr error: %s", + dlpi_strerror(retval)); + dlpi_close(dh); + return -1; + } + os_memcpy(ea, physaddr, ETH_ALEN); + dlpi_close(dh); +#else /* __sun__ */ struct if_msghdr *ifm; struct sockaddr_dl *sdl; u_char *p, *buf; @@ -195,6 +219,7 @@ static int eth_get(const char *device, u8 ea[ETH_ALEN]) errno = ESRCH; return -1; } +#endif /* __sun__ */ return 0; } diff --git a/contrib/hostapd/src/l2_packet/l2_packet_linux.c b/contrib/hostapd/src/l2_packet/l2_packet_linux.c index 48d1bde024..1419830db7 100644 --- a/contrib/hostapd/src/l2_packet/l2_packet_linux.c +++ b/contrib/hostapd/src/l2_packet/l2_packet_linux.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with Linux packet sockets * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -51,7 +45,8 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, if (l2->l2_hdr) { ret = send(l2->fd, buf, len, 0); if (ret < 0) - perror("l2_packet_send - send"); + wpa_printf(MSG_ERROR, "l2_packet_send - send: %s", + strerror(errno)); } else { struct sockaddr_ll ll; os_memset(&ll, 0, sizeof(ll)); @@ -62,8 +57,10 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, 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"); + if (ret < 0) { + wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s", + strerror(errno)); + } } return ret; } @@ -82,7 +79,8 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, &fromlen); if (res < 0) { - perror("l2_packet_receive - recvfrom"); + wpa_printf(MSG_DEBUG, "l2_packet_receive - recvfrom: %s", + strerror(errno)); return; } @@ -111,14 +109,16 @@ struct l2_packet_data * l2_packet_init( l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, htons(protocol)); if (l2->fd < 0) { - perror("socket(PF_PACKET)"); + wpa_printf(MSG_ERROR, "%s: socket(PF_PACKET): %s", + __func__, strerror(errno)); 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]"); + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s", + __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; @@ -130,14 +130,16 @@ struct l2_packet_data * l2_packet_init( 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]"); + wpa_printf(MSG_ERROR, "%s: bind[PF_PACKET]: %s", + __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; } if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) { - perror("ioctl[SIOCGIFHWADDR]"); + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s", + __func__, strerror(errno)); close(l2->fd); os_free(l2); return NULL; @@ -173,14 +175,16 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "%s: socket: %s", + __func__, strerror(errno)); 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]"); + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFADDR]: %s", + __func__, strerror(errno)); close(s); return -1; } diff --git a/contrib/hostapd/src/l2_packet/l2_packet_ndis.c b/contrib/hostapd/src/l2_packet/l2_packet_ndis.c index 7de58808d6..23b8ddcc9e 100644 --- a/contrib/hostapd/src/l2_packet/l2_packet_ndis.c +++ b/contrib/hostapd/src/l2_packet/l2_packet_ndis.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This implementation requires Windows specific event loop implementation, * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with @@ -137,11 +131,17 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, DWORD err = GetLastError(); #ifndef _WIN32_WCE if (err == ERROR_IO_PENDING) { - /* For now, just assume that the packet will be sent in - * time before the next write happens. This could be - * cleaned up at some point to actually wait for - * completion before starting new writes. - */ + 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 */ diff --git a/contrib/hostapd/src/l2_packet/l2_packet_none.c b/contrib/hostapd/src/l2_packet/l2_packet_none.c index 5e3f6e9723..b01e830220 100644 --- a/contrib/hostapd/src/l2_packet/l2_packet_none.c +++ b/contrib/hostapd/src/l2_packet/l2_packet_none.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling example with dummy functions * Copyright (c) 2003-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. * * This file can be used as a starting point for layer2 packet implementation. */ diff --git a/contrib/hostapd/src/l2_packet/l2_packet_pcap.c b/contrib/hostapd/src/l2_packet/l2_packet_pcap.c index 8156e294b1..45aef56bcd 100644 --- a/contrib/hostapd/src/l2_packet/l2_packet_pcap.c +++ b/contrib/hostapd/src/l2_packet/l2_packet_pcap.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/hostapd/src/l2_packet/l2_packet_privsep.c b/contrib/hostapd/src/l2_packet/l2_packet_privsep.c index c0e7c495a1..6b117ca2b9 100644 --- a/contrib/hostapd/src/l2_packet/l2_packet_privsep.c +++ b/contrib/hostapd/src/l2_packet/l2_packet_privsep.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with privilege separation * Copyright (c) 2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,7 +12,7 @@ #include "common.h" #include "eloop.h" #include "l2_packet.h" -#include "privsep_commands.h" +#include "common/privsep_commands.h" struct l2_packet_data { @@ -179,7 +173,7 @@ struct l2_packet_data * l2_packet_init( 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)"); + perror("l2-pkt-privsep: bind(PF_UNIX)"); goto fail; } diff --git a/contrib/hostapd/src/l2_packet/l2_packet_winpcap.c b/contrib/hostapd/src/l2_packet/l2_packet_winpcap.c index f76b386fc0..b6e5088938 100644 --- a/contrib/hostapd/src/l2_packet/l2_packet_winpcap.c +++ b/contrib/hostapd/src/l2_packet/l2_packet_winpcap.c @@ -2,14 +2,8 @@ * WPA Supplicant - Layer2 packet handling with WinPcap RX thread * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 diff --git a/contrib/hostapd/src/p2p/p2p.c b/contrib/hostapd/src/p2p/p2p.c new file mode 100644 index 0000000000..957dee5bd9 --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p.c @@ -0,0 +1,4598 @@ +/* + * Wi-Fi Direct - P2P module + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "wps/wps_i.h" +#include "p2p_i.h" +#include "p2p.h" + + +static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx); +static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev); +static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, size_t len, + int rx_freq); +static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, + size_t len); +static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx); +static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); + + +/* + * p2p_scan recovery timeout + * + * Many drivers are using 30 second timeout on scan results. Allow a bit larger + * timeout for this to avoid hitting P2P timeout unnecessarily. + */ +#define P2P_SCAN_TIMEOUT 35 + +/** + * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer + * entries will be removed + */ +#ifndef P2P_PEER_EXPIRATION_AGE +#define P2P_PEER_EXPIRATION_AGE 60 +#endif /* P2P_PEER_EXPIRATION_AGE */ + +#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) + +static void p2p_expire_peers(struct p2p_data *p2p) +{ + struct p2p_device *dev, *n; + struct os_reltime now; + size_t i; + + os_get_reltime(&now); + dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { + if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec) + continue; + + if (dev == p2p->go_neg_peer) { + /* + * GO Negotiation is in progress with the peer, so + * don't expire the peer entry until GO Negotiation + * fails or times out. + */ + continue; + } + + if (p2p->cfg->go_connected && + p2p->cfg->go_connected(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr)) { + /* + * We are connected as a client to a group in which the + * peer is the GO, so do not expire the peer entry. + */ + os_get_reltime(&dev->last_seen); + continue; + } + + for (i = 0; i < p2p->num_groups; i++) { + if (p2p_group_is_client_connected( + p2p->groups[i], dev->info.p2p_device_addr)) + break; + } + if (i < p2p->num_groups) { + /* + * The peer is connected as a client in a group where + * we are the GO, so do not expire the peer entry. + */ + os_get_reltime(&dev->last_seen); + continue; + } + + p2p_dbg(p2p, "Expiring old peer entry " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); + dl_list_del(&dev->list); + p2p_device_free(p2p, dev); + } +} + + +static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + p2p_expire_peers(p2p); + eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, + p2p_expiration_timeout, p2p, NULL); +} + + +static const char * p2p_state_txt(int state) +{ + switch (state) { + case P2P_IDLE: + return "IDLE"; + case P2P_SEARCH: + return "SEARCH"; + case P2P_CONNECT: + return "CONNECT"; + case P2P_CONNECT_LISTEN: + return "CONNECT_LISTEN"; + case P2P_GO_NEG: + return "GO_NEG"; + case P2P_LISTEN_ONLY: + return "LISTEN_ONLY"; + case P2P_WAIT_PEER_CONNECT: + return "WAIT_PEER_CONNECT"; + case P2P_WAIT_PEER_IDLE: + return "WAIT_PEER_IDLE"; + case P2P_SD_DURING_FIND: + return "SD_DURING_FIND"; + case P2P_PROVISIONING: + return "PROVISIONING"; + case P2P_PD_DURING_FIND: + return "PD_DURING_FIND"; + case P2P_INVITE: + return "INVITE"; + case P2P_INVITE_LISTEN: + return "INVITE_LISTEN"; + default: + return "?"; + } +} + + +const char * p2p_get_state_txt(struct p2p_data *p2p) +{ + return p2p_state_txt(p2p->state); +} + + +u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev = NULL; + + if (!addr || !p2p) + return 0; + + dev = p2p_get_device(p2p, addr); + if (dev) + return dev->wps_prov_info; + else + return 0; +} + + +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev = NULL; + + if (!addr || !p2p) + return; + + dev = p2p_get_device(p2p, addr); + if (dev) + dev->wps_prov_info = 0; +} + + +void p2p_set_state(struct p2p_data *p2p, int new_state) +{ + p2p_dbg(p2p, "State %s -> %s", + p2p_state_txt(p2p->state), p2p_state_txt(new_state)); + p2p->state = new_state; +} + + +void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec) +{ + p2p_dbg(p2p, "Set timeout (state=%s): %u.%06u sec", + p2p_state_txt(p2p->state), sec, usec); + eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); + eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL); +} + + +void p2p_clear_timeout(struct p2p_data *p2p) +{ + p2p_dbg(p2p, "Clear timeout (state=%s)", p2p_state_txt(p2p->state)); + eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); +} + + +void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, + int status) +{ + struct p2p_go_neg_results res; + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); + if (p2p->go_neg_peer) { + p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; + p2p->go_neg_peer->wps_method = WPS_NOT_READY; + p2p->go_neg_peer->oob_pw_id = 0; + } + p2p->go_neg_peer = NULL; + + os_memset(&res, 0, sizeof(res)); + res.status = status; + if (peer) { + os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, + ETH_ALEN); + os_memcpy(res.peer_interface_addr, peer->intended_addr, + ETH_ALEN); + } + p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); +} + + +static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) +{ + unsigned int r, tu; + int freq; + struct wpabuf *ies; + + p2p_dbg(p2p, "Starting short listen state (state=%s)", + p2p_state_txt(p2p->state)); + + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown regulatory class/channel"); + return; + } + + os_get_random((u8 *) &r, sizeof(r)); + tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) + + p2p->min_disc_int) * 100; + if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu) + tu = p2p->max_disc_tu; + if (!dev_disc && tu < 100) + tu = 100; /* Need to wait in non-device discovery use cases */ + if (p2p->cfg->max_listen && 1024 * tu / 1000 > p2p->cfg->max_listen) + tu = p2p->cfg->max_listen * 1000 / 1024; + + if (tu == 0) { + p2p_dbg(p2p, "Skip listen state since duration was 0 TU"); + p2p_set_timeout(p2p, 0, 0); + return; + } + + p2p->pending_listen_freq = freq; + p2p->pending_listen_sec = 0; + p2p->pending_listen_usec = 1024 * tu; + + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return; + + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000, + ies) < 0) { + p2p_dbg(p2p, "Failed to start listen mode"); + p2p->pending_listen_freq = 0; + } + wpabuf_free(ies); +} + + +int p2p_listen(struct p2p_data *p2p, unsigned int timeout) +{ + int freq; + struct wpabuf *ies; + + p2p_dbg(p2p, "Going to listen(only) state"); + + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown regulatory class/channel"); + return -1; + } + + p2p->pending_listen_freq = freq; + p2p->pending_listen_sec = timeout / 1000; + p2p->pending_listen_usec = (timeout % 1000) * 1000; + + if (p2p->p2p_scan_running) { + if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) { + p2p_dbg(p2p, "p2p_scan running - connect is already pending - skip listen"); + return 0; + } + p2p_dbg(p2p, "p2p_scan running - delay start of listen state"); + p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN; + return 0; + } + + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return -1; + + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) { + p2p_dbg(p2p, "Failed to start listen mode"); + p2p->pending_listen_freq = 0; + wpabuf_free(ies); + return -1; + } + wpabuf_free(ies); + + p2p_set_state(p2p, P2P_LISTEN_ONLY); + + return 0; +} + + +static void p2p_device_clear_reported(struct p2p_data *p2p) +{ + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) + dev->flags &= ~P2P_DEV_REPORTED; +} + + +/** + * p2p_get_device - Fetch a peer entry + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer + * Returns: Pointer to the device entry or %NULL if not found + */ +struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0) + return dev; + } + return NULL; +} + + +/** + * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address + * @p2p: P2P module context from p2p_init() + * @addr: P2P Interface Address of the peer + * Returns: Pointer to the device entry or %NULL if not found + */ +struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, + const u8 *addr) +{ + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0) + return dev; + } + return NULL; +} + + +/** + * p2p_create_device - Create a peer entry + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer + * Returns: Pointer to the device entry or %NULL on failure + * + * If there is already an entry for the peer, it will be returned instead of + * creating a new one. + */ +static struct p2p_device * p2p_create_device(struct p2p_data *p2p, + const u8 *addr) +{ + struct p2p_device *dev, *oldest = NULL; + size_t count = 0; + + dev = p2p_get_device(p2p, addr); + if (dev) + return dev; + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + count++; + if (oldest == NULL || + os_reltime_before(&dev->last_seen, &oldest->last_seen)) + oldest = dev; + } + if (count + 1 > p2p->cfg->max_peers && oldest) { + p2p_dbg(p2p, "Remove oldest peer entry to make room for a new peer"); + dl_list_del(&oldest->list); + p2p_device_free(p2p, oldest); + } + + dev = os_zalloc(sizeof(*dev)); + if (dev == NULL) + return NULL; + dl_list_add(&p2p->devices, &dev->list); + os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN); + + return dev; +} + + +static void p2p_copy_client_info(struct p2p_device *dev, + struct p2p_client_info *cli) +{ + os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len); + dev->info.device_name[cli->dev_name_len] = '\0'; + dev->info.dev_capab = cli->dev_capab; + dev->info.config_methods = cli->config_methods; + os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8); + dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types; + os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types, + dev->info.wps_sec_dev_type_list_len); +} + + +static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, + const u8 *go_interface_addr, int freq, + const u8 *gi, size_t gi_len) +{ + struct p2p_group_info info; + size_t c; + struct p2p_device *dev; + + if (gi == NULL) + return 0; + + if (p2p_group_info_parse(gi, gi_len, &info) < 0) + return -1; + + /* + * Clear old data for this group; if the devices are still in the + * group, the information will be restored in the loop following this. + */ + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(dev->member_in_go_iface, go_interface_addr, + ETH_ALEN) == 0) { + os_memset(dev->member_in_go_iface, 0, ETH_ALEN); + os_memset(dev->member_in_go_dev, 0, ETH_ALEN); + } + } + + for (c = 0; c < info.num_clients; c++) { + struct p2p_client_info *cli = &info.client[c]; + if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr, + ETH_ALEN) == 0) + continue; /* ignore our own entry */ + dev = p2p_get_device(p2p, cli->p2p_device_addr); + if (dev) { + if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY | + P2P_DEV_PROBE_REQ_ONLY)) { + /* + * Update information since we have not + * received this directly from the client. + */ + p2p_copy_client_info(dev, cli); + } else { + /* + * Need to update P2P Client Discoverability + * flag since it is valid only in P2P Group + * Info attribute. + */ + dev->info.dev_capab &= + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + dev->info.dev_capab |= + cli->dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + } + if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { + dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; + } + } else { + dev = p2p_create_device(p2p, cli->p2p_device_addr); + if (dev == NULL) + continue; + dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; + p2p_copy_client_info(dev, cli); + dev->oper_freq = freq; + p2p->cfg->dev_found(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr, + &dev->info, 1); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + } + + os_memcpy(dev->interface_addr, cli->p2p_interface_addr, + ETH_ALEN); + os_get_reltime(&dev->last_seen); + os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); + os_memcpy(dev->member_in_go_iface, go_interface_addr, + ETH_ALEN); + } + + return 0; +} + + +static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev, + int probe_req, const struct p2p_message *msg) +{ + os_memcpy(dev->info.device_name, msg->device_name, + sizeof(dev->info.device_name)); + + if (msg->manufacturer && + msg->manufacturer_len < sizeof(dev->info.manufacturer)) { + os_memset(dev->info.manufacturer, 0, + sizeof(dev->info.manufacturer)); + os_memcpy(dev->info.manufacturer, msg->manufacturer, + msg->manufacturer_len); + } + + if (msg->model_name && + msg->model_name_len < sizeof(dev->info.model_name)) { + os_memset(dev->info.model_name, 0, + sizeof(dev->info.model_name)); + os_memcpy(dev->info.model_name, msg->model_name, + msg->model_name_len); + } + + if (msg->model_number && + msg->model_number_len < sizeof(dev->info.model_number)) { + os_memset(dev->info.model_number, 0, + sizeof(dev->info.model_number)); + os_memcpy(dev->info.model_number, msg->model_number, + msg->model_number_len); + } + + if (msg->serial_number && + msg->serial_number_len < sizeof(dev->info.serial_number)) { + os_memset(dev->info.serial_number, 0, + sizeof(dev->info.serial_number)); + os_memcpy(dev->info.serial_number, msg->serial_number, + msg->serial_number_len); + } + + if (msg->pri_dev_type) + os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type, + sizeof(dev->info.pri_dev_type)); + else if (msg->wps_pri_dev_type) + os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type, + sizeof(dev->info.pri_dev_type)); + + if (msg->wps_sec_dev_type_list) { + os_memcpy(dev->info.wps_sec_dev_type_list, + msg->wps_sec_dev_type_list, + msg->wps_sec_dev_type_list_len); + dev->info.wps_sec_dev_type_list_len = + msg->wps_sec_dev_type_list_len; + } + + if (msg->capability) { + /* + * P2P Client Discoverability bit is reserved in all frames + * that use this function, so do not change its value here. + */ + dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + dev->info.dev_capab |= msg->capability[0] & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + dev->info.group_capab = msg->capability[1]; + } + + if (msg->ext_listen_timing) { + dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing); + dev->ext_listen_interval = + WPA_GET_LE16(msg->ext_listen_timing + 2); + } + + if (!probe_req) { + u16 new_config_methods; + new_config_methods = msg->config_methods ? + msg->config_methods : msg->wps_config_methods; + if (new_config_methods && + dev->info.config_methods != new_config_methods) { + p2p_dbg(p2p, "Update peer " MACSTR + " config_methods 0x%x -> 0x%x", + MAC2STR(dev->info.p2p_device_addr), + dev->info.config_methods, + new_config_methods); + dev->info.config_methods = new_config_methods; + } + } +} + + +/** + * p2p_add_device - Add peer entries based on scan results or P2P frames + * @p2p: P2P module context from p2p_init() + * @addr: Source address of Beacon or Probe Response frame (may be either + * P2P Device Address or P2P Interface Address) + * @level: Signal level (signal strength of the received frame from the peer) + * @freq: Frequency on which the Beacon or Probe Response frame was received + * @rx_time: Time when the result was received + * @ies: IEs from the Beacon or Probe Response frame + * @ies_len: Length of ies buffer in octets + * @scan_res: Whether this was based on scan results + * Returns: 0 on success, -1 on failure + * + * If the scan result is for a GO, the clients in the group will also be added + * to the peer table. This function can also be used with some other frames + * like Provision Discovery Request that contains P2P Capability and P2P Device + * Info attributes. + */ +int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, + struct os_reltime *rx_time, int level, const u8 *ies, + size_t ies_len, int scan_res) +{ + struct p2p_device *dev; + struct p2p_message msg; + const u8 *p2p_dev_addr; + int i; + struct os_reltime time_now; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ies, ies_len, &msg)) { + p2p_dbg(p2p, "Failed to parse P2P IE for a device entry"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.p2p_device_addr) + p2p_dev_addr = msg.p2p_device_addr; + else if (msg.device_id) + p2p_dev_addr = msg.device_id; + else { + p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); + p2p_parse_free(&msg); + return -1; + } + + if (!is_zero_ether_addr(p2p->peer_filter) && + os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) { + p2p_dbg(p2p, "Do not add peer filter for " MACSTR + " due to peer filter", MAC2STR(p2p_dev_addr)); + p2p_parse_free(&msg); + return 0; + } + + dev = p2p_create_device(p2p, p2p_dev_addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return -1; + } + + if (rx_time == NULL) { + os_get_reltime(&time_now); + rx_time = &time_now; + } + + /* + * Update the device entry only if the new peer + * entry is newer than the one previously stored. + */ + if (dev->last_seen.sec > 0 && + os_reltime_before(rx_time, &dev->last_seen)) { + p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)", + (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec, + (unsigned int) dev->last_seen.sec, + (unsigned int) dev->last_seen.usec); + p2p_parse_free(&msg); + return -1; + } + + os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); + + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + + if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0) + os_memcpy(dev->interface_addr, addr, ETH_ALEN); + if (msg.ssid && + (msg.ssid[1] != P2P_WILDCARD_SSID_LEN || + os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) + != 0)) { + os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]); + dev->oper_ssid_len = msg.ssid[1]; + } + + if (freq >= 2412 && freq <= 2484 && msg.ds_params && + *msg.ds_params >= 1 && *msg.ds_params <= 14) { + int ds_freq; + if (*msg.ds_params == 14) + ds_freq = 2484; + else + ds_freq = 2407 + *msg.ds_params * 5; + if (freq != ds_freq) { + p2p_dbg(p2p, "Update Listen frequency based on DS Parameter Set IE: %d -> %d MHz", + freq, ds_freq); + freq = ds_freq; + } + } + + if (dev->listen_freq && dev->listen_freq != freq && scan_res) { + p2p_dbg(p2p, "Update Listen frequency based on scan results (" + MACSTR " %d -> %d MHz (DS param %d)", + MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, + freq, msg.ds_params ? *msg.ds_params : -1); + } + if (scan_res) { + dev->listen_freq = freq; + if (msg.group_info) + dev->oper_freq = freq; + } + dev->info.level = level; + + p2p_copy_wps_info(p2p, dev, 0, &msg); + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + wpabuf_free(dev->info.wps_vendor_ext[i]); + dev->info.wps_vendor_ext[i] = NULL; + } + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (msg.wps_vendor_ext[i] == NULL) + break; + dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy( + msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]); + if (dev->info.wps_vendor_ext[i] == NULL) + break; + } + + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + + if (scan_res) { + p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, + msg.group_info, msg.group_info_len); + } + + p2p_parse_free(&msg); + + if (p2p_pending_sd_req(p2p, dev)) + dev->flags |= P2P_DEV_SD_SCHEDULE; + + if (dev->flags & P2P_DEV_REPORTED) + return 0; + + p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)", + freq, (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec); + if (dev->flags & P2P_DEV_USER_REJECTED) { + p2p_dbg(p2p, "Do not report rejected device"); + return 0; + } + + if (dev->info.config_methods == 0 && + (freq == 2412 || freq == 2437 || freq == 2462)) { + /* + * If we have only seen a Beacon frame from a GO, we do not yet + * know what WPS config methods it supports. Since some + * applications use config_methods value from P2P-DEVICE-FOUND + * events, postpone reporting this peer until we've fully + * discovered its capabilities. + * + * At least for now, do this only if the peer was detected on + * one of the social channels since that peer can be easily be + * found again and there are no limitations of having to use + * passive scan on this channels, so this can be done through + * Probe Response frame that includes the config_methods + * information. + */ + p2p_dbg(p2p, "Do not report peer " MACSTR + " with unknown config methods", MAC2STR(addr)); + return 0; + } + + p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + + return 0; +} + + +static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) +{ + int i; + + if (p2p->go_neg_peer == dev) { + /* + * If GO Negotiation is in progress, report that it has failed. + */ + p2p_go_neg_failed(p2p, dev, -1); + p2p->go_neg_peer = NULL; + } + if (p2p->invite_peer == dev) + p2p->invite_peer = NULL; + if (p2p->sd_peer == dev) + p2p->sd_peer = NULL; + if (p2p->pending_client_disc_go == dev) + p2p->pending_client_disc_go = NULL; + + /* dev_lost() device, but only if it was previously dev_found() */ + if (dev->flags & P2P_DEV_REPORTED_ONCE) + p2p->cfg->dev_lost(p2p->cfg->cb_ctx, + dev->info.p2p_device_addr); + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + wpabuf_free(dev->info.wps_vendor_ext[i]); + dev->info.wps_vendor_ext[i] = NULL; + } + + wpabuf_free(dev->info.wfd_subelems); + + os_free(dev); +} + + +static int p2p_get_next_prog_freq(struct p2p_data *p2p) +{ + struct p2p_channels *c; + struct p2p_reg_class *cla; + size_t cl, ch; + int found = 0; + u8 reg_class; + u8 channel; + int freq; + + c = &p2p->cfg->channels; + for (cl = 0; cl < c->reg_classes; cl++) { + cla = &c->reg_class[cl]; + if (cla->reg_class != p2p->last_prog_scan_class) + continue; + for (ch = 0; ch < cla->channels; ch++) { + if (cla->channel[ch] == p2p->last_prog_scan_chan) { + found = 1; + break; + } + } + if (found) + break; + } + + if (!found) { + /* Start from beginning */ + reg_class = c->reg_class[0].reg_class; + channel = c->reg_class[0].channel[0]; + } else { + /* Pick the next channel */ + ch++; + if (ch == cla->channels) { + cl++; + if (cl == c->reg_classes) + cl = 0; + ch = 0; + } + reg_class = c->reg_class[cl].reg_class; + channel = c->reg_class[cl].channel[ch]; + } + + freq = p2p_channel_to_freq(reg_class, channel); + p2p_dbg(p2p, "Next progressive search channel: reg_class %u channel %u -> %d MHz", + reg_class, channel, freq); + p2p->last_prog_scan_class = reg_class; + p2p->last_prog_scan_chan = channel; + + if (freq == 2412 || freq == 2437 || freq == 2462) + return 0; /* No need to add social channels */ + return freq; +} + + +static void p2p_search(struct p2p_data *p2p) +{ + int freq = 0; + enum p2p_scan_type type; + u16 pw_id = DEV_PW_DEFAULT; + int res; + + if (p2p->drv_in_listen) { + p2p_dbg(p2p, "Driver is still in Listen state - wait for it to end before continuing"); + return; + } + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + + if (p2p->find_type == P2P_FIND_PROGRESSIVE && + (freq = p2p_get_next_prog_freq(p2p)) > 0) { + type = P2P_SCAN_SOCIAL_PLUS_ONE; + p2p_dbg(p2p, "Starting search (+ freq %u)", freq); + } else { + type = P2P_SCAN_SOCIAL; + p2p_dbg(p2p, "Starting search"); + } + + res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, + p2p->num_req_dev_types, p2p->req_dev_types, + p2p->find_dev_id, pw_id); + if (res < 0) { + p2p_dbg(p2p, "Scan request failed"); + p2p_continue_find(p2p); + } else { + p2p_dbg(p2p, "Running p2p_scan"); + p2p->p2p_scan_running = 1; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, + p2p, NULL); + } +} + + +static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + p2p_dbg(p2p, "Find timeout -> stop"); + p2p_stop_find(p2p); +} + + +static int p2p_run_after_scan(struct p2p_data *p2p) +{ + struct p2p_device *dev; + enum p2p_after_scan op; + + if (p2p->after_scan_tx) { + p2p->after_scan_tx_in_progress = 1; + p2p_dbg(p2p, "Send pending Action frame at p2p_scan completion"); + p2p->cfg->send_action(p2p->cfg->cb_ctx, + p2p->after_scan_tx->freq, + p2p->after_scan_tx->dst, + p2p->after_scan_tx->src, + p2p->after_scan_tx->bssid, + (u8 *) (p2p->after_scan_tx + 1), + p2p->after_scan_tx->len, + p2p->after_scan_tx->wait_time); + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; + return 1; + } + + op = p2p->start_after_scan; + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + switch (op) { + case P2P_AFTER_SCAN_NOTHING: + break; + case P2P_AFTER_SCAN_LISTEN: + p2p_dbg(p2p, "Start previously requested Listen state"); + p2p_listen(p2p, p2p->pending_listen_sec * 1000 + + p2p->pending_listen_usec / 1000); + return 1; + case P2P_AFTER_SCAN_CONNECT: + p2p_dbg(p2p, "Start previously requested connect with " MACSTR, + MAC2STR(p2p->after_scan_peer)); + dev = p2p_get_device(p2p, p2p->after_scan_peer); + if (dev == NULL) { + p2p_dbg(p2p, "Peer not known anymore"); + break; + } + p2p_connect_send(p2p, dev); + return 1; + } + + return 0; +} + + +static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + int running; + p2p_dbg(p2p, "p2p_scan timeout (running=%d)", p2p->p2p_scan_running); + running = p2p->p2p_scan_running; + /* Make sure we recover from missed scan results callback */ + p2p->p2p_scan_running = 0; + + if (running) + p2p_run_after_scan(p2p); +} + + +static void p2p_free_req_dev_types(struct p2p_data *p2p) +{ + p2p->num_req_dev_types = 0; + os_free(p2p->req_dev_types); + p2p->req_dev_types = NULL; +} + + +int p2p_find(struct p2p_data *p2p, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id, unsigned int search_delay) +{ + int res; + + p2p_dbg(p2p, "Starting find (type=%d)", type); + os_get_reltime(&p2p->find_start); + if (p2p->p2p_scan_running) { + p2p_dbg(p2p, "p2p_scan is already running"); + } + + p2p_free_req_dev_types(p2p); + if (req_dev_types && num_req_dev_types) { + p2p->req_dev_types = os_malloc(num_req_dev_types * + WPS_DEV_TYPE_LEN); + if (p2p->req_dev_types == NULL) + return -1; + os_memcpy(p2p->req_dev_types, req_dev_types, + num_req_dev_types * WPS_DEV_TYPE_LEN); + p2p->num_req_dev_types = num_req_dev_types; + } + + if (dev_id) { + os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN); + p2p->find_dev_id = p2p->find_dev_id_buf; + } else + p2p->find_dev_id = NULL; + + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + p2p_clear_timeout(p2p); + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->find_type = type; + p2p_device_clear_reported(p2p); + p2p_set_state(p2p, P2P_SEARCH); + p2p->search_delay = search_delay; + p2p->in_search_delay = 0; + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + p2p->last_p2p_find_timeout = timeout; + if (timeout) + eloop_register_timeout(timeout, 0, p2p_find_timeout, + p2p, NULL); + switch (type) { + case P2P_FIND_START_WITH_FULL: + case P2P_FIND_PROGRESSIVE: + res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0, + p2p->num_req_dev_types, + p2p->req_dev_types, dev_id, + DEV_PW_DEFAULT); + break; + case P2P_FIND_ONLY_SOCIAL: + res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0, + p2p->num_req_dev_types, + p2p->req_dev_types, dev_id, + DEV_PW_DEFAULT); + break; + default: + return -1; + } + + if (res == 0) { + p2p_dbg(p2p, "Running p2p_scan"); + p2p->p2p_scan_running = 1; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, + p2p, NULL); + } else if (p2p->p2p_scan_running) { + p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running"); + /* wait for the previous p2p_scan to complete */ + } else { + p2p_dbg(p2p, "Failed to start p2p_scan"); + p2p_set_state(p2p, P2P_IDLE); + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + } + + return res; +} + + +void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) +{ + p2p_dbg(p2p, "Stopping find"); + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + p2p_clear_timeout(p2p); + if (p2p->state == P2P_SEARCH) + p2p->cfg->find_stopped(p2p->cfg->cb_ctx); + p2p_set_state(p2p, P2P_IDLE); + p2p_free_req_dev_types(p2p); + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + if (p2p->go_neg_peer) + p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; + p2p->go_neg_peer = NULL; + p2p->sd_peer = NULL; + p2p->invite_peer = NULL; + p2p_stop_listen_for_freq(p2p, freq); +} + + +void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) +{ + if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) { + p2p_dbg(p2p, "Skip stop_listen since we are on correct channel for response"); + return; + } + if (p2p->in_listen) { + p2p->in_listen = 0; + p2p_clear_timeout(p2p); + } + if (p2p->drv_in_listen) { + /* + * The driver may not deliver callback to p2p_listen_end() + * when the operation gets canceled, so clear the internal + * variable that is tracking driver state. + */ + p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen); + p2p->drv_in_listen = 0; + } + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); +} + + +void p2p_stop_listen(struct p2p_data *p2p) +{ + if (p2p->state != P2P_LISTEN_ONLY) { + p2p_dbg(p2p, "Skip stop_listen since not in listen_only state."); + return; + } + + p2p_stop_listen_for_freq(p2p, 0); + p2p_set_state(p2p, P2P_IDLE); +} + + +void p2p_stop_find(struct p2p_data *p2p) +{ + p2p_stop_find_for_freq(p2p, 0); +} + + +static int p2p_prepare_channel_pref(struct p2p_data *p2p, + unsigned int force_freq, + unsigned int pref_freq, int go) +{ + u8 op_class, op_channel; + unsigned int freq = force_freq ? force_freq : pref_freq; + + p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d", + force_freq, pref_freq, go); + if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", freq); + return -1; + } + + if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) && + (go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class, + op_channel))) { + p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P", + freq, op_class, op_channel); + return -1; + } + + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + + if (force_freq) { + p2p->channels.reg_classes = 1; + p2p->channels.reg_class[0].channels = 1; + p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; + p2p->channels.reg_class[0].channel[0] = p2p->op_channel; + } else { + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + } + + return 0; +} + + +static void p2p_prepare_channel_best(struct p2p_data *p2p) +{ + u8 op_class, op_channel; + const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; + const int op_classes_vht[] = { 128, 0 }; + + p2p_dbg(p2p, "Prepare channel best"); + + if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 && + p2p_supported_freq(p2p, p2p->best_freq_overall) && + p2p_freq_to_channel(p2p->best_freq_overall, &op_class, &op_channel) + == 0) { + p2p_dbg(p2p, "Select best overall channel as operating channel preference"); + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 && + p2p_supported_freq(p2p, p2p->best_freq_5) && + p2p_freq_to_channel(p2p->best_freq_5, &op_class, &op_channel) + == 0) { + p2p_dbg(p2p, "Select best 5 GHz channel as operating channel preference"); + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 && + p2p_supported_freq(p2p, p2p->best_freq_24) && + p2p_freq_to_channel(p2p->best_freq_24, &op_class, + &op_channel) == 0) { + p2p_dbg(p2p, "Select best 2.4 GHz channel as operating channel preference"); + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + } else if (p2p->cfg->num_pref_chan > 0 && + p2p_channels_includes(&p2p->cfg->channels, + p2p->cfg->pref_chan[0].op_class, + p2p->cfg->pref_chan[0].chan)) { + p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference"); + p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class; + p2p->op_channel = p2p->cfg->pref_chan[0].chan; + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible VHT channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_ht40, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible HT40 channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_5ghz, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else { + p2p_dbg(p2p, "Select pre-configured channel as operating channel preference"); + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + } + + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); +} + + +/** + * p2p_prepare_channel - Select operating channel for GO Negotiation + * @p2p: P2P module context from p2p_init() + * @dev: Selected peer device + * @force_freq: Forced frequency in MHz or 0 if not forced + * @pref_freq: Preferred frequency in MHz or 0 if no preference + * @go: Whether the local end will be forced to be GO + * Returns: 0 on success, -1 on failure (channel not supported for P2P) + * + * This function is used to do initial operating channel selection for GO + * Negotiation prior to having received peer information. The selected channel + * may be further optimized in p2p_reselect_channel() once the peer information + * is available. + */ +int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, + unsigned int force_freq, unsigned int pref_freq, int go) +{ + p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d", + force_freq, pref_freq, go); + if (force_freq || pref_freq) { + if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) < + 0) + return -1; + } else { + p2p_prepare_channel_best(p2p); + } + p2p_channels_dump(p2p, "prepared channels", &p2p->channels); + if (go) + p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq); + else if (!force_freq) + p2p_channels_union(&p2p->channels, &p2p->cfg->cli_channels, + &p2p->channels); + p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels); + + p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s", + p2p->op_reg_class, p2p->op_channel, + force_freq ? " (forced)" : ""); + + if (force_freq) + dev->flags |= P2P_DEV_FORCE_FREQ; + else + dev->flags &= ~P2P_DEV_FORCE_FREQ; + + return 0; +} + + +static void p2p_set_dev_persistent(struct p2p_device *dev, + int persistent_group) +{ + switch (persistent_group) { + case 0: + dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP | + P2P_DEV_PREFER_PERSISTENT_RECONN); + break; + case 1: + dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP; + dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN; + break; + case 2: + dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP | + P2P_DEV_PREFER_PERSISTENT_RECONN; + break; + } +} + + +int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id) +{ + struct p2p_device *dev; + + p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR + " GO Intent=%d Intended Interface Address=" MACSTR + " wps_method=%d persistent_group=%d pd_before_go_neg=%d " + "oob_pw_id=%u", + MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), + wps_method, persistent_group, pd_before_go_neg, oob_pw_id); + + dev = p2p_get_device(p2p, peer_addr); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + p2p_dbg(p2p, "Cannot connect to unknown P2P Device " MACSTR, + MAC2STR(peer_addr)); + return -1; + } + + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, + go_intent == 15) < 0) + return -1; + + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { + if (!(dev->info.dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR + " that is in a group and is not discoverable", + MAC2STR(peer_addr)); + return -1; + } + if (dev->oper_freq <= 0) { + p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR + " with incomplete information", + MAC2STR(peer_addr)); + return -1; + } + + /* + * First, try to connect directly. If the peer does not + * acknowledge frames, assume it is sleeping and use device + * discoverability via the GO at that point. + */ + } + + p2p->ssid_set = 0; + if (force_ssid) { + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID", + force_ssid, force_ssid_len); + os_memcpy(p2p->ssid, force_ssid, force_ssid_len); + p2p->ssid_len = force_ssid_len; + p2p->ssid_set = 1; + } + + dev->flags &= ~P2P_DEV_NOT_YET_READY; + dev->flags &= ~P2P_DEV_USER_REJECTED; + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + if (pd_before_go_neg) + dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG; + else { + dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; + /* + * Assign dialog token and tie breaker here to use the same + * values in each retry within the same GO Negotiation exchange. + */ + dev->dialog_token++; + if (dev->dialog_token == 0) + dev->dialog_token = 1; + dev->tie_breaker = p2p->next_tie_breaker; + p2p->next_tie_breaker = !p2p->next_tie_breaker; + } + dev->connect_reqs = 0; + dev->go_neg_req_sent = 0; + dev->go_state = UNKNOWN_GO; + p2p_set_dev_persistent(dev, persistent_group); + p2p->go_intent = go_intent; + os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); + + if (p2p->state != P2P_IDLE) + p2p_stop_find(p2p); + + if (p2p->after_scan_tx) { + /* + * We need to drop the pending frame to avoid issues with the + * new GO Negotiation, e.g., when the pending frame was from a + * previous attempt at starting a GO Negotiation. + */ + p2p_dbg(p2p, "Dropped previous pending Action frame TX that was waiting for p2p_scan completion"); + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; + } + + dev->wps_method = wps_method; + dev->oob_pw_id = oob_pw_id; + dev->status = P2P_SC_SUCCESS; + + if (p2p->p2p_scan_running) { + p2p_dbg(p2p, "p2p_scan running - delay connect send"); + p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT; + os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN); + return 0; + } + p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; + + return p2p_connect_send(p2p, dev); +} + + +int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + unsigned int pref_freq, u16 oob_pw_id) +{ + struct p2p_device *dev; + + p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR + " GO Intent=%d Intended Interface Address=" MACSTR + " wps_method=%d persistent_group=%d oob_pw_id=%u", + MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), + wps_method, persistent_group, oob_pw_id); + + dev = p2p_get_device(p2p, peer_addr); + if (dev == NULL) { + p2p_dbg(p2p, "Cannot authorize unknown P2P Device " MACSTR, + MAC2STR(peer_addr)); + return -1; + } + + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent == + 15) < 0) + return -1; + + p2p->ssid_set = 0; + if (force_ssid) { + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID", + force_ssid, force_ssid_len); + os_memcpy(p2p->ssid, force_ssid, force_ssid_len); + p2p->ssid_len = force_ssid_len; + p2p->ssid_set = 1; + } + + dev->flags &= ~P2P_DEV_NOT_YET_READY; + dev->flags &= ~P2P_DEV_USER_REJECTED; + dev->go_neg_req_sent = 0; + dev->go_state = UNKNOWN_GO; + p2p_set_dev_persistent(dev, persistent_group); + p2p->go_intent = go_intent; + os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); + + dev->wps_method = wps_method; + dev->oob_pw_id = oob_pw_id; + dev->status = P2P_SC_SUCCESS; + + return 0; +} + + +void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, + struct p2p_device *dev, struct p2p_message *msg) +{ + os_get_reltime(&dev->last_seen); + + p2p_copy_wps_info(p2p, dev, 0, msg); + + if (msg->listen_channel) { + int freq; + freq = p2p_channel_to_freq(msg->listen_channel[3], + msg->listen_channel[4]); + if (freq < 0) { + p2p_dbg(p2p, "Unknown peer Listen channel: " + "country=%c%c(0x%02x) reg_class=%u channel=%u", + msg->listen_channel[0], + msg->listen_channel[1], + msg->listen_channel[2], + msg->listen_channel[3], + msg->listen_channel[4]); + } else { + p2p_dbg(p2p, "Update peer " MACSTR + " Listen channel: %u -> %u MHz", + MAC2STR(dev->info.p2p_device_addr), + dev->listen_freq, freq); + dev->listen_freq = freq; + } + } + + if (msg->wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems); + } + + if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { + dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; + p2p_dbg(p2p, "Completed device entry based on data from GO Negotiation Request"); + } else { + p2p_dbg(p2p, "Created device entry based on GO Neg Req: " + MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' " + "listen_freq=%d", + MAC2STR(dev->info.p2p_device_addr), + dev->info.dev_capab, dev->info.group_capab, + dev->info.device_name, dev->listen_freq); + } + + dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY; + + if (dev->flags & P2P_DEV_USER_REJECTED) { + p2p_dbg(p2p, "Do not report rejected device"); + return; + } + + p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; +} + + +void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len) +{ + os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2); + os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2], + p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len); + *ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len; +} + + +int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params) +{ + p2p_build_ssid(p2p, params->ssid, ¶ms->ssid_len); + p2p_random(params->passphrase, 8); + return 0; +} + + +void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) +{ + struct p2p_go_neg_results res; + int go = peer->go_state == LOCAL_GO; + struct p2p_channels intersection; + int freqs; + size_t i, j; + + p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)", + MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer"); + + os_memset(&res, 0, sizeof(res)); + res.role_go = go; + os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); + os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); + res.wps_method = peer->wps_method; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + res.persistent_group = 2; + else + res.persistent_group = 1; + } + + if (go) { + /* Setup AP mode for WPS provisioning */ + res.freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); + res.ssid_len = p2p->ssid_len; + p2p_random(res.passphrase, 8); + } else { + res.freq = peer->oper_freq; + if (p2p->ssid_len) { + os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); + res.ssid_len = p2p->ssid_len; + } + } + + p2p_channels_dump(p2p, "own channels", &p2p->channels); + p2p_channels_dump(p2p, "peer channels", &peer->channels); + p2p_channels_intersect(&p2p->channels, &peer->channels, + &intersection); + if (go) { + p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq); + p2p_channels_dump(p2p, "intersection after no-GO removal", + &intersection); + } + freqs = 0; + for (i = 0; i < intersection.reg_classes; i++) { + struct p2p_reg_class *c = &intersection.reg_class[i]; + if (freqs + 1 == P2P_MAX_CHANNELS) + break; + for (j = 0; j < c->channels; j++) { + int freq; + if (freqs + 1 == P2P_MAX_CHANNELS) + break; + freq = p2p_channel_to_freq(c->reg_class, c->channel[j]); + if (freq < 0) + continue; + res.freq_list[freqs++] = freq; + } + } + + res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout; + + p2p_clear_timeout(p2p); + p2p->ssid_set = 0; + peer->go_neg_req_sent = 0; + peer->wps_method = WPS_NOT_READY; + peer->oob_pw_id = 0; + + p2p_set_state(p2p, P2P_PROVISIONING); + p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); +} + + +static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + p2p_dbg(p2p, "RX P2P Public Action from " MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len); + + if (len < 1) + return; + + switch (data[0]) { + case P2P_GO_NEG_REQ: + p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_GO_NEG_RESP: + p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_GO_NEG_CONF: + p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1); + break; + case P2P_INVITATION_REQ: + p2p_process_invitation_req(p2p, sa, data + 1, len - 1, + rx_freq); + break; + case P2P_INVITATION_RESP: + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); + break; + case P2P_PROV_DISC_REQ: + p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_PROV_DISC_RESP: + p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1); + break; + case P2P_DEV_DISC_REQ: + p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq); + break; + case P2P_DEV_DISC_RESP: + p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1); + break; + default: + p2p_dbg(p2p, "Unsupported P2P Public Action frame type %d", + data[0]); + break; + } +} + + +static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *bssid, const u8 *data, + size_t len, int freq) +{ + if (len < 1) + return; + + switch (data[0]) { + case WLAN_PA_VENDOR_SPECIFIC: + data++; + len--; + if (len < 3) + return; + if (WPA_GET_BE24(data) != OUI_WFA) + return; + + data += 3; + len -= 3; + if (len < 1) + return; + + if (*data != P2P_OUI_TYPE) + return; + + p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_INITIAL_REQ: + p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_INITIAL_RESP: + p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_COMEBACK_REQ: + p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq); + break; + case WLAN_PA_GAS_COMEBACK_RESP: + p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq); + break; + } +} + + +void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *bssid, u8 category, + const u8 *data, size_t len, int freq) +{ + if (category == WLAN_ACTION_PUBLIC) { + p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq); + return; + } + + if (category != WLAN_ACTION_VENDOR_SPECIFIC) + return; + + if (len < 4) + return; + + if (WPA_GET_BE24(data) != OUI_WFA) + return; + data += 3; + len -= 3; + + if (*data != P2P_OUI_TYPE) + return; + data++; + len--; + + /* P2P action frame */ + p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len); + + if (len < 1) + return; + switch (data[0]) { + case P2P_NOA: + p2p_dbg(p2p, "Received P2P Action - Notice of Absence"); + /* TODO */ + break; + case P2P_PRESENCE_REQ: + p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq); + break; + case P2P_PRESENCE_RESP: + p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1); + break; + case P2P_GO_DISC_REQ: + p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq); + break; + default: + p2p_dbg(p2p, "Received P2P Action - unknown type %u", data[0]); + break; + } +} + + +static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + if (p2p->go_neg_peer == NULL) + return; + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->go_neg_peer->status = P2P_SC_SUCCESS; + p2p_connect_send(p2p, p2p->go_neg_peer); +} + + +static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + if (p2p->invite_peer == NULL) + return; + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, + p2p->invite_dev_pw_id); +} + + +static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, + const u8 *ie, size_t ie_len) +{ + struct p2p_message msg; + struct p2p_device *dev; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL) + { + p2p_parse_free(&msg); + return; /* not a P2P probe */ + } + + if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN || + os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) + != 0) { + /* The Probe Request is not part of P2P Device Discovery. It is + * not known whether the source address of the frame is the P2P + * Device Address or P2P Interface Address. Do not add a new + * peer entry based on this frames. + */ + p2p_parse_free(&msg); + return; + } + + dev = p2p_get_device(p2p, addr); + if (dev) { + if (dev->country[0] == 0 && msg.listen_channel) + os_memcpy(dev->country, msg.listen_channel, 3); + os_get_reltime(&dev->last_seen); + p2p_parse_free(&msg); + return; /* already known */ + } + + dev = p2p_create_device(p2p, addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return; + } + + os_get_reltime(&dev->last_seen); + dev->flags |= P2P_DEV_PROBE_REQ_ONLY; + + if (msg.listen_channel) { + os_memcpy(dev->country, msg.listen_channel, 3); + dev->listen_freq = p2p_channel_to_freq(msg.listen_channel[3], + msg.listen_channel[4]); + } + + p2p_copy_wps_info(p2p, dev, 1, &msg); + + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + + p2p_parse_free(&msg); + + p2p_dbg(p2p, "Created device entry based on Probe Req: " MACSTR + " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d", + MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, + dev->info.group_capab, dev->info.device_name, + dev->listen_freq); +} + + +struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, + const u8 *addr, + struct p2p_message *msg) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, addr); + if (dev) { + os_get_reltime(&dev->last_seen); + return dev; /* already known */ + } + + dev = p2p_create_device(p2p, addr); + if (dev == NULL) + return NULL; + + p2p_add_dev_info(p2p, addr, dev, msg); + + return dev; +} + + +static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type) +{ + if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0) + return 1; + if (os_memcmp(dev_type, req_dev_type, 2) == 0 && + WPA_GET_BE32(&req_dev_type[2]) == 0 && + WPA_GET_BE16(&req_dev_type[6]) == 0) + return 1; /* Category match with wildcard OUI/sub-category */ + return 0; +} + + +int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], + size_t num_req_dev_type) +{ + size_t i; + for (i = 0; i < num_req_dev_type; i++) { + if (dev_type_match(dev_type, req_dev_type[i])) + return 1; + } + return 0; +} + + +/** + * p2p_match_dev_type - Match local device type with requested type + * @p2p: P2P module context from p2p_init() + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) + * Returns: 1 on match, 0 on mismatch + * + * This function can be used to match the Requested Device Type attribute in + * WPS IE with the local device types for deciding whether to reply to a Probe + * Request frame. + */ +int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) +{ + struct wps_parse_attr attr; + size_t i; + + if (wps_parse_msg(wps, &attr)) + return 1; /* assume no Requested Device Type attributes */ + + if (attr.num_req_dev_type == 0) + return 1; /* no Requested Device Type attributes -> match */ + + if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type, + attr.num_req_dev_type)) + return 1; /* Own Primary Device Type matches */ + + for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) + if (dev_type_list_match(p2p->cfg->sec_dev_type[i], + attr.req_dev_type, + attr.num_req_dev_type)) + return 1; /* Own Secondary Device Type matches */ + + /* No matching device type found */ + return 0; +} + + +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) +{ + struct wpabuf *buf; + u8 *len; + int pw_id = -1; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_resp) + extra = wpabuf_len(p2p->wfd_ie_probe_resp); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + if (p2p->go_neg_peer) { + /* Advertise immediate availability of WPS credential */ + pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method); + } + + if (p2p_build_wps_ie(p2p, buf, pw_id, 1) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for Probe Response"); + wpabuf_free(buf); + return NULL; + } + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_resp) + wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp); +#endif /* CONFIG_WIFI_DISPLAY */ + + /* P2P IE */ + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, + p2p->ext_listen_interval); + p2p_buf_add_device_info(buf, p2p, NULL); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +static enum p2p_probe_req_status +p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len) +{ + struct ieee802_11_elems elems; + struct wpabuf *buf; + struct ieee80211_mgmt *resp; + struct p2p_message msg; + struct wpabuf *ies; + + if (!p2p->in_listen || !p2p->drv_in_listen) { + /* not in Listen state - ignore Probe Request */ + return P2P_PREQ_NOT_LISTEN; + } + + if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == + ParseFailed) { + /* Ignore invalid Probe Request frames */ + return P2P_PREQ_MALFORMED; + } + + if (elems.p2p == NULL) { + /* not a P2P probe - ignore it */ + return P2P_PREQ_NOT_P2P; + } + + if (dst && !is_broadcast_ether_addr(dst) && + os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) { + /* Not sent to the broadcast address or our P2P Device Address + */ + return P2P_PREQ_NOT_PROCESSED; + } + + if (bssid && !is_broadcast_ether_addr(bssid)) { + /* Not sent to the Wildcard BSSID */ + return P2P_PREQ_NOT_PROCESSED; + } + + if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN || + os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != + 0) { + /* not using P2P Wildcard SSID - ignore */ + return P2P_PREQ_NOT_PROCESSED; + } + + if (supp_rates_11b_only(&elems)) { + /* Indicates support for 11b rates only */ + return P2P_PREQ_NOT_P2P; + } + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg) < 0) { + /* Could not parse P2P attributes */ + return P2P_PREQ_NOT_P2P; + } + + if (msg.device_id && + os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) { + /* Device ID did not match */ + p2p_parse_free(&msg); + return P2P_PREQ_NOT_PROCESSED; + } + + /* Check Requested Device Type match */ + if (msg.wps_attributes && + !p2p_match_dev_type(p2p, msg.wps_attributes)) { + /* No match with Requested Device Type */ + p2p_parse_free(&msg); + return P2P_PREQ_NOT_PROCESSED; + } + p2p_parse_free(&msg); + + if (!p2p->cfg->send_probe_resp) { + /* Response generated elsewhere */ + return P2P_PREQ_NOT_PROCESSED; + } + + p2p_dbg(p2p, "Reply to P2P Probe Request in Listen state"); + + /* + * We do not really have a specific BSS that this frame is advertising, + * so build a frame that has some information in valid format. This is + * really only used for discovery purposes, not to learn exact BSS + * parameters. + */ + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return P2P_PREQ_NOT_PROCESSED; + + buf = wpabuf_alloc(200 + wpabuf_len(ies)); + if (buf == NULL) { + wpabuf_free(ies); + return P2P_PREQ_NOT_PROCESSED; + } + + resp = NULL; + resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp); + + resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_RESP << 4)); + os_memcpy(resp->da, addr, ETH_ALEN); + os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); + os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); + resp->u.probe_resp.beacon_int = host_to_le16(100); + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + resp->u.probe_resp.capab_info = + host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | + WLAN_CAPABILITY_PRIVACY | + WLAN_CAPABILITY_SHORT_SLOT_TIME); + + wpabuf_put_u8(buf, WLAN_EID_SSID); + wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); + wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + + wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); + wpabuf_put_u8(buf, 8); + wpabuf_put_u8(buf, (60 / 5) | 0x80); + wpabuf_put_u8(buf, 90 / 5); + wpabuf_put_u8(buf, (120 / 5) | 0x80); + wpabuf_put_u8(buf, 180 / 5); + wpabuf_put_u8(buf, (240 / 5) | 0x80); + wpabuf_put_u8(buf, 360 / 5); + wpabuf_put_u8(buf, 480 / 5); + wpabuf_put_u8(buf, 540 / 5); + + wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, p2p->cfg->channel); + + wpabuf_put_buf(buf, ies); + wpabuf_free(ies); + + p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf); + + wpabuf_free(buf); + + return P2P_PREQ_NOT_PROCESSED; +} + + +enum p2p_probe_req_status +p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len) +{ + enum p2p_probe_req_status res; + + p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); + + res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len); + + if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) && + p2p->go_neg_peer && + os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) + == 0 && + !(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { + /* Received a Probe Request from GO Negotiation peer */ + p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout"); + eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); + eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); + return P2P_PREQ_PROCESSED; + } + + if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && + p2p->invite_peer && + os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN) + == 0) { + /* Received a Probe Request from Invite peer */ + p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout"); + eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); + return P2P_PREQ_PROCESSED; + } + + return res; +} + + +static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid, + u8 *buf, size_t len, struct wpabuf *p2p_ie) +{ + struct wpabuf *tmp; + u8 *lpos; + size_t tmplen; + int res; + u8 group_capab; + + if (p2p_ie == NULL) + return 0; /* WLAN AP is not a P2P manager */ + + /* + * (Re)Association Request - P2P IE + * P2P Capability attribute (shall be present) + * P2P Interface attribute (present if concurrent device and + * P2P Management is enabled) + */ + tmp = wpabuf_alloc(200); + if (tmp == NULL) + return -1; + + lpos = p2p_buf_add_ie_hdr(tmp); + group_capab = 0; + if (p2p->num_groups > 0) { + group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; + if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && + (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) && + p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + } + p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab); + if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) && + (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED)) + p2p_buf_add_p2p_interface(tmp, p2p); + p2p_buf_update_ie_hdr(tmp, lpos); + + tmplen = wpabuf_len(tmp); + if (tmplen > len) + res = -1; + else { + os_memcpy(buf, wpabuf_head(tmp), tmplen); + res = tmplen; + } + wpabuf_free(tmp); + + return res; +} + + +int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, + size_t len, int p2p_group, struct wpabuf *p2p_ie) +{ + struct wpabuf *tmp; + u8 *lpos; + struct p2p_device *peer; + size_t tmplen; + int res; + size_t extra = 0; + + if (!p2p_group) + return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_assoc_req) + extra = wpabuf_len(p2p->wfd_ie_assoc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + /* + * (Re)Association Request - P2P IE + * P2P Capability attribute (shall be present) + * Extended Listen Timing (may be present) + * P2P Device Info attribute (shall be present) + */ + tmp = wpabuf_alloc(200 + extra); + if (tmp == NULL) + return -1; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_assoc_req) + wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + peer = bssid ? p2p_get_device(p2p, bssid) : NULL; + + lpos = p2p_buf_add_ie_hdr(tmp); + p2p_buf_add_capability(tmp, p2p->dev_capab, 0); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period, + p2p->ext_listen_interval); + p2p_buf_add_device_info(tmp, p2p, peer); + p2p_buf_update_ie_hdr(tmp, lpos); + + tmplen = wpabuf_len(tmp); + if (tmplen > len) + res = -1; + else { + os_memcpy(buf, wpabuf_head(tmp), tmplen); + res = tmplen; + } + wpabuf_free(tmp); + + return res; +} + + +int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end) +{ + struct wpabuf *p2p_ie; + int ret; + + p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE); + if (p2p_ie == NULL) + return 0; + + ret = p2p_attr_text(p2p_ie, buf, end); + wpabuf_free(p2p_ie); + return ret; +} + + +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return -1; + + if (msg.p2p_device_addr) { + os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); + return 0; + } else if (msg.device_id) { + os_memcpy(dev_addr, msg.device_id, ETH_ALEN); + return 0; + } + return -1; +} + + +int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr) +{ + struct wpabuf *p2p_ie; + int ret; + + p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, + P2P_IE_VENDOR_TYPE); + if (p2p_ie == NULL) + return -1; + ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr); + wpabuf_free(p2p_ie); + return ret; +} + + +static void p2p_clear_go_neg(struct p2p_data *p2p) +{ + p2p->go_neg_peer = NULL; + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); +} + + +void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr) +{ + if (p2p->go_neg_peer == NULL) { + p2p_dbg(p2p, "No pending Group Formation - ignore WPS registration success notification"); + return; /* No pending Group Formation */ + } + + if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) != + 0) { + p2p_dbg(p2p, "Ignore WPS registration success notification for " + MACSTR " (GO Negotiation peer " MACSTR ")", + MAC2STR(mac_addr), + MAC2STR(p2p->go_neg_peer->intended_addr)); + return; /* Ignore unexpected peer address */ + } + + p2p_dbg(p2p, "Group Formation completed successfully with " MACSTR, + MAC2STR(mac_addr)); + + p2p_clear_go_neg(p2p); +} + + +void p2p_group_formation_failed(struct p2p_data *p2p) +{ + if (p2p->go_neg_peer == NULL) { + p2p_dbg(p2p, "No pending Group Formation - ignore group formation failure notification"); + return; /* No pending Group Formation */ + } + + p2p_dbg(p2p, "Group Formation failed with " MACSTR, + MAC2STR(p2p->go_neg_peer->intended_addr)); + + p2p_clear_go_neg(p2p); +} + + +struct p2p_data * p2p_init(const struct p2p_config *cfg) +{ + struct p2p_data *p2p; + + if (cfg->max_peers < 1) + return NULL; + + p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg)); + if (p2p == NULL) + return NULL; + p2p->cfg = (struct p2p_config *) (p2p + 1); + os_memcpy(p2p->cfg, cfg, sizeof(*cfg)); + if (cfg->dev_name) + p2p->cfg->dev_name = os_strdup(cfg->dev_name); + if (cfg->manufacturer) + p2p->cfg->manufacturer = os_strdup(cfg->manufacturer); + if (cfg->model_name) + p2p->cfg->model_name = os_strdup(cfg->model_name); + if (cfg->model_number) + p2p->cfg->model_number = os_strdup(cfg->model_number); + if (cfg->serial_number) + p2p->cfg->serial_number = os_strdup(cfg->serial_number); + if (cfg->pref_chan) { + p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan * + sizeof(struct p2p_channel)); + if (p2p->cfg->pref_chan) { + os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan, + cfg->num_pref_chan * + sizeof(struct p2p_channel)); + } else + p2p->cfg->num_pref_chan = 0; + } + + p2p->min_disc_int = 1; + p2p->max_disc_int = 3; + p2p->max_disc_tu = -1; + + os_get_random(&p2p->next_tie_breaker, 1); + p2p->next_tie_breaker &= 0x01; + if (cfg->sd_request) + p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; + p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE; + if (cfg->concurrent_operations) + p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER; + p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + + dl_list_init(&p2p->devices); + + eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0, + p2p_expiration_timeout, p2p, NULL); + + p2p->go_timeout = 100; + p2p->client_timeout = 20; + + p2p_dbg(p2p, "initialized"); + p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); + p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); + + return p2p; +} + + +void p2p_deinit(struct p2p_data *p2p) +{ +#ifdef CONFIG_WIFI_DISPLAY + wpabuf_free(p2p->wfd_ie_beacon); + wpabuf_free(p2p->wfd_ie_probe_req); + wpabuf_free(p2p->wfd_ie_probe_resp); + wpabuf_free(p2p->wfd_ie_assoc_req); + wpabuf_free(p2p->wfd_ie_invitation); + wpabuf_free(p2p->wfd_ie_prov_disc_req); + wpabuf_free(p2p->wfd_ie_prov_disc_resp); + wpabuf_free(p2p->wfd_ie_go_neg); + wpabuf_free(p2p->wfd_dev_info); + wpabuf_free(p2p->wfd_assoc_bssid); + wpabuf_free(p2p->wfd_coupled_sink_info); +#endif /* CONFIG_WIFI_DISPLAY */ + + eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); + eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); + p2p_flush(p2p); + p2p_free_req_dev_types(p2p); + os_free(p2p->cfg->dev_name); + os_free(p2p->cfg->manufacturer); + os_free(p2p->cfg->model_name); + os_free(p2p->cfg->model_number); + os_free(p2p->cfg->serial_number); + os_free(p2p->cfg->pref_chan); + os_free(p2p->groups); + wpabuf_free(p2p->sd_resp); + os_free(p2p->after_scan_tx); + p2p_remove_wps_vendor_extensions(p2p); + os_free(p2p->no_go_freq.range); + os_free(p2p); +} + + +void p2p_flush(struct p2p_data *p2p) +{ + struct p2p_device *dev, *prev; + p2p_stop_find(p2p); + dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device, + list) { + dl_list_del(&dev->list); + p2p_device_free(p2p, dev); + } + p2p_free_sd_queries(p2p); + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; +} + + +int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, addr); + if (dev == NULL) + return -1; + + p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr)); + + if (p2p->go_neg_peer == dev) + p2p->go_neg_peer = NULL; + + dev->wps_method = WPS_NOT_READY; + dev->oob_pw_id = 0; + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + + /* Check if after_scan_tx is for this peer. If so free it */ + if (p2p->after_scan_tx && + os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) { + os_free(p2p->after_scan_tx); + p2p->after_scan_tx = NULL; + } + + return 0; +} + + +int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name) +{ + os_free(p2p->cfg->dev_name); + if (dev_name) { + p2p->cfg->dev_name = os_strdup(dev_name); + if (p2p->cfg->dev_name == NULL) + return -1; + } else + p2p->cfg->dev_name = NULL; + return 0; +} + + +int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer) +{ + os_free(p2p->cfg->manufacturer); + p2p->cfg->manufacturer = NULL; + if (manufacturer) { + p2p->cfg->manufacturer = os_strdup(manufacturer); + if (p2p->cfg->manufacturer == NULL) + return -1; + } + + return 0; +} + + +int p2p_set_model_name(struct p2p_data *p2p, const char *model_name) +{ + os_free(p2p->cfg->model_name); + p2p->cfg->model_name = NULL; + if (model_name) { + p2p->cfg->model_name = os_strdup(model_name); + if (p2p->cfg->model_name == NULL) + return -1; + } + + return 0; +} + + +int p2p_set_model_number(struct p2p_data *p2p, const char *model_number) +{ + os_free(p2p->cfg->model_number); + p2p->cfg->model_number = NULL; + if (model_number) { + p2p->cfg->model_number = os_strdup(model_number); + if (p2p->cfg->model_number == NULL) + return -1; + } + + return 0; +} + + +int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number) +{ + os_free(p2p->cfg->serial_number); + p2p->cfg->serial_number = NULL; + if (serial_number) { + p2p->cfg->serial_number = os_strdup(serial_number); + if (p2p->cfg->serial_number == NULL) + return -1; + } + + return 0; +} + + +void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods) +{ + p2p->cfg->config_methods = config_methods; +} + + +void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid) +{ + os_memcpy(p2p->cfg->uuid, uuid, 16); +} + + +int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type) +{ + os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8); + return 0; +} + + +int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], + size_t num_dev_types) +{ + if (num_dev_types > P2P_SEC_DEVICE_TYPES) + num_dev_types = P2P_SEC_DEVICE_TYPES; + p2p->cfg->num_sec_dev_types = num_dev_types; + os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8); + return 0; +} + + +void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p) +{ + int i; + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + wpabuf_free(p2p->wps_vendor_ext[i]); + p2p->wps_vendor_ext[i] = NULL; + } +} + + +int p2p_add_wps_vendor_extension(struct p2p_data *p2p, + const struct wpabuf *vendor_ext) +{ + int i; + + if (vendor_ext == NULL) + return -1; + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (p2p->wps_vendor_ext[i] == NULL) + break; + } + if (i >= P2P_MAX_WPS_VENDOR_EXT) + return -1; + + p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext); + if (p2p->wps_vendor_ext[i] == NULL) + return -1; + + return 0; +} + + +int p2p_set_country(struct p2p_data *p2p, const char *country) +{ + os_memcpy(p2p->cfg->country, country, 3); + return 0; +} + + +void p2p_continue_find(struct p2p_data *p2p) +{ + struct p2p_device *dev; + p2p_set_state(p2p, P2P_SEARCH); + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (dev->flags & P2P_DEV_SD_SCHEDULE) { + if (p2p_start_sd(p2p, dev) == 0) + return; + else + break; + } else if (dev->req_config_methods && + !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { + p2p_dbg(p2p, "Send pending Provision Discovery Request to " + MACSTR " (config methods 0x%x)", + MAC2STR(dev->info.p2p_device_addr), + dev->req_config_methods); + if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0) + return; + } + } + + p2p_listen_in_find(p2p, 1); +} + + +static void p2p_sd_cb(struct p2p_data *p2p, int success) +{ + p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d", + success); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + if (!success) { + if (p2p->sd_peer) { + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + } + p2p_continue_find(p2p); + return; + } + + if (p2p->sd_peer == NULL) { + p2p_dbg(p2p, "No SD peer entry known"); + p2p_continue_find(p2p); + return; + } + + /* Wait for response from the peer */ + p2p_set_state(p2p, P2P_SD_DURING_FIND); + p2p_set_timeout(p2p, 0, 200000); +} + + +/** + * p2p_retry_pd - Retry any pending provision disc requests in IDLE state + * @p2p: P2P module context from p2p_init() + */ +static void p2p_retry_pd(struct p2p_data *p2p) +{ + struct p2p_device *dev; + + if (p2p->state != P2P_IDLE) + return; + + /* + * Retry the prov disc req attempt only for the peer that the user had + * requested. + */ + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(p2p->pending_pd_devaddr, + dev->info.p2p_device_addr, ETH_ALEN) != 0) + continue; + if (!dev->req_config_methods) + continue; + + p2p_dbg(p2p, "Send pending Provision Discovery Request to " + MACSTR " (config methods 0x%x)", + MAC2STR(dev->info.p2p_device_addr), + dev->req_config_methods); + p2p_send_prov_disc_req(p2p, dev, + dev->flags & P2P_DEV_PD_FOR_JOIN, + p2p->pd_force_freq); + return; + } +} + + +static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) +{ + p2p_dbg(p2p, "Provision Discovery Request TX callback: success=%d", + success); + + /* + * Postpone resetting the pending action state till after we actually + * time out. This allows us to take some action like notifying any + * interested parties about no response to the request. + * + * When the timer (below) goes off we check in IDLE, SEARCH, or + * LISTEN_ONLY state, which are the only allowed states to issue a PD + * requests in, if this was still pending and then raise notification. + */ + + if (!success) { + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + if (p2p->user_initiated_pd && + (p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY)) + { + /* Retry request from timeout to avoid busy loops */ + p2p->pending_action_state = P2P_PENDING_PD; + p2p_set_timeout(p2p, 0, 50000); + } else if (p2p->state != P2P_IDLE) + p2p_continue_find(p2p); + else if (p2p->user_initiated_pd) { + p2p->pending_action_state = P2P_PENDING_PD; + p2p_set_timeout(p2p, 0, 300000); + } + return; + } + + /* + * This postponing, of resetting pending_action_state, needs to be + * done only for user initiated PD requests and not internal ones. + */ + if (p2p->user_initiated_pd) + p2p->pending_action_state = P2P_PENDING_PD; + else + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + /* Wait for response from the peer */ + if (p2p->state == P2P_SEARCH) + p2p_set_state(p2p, P2P_PD_DURING_FIND); + p2p_set_timeout(p2p, 0, 200000); +} + + +int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, + struct os_reltime *rx_time, int level, const u8 *ies, + size_t ies_len) +{ + if (os_reltime_before(rx_time, &p2p->find_start)) { + /* + * The driver may have cached (e.g., in cfg80211 BSS table) the + * scan results for relatively long time. To avoid reporting + * stale information, update P2P peers only based on results + * that have based on frames received after the last p2p_find + * operation was started. + */ + p2p_dbg(p2p, "Ignore old scan result for " MACSTR + " (rx_time=%u.%06u)", + MAC2STR(bssid), (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec); + return 0; + } + + p2p_add_device(p2p, bssid, freq, rx_time, level, ies, ies_len, 1); + + return 0; +} + + +void p2p_scan_res_handled(struct p2p_data *p2p) +{ + if (!p2p->p2p_scan_running) { + p2p_dbg(p2p, "p2p_scan was not running, but scan results received"); + } + p2p->p2p_scan_running = 0; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + + if (p2p_run_after_scan(p2p)) + return; + if (p2p->state == P2P_SEARCH) + p2p_continue_find(p2p); +} + + +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) +{ + u8 *len; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_probe_req) + wpabuf_put_buf(ies, p2p->wfd_ie_probe_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + len = p2p_buf_add_ie_hdr(ies); + p2p_buf_add_capability(ies, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + if (dev_id) + p2p_buf_add_device_id(ies, dev_id); + if (p2p->cfg->reg_class && p2p->cfg->channel) + p2p_buf_add_listen_channel(ies, p2p->cfg->country, + p2p->cfg->reg_class, + p2p->cfg->channel); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period, + p2p->ext_listen_interval); + /* TODO: p2p_buf_add_operating_channel() if GO */ + p2p_buf_update_ie_hdr(ies, len); +} + + +size_t p2p_scan_ie_buf_len(struct p2p_data *p2p) +{ + size_t len = 100; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p && p2p->wfd_ie_probe_req) + len += wpabuf_len(p2p->wfd_ie_probe_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + return len; +} + + +int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end) +{ + return p2p_attr_text(p2p_ie, buf, end); +} + + +static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) +{ + struct p2p_device *dev = p2p->go_neg_peer; + int timeout; + + p2p_dbg(p2p, "GO Negotiation Request TX callback: success=%d", success); + + if (dev == NULL) { + p2p_dbg(p2p, "No pending GO Negotiation"); + return; + } + + if (success) { + if (dev->flags & P2P_DEV_USER_REJECTED) { + p2p_set_state(p2p, P2P_IDLE); + return; + } + } else if (dev->go_neg_req_sent) { + /* Cancel the increment from p2p_connect_send() on failure */ + dev->go_neg_req_sent--; + } + + if (!success && + (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) && + !is_zero_ether_addr(dev->member_in_go_dev)) { + p2p_dbg(p2p, "Peer " MACSTR " did not acknowledge request - try to use device discoverability through its GO", + MAC2STR(dev->info.p2p_device_addr)); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_send_dev_disc_req(p2p, dev); + return; + } + + /* + * Use P2P find, if needed, to find the other device from its listen + * channel. + */ + p2p_set_state(p2p, P2P_CONNECT); + timeout = success ? 500000 : 100000; + if (!success && p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE)) { + unsigned int r; + /* + * Peer is expected to wait our response and we will skip the + * listen phase. Add some randomness to the wait time here to + * make it less likely to hit cases where we could end up in + * sync with peer not listening. + */ + os_get_random((u8 *) &r, sizeof(r)); + timeout += r % 100000; + } + p2p_set_timeout(p2p, 0, timeout); +} + + +static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success) +{ + p2p_dbg(p2p, "GO Negotiation Response TX callback: success=%d", + success); + if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) { + p2p_dbg(p2p, "Ignore TX callback event - GO Negotiation is not running anymore"); + return; + } + p2p_set_state(p2p, P2P_CONNECT); + p2p_set_timeout(p2p, 0, 500000); +} + + +static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success, + const u8 *addr) +{ + p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success); + if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) { + p2p_go_neg_failed(p2p, p2p->go_neg_peer, + p2p->go_neg_peer->status); + } else if (success) { + struct p2p_device *dev; + dev = p2p_get_device(p2p, addr); + if (dev && + dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) + dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE; + } +} + + +static void p2p_go_neg_conf_cb(struct p2p_data *p2p, + enum p2p_send_action_result result) +{ + struct p2p_device *dev; + + p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (result == P2P_SEND_ACTION_FAILED) { + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return; + } + if (result == P2P_SEND_ACTION_NO_ACK) { + /* + * It looks like the TX status for GO Negotiation Confirm is + * often showing failure even when the peer has actually + * received the frame. Since the peer may change channels + * immediately after having received the frame, we may not see + * an Ack for retries, so just dropping a single frame may + * trigger this. To allow the group formation to succeed if the + * peer did indeed receive the frame, continue regardless of + * the TX status. + */ + p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported"); + } + + dev = p2p->go_neg_peer; + if (dev == NULL) + return; + + p2p_go_complete(p2p, dev); +} + + +void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + enum p2p_send_action_result result) +{ + enum p2p_pending_action_state state; + int success; + + p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR + " src=" MACSTR " bssid=" MACSTR " result=%d", + p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src), + MAC2STR(bssid), result); + success = result == P2P_SEND_ACTION_SUCCESS; + state = p2p->pending_action_state; + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + switch (state) { + case P2P_NO_PENDING_ACTION: + if (p2p->after_scan_tx_in_progress) { + p2p->after_scan_tx_in_progress = 0; + if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING && + p2p_run_after_scan(p2p)) + break; + if (p2p->state == P2P_SEARCH) { + p2p_dbg(p2p, "Continue find after after_scan_tx completion"); + p2p_continue_find(p2p); + } + } + break; + case P2P_PENDING_GO_NEG_REQUEST: + p2p_go_neg_req_cb(p2p, success); + break; + case P2P_PENDING_GO_NEG_RESPONSE: + p2p_go_neg_resp_cb(p2p, success); + break; + case P2P_PENDING_GO_NEG_RESPONSE_FAILURE: + p2p_go_neg_resp_failure_cb(p2p, success, dst); + break; + case P2P_PENDING_GO_NEG_CONFIRM: + p2p_go_neg_conf_cb(p2p, result); + break; + case P2P_PENDING_SD: + p2p_sd_cb(p2p, success); + break; + case P2P_PENDING_PD: + p2p_prov_disc_cb(p2p, success); + break; + case P2P_PENDING_INVITATION_REQUEST: + p2p_invitation_req_cb(p2p, success); + break; + case P2P_PENDING_INVITATION_RESPONSE: + p2p_invitation_resp_cb(p2p, success); + break; + case P2P_PENDING_DEV_DISC_REQUEST: + p2p_dev_disc_req_cb(p2p, success); + break; + case P2P_PENDING_DEV_DISC_RESPONSE: + p2p_dev_disc_resp_cb(p2p, success); + break; + case P2P_PENDING_GO_DISC_REQ: + p2p_go_disc_req_cb(p2p, success); + break; + } + + p2p->after_scan_tx_in_progress = 0; +} + + +void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, + unsigned int duration) +{ + if (freq == p2p->pending_client_disc_freq) { + p2p_dbg(p2p, "Client discoverability remain-awake completed"); + p2p->pending_client_disc_freq = 0; + return; + } + + if (freq != p2p->pending_listen_freq) { + p2p_dbg(p2p, "Unexpected listen callback for freq=%u duration=%u (pending_listen_freq=%u)", + freq, duration, p2p->pending_listen_freq); + return; + } + + p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback", + p2p->pending_listen_sec, p2p->pending_listen_usec, + p2p->pending_listen_freq); + p2p->in_listen = 1; + p2p->drv_in_listen = freq; + if (p2p->pending_listen_sec || p2p->pending_listen_usec) { + /* + * Add 20 msec extra wait to avoid race condition with driver + * remain-on-channel end event, i.e., give driver more time to + * complete the operation before our timeout expires. + */ + p2p_set_timeout(p2p, p2p->pending_listen_sec, + p2p->pending_listen_usec + 20000); + } + + p2p->pending_listen_freq = 0; +} + + +int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) +{ + p2p_dbg(p2p, "Driver ended Listen state (freq=%u)", freq); + p2p->drv_in_listen = 0; + if (p2p->in_listen) + return 0; /* Internal timeout will trigger the next step */ + + if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { + if (p2p->go_neg_peer->connect_reqs >= 120) { + p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return 0; + } + + p2p_set_state(p2p, P2P_CONNECT); + p2p_connect_send(p2p, p2p->go_neg_peer); + return 1; + } else if (p2p->state == P2P_SEARCH) { + if (p2p->p2p_scan_running) { + /* + * Search is already in progress. This can happen if + * an Action frame RX is reported immediately after + * the end of a remain-on-channel operation and the + * response frame to that is sent using an offchannel + * operation while in p2p_find. Avoid an attempt to + * restart a scan here. + */ + p2p_dbg(p2p, "p2p_scan already in progress - do not try to start a new one"); + return 1; + } + if (p2p->pending_listen_freq) { + /* + * Better wait a bit if the driver is unable to start + * offchannel operation for some reason. p2p_search() + * will be started from internal timeout. + */ + p2p_dbg(p2p, "Listen operation did not seem to start - delay search phase to avoid busy loop"); + p2p_set_timeout(p2p, 0, 100000); + return 1; + } + if (p2p->search_delay) { + p2p_dbg(p2p, "Delay search operation by %u ms", + p2p->search_delay); + p2p_set_timeout(p2p, p2p->search_delay / 1000, + (p2p->search_delay % 1000) * 1000); + return 1; + } + p2p_search(p2p); + return 1; + } + + return 0; +} + + +static void p2p_timeout_connect(struct p2p_data *p2p) +{ + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { + p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed"); + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return; + } + if (p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE) && + p2p->go_neg_peer->connect_reqs < 120) { + p2p_dbg(p2p, "Peer expected to wait our response - skip listen"); + p2p_connect_send(p2p, p2p->go_neg_peer); + return; + } + if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) { + p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)"); + p2p_set_state(p2p, P2P_CONNECT_LISTEN); + p2p_set_timeout(p2p, 0, 30000); + return; + } + p2p_set_state(p2p, P2P_CONNECT_LISTEN); + p2p_listen_in_find(p2p, 0); +} + + +static void p2p_timeout_connect_listen(struct p2p_data *p2p) +{ + if (p2p->go_neg_peer) { + if (p2p->drv_in_listen) { + p2p_dbg(p2p, "Driver is still in Listen state; wait for it to complete"); + return; + } + + if (p2p->go_neg_peer->connect_reqs >= 120) { + p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); + p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + return; + } + + p2p_set_state(p2p, P2P_CONNECT); + p2p_connect_send(p2p, p2p->go_neg_peer); + } else + p2p_set_state(p2p, P2P_IDLE); +} + + +static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p) +{ + p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); + + if (p2p->cfg->is_concurrent_session_active && + p2p->cfg->is_concurrent_session_active(p2p->cfg->cb_ctx)) + p2p_set_timeout(p2p, 0, 500000); + else + p2p_set_timeout(p2p, 0, 200000); +} + + +static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) +{ + struct p2p_device *dev = p2p->go_neg_peer; + + if (dev == NULL) { + p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait"); + return; + } + + dev->wait_count++; + if (dev->wait_count >= 120) { + p2p_dbg(p2p, "Timeout on waiting peer to become ready for GO Negotiation"); + p2p_go_neg_failed(p2p, dev, -1); + return; + } + + p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation"); + p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT); + p2p_listen_in_find(p2p, 0); +} + + +static void p2p_timeout_sd_during_find(struct p2p_data *p2p) +{ + p2p_dbg(p2p, "Service Discovery Query timeout"); + if (p2p->sd_peer) { + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + } + p2p_continue_find(p2p); +} + + +static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) +{ + p2p_dbg(p2p, "Provision Discovery Request timeout"); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_continue_find(p2p); +} + + +static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) +{ + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + + /* + * For user initiated PD requests that we have not gotten any responses + * for while in IDLE state, we retry them a couple of times before + * giving up. + */ + if (!p2p->user_initiated_pd) + return; + + p2p_dbg(p2p, "User initiated Provision Discovery Request timeout"); + + if (p2p->pd_retries) { + p2p->pd_retries--; + p2p_retry_pd(p2p); + } else { + struct p2p_device *dev; + int for_join = 0; + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(p2p->pending_pd_devaddr, + dev->info.p2p_device_addr, ETH_ALEN) != 0) + continue; + if (dev->req_config_methods && + (dev->flags & P2P_DEV_PD_FOR_JOIN)) + for_join = 1; + } + + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, + p2p->pending_pd_devaddr, + for_join ? + P2P_PROV_DISC_TIMEOUT_JOIN : + P2P_PROV_DISC_TIMEOUT); + p2p_reset_pending_pd(p2p); + } +} + + +static void p2p_timeout_invite(struct p2p_data *p2p) +{ + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_set_state(p2p, P2P_INVITE_LISTEN); + if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { + /* + * Better remain on operating channel instead of listen channel + * when running a group. + */ + p2p_dbg(p2p, "Inviting in active GO role - wait on operating channel"); + p2p_set_timeout(p2p, 0, 100000); + return; + } + p2p_listen_in_find(p2p, 0); +} + + +static void p2p_timeout_invite_listen(struct p2p_data *p2p) +{ + if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) { + p2p_set_state(p2p, P2P_INVITE); + p2p_invite_send(p2p, p2p->invite_peer, + p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); + } else { + if (p2p->invite_peer) { + p2p_dbg(p2p, "Invitation Request retry limit reached"); + if (p2p->cfg->invitation_result) + p2p->cfg->invitation_result( + p2p->cfg->cb_ctx, -1, NULL, NULL, + p2p->invite_peer->info.p2p_device_addr, + 0); + } + p2p_set_state(p2p, P2P_IDLE); + } +} + + +static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + + p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state)); + + p2p->in_listen = 0; + + switch (p2p->state) { + case P2P_IDLE: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); + break; + case P2P_SEARCH: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); + if (p2p->search_delay && !p2p->in_search_delay) { + p2p_dbg(p2p, "Delay search operation by %u ms", + p2p->search_delay); + p2p->in_search_delay = 1; + p2p_set_timeout(p2p, p2p->search_delay / 1000, + (p2p->search_delay % 1000) * 1000); + break; + } + p2p->in_search_delay = 0; + p2p_search(p2p); + break; + case P2P_CONNECT: + p2p_timeout_connect(p2p); + break; + case P2P_CONNECT_LISTEN: + p2p_timeout_connect_listen(p2p); + break; + case P2P_GO_NEG: + break; + case P2P_LISTEN_ONLY: + /* Check if we timed out waiting for PD req */ + if (p2p->pending_action_state == P2P_PENDING_PD) + p2p_timeout_prov_disc_req(p2p); + + if (p2p->ext_listen_only) { + p2p_dbg(p2p, "Extended Listen Timing - Listen State completed"); + p2p->ext_listen_only = 0; + p2p_set_state(p2p, P2P_IDLE); + } + break; + case P2P_WAIT_PEER_CONNECT: + p2p_timeout_wait_peer_connect(p2p); + break; + case P2P_WAIT_PEER_IDLE: + p2p_timeout_wait_peer_idle(p2p); + break; + case P2P_SD_DURING_FIND: + p2p_timeout_sd_during_find(p2p); + break; + case P2P_PROVISIONING: + break; + case P2P_PD_DURING_FIND: + p2p_timeout_prov_disc_during_find(p2p); + break; + case P2P_INVITE: + p2p_timeout_invite(p2p); + break; + case P2P_INVITE_LISTEN: + p2p_timeout_invite_listen(p2p); + break; + } +} + + +int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, peer_addr); + p2p_dbg(p2p, "Local request to reject connection attempts by peer " + MACSTR, MAC2STR(peer_addr)); + if (dev == NULL) { + p2p_dbg(p2p, "Peer " MACSTR " unknown", MAC2STR(peer_addr)); + return -1; + } + dev->status = P2P_SC_FAIL_REJECTED_BY_USER; + dev->flags |= P2P_DEV_USER_REJECTED; + return 0; +} + + +const char * p2p_wps_method_text(enum p2p_wps_method method) +{ + switch (method) { + case WPS_NOT_READY: + return "not-ready"; + case WPS_PIN_DISPLAY: + return "Display"; + case WPS_PIN_KEYPAD: + return "Keypad"; + case WPS_PBC: + return "PBC"; + case WPS_NFC: + return "NFC"; + } + + return "??"; +} + + +static const char * p2p_go_state_text(enum p2p_go_state go_state) +{ + switch (go_state) { + case UNKNOWN_GO: + return "unknown"; + case LOCAL_GO: + return "local"; + case REMOTE_GO: + return "remote"; + } + + return "??"; +} + + +const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, + const u8 *addr, int next) +{ + struct p2p_device *dev; + + if (addr) + dev = p2p_get_device(p2p, addr); + else + dev = dl_list_first(&p2p->devices, struct p2p_device, list); + + if (dev && next) { + dev = dl_list_first(&dev->list, struct p2p_device, list); + if (&dev->list == &p2p->devices) + dev = NULL; + } + + if (dev == NULL) + return NULL; + + return &dev->info; +} + + +int p2p_get_peer_info_txt(const struct p2p_peer_info *info, + char *buf, size_t buflen) +{ + struct p2p_device *dev; + int res; + char *pos, *end; + struct os_reltime now; + + if (info == NULL) + return -1; + + dev = (struct p2p_device *) (((u8 *) info) - + offsetof(struct p2p_device, info)); + + pos = buf; + end = buf + buflen; + + os_get_reltime(&now); + res = os_snprintf(pos, end - pos, + "age=%d\n" + "listen_freq=%d\n" + "wps_method=%s\n" + "interface_addr=" MACSTR "\n" + "member_in_go_dev=" MACSTR "\n" + "member_in_go_iface=" MACSTR "\n" + "go_neg_req_sent=%d\n" + "go_state=%s\n" + "dialog_token=%u\n" + "intended_addr=" MACSTR "\n" + "country=%c%c\n" + "oper_freq=%d\n" + "req_config_methods=0x%x\n" + "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" + "status=%d\n" + "wait_count=%u\n" + "invitation_reqs=%u\n", + (int) (now.sec - dev->last_seen.sec), + dev->listen_freq, + p2p_wps_method_text(dev->wps_method), + MAC2STR(dev->interface_addr), + MAC2STR(dev->member_in_go_dev), + MAC2STR(dev->member_in_go_iface), + dev->go_neg_req_sent, + p2p_go_state_text(dev->go_state), + dev->dialog_token, + MAC2STR(dev->intended_addr), + dev->country[0] ? dev->country[0] : '_', + dev->country[1] ? dev->country[1] : '_', + dev->oper_freq, + dev->req_config_methods, + dev->flags & P2P_DEV_PROBE_REQ_ONLY ? + "[PROBE_REQ_ONLY]" : "", + dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "", + dev->flags & P2P_DEV_NOT_YET_READY ? + "[NOT_YET_READY]" : "", + dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "", + dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" : + "", + dev->flags & P2P_DEV_PD_PEER_DISPLAY ? + "[PD_PEER_DISPLAY]" : "", + dev->flags & P2P_DEV_PD_PEER_KEYPAD ? + "[PD_PEER_KEYPAD]" : "", + dev->flags & P2P_DEV_USER_REJECTED ? + "[USER_REJECTED]" : "", + dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ? + "[PEER_WAITING_RESPONSE]" : "", + dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ? + "[PREFER_PERSISTENT_GROUP]" : "", + dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ? + "[WAIT_GO_NEG_RESPONSE]" : "", + dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ? + "[WAIT_GO_NEG_CONFIRM]" : "", + dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ? + "[GROUP_CLIENT_ONLY]" : "", + dev->flags & P2P_DEV_FORCE_FREQ ? + "[FORCE_FREQ]" : "", + dev->flags & P2P_DEV_PD_FOR_JOIN ? + "[PD_FOR_JOIN]" : "", + dev->status, + dev->wait_count, + dev->invitation_reqs); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + if (dev->ext_listen_period) { + res = os_snprintf(pos, end - pos, + "ext_listen_period=%u\n" + "ext_listen_interval=%u\n", + dev->ext_listen_period, + dev->ext_listen_interval); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } + + if (dev->oper_ssid_len) { + res = os_snprintf(pos, end - pos, + "oper_ssid=%s\n", + wpa_ssid_txt(dev->oper_ssid, + dev->oper_ssid_len)); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } + +#ifdef CONFIG_WIFI_DISPLAY + if (dev->info.wfd_subelems) { + res = os_snprintf(pos, end - pos, "wfd_subelems="); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + + pos += wpa_snprintf_hex(pos, end - pos, + wpabuf_head(dev->info.wfd_subelems), + wpabuf_len(dev->info.wfd_subelems)); + + res = os_snprintf(pos, end - pos, "\n"); + if (res < 0 || res >= end - pos) + return pos - buf; + pos += res; + } +#endif /* CONFIG_WIFI_DISPLAY */ + + return pos - buf; +} + + +int p2p_peer_known(struct p2p_data *p2p, const u8 *addr) +{ + return p2p_get_device(p2p, addr) != NULL; +} + + +void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled) +{ + if (enabled) { + p2p_dbg(p2p, "Client discoverability enabled"); + p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + } else { + p2p_dbg(p2p, "Client discoverability disabled"); + p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + } +} + + +static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1, + u32 duration2, u32 interval2) +{ + struct wpabuf *req; + struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL; + u8 *len; + + req = wpabuf_alloc(100); + if (req == NULL) + return NULL; + + if (duration1 || interval1) { + os_memset(&desc1, 0, sizeof(desc1)); + desc1.count_type = 1; + desc1.duration = duration1; + desc1.interval = interval1; + ptr1 = &desc1; + + if (duration2 || interval2) { + os_memset(&desc2, 0, sizeof(desc2)); + desc2.count_type = 2; + desc2.duration = duration2; + desc2.interval = interval2; + ptr2 = &desc2; + } + } + + p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1); + len = p2p_buf_add_ie_hdr(req); + p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2); + p2p_buf_update_ie_hdr(req, len); + + return req; +} + + +int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, + const u8 *own_interface_addr, unsigned int freq, + u32 duration1, u32 interval1, u32 duration2, + u32 interval2) +{ + struct wpabuf *req; + + p2p_dbg(p2p, "Send Presence Request to GO " MACSTR + " (own interface " MACSTR ") freq=%u dur1=%u int1=%u " + "dur2=%u int2=%u", + MAC2STR(go_interface_addr), MAC2STR(own_interface_addr), + freq, duration1, interval1, duration2, interval2); + + req = p2p_build_presence_req(duration1, interval1, duration2, + interval2); + if (req == NULL) + return -1; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr, + go_interface_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + } + wpabuf_free(req); + + return 0; +} + + +static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa, + size_t noa_len, u8 dialog_token) +{ + struct wpabuf *resp; + u8 *len; + + resp = wpabuf_alloc(100 + noa_len); + if (resp == NULL) + return NULL; + + p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token); + len = p2p_buf_add_ie_hdr(resp); + p2p_buf_add_status(resp, status); + if (noa) { + wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE); + wpabuf_put_le16(resp, noa_len); + wpabuf_put_data(resp, noa, noa_len); + } else + p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL); + p2p_buf_update_ie_hdr(resp, len); + + return resp; +} + + +static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, size_t len, + int rx_freq) +{ + struct p2p_message msg; + u8 status; + struct wpabuf *resp; + size_t g; + struct p2p_group *group = NULL; + int parsed = 0; + u8 noa[50]; + int noa_len; + + p2p_dbg(p2p, "Received P2P Action - P2P Presence Request"); + + for (g = 0; g < p2p->num_groups; g++) { + if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]), + ETH_ALEN) == 0) { + group = p2p->groups[g]; + break; + } + } + if (group == NULL) { + p2p_dbg(p2p, "Ignore P2P Presence Request for unknown group " + MACSTR, MAC2STR(da)); + return; + } + + if (p2p_parse(data, len, &msg) < 0) { + p2p_dbg(p2p, "Failed to parse P2P Presence Request"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + parsed = 1; + + if (msg.noa == NULL) { + p2p_dbg(p2p, "No NoA attribute in P2P Presence Request"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len); + +fail: + if (p2p->cfg->get_noa) + noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa, + sizeof(noa)); + else + noa_len = -1; + resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL, + noa_len > 0 ? noa_len : 0, + msg.dialog_token); + if (parsed) + p2p_parse_free(&msg); + if (resp == NULL) + return; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, rx_freq, sa, da, da, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + } + wpabuf_free(resp); +} + + +static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, + const u8 *sa, const u8 *data, size_t len) +{ + struct p2p_message msg; + + p2p_dbg(p2p, "Received P2P Action - P2P Presence Response"); + + if (p2p_parse(data, len, &msg) < 0) { + p2p_dbg(p2p, "Failed to parse P2P Presence Response"); + return; + } + + if (msg.status == NULL || msg.noa == NULL) { + p2p_dbg(p2p, "No Status or NoA attribute in P2P Presence Response"); + p2p_parse_free(&msg); + return; + } + + if (p2p->cfg->presence_resp) { + p2p->cfg->presence_resp(p2p->cfg->cb_ctx, sa, *msg.status, + msg.noa, msg.noa_len); + } + + if (*msg.status) { + p2p_dbg(p2p, "P2P Presence Request was rejected: status %u", + *msg.status); + p2p_parse_free(&msg); + return; + } + + p2p_dbg(p2p, "P2P Presence Request was accepted"); + wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA", + msg.noa, msg.noa_len); + /* TODO: process NoA */ + p2p_parse_free(&msg); +} + + +static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + + if (p2p->ext_listen_interval) { + /* Schedule next extended listen timeout */ + eloop_register_timeout(p2p->ext_listen_interval_sec, + p2p->ext_listen_interval_usec, + p2p_ext_listen_timeout, p2p, NULL); + } + + if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) { + /* + * This should not really happen, but it looks like the Listen + * command may fail is something else (e.g., a scan) was + * running at an inconvenient time. As a workaround, allow new + * Extended Listen operation to be started. + */ + p2p_dbg(p2p, "Previous Extended Listen operation had not been completed - try again"); + p2p->ext_listen_only = 0; + p2p_set_state(p2p, P2P_IDLE); + } + + if (p2p->state != P2P_IDLE) { + p2p_dbg(p2p, "Skip Extended Listen timeout in active state (%s)", p2p_state_txt(p2p->state)); + return; + } + + p2p_dbg(p2p, "Extended Listen timeout"); + p2p->ext_listen_only = 1; + if (p2p_listen(p2p, p2p->ext_listen_period) < 0) { + p2p_dbg(p2p, "Failed to start Listen state for Extended Listen Timing"); + p2p->ext_listen_only = 0; + } +} + + +int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, + unsigned int interval) +{ + if (period > 65535 || interval > 65535 || period > interval || + (period == 0 && interval > 0) || (period > 0 && interval == 0)) { + p2p_dbg(p2p, "Invalid Extended Listen Timing request: period=%u interval=%u", + period, interval); + return -1; + } + + eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); + + if (interval == 0) { + p2p_dbg(p2p, "Disabling Extended Listen Timing"); + p2p->ext_listen_period = 0; + p2p->ext_listen_interval = 0; + return 0; + } + + p2p_dbg(p2p, "Enabling Extended Listen Timing: period %u msec, interval %u msec", + period, interval); + p2p->ext_listen_period = period; + p2p->ext_listen_interval = interval; + p2p->ext_listen_interval_sec = interval / 1000; + p2p->ext_listen_interval_usec = (interval % 1000) * 1000; + + eloop_register_timeout(p2p->ext_listen_interval_sec, + p2p->ext_listen_interval_usec, + p2p_ext_listen_timeout, p2p, NULL); + + return 0; +} + + +void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len) +{ + struct p2p_message msg; + + if (bssid == NULL || ie == NULL) + return; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg)) + return; + if (msg.minor_reason_code == NULL) { + p2p_parse_free(&msg); + return; + } + + p2p_dbg(p2p, "Deauthentication notification BSSID " MACSTR + " reason_code=%u minor_reason_code=%u", + MAC2STR(bssid), reason_code, *msg.minor_reason_code); + + p2p_parse_free(&msg); +} + + +void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len) +{ + struct p2p_message msg; + + if (bssid == NULL || ie == NULL) + return; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_ies(ie, ie_len, &msg)) + return; + if (msg.minor_reason_code == NULL) { + p2p_parse_free(&msg); + return; + } + + p2p_dbg(p2p, "Disassociation notification BSSID " MACSTR + " reason_code=%u minor_reason_code=%u", + MAC2STR(bssid), reason_code, *msg.minor_reason_code); + + p2p_parse_free(&msg); +} + + +void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) +{ + if (enabled) { + p2p_dbg(p2p, "Managed P2P Device operations enabled"); + p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED; + } else { + p2p_dbg(p2p, "Managed P2P Device operations disabled"); + p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED; + } +} + + +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel) +{ + if (p2p_channel_to_freq(reg_class, channel) < 0) + return -1; + + p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u", + reg_class, channel); + p2p->cfg->reg_class = reg_class; + p2p->cfg->channel = channel; + + return 0; +} + + +int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) +{ + p2p_dbg(p2p, "New SSID postfix: %s", wpa_ssid_txt(postfix, len)); + if (postfix == NULL) { + p2p->cfg->ssid_postfix_len = 0; + return 0; + } + if (len > sizeof(p2p->cfg->ssid_postfix)) + return -1; + os_memcpy(p2p->cfg->ssid_postfix, postfix, len); + p2p->cfg->ssid_postfix_len = len; + return 0; +} + + +int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, + int cfg_op_channel) +{ + if (p2p_channel_to_freq(op_reg_class, op_channel) < 0) + return -1; + + p2p_dbg(p2p, "Set Operating channel: reg_class %u channel %u", + op_reg_class, op_channel); + p2p->cfg->op_reg_class = op_reg_class; + p2p->cfg->op_channel = op_channel; + p2p->cfg->cfg_op_channel = cfg_op_channel; + return 0; +} + + +int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, + const struct p2p_channel *pref_chan) +{ + struct p2p_channel *n; + + if (pref_chan) { + n = os_malloc(num_pref_chan * sizeof(struct p2p_channel)); + if (n == NULL) + return -1; + os_memcpy(n, pref_chan, + num_pref_chan * sizeof(struct p2p_channel)); + } else + n = NULL; + + os_free(p2p->cfg->pref_chan); + p2p->cfg->pref_chan = n; + p2p->cfg->num_pref_chan = num_pref_chan; + + return 0; +} + + +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list) +{ + struct wpa_freq_range *tmp; + + if (list == NULL || list->num == 0) { + os_free(p2p->no_go_freq.range); + p2p->no_go_freq.range = NULL; + p2p->no_go_freq.num = 0; + return 0; + } + + tmp = os_calloc(list->num, sizeof(struct wpa_freq_range)); + if (tmp == NULL) + return -1; + os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range)); + os_free(p2p->no_go_freq.range); + p2p->no_go_freq.range = tmp; + p2p->no_go_freq.num = list->num; + p2p_dbg(p2p, "Updated no GO chan list"); + + return 0; +} + + +int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, + u8 *iface_addr) +{ + struct p2p_device *dev = p2p_get_device(p2p, dev_addr); + if (dev == NULL || is_zero_ether_addr(dev->interface_addr)) + return -1; + os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN); + return 0; +} + + +int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, + u8 *dev_addr) +{ + struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); + if (dev == NULL) + return -1; + os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN); + return 0; +} + + +void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr) +{ + os_memcpy(p2p->peer_filter, addr, ETH_ALEN); + if (is_zero_ether_addr(p2p->peer_filter)) + p2p_dbg(p2p, "Disable peer filter"); + else + p2p_dbg(p2p, "Enable peer filter for " MACSTR, + MAC2STR(p2p->peer_filter)); +} + + +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled) +{ + p2p_dbg(p2p, "Cross connection %s", enabled ? "enabled" : "disabled"); + if (p2p->cross_connect == enabled) + return; + p2p->cross_connect = enabled; + /* TODO: may need to tear down any action group where we are GO(?) */ +} + + +int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr) +{ + struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr); + if (dev == NULL) + return -1; + if (dev->oper_freq <= 0) + return -1; + return dev->oper_freq; +} + + +void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled) +{ + p2p_dbg(p2p, "Intra BSS distribution %s", + enabled ? "enabled" : "disabled"); + p2p->cfg->p2p_intra_bss = enabled; +} + + +void p2p_update_channel_list(struct p2p_data *p2p, + const struct p2p_channels *chan, + const struct p2p_channels *cli_chan) +{ + p2p_dbg(p2p, "Update channel list"); + os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels)); + p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); + os_memcpy(&p2p->cfg->cli_channels, cli_chan, + sizeof(struct p2p_channels)); + p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); +} + + +int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time) +{ + if (p2p->p2p_scan_running) { + p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes"); + if (p2p->after_scan_tx) { + p2p_dbg(p2p, "Dropped previous pending Action frame TX"); + os_free(p2p->after_scan_tx); + } + p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) + + len); + if (p2p->after_scan_tx == NULL) + return -1; + p2p->after_scan_tx->freq = freq; + os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN); + os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN); + os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN); + p2p->after_scan_tx->len = len; + p2p->after_scan_tx->wait_time = wait_time; + os_memcpy(p2p->after_scan_tx + 1, buf, len); + return 0; + } + + return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, + buf, len, wait_time); +} + + +void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, + int freq_overall) +{ + p2p_dbg(p2p, "Best channel: 2.4 GHz: %d, 5 GHz: %d, overall: %d", + freq_24, freq_5, freq_overall); + p2p->best_freq_24 = freq_24; + p2p->best_freq_5 = freq_5; + p2p->best_freq_overall = freq_overall; +} + + +void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq) +{ + p2p_dbg(p2p, "Own frequency preference: %d MHz", freq); + p2p->own_freq_preference = freq; +} + + +const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p) +{ + if (p2p == NULL || p2p->go_neg_peer == NULL) + return NULL; + return p2p->go_neg_peer->info.p2p_device_addr; +} + + +const struct p2p_peer_info * +p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) +{ + struct p2p_device *dev; + + if (addr) { + dev = p2p_get_device(p2p, addr); + if (!dev) + return NULL; + + if (!next) { + if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) + return NULL; + + return &dev->info; + } else { + do { + dev = dl_list_first(&dev->list, + struct p2p_device, + list); + if (&dev->list == &p2p->devices) + return NULL; + } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY); + } + } else { + dev = dl_list_first(&p2p->devices, struct p2p_device, list); + if (!dev) + return NULL; + while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { + dev = dl_list_first(&dev->list, + struct p2p_device, + list); + if (&dev->list == &p2p->devices) + return NULL; + } + } + + return &dev->info; +} + + +int p2p_in_progress(struct p2p_data *p2p) +{ + if (p2p == NULL) + return 0; + if (p2p->state == P2P_SEARCH) + return 2; + return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING; +} + + +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, + u8 client_timeout) +{ + if (p2p) { + p2p->go_timeout = go_timeout; + p2p->client_timeout = client_timeout; + } +} + + +#ifdef CONFIG_WIFI_DISPLAY + +static void p2p_update_wfd_ie_groups(struct p2p_data *p2p) +{ + size_t g; + struct p2p_group *group; + + for (g = 0; g < p2p->num_groups; g++) { + group = p2p->groups[g]; + p2p_group_force_beacon_update_ies(group); + } +} + + +int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_beacon); + p2p->wfd_ie_beacon = ie; + p2p_update_wfd_ie_groups(p2p); + return 0; +} + + +int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_probe_req); + p2p->wfd_ie_probe_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_probe_resp); + p2p->wfd_ie_probe_resp = ie; + p2p_update_wfd_ie_groups(p2p); + return 0; +} + + +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_assoc_req); + p2p->wfd_ie_assoc_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_invitation); + p2p->wfd_ie_invitation = ie; + return 0; +} + + +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_prov_disc_req); + p2p->wfd_ie_prov_disc_req = ie; + return 0; +} + + +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_prov_disc_resp); + p2p->wfd_ie_prov_disc_resp = ie; + return 0; +} + + +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie) +{ + wpabuf_free(p2p->wfd_ie_go_neg); + p2p->wfd_ie_go_neg = ie; + return 0; +} + + +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_dev_info); + if (elem) { + p2p->wfd_dev_info = wpabuf_dup(elem); + if (p2p->wfd_dev_info == NULL) + return -1; + } else + p2p->wfd_dev_info = NULL; + + return 0; +} + + +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_assoc_bssid); + if (elem) { + p2p->wfd_assoc_bssid = wpabuf_dup(elem); + if (p2p->wfd_assoc_bssid == NULL) + return -1; + } else + p2p->wfd_assoc_bssid = NULL; + + return 0; +} + + +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, + const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_coupled_sink_info); + if (elem) { + p2p->wfd_coupled_sink_info = wpabuf_dup(elem); + if (p2p->wfd_coupled_sink_info == NULL) + return -1; + } else + p2p->wfd_coupled_sink_info = NULL; + + return 0; +} + +#endif /* CONFIG_WIFI_DISPLAY */ + + +int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, + int max_disc_tu) +{ + if (min_disc_int > max_disc_int || min_disc_int < 0 || max_disc_int < 0) + return -1; + + p2p->min_disc_int = min_disc_int; + p2p->max_disc_int = max_disc_int; + p2p->max_disc_tu = max_disc_tu; + p2p_dbg(p2p, "Set discoverable interval: min=%d max=%d max_tu=%d", + min_disc_int, max_disc_int, max_disc_tu); + + return 0; +} + + +void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_DEBUG, buf); +} + + +void p2p_info(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_INFO, buf); +} + + +void p2p_err(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf); +} + + +#ifdef CONFIG_WPS_NFC + +static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + struct wpabuf *buf; + u8 op_class, channel; + enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP; + + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + op_class = p2p->cfg->reg_class; + channel = p2p->cfg->channel; + + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + p2p_buf_add_device_info(buf, p2p, NULL); + + if (p2p->num_groups > 0) { + role = P2P_GO_IN_A_GROUP; + p2p_freq_to_channel(p2p_group_get_freq(p2p->groups[0]), + &op_class, &channel); + } else if (client_freq > 0) { + role = P2P_CLIENT_IN_A_GROUP; + p2p_freq_to_channel(client_freq, &op_class, &channel); + } + + p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class, + channel, role); + + if (p2p->num_groups > 0) { + /* Limit number of clients to avoid very long message */ + p2p_buf_add_group_info(p2p->groups[0], buf, 5); + p2p_group_buf_add_id(p2p->groups[0], buf); + } else if (client_freq > 0 && + go_dev_addr && !is_zero_ether_addr(go_dev_addr) && + ssid && ssid_len > 0) { + /* + * Add the optional P2P Group ID to indicate in which group this + * device is a P2P Client. + */ + p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len); + } + + return buf; +} + + +struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, + ssid_len); +} + + +struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, + ssid_len); +} + + +int p2p_process_nfc_connection_handover(struct p2p_data *p2p, + struct p2p_nfc_params *params) +{ + struct p2p_message msg; + struct p2p_device *dev; + const u8 *p2p_dev_addr; + int freq; + enum p2p_role_indication role; + + params->next_step = NO_ACTION; + + if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len, + params->p2p_attr, params->p2p_len, &msg)) { + p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.p2p_device_addr) + p2p_dev_addr = msg.p2p_device_addr; + else if (msg.device_id) + p2p_dev_addr = msg.device_id; + else { + p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.oob_dev_password) { + os_memcpy(params->oob_dev_pw, msg.oob_dev_password, + msg.oob_dev_password_len); + params->oob_dev_pw_len = msg.oob_dev_password_len; + } + + dev = p2p_create_device(p2p, p2p_dev_addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return -1; + } + + params->peer = &dev->info; + + os_get_reltime(&dev->last_seen); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + p2p_copy_wps_info(p2p, dev, 0, &msg); + + if (!msg.oob_go_neg_channel) { + p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included"); + return -1; + } + + if (msg.oob_go_neg_channel[3] == 0 && + msg.oob_go_neg_channel[4] == 0) + freq = 0; + else + freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3], + msg.oob_go_neg_channel[4]); + if (freq < 0) { + p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); + return -1; + } + role = msg.oob_go_neg_channel[5]; + + if (role == P2P_GO_IN_A_GROUP) { + p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq); + params->go_freq = freq; + } else if (role == P2P_CLIENT_IN_A_GROUP) { + p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz", + freq); + params->go_freq = freq; + } else + p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq); + dev->oob_go_neg_freq = freq; + + if (!params->sel && role != P2P_GO_IN_A_GROUP) { + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Own listen channel not known"); + return -1; + } + p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq); + dev->oob_go_neg_freq = freq; + } + + if (msg.group_id) { + os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN); + params->go_ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN, + params->go_ssid_len); + } + + p2p_parse_free(&msg); + + if (dev->flags & P2P_DEV_USER_REJECTED) { + p2p_dbg(p2p, "Do not report rejected device"); + return 0; + } + + if (!(dev->flags & P2P_DEV_REPORTED)) { + p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + } + + if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0) + params->next_step = BOTH_GO; + else if (role == P2P_GO_IN_A_GROUP) + params->next_step = JOIN_GROUP; + else if (role == P2P_CLIENT_IN_A_GROUP) { + dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; + params->next_step = PEER_CLIENT; + } else if (p2p->num_groups > 0) + params->next_step = AUTH_JOIN; + else if (params->sel) + params->next_step = INIT_GO_NEG; + else + params->next_step = RESP_GO_NEG; + + return 0; +} + + +void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, + int go_intent, + const u8 *own_interface_addr) +{ + + p2p->authorized_oob_dev_pw_id = dev_pw_id; + if (dev_pw_id == 0) { + p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover"); + return; + } + + p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover", + dev_pw_id); + + p2p->go_intent = go_intent; + os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); +} + +#endif /* CONFIG_WPS_NFC */ diff --git a/contrib/hostapd/src/p2p/p2p.h b/contrib/hostapd/src/p2p/p2p.h new file mode 100644 index 0000000000..08e7176c1d --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p.h @@ -0,0 +1,1944 @@ +/* + * Wi-Fi Direct - P2P module + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef P2P_H +#define P2P_H + +#include "wps/wps_defs.h" + +/** + * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes + */ +#define P2P_MAX_REG_CLASSES 10 + +/** + * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class + */ +#define P2P_MAX_REG_CLASS_CHANNELS 20 + +/** + * struct p2p_channels - List of supported channels + */ +struct p2p_channels { + /** + * struct p2p_reg_class - Supported regulatory class + */ + struct p2p_reg_class { + /** + * reg_class - Regulatory class (IEEE 802.11-2007, Annex J) + */ + u8 reg_class; + + /** + * channel - Supported channels + */ + u8 channel[P2P_MAX_REG_CLASS_CHANNELS]; + + /** + * channels - Number of channel entries in use + */ + size_t channels; + } reg_class[P2P_MAX_REG_CLASSES]; + + /** + * reg_classes - Number of reg_class entries in use + */ + size_t reg_classes; +}; + +enum p2p_wps_method { + WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC +}; + +/** + * struct p2p_go_neg_results - P2P Group Owner Negotiation results + */ +struct p2p_go_neg_results { + /** + * status - Negotiation result (Status Code) + * + * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate + * failed negotiation. + */ + int status; + + /** + * role_go - Whether local end is Group Owner + */ + int role_go; + + /** + * freq - Frequency of the group operational channel in MHz + */ + int freq; + + int ht40; + + int vht; + + /** + * ssid - SSID of the group + */ + u8 ssid[32]; + + /** + * ssid_len - Length of SSID in octets + */ + size_t ssid_len; + + /** + * psk - WPA pre-shared key (256 bits) (GO only) + */ + u8 psk[32]; + + /** + * psk_set - Whether PSK field is configured (GO only) + */ + int psk_set; + + /** + * passphrase - WPA2-Personal passphrase for the group (GO only) + */ + char passphrase[64]; + + /** + * peer_device_addr - P2P Device Address of the peer + */ + u8 peer_device_addr[ETH_ALEN]; + + /** + * peer_interface_addr - P2P Interface Address of the peer + */ + u8 peer_interface_addr[ETH_ALEN]; + + /** + * wps_method - WPS method to be used during provisioning + */ + enum p2p_wps_method wps_method; + +#define P2P_MAX_CHANNELS 50 + + /** + * freq_list - Zero-terminated list of possible operational channels + */ + int freq_list[P2P_MAX_CHANNELS]; + + /** + * persistent_group - Whether the group should be made persistent + * 0 = not persistent + * 1 = persistent group without persistent reconnect + * 2 = persistent group with persistent reconnect + */ + int persistent_group; + + /** + * peer_config_timeout - Peer configuration timeout (in 10 msec units) + */ + unsigned int peer_config_timeout; +}; + +struct p2p_data; + +enum p2p_scan_type { + P2P_SCAN_SOCIAL, + P2P_SCAN_FULL, + P2P_SCAN_SOCIAL_PLUS_ONE +}; + +#define P2P_MAX_WPS_VENDOR_EXT 10 + +/** + * struct p2p_peer_info - P2P peer information + */ +struct p2p_peer_info { + /** + * p2p_device_addr - P2P Device Address of the peer + */ + u8 p2p_device_addr[ETH_ALEN]; + + /** + * pri_dev_type - Primary Device Type + */ + u8 pri_dev_type[8]; + + /** + * device_name - Device Name (0..32 octets encoded in UTF-8) + */ + char device_name[33]; + + /** + * manufacturer - Manufacturer (0..64 octets encoded in UTF-8) + */ + char manufacturer[65]; + + /** + * model_name - Model Name (0..32 octets encoded in UTF-8) + */ + char model_name[33]; + + /** + * model_number - Model Number (0..32 octets encoded in UTF-8) + */ + char model_number[33]; + + /** + * serial_number - Serial Number (0..32 octets encoded in UTF-8) + */ + char serial_number[33]; + + /** + * level - Signal level + */ + int level; + + /** + * config_methods - WPS Configuration Methods + */ + u16 config_methods; + + /** + * dev_capab - Device Capabilities + */ + u8 dev_capab; + + /** + * group_capab - Group Capabilities + */ + u8 group_capab; + + /** + * wps_sec_dev_type_list - WPS secondary device type list + * + * This list includes from 0 to 16 Secondary Device Types as indicated + * by wps_sec_dev_type_list_len (8 * number of types). + */ + u8 wps_sec_dev_type_list[128]; + + /** + * wps_sec_dev_type_list_len - Length of secondary device type list + */ + size_t wps_sec_dev_type_list_len; + + struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + + /** + * wfd_subelems - Wi-Fi Display subelements from WFD IE(s) + */ + struct wpabuf *wfd_subelems; +}; + +enum p2p_prov_disc_status { + P2P_PROV_DISC_SUCCESS, + P2P_PROV_DISC_TIMEOUT, + P2P_PROV_DISC_REJECTED, + P2P_PROV_DISC_TIMEOUT_JOIN, +}; + +struct p2p_channel { + u8 op_class; + u8 chan; +}; + +/** + * struct p2p_config - P2P configuration + * + * This configuration is provided to the P2P module during initialization with + * p2p_init(). + */ +struct p2p_config { + /** + * country - Country code to use in P2P operations + */ + char country[3]; + + /** + * reg_class - Regulatory class for own listen channel + */ + u8 reg_class; + + /** + * channel - Own listen channel + */ + u8 channel; + + /** + * Regulatory class for own operational channel + */ + u8 op_reg_class; + + /** + * op_channel - Own operational channel + */ + u8 op_channel; + + /** + * cfg_op_channel - Whether op_channel is hardcoded in configuration + */ + u8 cfg_op_channel; + + /** + * channels - Own supported regulatory classes and channels + * + * List of supposerted channels per regulatory class. The regulatory + * classes are defined in IEEE Std 802.11-2007 Annex J and the + * numbering of the clases depends on the configured country code. + */ + struct p2p_channels channels; + + /** + * cli_channels - Additional client channels + * + * This list of channels (if any) will be used when advertising local + * channels during GO Negotiation or Invitation for the cases where the + * local end may become the client. This may allow the peer to become a + * GO on additional channels if it supports these options. The main use + * case for this is to include passive-scan channels on devices that may + * not know their current location and have configured most channels to + * not allow initiation of radition (i.e., another device needs to take + * master responsibilities). + */ + struct p2p_channels cli_channels; + + /** + * num_pref_chan - Number of pref_chan entries + */ + unsigned int num_pref_chan; + + /** + * pref_chan - Preferred channels for GO Negotiation + */ + struct p2p_channel *pref_chan; + + /** + * pri_dev_type - Primary Device Type (see WPS) + */ + u8 pri_dev_type[8]; + + /** + * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types + */ +#define P2P_SEC_DEVICE_TYPES 5 + + /** + * sec_dev_type - Optional secondary device types + */ + u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8]; + + /** + * num_sec_dev_types - Number of sec_dev_type entries + */ + size_t num_sec_dev_types; + + /** + * dev_addr - P2P Device Address + */ + u8 dev_addr[ETH_ALEN]; + + /** + * dev_name - Device Name + */ + char *dev_name; + + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + + u8 uuid[16]; + u16 config_methods; + + /** + * concurrent_operations - Whether concurrent operations are supported + */ + int concurrent_operations; + + /** + * max_peers - Maximum number of discovered peers to remember + * + * If more peers are discovered, older entries will be removed to make + * room for the new ones. + */ + size_t max_peers; + + /** + * p2p_intra_bss - Intra BSS communication is supported + */ + int p2p_intra_bss; + + /** + * ssid_postfix - Postfix data to add to the SSID + * + * This data will be added to the end of the SSID after the + * DIRECT- prefix. + */ + u8 ssid_postfix[32 - 9]; + + /** + * ssid_postfix_len - Length of the ssid_postfix data + */ + size_t ssid_postfix_len; + + /** + * max_listen - Maximum listen duration in ms + */ + unsigned int max_listen; + + /** + * cb_ctx - Context to use with callback functions + */ + void *cb_ctx; + + /** + * debug_print - Debug print + * @ctx: Callback context from cb_ctx + * @level: Debug verbosity level (MSG_*) + * @msg: Debug message + */ + void (*debug_print)(void *ctx, int level, const char *msg); + + + /* Callbacks to request lower layer driver operations */ + + /** + * p2p_scan - Request a P2P scan/search + * @ctx: Callback context from cb_ctx + * @type: Scan type + * @freq: Specific frequency (MHz) to scan or 0 for no restriction + * @num_req_dev_types: Number of requested device types + * @req_dev_types: Array containing requested device types + * @dev_id: Device ID to search for or %NULL to find all devices + * @pw_id: Device Password ID + * Returns: 0 on success, -1 on failure + * + * This callback function is used to request a P2P scan or search + * operation to be completed. Type type argument specifies which type + * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the + * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL + * indicates that all channels are to be scanned. + * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels + * plus one extra channel specified by freq. + * + * The full scan is used for the initial scan to find group owners from + * all. The other types are used during search phase scan of the social + * channels (with potential variation if the Listen channel of the + * target peer is known or if other channels are scanned in steps). + * + * The scan results are returned after this call by calling + * p2p_scan_res_handler() for each scan result that has a P2P IE and + * then calling p2p_scan_res_handled() to indicate that all scan + * results have been indicated. + */ + int (*p2p_scan)(void *ctx, enum p2p_scan_type type, int freq, + unsigned int num_req_dev_types, + const u8 *req_dev_types, const u8 *dev_id, u16 pw_id); + + /** + * send_probe_resp - Transmit a Probe Response frame + * @ctx: Callback context from cb_ctx + * @buf: Probe Response frame (including the header and body) + * Returns: 0 on success, -1 on failure + * + * This function is used to reply to Probe Request frames that were + * indicated with a call to p2p_probe_req_rx(). The response is to be + * sent on the same channel or to be dropped if the driver is not + * anymore listening to Probe Request frames. + * + * Alternatively, the responsibility for building the Probe Response + * frames in Listen state may be in another system component in which + * case this function need to be implemented (i.e., the function + * pointer can be %NULL). The WPS and P2P IEs to be added for Probe + * Response frames in such a case are available from the + * start_listen() callback. It should be noted that the received Probe + * Request frames must be indicated by calling p2p_probe_req_rx() even + * if this send_probe_resp() is not used. + */ + int (*send_probe_resp)(void *ctx, const struct wpabuf *buf); + + /** + * send_action - Transmit an Action frame + * @ctx: Callback context from cb_ctx + * @freq: Frequency in MHz for the channel on which to transmit + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @buf: Frame body (starting from Category field) + * @len: Length of buf in octets + * @wait_time: How many msec to wait for a response frame + * Returns: 0 on success, -1 on failure + * + * The Action frame may not be transmitted immediately and the status + * of the transmission must be reported by calling + * p2p_send_action_cb() once the frame has either been transmitted or + * it has been dropped due to excessive retries or other failure to + * transmit. + */ + int (*send_action)(void *ctx, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time); + + /** + * send_action_done - Notify that Action frame sequence was completed + * @ctx: Callback context from cb_ctx + * + * This function is called when the Action frame sequence that was + * started with send_action() has been completed, i.e., when there is + * no need to wait for a response from the destination peer anymore. + */ + void (*send_action_done)(void *ctx); + + /** + * start_listen - Start Listen state + * @ctx: Callback context from cb_ctx + * @freq: Frequency of the listen channel in MHz + * @duration: Duration for the Listen state in milliseconds + * @probe_resp_ie: IE(s) to be added to Probe Response frames + * Returns: 0 on success, -1 on failure + * + * This Listen state may not start immediately since the driver may + * have other pending operations to complete first. Once the Listen + * state has started, p2p_listen_cb() must be called to notify the P2P + * module. Once the Listen state is stopped, p2p_listen_end() must be + * called to notify the P2P module that the driver is not in the Listen + * state anymore. + * + * If the send_probe_resp() is not used for generating the response, + * the IEs from probe_resp_ie need to be added to the end of the Probe + * Response frame body. If send_probe_resp() is used, the probe_resp_ie + * information can be ignored. + */ + int (*start_listen)(void *ctx, unsigned int freq, + unsigned int duration, + const struct wpabuf *probe_resp_ie); + /** + * stop_listen - Stop Listen state + * @ctx: Callback context from cb_ctx + * + * This callback can be used to stop a Listen state operation that was + * previously requested with start_listen(). + */ + void (*stop_listen)(void *ctx); + + /** + * get_noa - Get current Notice of Absence attribute payload + * @ctx: Callback context from cb_ctx + * @interface_addr: P2P Interface Address of the GO + * @buf: Buffer for returning NoA + * @buf_len: Buffer length in octets + * Returns: Number of octets used in buf, 0 to indicate no NoA is being + * advertized, or -1 on failure + * + * This function is used to fetch the current Notice of Absence + * attribute value from GO. + */ + int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf, + size_t buf_len); + + /* Callbacks to notify events to upper layer management entity */ + + /** + * dev_found - Notification of a found P2P Device + * @ctx: Callback context from cb_ctx + * @addr: Source address of the message triggering this notification + * @info: P2P peer information + * @new_device: Inform if the peer is newly found + * + * This callback is used to notify that a new P2P Device has been + * found. This may happen, e.g., during Search state based on scan + * results or during Listen state based on receive Probe Request and + * Group Owner Negotiation Request. + */ + void (*dev_found)(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, + int new_device); + + /** + * dev_lost - Notification of a lost P2P Device + * @ctx: Callback context from cb_ctx + * @dev_addr: P2P Device Address of the lost P2P Device + * + * This callback is used to notify that a P2P Device has been deleted. + */ + void (*dev_lost)(void *ctx, const u8 *dev_addr); + + /** + * find_stopped - Notification of a p2p_find operation stopping + * @ctx: Callback context from cb_ctx + */ + void (*find_stopped)(void *ctx); + + /** + * go_neg_req_rx - Notification of a receive GO Negotiation Request + * @ctx: Callback context from cb_ctx + * @src: Source address of the message triggering this notification + * @dev_passwd_id: WPS Device Password ID + * + * This callback is used to notify that a P2P Device is requesting + * group owner negotiation with us, but we do not have all the + * necessary information to start GO Negotiation. This indicates that + * the local user has not authorized the connection yet by providing a + * PIN or PBC button press. This information can be provided with a + * call to p2p_connect(). + */ + void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id); + + /** + * go_neg_completed - Notification of GO Negotiation results + * @ctx: Callback context from cb_ctx + * @res: GO Negotiation results + * + * This callback is used to notify that Group Owner Negotiation has + * been completed. Non-zero struct p2p_go_neg_results::status indicates + * failed negotiation. In case of success, this function is responsible + * for creating a new group interface (or using the existing interface + * depending on driver features), setting up the group interface in + * proper mode based on struct p2p_go_neg_results::role_go and + * initializing WPS provisioning either as a Registrar (if GO) or as an + * Enrollee. Successful WPS provisioning must be indicated by calling + * p2p_wps_success_cb(). The callee is responsible for timing out group + * formation if WPS provisioning cannot be completed successfully + * within 15 seconds. + */ + void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res); + + /** + * sd_request - Callback on Service Discovery Request + * @ctx: Callback context from cb_ctx + * @freq: Frequency (in MHz) of the channel + * @sa: Source address of the request + * @dialog_token: Dialog token + * @update_indic: Service Update Indicator from the source of request + * @tlvs: P2P Service Request TLV(s) + * @tlvs_len: Length of tlvs buffer in octets + * + * This callback is used to indicate reception of a service discovery + * request. Response to the query must be indicated by calling + * p2p_sd_response() with the context information from the arguments to + * this callback function. + * + * This callback handler can be set to %NULL to indicate that service + * discovery is not supported. + */ + void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len); + + /** + * sd_response - Callback on Service Discovery Response + * @ctx: Callback context from cb_ctx + * @sa: Source address of the request + * @update_indic: Service Update Indicator from the source of response + * @tlvs: P2P Service Response TLV(s) + * @tlvs_len: Length of tlvs buffer in octets + * + * This callback is used to indicate reception of a service discovery + * response. This callback handler can be set to %NULL if no service + * discovery requests are used. The information provided with this call + * is replies to the queries scheduled with p2p_sd_request(). + */ + void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len); + + /** + * prov_disc_req - Callback on Provisiong Discovery Request + * @ctx: Callback context from cb_ctx + * @peer: Source address of the request + * @config_methods: Requested WPS Config Method + * @dev_addr: P2P Device Address of the found P2P Device + * @pri_dev_type: Primary Device Type + * @dev_name: Device Name + * @supp_config_methods: Supported configuration Methods + * @dev_capab: Device Capabilities + * @group_capab: Group Capabilities + * @group_id: P2P Group ID (or %NULL if not included) + * @group_id_len: Length of P2P Group ID + * + * This callback is used to indicate reception of a Provision Discovery + * Request frame that the P2P module accepted. + */ + void (*prov_disc_req)(void *ctx, const u8 *peer, u16 config_methods, + const u8 *dev_addr, const u8 *pri_dev_type, + const char *dev_name, u16 supp_config_methods, + u8 dev_capab, u8 group_capab, + const u8 *group_id, size_t group_id_len); + + /** + * prov_disc_resp - Callback on Provisiong Discovery Response + * @ctx: Callback context from cb_ctx + * @peer: Source address of the response + * @config_methods: Value from p2p_prov_disc_req() or 0 on failure + * + * This callback is used to indicate reception of a Provision Discovery + * Response frame for a pending request scheduled with + * p2p_prov_disc_req(). This callback handler can be set to %NULL if + * provision discovery is not used. + */ + void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods); + + /** + * prov_disc_fail - Callback on Provision Discovery failure + * @ctx: Callback context from cb_ctx + * @peer: Source address of the response + * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS + * + * This callback is used to indicate either a failure or no response + * to an earlier provision discovery request. + * + * This callback handler can be set to %NULL if provision discovery + * is not used or failures do not need to be indicated. + */ + void (*prov_disc_fail)(void *ctx, const u8 *peer, + enum p2p_prov_disc_status status); + + /** + * invitation_process - Optional callback for processing Invitations + * @ctx: Callback context from cb_ctx + * @sa: Source address of the Invitation Request + * @bssid: P2P Group BSSID from the request or %NULL if not included + * @go_dev_addr: GO Device Address from P2P Group ID + * @ssid: SSID from P2P Group ID + * @ssid_len: Length of ssid buffer in octets + * @go: Variable for returning whether the local end is GO in the group + * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO) + * @force_freq: Variable for returning forced frequency for the group + * @persistent_group: Whether this is an invitation to reinvoke a + * persistent group (instead of invitation to join an active + * group) + * @channels: Available operating channels for the group + * @dev_pw_id: Device Password ID for NFC static handover or -1 if not + * used + * Returns: Status code (P2P_SC_*) + * + * This optional callback can be used to implement persistent reconnect + * by allowing automatic restarting of persistent groups without user + * interaction. If this callback is not implemented (i.e., is %NULL), + * the received Invitation Request frames are replied with + * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the + * invitation_result() callback. + * + * If the requested parameters are acceptable and the group is known, + * %P2P_SC_SUCCESS may be returned. If the requested group is unknown, + * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED + * can be returned if there is not enough data to provide immediate + * response, i.e., if some sort of user interaction is needed. The + * invitation_received() callback will be called in that case + * immediately after this call. + */ + u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *go_dev_addr, const u8 *ssid, + size_t ssid_len, int *go, u8 *group_bssid, + int *force_freq, int persistent_group, + const struct p2p_channels *channels, + int dev_pw_id); + + /** + * invitation_received - Callback on Invitation Request RX + * @ctx: Callback context from cb_ctx + * @sa: Source address of the Invitation Request + * @bssid: P2P Group BSSID or %NULL if not received + * @ssid: SSID of the group + * @ssid_len: Length of ssid in octets + * @go_dev_addr: GO Device Address + * @status: Response Status + * @op_freq: Operational frequency for the group + * + * This callback is used to indicate sending of an Invitation Response + * for a received Invitation Request. If status == 0 (success), the + * upper layer code is responsible for starting the group. status == 1 + * indicates need to get user authorization for the group. Other status + * values indicate that the invitation request was rejected. + */ + void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid, + const u8 *ssid, size_t ssid_len, + const u8 *go_dev_addr, u8 status, + int op_freq); + + /** + * invitation_result - Callback on Invitation result + * @ctx: Callback context from cb_ctx + * @status: Negotiation result (Status Code) + * @bssid: P2P Group BSSID or %NULL if not received + * @channels: Available operating channels for the group + * @addr: Peer address + * @freq: Frequency (in MHz) indicated during invitation or 0 + * + * This callback is used to indicate result of an Invitation procedure + * started with a call to p2p_invite(). The indicated status code is + * the value received from the peer in Invitation Response with 0 + * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a + * local failure in transmitting the Invitation Request. + */ + void (*invitation_result)(void *ctx, int status, const u8 *bssid, + const struct p2p_channels *channels, + const u8 *addr, int freq); + + /** + * go_connected - Check whether we are connected to a GO + * @ctx: Callback context from cb_ctx + * @dev_addr: P2P Device Address of a GO + * Returns: 1 if we are connected as a P2P client to the specified GO + * or 0 if not. + */ + int (*go_connected)(void *ctx, const u8 *dev_addr); + + /** + * presence_resp - Callback on Presence Response + * @ctx: Callback context from cb_ctx + * @src: Source address (GO's P2P Interface Address) + * @status: Result of the request (P2P_SC_*) + * @noa: Returned NoA value + * @noa_len: Length of the NoA buffer in octets + */ + void (*presence_resp)(void *ctx, const u8 *src, u8 status, + const u8 *noa, size_t noa_len); + + /** + * is_concurrent_session_active - Check whether concurrent session is + * active on other virtual interfaces + * @ctx: Callback context from cb_ctx + * Returns: 1 if concurrent session is active on other virtual interface + * or 0 if not. + */ + int (*is_concurrent_session_active)(void *ctx); +}; + + +/* P2P module initialization/deinitialization */ + +/** + * p2p_init - Initialize P2P module + * @cfg: P2P module configuration + * Returns: Pointer to private data or %NULL on failure + * + * This function is used to initialize global P2P module context (one per + * device). The P2P module will keep a copy of the configuration data, so the + * caller does not need to maintain this structure. However, the callback + * functions and the context parameters to them must be kept available until + * the P2P module is deinitialized with p2p_deinit(). + */ +struct p2p_data * p2p_init(const struct p2p_config *cfg); + +/** + * p2p_deinit - Deinitialize P2P module + * @p2p: P2P module context from p2p_init() + */ +void p2p_deinit(struct p2p_data *p2p); + +/** + * p2p_flush - Flush P2P module state + * @p2p: P2P module context from p2p_init() + * + * This command removes the P2P module state like peer device entries. + */ +void p2p_flush(struct p2p_data *p2p); + +/** + * p2p_unauthorize - Unauthorize the specified peer device + * @p2p: P2P module context from p2p_init() + * @addr: P2P peer entry to be unauthorized + * Returns: 0 on success, -1 on failure + * + * This command removes any connection authorization from the specified P2P + * peer device address. This can be used, e.g., to cancel effect of a previous + * p2p_authorize() or p2p_connect() call that has not yet resulted in completed + * GO Negotiation. + */ +int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_dev_name - Set device name + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name); + +int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer); +int p2p_set_model_name(struct p2p_data *p2p, const char *model_name); +int p2p_set_model_number(struct p2p_data *p2p, const char *model_number); +int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number); + +void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods); +void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid); + +/** + * p2p_set_pri_dev_type - Set primary device type + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type); + +/** + * p2p_set_sec_dev_types - Set secondary device types + * @p2p: P2P module context from p2p_init() + * Returns: 0 on success, -1 on failure + * + * This function can be used to update the P2P module configuration with + * information that was not available at the time of the p2p_init() call. + */ +int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8], + size_t num_dev_types); + +int p2p_set_country(struct p2p_data *p2p, const char *country); + + +/* Commands from upper layer management entity */ + +enum p2p_discovery_type { + P2P_FIND_START_WITH_FULL, + P2P_FIND_ONLY_SOCIAL, + P2P_FIND_PROGRESSIVE +}; + +/** + * p2p_find - Start P2P Find (Device Discovery) + * @p2p: P2P module context from p2p_init() + * @timeout: Timeout for find operation in seconds or 0 for no timeout + * @type: Device Discovery type + * @num_req_dev_types: Number of requested device types + * @req_dev_types: Requested device types array, must be an array + * containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no + * requested device types. + * @dev_id: Device ID to search for or %NULL to find all devices + * @search_delay: Extra delay in milliseconds between search iterations + * Returns: 0 on success, -1 on failure + */ +int p2p_find(struct p2p_data *p2p, unsigned int timeout, + enum p2p_discovery_type type, + unsigned int num_req_dev_types, const u8 *req_dev_types, + const u8 *dev_id, unsigned int search_delay); + +/** + * p2p_stop_find - Stop P2P Find (Device Discovery) + * @p2p: P2P module context from p2p_init() + */ +void p2p_stop_find(struct p2p_data *p2p); + +/** + * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq + * @p2p: P2P module context from p2p_init() + * @freq: Frequency in MHz for next operation + * + * This is like p2p_stop_find(), but Listen state is not stopped if we are + * already on the same frequency. + */ +void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq); + +/** + * p2p_listen - Start P2P Listen state for specified duration + * @p2p: P2P module context from p2p_init() + * @timeout: Listen state duration in milliseconds + * Returns: 0 on success, -1 on failure + * + * This function can be used to request the P2P module to keep the device + * discoverable on the listen channel for an extended set of time. At least in + * its current form, this is mainly used for testing purposes and may not be of + * much use for normal P2P operations. + */ +int p2p_listen(struct p2p_data *p2p, unsigned int timeout); + +/** + * p2p_stop_listen - Stop P2P Listen + * @p2p: P2P module context from p2p_init() + */ +void p2p_stop_listen(struct p2p_data *p2p); + +/** + * p2p_connect - Start P2P group formation (GO negotiation) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @wps_method: WPS method to be used in provisioning + * @go_intent: Local GO intent value (1..15) + * @own_interface_addr: Intended interface address to use with the group + * @force_freq: The only allowed channel frequency in MHz or 0 + * @persistent_group: Whether to create a persistent group (0 = no, 1 = + * persistent group without persistent reconnect, 2 = persistent group with + * persistent reconnect) + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate + * a new SSID + * @force_ssid_len: Length of $force_ssid buffer + * @pd_before_go_neg: Whether to send Provision Discovery prior to GO + * Negotiation as an interoperability workaround when initiating group + * formation + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) + * Returns: 0 on success, -1 on failure + */ +int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id); + +/** + * p2p_authorize - Authorize P2P group formation (GO negotiation) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @wps_method: WPS method to be used in provisioning + * @go_intent: Local GO intent value (1..15) + * @own_interface_addr: Intended interface address to use with the group + * @force_freq: The only allowed channel frequency in MHz or 0 + * @persistent_group: Whether to create a persistent group (0 = no, 1 = + * persistent group without persistent reconnect, 2 = persistent group with + * persistent reconnect) + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate + * a new SSID + * @force_ssid_len: Length of $force_ssid buffer + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) + * Returns: 0 on success, -1 on failure + * + * This is like p2p_connect(), but the actual group negotiation is not + * initiated automatically, i.e., the other end is expected to do that. + */ +int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, + enum p2p_wps_method wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group, + const u8 *force_ssid, size_t force_ssid_len, + unsigned int pref_freq, u16 oob_pw_id); + +/** + * p2p_reject - Reject peer device (explicitly block connection attempts) + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * Returns: 0 on success, -1 on failure + */ +int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr); + +/** + * p2p_prov_disc_req - Send Provision Discovery Request + * @p2p: P2P module context from p2p_init() + * @peer_addr: MAC address of the peer P2P client + * @config_methods: WPS Config Methods value (only one bit set) + * @join: Whether this is used by a client joining an active group + * @force_freq: Forced TX frequency for the frame (mainly for the join case) + * @user_initiated_pd: Flag to indicate if initiated by user or not + * Returns: 0 on success, -1 on failure + * + * This function can be used to request a discovered P2P peer to display a PIN + * (config_methods = WPS_CONFIG_DISPLAY) or be prepared to enter a PIN from us + * (config_methods = WPS_CONFIG_KEYPAD). The Provision Discovery Request frame + * is transmitted once immediately and if no response is received, the frame + * will be sent again whenever the target device is discovered during device + * dsicovery (start with a p2p_find() call). Response from the peer is + * indicated with the p2p_config::prov_disc_resp() callback. + */ +int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, + u16 config_methods, int join, int force_freq, + int user_initiated_pd); + +/** + * p2p_sd_request - Schedule a service discovery query + * @p2p: P2P module context from p2p_init() + * @dst: Destination peer or %NULL to apply for all peers + * @tlvs: P2P Service Query TLV(s) + * Returns: Reference to the query or %NULL on failure + * + * Response to the query is indicated with the p2p_config::sd_response() + * callback. + */ +void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs); + +#ifdef CONFIG_WIFI_DISPLAY +void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs); +#endif /* CONFIG_WIFI_DISPLAY */ + +/** + * p2p_sd_cancel_request - Cancel a pending service discovery query + * @p2p: P2P module context from p2p_init() + * @req: Query reference from p2p_sd_request() + * Returns: 0 if request for cancelled; -1 if not found + */ +int p2p_sd_cancel_request(struct p2p_data *p2p, void *req); + +/** + * p2p_sd_response - Send response to a service discovery query + * @p2p: P2P module context from p2p_init() + * @freq: Frequency from p2p_config::sd_request() callback + * @dst: Destination address from p2p_config::sd_request() callback + * @dialog_token: Dialog token from p2p_config::sd_request() callback + * @resp_tlvs: P2P Service Response TLV(s) + * + * This function is called as a response to the request indicated with + * p2p_config::sd_request() callback. + */ +void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, + u8 dialog_token, const struct wpabuf *resp_tlvs); + +/** + * p2p_sd_service_update - Indicate a change in local services + * @p2p: P2P module context from p2p_init() + * + * This function needs to be called whenever there is a change in availability + * of the local services. This will increment the Service Update Indicator + * value which will be used in SD Request and Response frames. + */ +void p2p_sd_service_update(struct p2p_data *p2p); + + +enum p2p_invite_role { + P2P_INVITE_ROLE_GO, + P2P_INVITE_ROLE_ACTIVE_GO, + P2P_INVITE_ROLE_CLIENT +}; + +/** + * p2p_invite - Invite a P2P Device into a group + * @p2p: P2P module context from p2p_init() + * @peer: Device Address of the peer P2P Device + * @role: Local role in the group + * @bssid: Group BSSID or %NULL if not known + * @ssid: Group SSID + * @ssid_len: Length of ssid in octets + * @force_freq: The only allowed channel frequency in MHz or 0 + * @go_dev_addr: Forced GO Device Address or %NULL if none + * @persistent_group: Whether this is to reinvoke a persistent group + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) + * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover + * case or -1 if not used + * Returns: 0 on success, -1 on failure + */ +int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + unsigned int force_freq, const u8 *go_dev_addr, + int persistent_group, unsigned int pref_freq, int dev_pw_id); + +/** + * p2p_presence_req - Request GO presence + * @p2p: P2P module context from p2p_init() + * @go_interface_addr: GO P2P Interface Address + * @own_interface_addr: Own P2P Interface Address for this group + * @freq: Group operating frequence (in MHz) + * @duration1: Preferred presence duration in microseconds + * @interval1: Preferred presence interval in microseconds + * @duration2: Acceptable presence duration in microseconds + * @interval2: Acceptable presence interval in microseconds + * Returns: 0 on success, -1 on failure + * + * If both duration and interval values are zero, the parameter pair is not + * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0). + */ +int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, + const u8 *own_interface_addr, unsigned int freq, + u32 duration1, u32 interval1, u32 duration2, + u32 interval2); + +/** + * p2p_ext_listen - Set Extended Listen Timing + * @p2p: P2P module context from p2p_init() + * @freq: Group operating frequence (in MHz) + * @period: Availability period in milliseconds (1-65535; 0 to disable) + * @interval: Availability interval in milliseconds (1-65535; 0 to disable) + * Returns: 0 on success, -1 on failure + * + * This function can be used to enable or disable (period = interval = 0) + * Extended Listen Timing. When enabled, the P2P Device will become + * discoverable (go into Listen State) every @interval milliseconds for at + * least @period milliseconds. + */ +int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, + unsigned int interval); + +/* Event notifications from upper layer management operations */ + +/** + * p2p_wps_success_cb - Report successfully completed WPS provisioning + * @p2p: P2P module context from p2p_init() + * @mac_addr: Peer address + * + * This function is used to report successfully completed WPS provisioning + * during group formation in both GO/Registrar and client/Enrollee roles. + */ +void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr); + +/** + * p2p_group_formation_failed - Report failed WPS provisioning + * @p2p: P2P module context from p2p_init() + * + * This function is used to report failed group formation. This can happen + * either due to failed WPS provisioning or due to 15 second timeout during + * the provisioning phase. + */ +void p2p_group_formation_failed(struct p2p_data *p2p); + +/** + * p2p_get_provisioning_info - Get any stored provisioning info + * @p2p: P2P module context from p2p_init() + * @addr: Peer P2P Device Address + * Returns: WPS provisioning information (WPS config method) or 0 if no + * information is available + * + * This function is used to retrieve stored WPS provisioning info for the given + * peer. + */ +u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_clear_provisioning_info - Clear any stored provisioning info + * @p2p: P2P module context from p2p_init() + * @iface_addr: Peer P2P Device Address + * + * This function is used to clear stored WPS provisioning info for the given + * peer. + */ +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr); + + +/* Event notifications from lower layer driver operations */ + +/** + * enum p2p_probe_req_status + * + * @P2P_PREQ_MALFORMED: frame was not well-formed + * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored + * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request + * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed + * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P + */ +enum p2p_probe_req_status { + P2P_PREQ_MALFORMED, + P2P_PREQ_NOT_LISTEN, + P2P_PREQ_NOT_P2P, + P2P_PREQ_NOT_PROCESSED, + P2P_PREQ_PROCESSED +}; + +/** + * p2p_probe_req_rx - Report reception of a Probe Request frame + * @p2p: P2P module context from p2p_init() + * @addr: Source MAC address + * @dst: Destination MAC address if available or %NULL + * @bssid: BSSID if available or %NULL + * @ie: Information elements from the Probe Request frame body + * @ie_len: Length of ie buffer in octets + * Returns: value indicating the type and status of the probe request + */ +enum p2p_probe_req_status +p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, + const u8 *bssid, const u8 *ie, size_t ie_len); + +/** + * p2p_rx_action - Report received Action frame + * @p2p: P2P module context from p2p_init() + * @da: Destination address of the received Action frame + * @sa: Source address of the received Action frame + * @bssid: Address 3 of the received Action frame + * @category: Category of the received Action frame + * @data: Action frame body after the Category field + * @len: Length of the data buffer in octets + * @freq: Frequency (in MHz) on which the frame was received + */ +void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *bssid, u8 category, + const u8 *data, size_t len, int freq); + +/** + * p2p_scan_res_handler - Indicate a P2P scan results + * @p2p: P2P module context from p2p_init() + * @bssid: BSSID of the scan result + * @freq: Frequency of the channel on which the device was found in MHz + * @rx_time: Time when the result was received + * @level: Signal level (signal strength of the received Beacon/Probe Response + * frame) + * @ies: Pointer to IEs from the scan result + * @ies_len: Length of the ies buffer + * Returns: 0 to continue or 1 to stop scan result indication + * + * This function is called to indicate a scan result entry with P2P IE from a + * scan requested with struct p2p_config::p2p_scan(). This can be called during + * the actual scan process (i.e., whenever a new device is found) or as a + * sequence of calls after the full scan has been completed. The former option + * can result in optimized operations, but may not be supported by all + * driver/firmware designs. The ies buffer need to include at least the P2P IE, + * but it is recommended to include all IEs received from the device. The + * caller does not need to check that the IEs contain a P2P IE before calling + * this function since frames will be filtered internally if needed. + * + * This function will return 1 if it wants to stop scan result iteration (and + * scan in general if it is still in progress). This is used to allow faster + * start of a pending operation, e.g., to start a pending GO negotiation. + */ +int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, + struct os_reltime *rx_time, int level, const u8 *ies, + size_t ies_len); + +/** + * p2p_scan_res_handled - Indicate end of scan results + * @p2p: P2P module context from p2p_init() + * + * This function is called to indicate that all P2P scan results from a scan + * have been reported with zero or more calls to p2p_scan_res_handler(). This + * function must be called as a response to successful + * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler() + * calls stopped iteration. + */ +void p2p_scan_res_handled(struct p2p_data *p2p); + +enum p2p_send_action_result { + P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */, + P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */, + P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */ +}; + +/** + * p2p_send_action_cb - Notify TX status of an Action frame + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @result: Result of the transmission attempt + * + * This function is used to indicate the result of an Action frame transmission + * that was requested with struct p2p_config::send_action() callback. + */ +void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + enum p2p_send_action_result result); + +/** + * p2p_listen_cb - Indicate the start of a requested Listen state + * @p2p: P2P module context from p2p_init() + * @freq: Listen channel frequency in MHz + * @duration: Duration for the Listen state in milliseconds + * + * This function is used to indicate that a Listen state requested with + * struct p2p_config::start_listen() callback has started. + */ +void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, + unsigned int duration); + +/** + * p2p_listen_end - Indicate the end of a requested Listen state + * @p2p: P2P module context from p2p_init() + * @freq: Listen channel frequency in MHz + * Returns: 0 if no operations were started, 1 if an operation was started + * + * This function is used to indicate that a Listen state requested with + * struct p2p_config::start_listen() callback has ended. + */ +int p2p_listen_end(struct p2p_data *p2p, unsigned int freq); + +void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len); + +void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, + const u8 *ie, size_t ie_len); + + +/* Per-group P2P state for GO */ + +struct p2p_group; + +/** + * struct p2p_group_config - P2P group configuration + * + * This configuration is provided to the P2P module during initialization of + * the per-group information with p2p_group_init(). + */ +struct p2p_group_config { + /** + * persistent_group - Whether the group is persistent + * 0 = not a persistent group + * 1 = persistent group without persistent reconnect + * 2 = persistent group with persistent reconnect + */ + int persistent_group; + + /** + * interface_addr - P2P Interface Address of the group + */ + u8 interface_addr[ETH_ALEN]; + + /** + * max_clients - Maximum number of clients in the group + */ + unsigned int max_clients; + + /** + * ssid - Group SSID + */ + u8 ssid[32]; + + /** + * ssid_len - Length of SSID + */ + size_t ssid_len; + + /** + * freq - Operating channel of the group + */ + int freq; + + /** + * cb_ctx - Context to use with callback functions + */ + void *cb_ctx; + + /** + * ie_update - Notification of IE update + * @ctx: Callback context from cb_ctx + * @beacon_ies: P2P IE for Beacon frames or %NULL if no change + * @proberesp_ies: P2P Ie for Probe Response frames + * + * P2P module uses this callback function to notify whenever the P2P IE + * in Beacon or Probe Response frames should be updated based on group + * events. + * + * The callee is responsible for freeing the returned buffer(s) with + * wpabuf_free(). + */ + void (*ie_update)(void *ctx, struct wpabuf *beacon_ies, + struct wpabuf *proberesp_ies); + + /** + * idle_update - Notification of changes in group idle state + * @ctx: Callback context from cb_ctx + * @idle: Whether the group is idle (no associated stations) + */ + void (*idle_update)(void *ctx, int idle); +}; + +/** + * p2p_group_init - Initialize P2P group + * @p2p: P2P module context from p2p_init() + * @config: P2P group configuration (will be freed by p2p_group_deinit()) + * Returns: Pointer to private data or %NULL on failure + * + * This function is used to initialize per-group P2P module context. Currently, + * this is only used to manage GO functionality and P2P clients do not need to + * create an instance of this per-group information. + */ +struct p2p_group * p2p_group_init(struct p2p_data *p2p, + struct p2p_group_config *config); + +/** + * p2p_group_deinit - Deinitialize P2P group + * @group: P2P group context from p2p_group_init() + */ +void p2p_group_deinit(struct p2p_group *group); + +/** + * p2p_group_notif_assoc - Notification of P2P client association with GO + * @group: P2P group context from p2p_group_init() + * @addr: Interface address of the P2P client + * @ie: IEs from the (Re)association Request frame + * @len: Length of the ie buffer in octets + * Returns: 0 on success, -1 on failure + */ +int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, + const u8 *ie, size_t len); + +/** + * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response + * @group: P2P group context from p2p_group_init() + * @status: Status value (P2P_SC_SUCCESS if association succeeded) + * Returns: P2P IE for (Re)association Response or %NULL on failure + * + * The caller is responsible for freeing the returned buffer with + * wpabuf_free(). + */ +struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status); + +/** + * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO + * @group: P2P group context from p2p_group_init() + * @addr: Interface address of the P2P client + */ +void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr); + +/** + * p2p_group_notif_formation_done - Notification of completed group formation + * @group: P2P group context from p2p_group_init() + */ +void p2p_group_notif_formation_done(struct p2p_group *group); + +/** + * p2p_group_notif_noa - Notification of NoA change + * @group: P2P group context from p2p_group_init() + * @noa: Notice of Absence attribute payload, %NULL if none + * @noa_len: Length of noa buffer in octets + * Returns: 0 on success, -1 on failure + * + * Notify the P2P group management about a new NoA contents. This will be + * inserted into the P2P IEs in Beacon and Probe Response frames with rest of + * the group information. + */ +int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa, + size_t noa_len); + +/** + * p2p_group_match_dev_type - Match device types in group with requested type + * @group: P2P group context from p2p_group_init() + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) + * Returns: 1 on match, 0 on mismatch + * + * This function can be used to match the Requested Device Type attribute in + * WPS IE with the device types of a group member for deciding whether a GO + * should reply to a Probe Request frame. Match will be reported if the WPS IE + * is not requested any specific device type. + */ +int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps); + +/** + * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id + */ +int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p); + +/** + * p2p_group_go_discover - Send GO Discoverability Request to a group client + * @group: P2P group context from p2p_group_init() + * Returns: 0 on success (frame scheduled); -1 if client was not found + */ +int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, + const u8 *searching_dev, int rx_freq); + + +/* Generic helper functions */ + +/** + * p2p_ie_text - Build text format description of P2P IE + * @p2p_ie: P2P IE + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on failure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end); + +/** + * p2p_scan_result_text - Build text format description of P2P IE + * @ies: Information elements from scan results + * @ies_len: ies buffer length in octets + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on failure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end); + +/** + * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated + * P2P IE + * @p2p_ie: P2P IE + * @dev_addr: Buffer for returning P2P Device Address + * Returns: 0 on success or -1 if P2P Device Address could not be parsed + */ +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr); + +/** + * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s) + * @ies: Information elements from scan results + * @ies_len: ies buffer length in octets + * @dev_addr: Buffer for returning P2P Device Address + * Returns: 0 on success or -1 if P2P Device Address could not be parsed + */ +int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr); + +/** + * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame + * @p2p: P2P module context from p2p_init() + * @bssid: BSSID + * @buf: Buffer for writing the P2P IE + * @len: Maximum buf length in octets + * @p2p_group: Whether this is for association with a P2P GO + * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none + * Returns: Number of octets written into buf or -1 on failure + */ +int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, + size_t len, int p2p_group, struct wpabuf *p2p_ie); + +/** + * p2p_scan_ie - Build P2P IE for Probe Request + * @p2p: P2P module context from p2p_init() + * @ies: Buffer for writing P2P IE + * @dev_id: Device ID to search for or %NULL for any + */ +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id); + +/** + * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie + * @p2p: P2P module context from p2p_init() + * Returns: Number of octets that p2p_scan_ie() may add to the buffer + */ +size_t p2p_scan_ie_buf_len(struct p2p_data *p2p); + +/** + * p2p_go_params - Generate random P2P group parameters + * @p2p: P2P module context from p2p_init() + * @params: Buffer for parameters + * Returns: 0 on success, -1 on failure + */ +int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params); + +/** + * p2p_get_group_capab - Get Group Capability from P2P IE data + * @p2p_ie: P2P IE(s) contents + * Returns: Group Capability + */ +u8 p2p_get_group_capab(const struct wpabuf *p2p_ie); + +/** + * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection + * @p2p_ie: P2P IE(s) contents + * Returns: 0 if cross connection is allow, 1 if not + */ +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie); + +/** + * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data + * @p2p_ie: P2P IE(s) contents + * Returns: Pointer to P2P Device Address or %NULL if not included + */ +const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie); + +/** + * p2p_get_peer_info - Get P2P peer information + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer or %NULL to indicate the first peer + * @next: Whether to select the peer entry following the one indicated by addr + * Returns: Pointer to peer info or %NULL if not found + */ +const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p, + const u8 *addr, int next); + +/** + * p2p_get_peer_info_txt - Get internal P2P peer information in text format + * @info: Pointer to P2P peer info from p2p_get_peer_info() + * @buf: Buffer for returning text + * @buflen: Maximum buffer length + * Returns: Number of octets written to the buffer or -1 on failure + * + * Note: This information is internal to the P2P module and subject to change. + * As such, this should not really be used by external programs for purposes + * other than debugging. + */ +int p2p_get_peer_info_txt(const struct p2p_peer_info *info, + char *buf, size_t buflen); + +/** + * p2p_peer_known - Check whether P2P peer is known + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer + * Returns: 1 if the specified device is in the P2P peer table or 0 if not + */ +int p2p_peer_known(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_client_discoverability - Set client discoverability capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether client discoverability will be enabled + * + * This function can be used to disable (and re-enable) client discoverability. + * This capability is enabled by default and should not be disabled in normal + * use cases, i.e., this is mainly for testing purposes. + */ +void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled); + +/** + * p2p_set_managed_oper - Set managed P2P Device operations capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether managed P2P Device operations will be enabled + */ +void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); + +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel); + +int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len); + +int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, + u8 *iface_addr); +int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr, + u8 *dev_addr); + +void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr); + +/** + * p2p_set_cross_connect - Set cross connection capability + * @p2p: P2P module context from p2p_init() + * @enabled: Whether cross connection will be enabled + */ +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled); + +int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr); + +/** + * p2p_set_intra_bss_dist - Set intra BSS distribution + * @p2p: P2P module context from p2p_init() + * @enabled: Whether intra BSS distribution will be enabled + */ +void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled); + +int p2p_channels_includes_freq(const struct p2p_channels *channels, + unsigned int freq); + +/** + * p2p_supported_freq - Check whether channel is supported for P2P + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); + +/** + * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq); + +/** + * p2p_supported_freq_cli - Check whether channel is supported for P2P client operation + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq); + +/** + * p2p_get_pref_freq - Get channel from preferred channel list + * @p2p: P2P module context from p2p_init() + * @channels: List of channels + * Returns: Preferred channel + */ +unsigned int p2p_get_pref_freq(struct p2p_data *p2p, + const struct p2p_channels *channels); + +void p2p_update_channel_list(struct p2p_data *p2p, + const struct p2p_channels *chan, + const struct p2p_channels *cli_chan); + +/** + * p2p_set_best_channels - Update best channel information + * @p2p: P2P module context from p2p_init() + * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band + * @freq_5: Frequency (MHz) of best channel in 5 GHz band + * @freq_overall: Frequency (MHz) of best channel overall + */ +void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, + int freq_overall); + +/** + * p2p_set_own_freq_preference - Set own preference for channel + * @p2p: P2P module context from p2p_init() + * @freq: Frequency (MHz) of the preferred channel or 0 if no preference + * + * This function can be used to set a preference on the operating channel based + * on frequencies used on the other virtual interfaces that share the same + * radio. If non-zero, this is used to try to avoid multi-channel concurrency. + */ +void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq); + +const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p); + +/** + * p2p_get_group_num_members - Get number of members in group + * @group: P2P group context from p2p_group_init() + * Returns: Number of members in the group + */ +unsigned int p2p_get_group_num_members(struct p2p_group *group); + +/** + * p2p_iterate_group_members - Iterate group members + * @group: P2P group context from p2p_group_init() + * @next: iteration pointer, must be a pointer to a void * that is set to %NULL + * on the first call and not modified later + * Returns: A P2P Interface Address for each call and %NULL for no more members + */ +const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next); + +/** + * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group + * @group: P2P group context from p2p_group_init() + * @addr: P2P Interface Address of the client + * Returns: P2P Device Address of the client if found or %NULL if no match + * found + */ +const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr); + +/** + * p2p_group_is_client_connected - Check whether a specific client is connected + * @group: P2P group context from p2p_group_init() + * @addr: P2P Device Address of the client + * Returns: 1 if client is connected or 0 if not + */ +int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr); + +/** + * p2p_get_peer_found - Get P2P peer info structure of a found peer + * @p2p: P2P module context from p2p_init() + * @addr: P2P Device Address of the peer or %NULL to indicate the first peer + * @next: Whether to select the peer entry following the one indicated by addr + * Returns: The first P2P peer info available or %NULL if no such peer exists + */ +const struct p2p_peer_info * +p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next); + +/** + * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions + * @p2p: P2P module context from p2p_init() + */ +void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p); + +/** + * p2p_add_wps_vendor_extension - Add a WPS vendor extension + * @p2p: P2P module context from p2p_init() + * @vendor_ext: The vendor extensions to add + * Returns: 0 on success, -1 on failure + * + * The wpabuf structures in the array are owned by the P2P + * module after this call. + */ +int p2p_add_wps_vendor_extension(struct p2p_data *p2p, + const struct wpabuf *vendor_ext); + +/** + * p2p_set_oper_channel - Set the P2P operating channel + * @p2p: P2P module context from p2p_init() + * @op_reg_class: Operating regulatory class to set + * @op_channel: operating channel to set + * @cfg_op_channel : Whether op_channel is hardcoded in configuration + * Returns: 0 on success, -1 on failure + */ +int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, + int cfg_op_channel); + +/** + * p2p_set_pref_chan - Set P2P preferred channel list + * @p2p: P2P module context from p2p_init() + * @num_pref_chan: Number of entries in pref_chan list + * @pref_chan: Preferred channels or %NULL to remove preferences + * Returns: 0 on success, -1 on failure + */ +int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, + const struct p2p_channel *pref_chan); + +/** + * p2p_set_no_go_freq - Set no GO channel ranges + * @p2p: P2P module context from p2p_init() + * @list: Channel ranges or %NULL to remove restriction + * Returns: 0 on success, -1 on failure + */ +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list); + +/** + * p2p_in_progress - Check whether a P2P operation is progress + * @p2p: P2P module context from p2p_init() + * Returns: 0 if P2P module is idle or 1 if an operation is in progress + */ +int p2p_in_progress(struct p2p_data *p2p); + +const char * p2p_wps_method_text(enum p2p_wps_method method); + +/** + * p2p_set_config_timeout - Set local config timeouts + * @p2p: P2P module context from p2p_init() + * @go_timeout: Time in 10 ms units it takes to start the GO mode + * @client_timeout: Time in 10 ms units it takes to start the client mode + */ +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, + u8 client_timeout); + +int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie); +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, + const struct wpabuf *elem); +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); + +/** + * p2p_set_disc_int - Set min/max discoverable interval for p2p_find + * @p2p: P2P module context from p2p_init() + * @min_disc_int: minDiscoverableInterval (in units of 100 TU); default 1 + * @max_disc_int: maxDiscoverableInterval (in units of 100 TU); default 3 + * @max_disc_tu: Maximum number of TUs (1.024 ms) for discoverable interval; or + * -1 not to limit + * Returns: 0 on success, or -1 on failure + * + * This function can be used to configure minDiscoverableInterval and + * maxDiscoverableInterval parameters for the Listen state during device + * discovery (p2p_find). A random number of 100 TU units is picked for each + * Listen state iteration from [min_disc_int,max_disc_int] range. + * + * max_disc_tu can be used to futher limit the discoverable duration. However, + * it should be noted that use of this parameter is not recommended since it + * would not be compliant with the P2P specification. + */ +int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, + int max_disc_tu); + +/** + * p2p_get_state_txt - Get current P2P state for debug purposes + * @p2p: P2P module context from p2p_init() + * Returns: Name of the current P2P module state + * + * It should be noted that the P2P module state names are internal information + * and subject to change at any point, i.e., this information should be used + * mainly for debugging purposes. + */ +const char * p2p_get_state_txt(struct p2p_data *p2p); + +struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len); +struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len); + +struct p2p_nfc_params { + int sel; + const u8 *wsc_attr; + size_t wsc_len; + const u8 *p2p_attr; + size_t p2p_len; + + enum { + NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG, + BOTH_GO, PEER_CLIENT + } next_step; + struct p2p_peer_info *peer; + u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN]; + size_t oob_dev_pw_len; + int go_freq; + u8 go_dev_addr[ETH_ALEN]; + u8 go_ssid[32]; + size_t go_ssid_len; +}; + +int p2p_process_nfc_connection_handover(struct p2p_data *p2p, + struct p2p_nfc_params *params); + +void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, + int go_intent, + const u8 *own_interface_addr); + +#endif /* P2P_H */ diff --git a/contrib/hostapd/src/p2p/p2p_build.c b/contrib/hostapd/src/p2p/p2p_build.c new file mode 100644 index 0000000000..664fadec2a --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p_build.c @@ -0,0 +1,477 @@ +/* + * P2P - IE builder + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps_i.h" +#include "p2p_i.h" + + +void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token) +{ + wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + + wpabuf_put_u8(buf, subtype); /* OUI Subtype */ + wpabuf_put_u8(buf, dialog_token); + wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token); +} + + +void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, + u8 dialog_token) +{ + wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + + wpabuf_put_u8(buf, subtype); /* OUI Subtype */ + wpabuf_put_u8(buf, dialog_token); + wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token); +} + + +u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf) +{ + u8 *len; + + /* P2P IE header */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(buf, 1); /* IE length to be filled */ + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpa_printf(MSG_DEBUG, "P2P: * P2P IE header"); + return len; +} + + +void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len) +{ + /* Update P2P IE Length */ + *len = (u8 *) wpabuf_put(buf, 0) - len - 1; +} + + +void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab) +{ + /* P2P Capability */ + wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY); + wpabuf_put_le16(buf, 2); + wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */ + wpabuf_put_u8(buf, group_capab); /* Group Capabilities */ + wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x", + dev_capab, group_capab); +} + + +void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent) +{ + /* Group Owner Intent */ + wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, go_intent); + wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u", + go_intent >> 1, go_intent & 0x01); +} + + +void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel) +{ + /* Listen Channel */ + wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL); + wpabuf_put_le16(buf, 5); + wpabuf_put_data(buf, country, 3); + wpabuf_put_u8(buf, reg_class); /* Regulatory Class */ + wpabuf_put_u8(buf, channel); /* Channel Number */ + wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u " + "Channel %u", reg_class, channel); +} + + +void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel) +{ + /* Operating Channel */ + wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL); + wpabuf_put_le16(buf, 5); + wpabuf_put_data(buf, country, 3); + wpabuf_put_u8(buf, reg_class); /* Regulatory Class */ + wpabuf_put_u8(buf, channel); /* Channel Number */ + wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u " + "Channel %u", reg_class, channel); +} + + +void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, + struct p2p_channels *chan) +{ + u8 *len; + size_t i; + + /* Channel List */ + wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST); + len = wpabuf_put(buf, 2); /* IE length to be filled */ + wpabuf_put_data(buf, country, 3); /* Country String */ + + for (i = 0; i < chan->reg_classes; i++) { + struct p2p_reg_class *c = &chan->reg_class[i]; + wpabuf_put_u8(buf, c->reg_class); + wpabuf_put_u8(buf, c->channels); + wpabuf_put_data(buf, c->channel, c->channels); + } + + /* Update attribute length */ + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); + wpa_hexdump(MSG_DEBUG, "P2P: * Channel List", + len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2); +} + + +void p2p_buf_add_status(struct wpabuf *buf, u8 status) +{ + /* Status */ + wpabuf_put_u8(buf, P2P_ATTR_STATUS); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, status); + wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status); +} + + +void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, + struct p2p_device *peer) +{ + u8 *len; + u16 methods; + size_t nlen, i; + + /* P2P Device Info */ + wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO); + len = wpabuf_put(buf, 2); /* IE length to be filled */ + + /* P2P Device address */ + wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); + + /* Config Methods */ + methods = 0; + if (peer && peer->wps_method != WPS_NOT_READY) { + if (peer->wps_method == WPS_PBC) + methods |= WPS_CONFIG_PUSHBUTTON; + else if (peer->wps_method == WPS_PIN_DISPLAY || + peer->wps_method == WPS_PIN_KEYPAD) + methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + } else if (p2p->cfg->config_methods) { + methods |= p2p->cfg->config_methods & + (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY | + WPS_CONFIG_KEYPAD); + } else { + methods |= WPS_CONFIG_PUSHBUTTON; + methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; + } + wpabuf_put_be16(buf, methods); + + /* Primary Device Type */ + wpabuf_put_data(buf, p2p->cfg->pri_dev_type, + sizeof(p2p->cfg->pri_dev_type)); + + /* Number of Secondary Device Types */ + wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types); + + /* Secondary Device Type List */ + for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) + wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i], + WPS_DEV_TYPE_LEN); + + /* Device Name */ + nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0; + wpabuf_put_be16(buf, ATTR_DEV_NAME); + wpabuf_put_be16(buf, nlen); + wpabuf_put_data(buf, p2p->cfg->dev_name, nlen); + + /* Update attribute length */ + WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2); + wpa_printf(MSG_DEBUG, "P2P: * Device Info"); +} + + +void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr) +{ + /* P2P Device ID */ + wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID); + wpabuf_put_le16(buf, ETH_ALEN); + wpabuf_put_data(buf, dev_addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr)); +} + + +void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout, + u8 client_timeout) +{ + /* Configuration Timeout */ + wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT); + wpabuf_put_le16(buf, 2); + wpabuf_put_u8(buf, go_timeout); + wpabuf_put_u8(buf, client_timeout); + wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms) " + "client %d (*10ms)", go_timeout, client_timeout); +} + + +void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr) +{ + /* Intended P2P Interface Address */ + wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR); + wpabuf_put_le16(buf, ETH_ALEN); + wpabuf_put_data(buf, interface_addr, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR, + MAC2STR(interface_addr)); +} + + +void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid) +{ + /* P2P Group BSSID */ + wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID); + wpabuf_put_le16(buf, ETH_ALEN); + wpabuf_put_data(buf, bssid, ETH_ALEN); + wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR, + MAC2STR(bssid)); +} + + +void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, + const u8 *ssid, size_t ssid_len) +{ + /* P2P Group ID */ + wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID); + wpabuf_put_le16(buf, ETH_ALEN + ssid_len); + wpabuf_put_data(buf, dev_addr, ETH_ALEN); + wpabuf_put_data(buf, ssid, ssid_len); + wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR, + MAC2STR(dev_addr)); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len); +} + + +void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags) +{ + /* Invitation Flags */ + wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS); + wpabuf_put_le16(buf, 1); + wpabuf_put_u8(buf, flags); + wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags); +} + + +static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc) +{ + if (desc == NULL) + return; + + wpabuf_put_u8(buf, desc->count_type); + wpabuf_put_le32(buf, desc->duration); + wpabuf_put_le32(buf, desc->interval); + wpabuf_put_le32(buf, desc->start_time); +} + + +void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, + struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2) +{ + /* Notice of Absence */ + wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE); + wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0)); + wpabuf_put_u8(buf, noa_index); + wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f)); + p2p_buf_add_noa_desc(buf, desc1); + p2p_buf_add_noa_desc(buf, desc2); + wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence"); +} + + +void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, + u16 interval) +{ + /* Extended Listen Timing */ + wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING); + wpabuf_put_le16(buf, 4); + wpabuf_put_le16(buf, period); + wpabuf_put_le16(buf, interval); + wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec " + "interval %u msec)", period, interval); +} + + +void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p) +{ + /* P2P Interface */ + wpabuf_put_u8(buf, P2P_ATTR_INTERFACE); + wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN); + /* P2P Device address */ + wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); + /* + * FIX: Fetch interface address list from driver. Do not include + * the P2P Device address if it is never used as interface address. + */ + /* P2P Interface Address Count */ + wpabuf_put_u8(buf, 1); + wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN); +} + + +void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, + u8 oper_class, u8 channel, + enum p2p_role_indication role) +{ + /* OOB Group Owner Negotiation Channel */ + wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL); + wpabuf_put_le16(buf, 6); + wpabuf_put_data(buf, country, 3); + wpabuf_put_u8(buf, oper_class); /* Operating Class */ + wpabuf_put_u8(buf, channel); /* Channel Number */ + wpabuf_put_u8(buf, (u8) role); /* Role indication */ + wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating " + "Class %u Channel %u Role %d", + oper_class, channel, role); +} + + +static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, + const char *val) +{ + size_t len; + + len = val ? os_strlen(val) : 0; + if (wpabuf_tailroom(buf) < 4 + len) + return -1; + wpabuf_put_be16(buf, attr); +#ifndef CONFIG_WPS_STRICT + if (len == 0) { + /* + * Some deployed WPS implementations fail to parse zeor-length + * attributes. As a workaround, send a space character if the + * device attribute string is empty. + */ + if (wpabuf_tailroom(buf) < 3) + return -1; + wpabuf_put_be16(buf, 1); + wpabuf_put_u8(buf, ' '); + return 0; + } +#endif /* CONFIG_WPS_STRICT */ + wpabuf_put_be16(buf, len); + if (val) + wpabuf_put_data(buf, val, len); + return 0; +} + + +int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, + int all_attr) +{ + u8 *len; + int i; + + if (wpabuf_tailroom(buf) < 6) + return -1; + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(buf, 1); + wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); + + if (wps_build_version(buf) < 0) + return -1; + + if (all_attr) { + if (wpabuf_tailroom(buf) < 5) + return -1; + wpabuf_put_be16(buf, ATTR_WPS_STATE); + wpabuf_put_be16(buf, 1); + wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED); + } + + if (pw_id >= 0) { + if (wpabuf_tailroom(buf) < 6) + return -1; + /* Device Password ID */ + wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID); + wpabuf_put_be16(buf, 2); + wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d", + pw_id); + wpabuf_put_be16(buf, pw_id); + } + + if (all_attr) { + if (wpabuf_tailroom(buf) < 5) + return -1; + wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE); + wpabuf_put_be16(buf, 1); + wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO); + + if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 || + p2p_add_wps_string(buf, ATTR_MANUFACTURER, + p2p->cfg->manufacturer) < 0 || + p2p_add_wps_string(buf, ATTR_MODEL_NAME, + p2p->cfg->model_name) < 0 || + p2p_add_wps_string(buf, ATTR_MODEL_NUMBER, + p2p->cfg->model_number) < 0 || + p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER, + p2p->cfg->serial_number) < 0) + return -1; + + if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN) + return -1; + wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE); + wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN); + wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN); + + if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name) + < 0) + return -1; + + if (wpabuf_tailroom(buf) < 6) + return -1; + wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); + wpabuf_put_be16(buf, 2); + wpabuf_put_be16(buf, p2p->cfg->config_methods); + } + + if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0) + return -1; + + if (all_attr && p2p->cfg->num_sec_dev_types) { + if (wpabuf_tailroom(buf) < + 4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types) + return -1; + wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST); + wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN * + p2p->cfg->num_sec_dev_types); + wpabuf_put_data(buf, p2p->cfg->sec_dev_type, + WPS_DEV_TYPE_LEN * + p2p->cfg->num_sec_dev_types); + } + + /* Add the WPS vendor extensions */ + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + if (p2p->wps_vendor_ext[i] == NULL) + break; + if (wpabuf_tailroom(buf) < + 4 + wpabuf_len(p2p->wps_vendor_ext[i])) + continue; + wpabuf_put_be16(buf, ATTR_VENDOR_EXT); + wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i])); + wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]); + } + + p2p_buf_update_ie_hdr(buf, len); + + return 0; +} diff --git a/contrib/hostapd/src/p2p/p2p_dev_disc.c b/contrib/hostapd/src/p2p/p2p_dev_disc.c new file mode 100644 index 0000000000..76d01cfc61 --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p_dev_disc.c @@ -0,0 +1,325 @@ +/* + * Wi-Fi Direct - P2P Device Discoverability procedure + * Copyright (c) 2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "p2p_i.h" +#include "p2p.h" + + +static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p, + struct p2p_device *go, + const u8 *dev_id) +{ + struct wpabuf *buf; + u8 *len; + + buf = wpabuf_alloc(100); + if (buf == NULL) + return NULL; + + go->dialog_token++; + if (go->dialog_token == 0) + go->dialog_token = 1; + p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_device_id(buf, dev_id); + p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, + go->oper_ssid_len); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success) +{ + p2p_dbg(p2p, "Device Discoverability Request TX callback: success=%d", + success); + + if (!success) { + /* + * Use P2P find, if needed, to find the other device or to + * retry device discoverability. + */ + p2p_set_state(p2p, P2P_CONNECT); + p2p_set_timeout(p2p, 0, 100000); + return; + } + + p2p_dbg(p2p, "GO acknowledged Device Discoverability Request - wait for response"); + /* + * TODO: is the remain-on-channel from Action frame TX long enough for + * most cases or should we try to increase its duration and/or start + * another remain-on-channel if needed once the previous one expires? + */ +} + + +int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) +{ + struct p2p_device *go; + struct wpabuf *req; + + go = p2p_get_device(p2p, dev->member_in_go_dev); + if (go == NULL || dev->oper_freq <= 0) { + p2p_dbg(p2p, "Could not find peer entry for GO and frequency to send Device Discoverability Request"); + return -1; + } + + req = p2p_build_dev_disc_req(p2p, go, dev->info.p2p_device_addr); + if (req == NULL) + return -1; + + p2p_dbg(p2p, "Sending Device Discoverability Request to GO " MACSTR + " for client " MACSTR, + MAC2STR(go->info.p2p_device_addr), + MAC2STR(dev->info.p2p_device_addr)); + + p2p->pending_client_disc_go = go; + os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr, + ETH_ALEN); + p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST; + if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr, + p2p->cfg->dev_addr, go->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 1000) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + wpabuf_free(req); + /* TODO: how to recover from failure? */ + return -1; + } + + wpabuf_free(req); + + return 0; +} + + +static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status) +{ + struct wpabuf *buf; + u8 *len; + + buf = wpabuf_alloc(100); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + p2p_buf_update_ie_hdr(buf, len); + + return buf; +} + + +void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success) +{ + p2p_dbg(p2p, "Device Discoverability Response TX callback: success=%d", + success); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); +} + + +static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token, + const u8 *addr, int freq, u8 status) +{ + struct wpabuf *resp; + + resp = p2p_build_dev_disc_resp(dialog_token, status); + if (resp == NULL) + return; + + p2p_dbg(p2p, "Sending Device Discoverability Response to " MACSTR + " (status %u freq %d)", + MAC2STR(addr), status, freq); + + p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE; + if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + } + + wpabuf_free(resp); +} + + +void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_message msg; + size_t g; + + p2p_dbg(p2p, "Received Device Discoverability Request from " MACSTR + " (freq=%d)", MAC2STR(sa), rx_freq); + + if (p2p_parse(data, len, &msg)) + return; + + if (msg.dialog_token == 0) { + p2p_dbg(p2p, "Invalid Dialog Token 0 (must be nonzero) in Device Discoverability Request"); + p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, + P2P_SC_FAIL_INVALID_PARAMS); + p2p_parse_free(&msg); + return; + } + + if (msg.device_id == NULL) { + p2p_dbg(p2p, "P2P Device ID attribute missing from Device Discoverability Request"); + p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, + P2P_SC_FAIL_INVALID_PARAMS); + p2p_parse_free(&msg); + return; + } + + for (g = 0; g < p2p->num_groups; g++) { + if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa, + rx_freq) == 0) { + p2p_dbg(p2p, "Scheduled GO Discoverability Request for the target device"); + /* + * P2P group code will use a callback to indicate TX + * status, so that we can reply to the request once the + * target client has acknowledged the request or it has + * timed out. + */ + p2p->pending_dev_disc_dialog_token = msg.dialog_token; + os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN); + p2p->pending_dev_disc_freq = rx_freq; + p2p_parse_free(&msg); + return; + } + } + + p2p_dbg(p2p, "Requested client was not found in any group or did not support client discoverability"); + p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, + P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); + p2p_parse_free(&msg); +} + + +void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_message msg; + struct p2p_device *go; + u8 status; + + p2p_dbg(p2p, "Received Device Discoverability Response from " MACSTR, + MAC2STR(sa)); + + go = p2p->pending_client_disc_go; + if (go == NULL || + os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) { + p2p_dbg(p2p, "Ignore unexpected Device Discoverability Response"); + return; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (msg.status == NULL) { + p2p_parse_free(&msg); + return; + } + + if (msg.dialog_token != go->dialog_token) { + p2p_dbg(p2p, "Ignore Device Discoverability Response with unexpected dialog token %u (expected %u)", + msg.dialog_token, go->dialog_token); + p2p_parse_free(&msg); + return; + } + + status = *msg.status; + p2p_parse_free(&msg); + + p2p_dbg(p2p, "Device Discoverability Response status %u", status); + + if (p2p->go_neg_peer == NULL || + os_memcmp(p2p->pending_client_disc_addr, + p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 || + os_memcmp(p2p->go_neg_peer->member_in_go_dev, + go->info.p2p_device_addr, ETH_ALEN) != 0) { + p2p_dbg(p2p, "No pending operation with the client discoverability peer anymore"); + return; + } + + if (status == 0) { + /* + * Peer is expected to be awake for at least 100 TU; try to + * connect immediately. + */ + p2p_dbg(p2p, "Client discoverability request succeeded"); + if (p2p->state == P2P_CONNECT) { + /* + * Change state to force the timeout to start in + * P2P_CONNECT again without going through the short + * Listen state. + */ + p2p_set_state(p2p, P2P_CONNECT_LISTEN); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } + p2p_set_timeout(p2p, 0, 0); + } else { + /* + * Client discoverability request failed; try to connect from + * timeout. + */ + p2p_dbg(p2p, "Client discoverability request failed"); + p2p_set_timeout(p2p, 0, 500000); + } + +} + + +void p2p_go_disc_req_cb(struct p2p_data *p2p, int success) +{ + p2p_dbg(p2p, "GO Discoverability Request TX callback: success=%d", + success); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + + if (p2p->pending_dev_disc_dialog_token == 0) { + p2p_dbg(p2p, "No pending Device Discoverability Request"); + return; + } + + p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token, + p2p->pending_dev_disc_addr, + p2p->pending_dev_disc_freq, + success ? P2P_SC_SUCCESS : + P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); + + p2p->pending_dev_disc_dialog_token = 0; +} + + +void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + unsigned int tu; + struct wpabuf *ies; + + p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU"); + + ies = p2p_build_probe_resp_ies(p2p); + if (ies == NULL) + return; + + /* Remain awake 100 TU on operating channel */ + p2p->pending_client_disc_freq = rx_freq; + tu = 100; + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000, + ies) < 0) { + p2p_dbg(p2p, "Failed to start listen mode for client discoverability"); + } + wpabuf_free(ies); +} diff --git a/contrib/hostapd/src/p2p/p2p_go_neg.c b/contrib/hostapd/src/p2p/p2p_go_neg.c new file mode 100644 index 0000000000..e28f93ea2b --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p_go_neg.c @@ -0,0 +1,1223 @@ +/* + * Wi-Fi Direct - P2P Group Owner Negotiation + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps_defs.h" +#include "p2p_i.h" +#include "p2p.h" + + +static int p2p_go_det(u8 own_intent, u8 peer_value) +{ + u8 peer_intent = peer_value >> 1; + if (own_intent == peer_intent) { + if (own_intent == P2P_MAX_GO_INTENT) + return -1; /* both devices want to become GO */ + + /* Use tie breaker bit to determine GO */ + return (peer_value & 0x01) ? 0 : 1; + } + + return own_intent > peer_intent; +} + + +int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, + struct p2p_device *dev, + const u8 *channel_list, size_t channel_list_len) +{ + const u8 *pos, *end; + struct p2p_channels *ch; + size_t channels; + struct p2p_channels intersection; + + ch = &dev->channels; + os_memset(ch, 0, sizeof(*ch)); + pos = channel_list; + end = channel_list + channel_list_len; + + if (end - pos < 3) + return -1; + os_memcpy(dev->country, pos, 3); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3); + if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) { + p2p_info(p2p, "Mismatching country (ours=%c%c peer's=%c%c)", + p2p->cfg->country[0], p2p->cfg->country[1], + pos[0], pos[1]); + return -1; + } + pos += 3; + + while (pos + 2 < end) { + struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes]; + cl->reg_class = *pos++; + if (pos + 1 + pos[0] > end) { + p2p_info(p2p, "Invalid peer Channel List"); + return -1; + } + channels = *pos++; + cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ? + P2P_MAX_REG_CLASS_CHANNELS : channels; + os_memcpy(cl->channel, pos, cl->channels); + pos += channels; + ch->reg_classes++; + if (ch->reg_classes == P2P_MAX_REG_CLASSES) + break; + } + + p2p_channels_intersect(own, &dev->channels, &intersection); + p2p_dbg(p2p, "Own reg_classes %d peer reg_classes %d intersection reg_classes %d", + (int) own->reg_classes, + (int) dev->channels.reg_classes, + (int) intersection.reg_classes); + if (intersection.reg_classes == 0) { + p2p_info(p2p, "No common channels found"); + return -1; + } + return 0; +} + + +static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *channel_list, size_t channel_list_len) +{ + return p2p_peer_channels_check(p2p, &p2p->channels, dev, + channel_list, channel_list_len); +} + + +u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) +{ + switch (wps_method) { + case WPS_PIN_DISPLAY: + return DEV_PW_REGISTRAR_SPECIFIED; + case WPS_PIN_KEYPAD: + return DEV_PW_USER_SPECIFIED; + case WPS_PBC: + return DEV_PW_PUSHBUTTON; + case WPS_NFC: + return DEV_PW_NFC_CONNECTION_HANDOVER; + default: + return DEV_PW_DEFAULT; + } +} + + +static const char * p2p_wps_method_str(enum p2p_wps_method wps_method) +{ + switch (wps_method) { + case WPS_PIN_DISPLAY: + return "Display"; + case WPS_PIN_KEYPAD: + return "Keypad"; + case WPS_PBC: + return "PBC"; + case WPS_NFC: + return "NFC"; + default: + return "??"; + } +} + + +static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, + struct p2p_device *peer) +{ + struct wpabuf *buf; + u8 *len; + u8 group_capab; + size_t extra = 0; + u16 pw_id; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + group_capab = 0; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); + p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker); + p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); + p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, + p2p->cfg->channel); + if (p2p->ext_listen_interval) + p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period, + p2p->ext_listen_interval); + p2p_buf_add_intended_addr(buf, p2p->intended_addr); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); + p2p_buf_add_device_info(buf, p2p, peer); + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, p2p->op_channel); + p2p_buf_update_ie_hdr(buf, len); + + /* WPS IE with Device Password ID attribute */ + pw_id = p2p_wps_method_pw_id(peer->wps_method); + if (peer->oob_pw_id) + pw_id = peer->oob_pw_id; + if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request"); + wpabuf_free(buf); + return NULL; + } + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) +{ + struct wpabuf *req; + int freq; + + if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { + u16 config_method; + p2p_dbg(p2p, "Use PD-before-GO-Neg workaround for " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); + if (dev->wps_method == WPS_PIN_DISPLAY) + config_method = WPS_CONFIG_KEYPAD; + else if (dev->wps_method == WPS_PIN_KEYPAD) + config_method = WPS_CONFIG_DISPLAY; + else if (dev->wps_method == WPS_PBC) + config_method = WPS_CONFIG_PUSHBUTTON; + else + return -1; + return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr, + config_method, 0, 0, 1); + } + + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (dev->oob_go_neg_freq > 0) + freq = dev->oob_go_neg_freq; + if (freq <= 0) { + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send GO Negotiation Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + req = p2p_build_go_neg_req(p2p, dev); + if (req == NULL) + return -1; + p2p_dbg(p2p, "Sending GO Negotiation Request"); + p2p_set_state(p2p, P2P_CONNECT); + p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST; + p2p->go_neg_peer = dev; + dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE; + dev->connect_reqs++; + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + /* Use P2P find to recover and retry */ + p2p_set_timeout(p2p, 0, 0); + } else + dev->go_neg_req_sent++; + + wpabuf_free(req); + + return 0; +} + + +static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, + struct p2p_device *peer, + u8 dialog_token, u8 status, + u8 tie_breaker) +{ + struct wpabuf *buf; + u8 *len; + u8 group_capab; + size_t extra = 0; + u16 pw_id; + + p2p_dbg(p2p, "Building GO Negotiation Response"); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + group_capab = 0; + if (peer && peer->go_state == LOCAL_GO) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + group_capab |= + P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + } + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); + p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); + p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); + if (peer && peer->go_state == REMOTE_GO) { + p2p_dbg(p2p, "Omit Operating Channel attribute"); + } else { + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + } + p2p_buf_add_intended_addr(buf, p2p->intended_addr); + if (status || peer == NULL) { + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); + } else if (peer->go_state == REMOTE_GO) { + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); + } else { + struct p2p_channels res; + p2p_channels_intersect(&p2p->channels, &peer->channels, + &res); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &res); + } + p2p_buf_add_device_info(buf, p2p, peer); + if (peer && peer->go_state == LOCAL_GO) { + p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, + p2p->ssid_len); + } + p2p_buf_update_ie_hdr(buf, len); + + /* WPS IE with Device Password ID attribute */ + pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY); + if (peer && peer->oob_pw_id) + pw_id = peer->oob_pw_id; + if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response"); + wpabuf_free(buf); + return NULL; + } + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + + return buf; +} + + +/** + * p2p_reselect_channel - Re-select operating channel based on peer information + * @p2p: P2P module context from p2p_init() + * @intersection: Support channel list intersection from local and peer + * + * This function is used to re-select the best channel after having received + * information from the peer to allow supported channel lists to be intersected. + * This can be used to improve initial channel selection done in + * p2p_prepare_channel() prior to the start of GO Negotiation. In addition, this + * can be used for Invitation case. + */ +void p2p_reselect_channel(struct p2p_data *p2p, + struct p2p_channels *intersection) +{ + struct p2p_reg_class *cl; + int freq; + u8 op_reg_class, op_channel; + unsigned int i; + const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; + const int op_classes_vht[] = { 128, 0 }; + + if (p2p->own_freq_preference > 0 && + p2p_freq_to_channel(p2p->own_freq_preference, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + p2p_dbg(p2p, "Pick own channel preference (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } + + if (p2p->best_freq_overall > 0 && + p2p_freq_to_channel(p2p->best_freq_overall, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + p2p_dbg(p2p, "Pick best overall channel (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } + + /* First, try to pick the best channel from another band */ + freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); + if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 && + !p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel) && + p2p_freq_to_channel(p2p->best_freq_5, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + p2p_dbg(p2p, "Pick best 5 GHz channel (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } + + if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 && + !p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel) && + p2p_freq_to_channel(p2p->best_freq_24, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + p2p_dbg(p2p, "Pick best 2.4 GHz channel (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } + + /* Select channel with highest preference if the peer supports it */ + for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { + if (p2p_channels_includes(intersection, + p2p->cfg->pref_chan[i].op_class, + p2p->cfg->pref_chan[i].chan)) { + p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class; + p2p->op_channel = p2p->cfg->pref_chan[i].chan; + p2p_dbg(p2p, "Pick highest preferred channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + } + + /* Try a channel where we might be able to use VHT */ + if (p2p_channel_select(intersection, op_classes_vht, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible VHT channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + + /* Try a channel where we might be able to use HT40 */ + if (p2p_channel_select(intersection, op_classes_ht40, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible HT40 channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + + /* Prefer a 5 GHz channel */ + if (p2p_channel_select(intersection, op_classes_5ghz, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + + /* + * Try to see if the original channel is in the intersection. If + * so, no need to change anything, as it already contains some + * randomness. + */ + if (p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel)) { + p2p_dbg(p2p, "Using original operating class and channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + + /* + * Fall back to whatever is included in the channel intersection since + * no better options seems to be available. + */ + cl = &intersection->reg_class[0]; + p2p_dbg(p2p, "Pick another channel (reg_class %u channel %u) from intersection", + cl->reg_class, cl->channel[0]); + p2p->op_reg_class = cl->reg_class; + p2p->op_channel = cl->channel[0]; +} + + +static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, + u8 *status) +{ + struct p2p_channels tmp, intersection; + + p2p_channels_dump(p2p, "own channels", &p2p->channels); + p2p_channels_dump(p2p, "peer channels", &dev->channels); + p2p_channels_intersect(&p2p->channels, &dev->channels, &tmp); + p2p_channels_dump(p2p, "intersection", &tmp); + p2p_channels_remove_freqs(&tmp, &p2p->no_go_freq); + p2p_channels_dump(p2p, "intersection after no-GO removal", &tmp); + p2p_channels_intersect(&tmp, &p2p->cfg->channels, &intersection); + p2p_channels_dump(p2p, "intersection with local channel list", + &intersection); + if (intersection.reg_classes == 0 || + intersection.reg_class[0].channels == 0) { + *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + p2p_dbg(p2p, "No common channels found"); + return -1; + } + + if (!p2p_channels_includes(&intersection, p2p->op_reg_class, + p2p->op_channel)) { + if (dev->flags & P2P_DEV_FORCE_FREQ) { + *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + p2p_dbg(p2p, "Peer does not support the forced channel"); + return -1; + } + + p2p_dbg(p2p, "Selected operating channel (op_class %u channel %u) not acceptable to the peer", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) && + !p2p->cfg->cfg_op_channel) { + p2p_dbg(p2p, "Try to optimize channel selection with peer information received; previously selected op_class %u channel %u", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + } + + if (!p2p->ssid_set) { + p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); + p2p->ssid_set = 1; + } + + return 0; +} + + +void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_device *dev = NULL; + struct wpabuf *resp; + struct p2p_message msg; + u8 status = P2P_SC_FAIL_INVALID_PARAMS; + int tie_breaker = 0; + int freq; + + p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)", + MAC2STR(sa), rx_freq); + + if (p2p_parse(data, len, &msg)) + return; + + if (!msg.capability) { + p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request"); +#ifdef CONFIG_P2P_STRICT + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (msg.go_intent) + tie_breaker = *msg.go_intent & 0x01; + else { + p2p_dbg(p2p, "Mandatory GO Intent attribute missing from GO Negotiation Request"); +#ifdef CONFIG_P2P_STRICT + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.config_timeout) { + p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Request"); +#ifdef CONFIG_P2P_STRICT + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.listen_channel) { + p2p_dbg(p2p, "No Listen Channel attribute received"); + goto fail; + } + if (!msg.operating_channel) { + p2p_dbg(p2p, "No Operating Channel attribute received"); + goto fail; + } + if (!msg.channel_list) { + p2p_dbg(p2p, "No Channel List attribute received"); + goto fail; + } + if (!msg.intended_addr) { + p2p_dbg(p2p, "No Intended P2P Interface Address attribute received"); + goto fail; + } + if (!msg.p2p_device_info) { + p2p_dbg(p2p, "No P2P Device Info attribute received"); + goto fail; + } + + if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) { + p2p_dbg(p2p, "Unexpected GO Negotiation Request SA=" MACSTR + " != dev_addr=" MACSTR, + MAC2STR(sa), MAC2STR(msg.p2p_device_addr)); + goto fail; + } + + dev = p2p_get_device(p2p, sa); + + if (msg.status && *msg.status) { + p2p_dbg(p2p, "Unexpected Status attribute (%d) in GO Negotiation Request", + *msg.status); + goto fail; + } + + if (dev == NULL) + dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg); + else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) + p2p_add_dev_info(p2p, sa, dev, &msg); + else if (!dev->listen_freq && !dev->oper_freq) { + /* + * This may happen if the peer entry was added based on PD + * Request and no Probe Request/Response frame has been received + * from this peer (or that information has timed out). + */ + p2p_dbg(p2p, "Update peer " MACSTR + " based on GO Neg Req since listen/oper freq not known", + MAC2STR(dev->info.p2p_device_addr)); + p2p_add_dev_info(p2p, sa, dev, &msg); + } + + if (dev && dev->flags & P2P_DEV_USER_REJECTED) { + p2p_dbg(p2p, "User has rejected this peer"); + status = P2P_SC_FAIL_REJECTED_BY_USER; + } else if (dev == NULL || + (dev->wps_method == WPS_NOT_READY && + (p2p->authorized_oob_dev_pw_id == 0 || + p2p->authorized_oob_dev_pw_id != + msg.dev_password_id))) { + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, + MAC2STR(sa)); + status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, + msg.dev_password_id); + } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { + p2p_dbg(p2p, "Already in Group Formation with another peer"); + status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } else { + int go; + + if (!p2p->go_neg_peer) { + p2p_dbg(p2p, "Starting GO Negotiation with previously authorized peer"); + if (!(dev->flags & P2P_DEV_FORCE_FREQ)) { + p2p_dbg(p2p, "Use default channel settings"); + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + } else { + p2p_dbg(p2p, "Use previously configured forced channel settings"); + } + } + + dev->flags &= ~P2P_DEV_NOT_YET_READY; + + if (!msg.go_intent) { + p2p_dbg(p2p, "No GO Intent attribute received"); + goto fail; + } + if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { + p2p_dbg(p2p, "Invalid GO Intent value (%u) received", + *msg.go_intent >> 1); + goto fail; + } + + if (dev->go_neg_req_sent && + os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) { + p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent"); + p2p_parse_free(&msg); + return; + } + + go = p2p_go_det(p2p->go_intent, *msg.go_intent); + if (go < 0) { + p2p_dbg(p2p, "Incompatible GO Intent"); + status = P2P_SC_FAIL_BOTH_GO_INTENT_15; + goto fail; + } + + if (p2p_peer_channels(p2p, dev, msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, "No common channels found"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + switch (msg.dev_password_id) { + case DEV_PW_REGISTRAR_SPECIFIED: + p2p_dbg(p2p, "PIN from peer Display"); + if (dev->wps_method != WPS_PIN_KEYPAD) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_USER_SPECIFIED: + p2p_dbg(p2p, "Peer entered PIN on Keypad"); + if (dev->wps_method != WPS_PIN_DISPLAY) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_PUSHBUTTON: + p2p_dbg(p2p, "Peer using pushbutton"); + if (dev->wps_method != WPS_PBC) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + default: + if (msg.dev_password_id && + msg.dev_password_id == dev->oob_pw_id) { + p2p_dbg(p2p, "Peer using NFC"); + if (dev->wps_method != WPS_NFC) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str( + dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + } +#ifdef CONFIG_WPS_NFC + if (p2p->authorized_oob_dev_pw_id && + msg.dev_password_id == + p2p->authorized_oob_dev_pw_id) { + p2p_dbg(p2p, "Using static handover with our device password from NFC Tag"); + dev->wps_method = WPS_NFC; + dev->oob_pw_id = p2p->authorized_oob_dev_pw_id; + break; + } +#endif /* CONFIG_WPS_NFC */ + p2p_dbg(p2p, "Unsupported Device Password ID %d", + msg.dev_password_id); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + + if (go && p2p_go_select_channel(p2p, dev, &status) < 0) + goto fail; + + dev->go_state = go ? LOCAL_GO : REMOTE_GO; + dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3], + msg.operating_channel[4]); + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", + dev->oper_freq); + + if (msg.config_timeout) { + dev->go_timeout = msg.config_timeout[0]; + dev->client_timeout = msg.config_timeout[1]; + } + + p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); + if (p2p->state != P2P_IDLE) + p2p_stop_find_for_freq(p2p, rx_freq); + p2p_set_state(p2p, P2P_GO_NEG); + p2p_clear_timeout(p2p); + dev->dialog_token = msg.dialog_token; + os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); + p2p->go_neg_peer = dev; + status = P2P_SC_SUCCESS; + } + +fail: + if (dev) + dev->status = status; + resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status, + !tie_breaker); + p2p_parse_free(&msg); + if (resp == NULL) + return; + p2p_dbg(p2p, "Sending GO Negotiation Response"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown regulatory class/channel"); + wpabuf_free(resp); + return; + } + if (status == P2P_SC_SUCCESS) { + p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE; + dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM; + if (os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) < 0) { + /* + * Peer has smaller address, so the GO Negotiation + * Response from us is expected to complete + * negotiation. Ignore a GO Negotiation Response from + * the peer if it happens to be received after this + * point due to a race condition in GO Negotiation + * Request transmission and processing. + */ + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + } + } else + p2p->pending_action_state = + P2P_PENDING_GO_NEG_RESPONSE_FAILURE; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + } + + wpabuf_free(resp); +} + + +static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, + struct p2p_device *peer, + u8 dialog_token, u8 status, + const u8 *resp_chan, int go) +{ + struct wpabuf *buf; + u8 *len; + struct p2p_channels res; + u8 group_capab; + size_t extra = 0; + + p2p_dbg(p2p, "Building GO Negotiation Confirm"); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + extra = wpabuf_len(p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + group_capab = 0; + if (peer->go_state == LOCAL_GO) { + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN) + group_capab |= + P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + } + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, + group_capab); + if (go || resp_chan == NULL) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + else + p2p_buf_add_operating_channel(buf, (const char *) resp_chan, + resp_chan[3], resp_chan[4]); + p2p_channels_intersect(&p2p->channels, &peer->channels, &res); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &res); + if (go) { + p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid, + p2p->ssid_len); + } + p2p_buf_update_ie_hdr(buf, len); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_go_neg) + wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_device *dev; + struct wpabuf *conf; + int go = -1; + struct p2p_message msg; + u8 status = P2P_SC_SUCCESS; + int freq; + + p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR + " (freq=%d)", MAC2STR(sa), rx_freq); + dev = p2p_get_device(p2p, sa); + if (dev == NULL || dev->wps_method == WPS_NOT_READY || + dev != p2p->go_neg_peer) { + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, + MAC2STR(sa)); + return; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) { + p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore"); + p2p_parse_free(&msg); + return; + } + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; + + if (msg.dialog_token != dev->dialog_token) { + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); + return; + } + + if (!msg.status) { + p2p_dbg(p2p, "No Status attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + if (*msg.status) { + p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); + dev->go_neg_req_sent = 0; + if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation"); + dev->flags |= P2P_DEV_NOT_YET_READY; + dev->wait_count = 0; + p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); + p2p_set_timeout(p2p, 0, 0); + } else { + p2p_dbg(p2p, "Stop GO Negotiation attempt"); + p2p_go_neg_failed(p2p, dev, *msg.status); + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_parse_free(&msg); + return; + } + + if (!msg.capability) { + p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Response"); +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.p2p_device_info) { + p2p_dbg(p2p, "Mandatory P2P Device Info attribute missing from GO Negotiation Response"); +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } + + if (!msg.intended_addr) { + p2p_dbg(p2p, "No Intended P2P Interface Address attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + if (!msg.go_intent) { + p2p_dbg(p2p, "No GO Intent attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { + p2p_dbg(p2p, "Invalid GO Intent value (%u) received", + *msg.go_intent >> 1); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + go = p2p_go_det(p2p->go_intent, *msg.go_intent); + if (go < 0) { + p2p_dbg(p2p, "Incompatible GO Intent"); + status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto fail; + } + + if (!go && msg.group_id) { + /* Store SSID for Provisioning step */ + p2p->ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); + } else if (!go) { + p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Response"); + p2p->ssid_len = 0; + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + if (!msg.config_timeout) { + p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Response"); +#ifdef CONFIG_P2P_STRICT + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; +#endif /* CONFIG_P2P_STRICT */ + } else { + dev->go_timeout = msg.config_timeout[0]; + dev->client_timeout = msg.config_timeout[1]; + } + + if (!msg.operating_channel && !go) { + /* + * Note: P2P Client may omit Operating Channel attribute to + * indicate it does not have a preference. + */ + p2p_dbg(p2p, "No Operating Channel attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + if (!msg.channel_list) { + p2p_dbg(p2p, "No Channel List attribute received"); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + if (p2p_peer_channels(p2p, dev, msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, "No common channels found"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + if (msg.operating_channel) { + dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3], + msg.operating_channel[4]); + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", + dev->oper_freq); + } else + dev->oper_freq = 0; + + switch (msg.dev_password_id) { + case DEV_PW_REGISTRAR_SPECIFIED: + p2p_dbg(p2p, "PIN from peer Display"); + if (dev->wps_method != WPS_PIN_KEYPAD) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_USER_SPECIFIED: + p2p_dbg(p2p, "Peer entered PIN on Keypad"); + if (dev->wps_method != WPS_PIN_DISPLAY) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + case DEV_PW_PUSHBUTTON: + p2p_dbg(p2p, "Peer using pushbutton"); + if (dev->wps_method != WPS_PBC) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + default: + if (msg.dev_password_id && + msg.dev_password_id == dev->oob_pw_id) { + p2p_dbg(p2p, "Peer using NFC"); + if (dev->wps_method != WPS_NFC) { + p2p_dbg(p2p, "We have wps_method=%s -> incompatible", + p2p_wps_method_str(dev->wps_method)); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + break; + } + p2p_dbg(p2p, "Unsupported Device Password ID %d", + msg.dev_password_id); + status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; + goto fail; + } + + if (go && p2p_go_select_channel(p2p, dev, &status) < 0) + goto fail; + + p2p_set_state(p2p, P2P_GO_NEG); + p2p_clear_timeout(p2p); + + p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); + os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); + +fail: + conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status, + msg.operating_channel, go); + p2p_parse_free(&msg); + if (conf == NULL) + return; + p2p_dbg(p2p, "Sending GO Negotiation Confirm"); + if (status == P2P_SC_SUCCESS) { + p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; + dev->go_state = go ? LOCAL_GO : REMOTE_GO; + } else + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (rx_freq > 0) + freq = rx_freq; + else + freq = dev->listen_freq; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, + wpabuf_head(conf), wpabuf_len(conf), 200) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + p2p_go_neg_failed(p2p, dev, -1); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } + wpabuf_free(conf); + if (status != P2P_SC_SUCCESS) { + p2p_dbg(p2p, "GO Negotiation failed"); + p2p_go_neg_failed(p2p, dev, status); + } +} + + +void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_device *dev; + struct p2p_message msg; + + p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR, + MAC2STR(sa)); + dev = p2p_get_device(p2p, sa); + if (dev == NULL || dev->wps_method == WPS_NOT_READY || + dev != p2p->go_neg_peer) { + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, + MAC2STR(sa)); + return; + } + + if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) { + p2p_dbg(p2p, "Stopped waiting for TX status on GO Negotiation Response since we already received Confirmation"); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { + p2p_dbg(p2p, "Was not expecting GO Negotiation Confirm - ignore"); + p2p_parse_free(&msg); + return; + } + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + + if (msg.dialog_token != dev->dialog_token) { + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); + return; + } + + if (!msg.status) { + p2p_dbg(p2p, "No Status attribute received"); + p2p_parse_free(&msg); + return; + } + if (*msg.status) { + p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); + p2p_go_neg_failed(p2p, dev, *msg.status); + p2p_parse_free(&msg); + return; + } + + if (dev->go_state == REMOTE_GO && msg.group_id) { + /* Store SSID for Provisioning step */ + p2p->ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); + } else if (dev->go_state == REMOTE_GO) { + p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation"); + p2p->ssid_len = 0; + p2p_go_neg_failed(p2p, dev, P2P_SC_FAIL_INVALID_PARAMS); + p2p_parse_free(&msg); + return; + } + + if (!msg.operating_channel) { + p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation"); +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + } else if (dev->go_state == REMOTE_GO) { + int oper_freq = p2p_channel_to_freq(msg.operating_channel[3], + msg.operating_channel[4]); + if (oper_freq != dev->oper_freq) { + p2p_dbg(p2p, "Updated peer (GO) operating channel preference from %d MHz to %d MHz", + dev->oper_freq, oper_freq); + dev->oper_freq = oper_freq; + } + } + + if (!msg.channel_list) { + p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation"); +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + } + + p2p_parse_free(&msg); + + if (dev->go_state == UNKNOWN_GO) { + /* + * This should not happen since GO negotiation has already + * been completed. + */ + p2p_dbg(p2p, "Unexpected GO Neg state - do not know which end becomes GO"); + return; + } + + /* + * The peer could have missed our ctrl::ack frame for GO Negotiation + * Confirm and continue retransmitting the frame. To reduce the + * likelihood of the peer not getting successful TX status for the + * GO Negotiation Confirm frame, wait a short time here before starting + * the group so that we will remain on the current channel to + * acknowledge any possible retransmission from the peer. + */ + p2p_dbg(p2p, "20 ms wait on current channel before starting group"); + os_sleep(0, 20000); + + p2p_go_complete(p2p, dev); +} diff --git a/contrib/hostapd/src/p2p/p2p_group.c b/contrib/hostapd/src/p2p/p2p_group.c new file mode 100644 index 0000000000..395ca089a8 --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p_group.c @@ -0,0 +1,1015 @@ +/* + * Wi-Fi Direct - P2P group operations + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "wps/wps_defs.h" +#include "wps/wps_i.h" +#include "p2p_i.h" +#include "p2p.h" + + +struct p2p_group_member { + struct p2p_group_member *next; + u8 addr[ETH_ALEN]; /* P2P Interface Address */ + u8 dev_addr[ETH_ALEN]; /* P2P Device Address */ + struct wpabuf *p2p_ie; + struct wpabuf *wfd_ie; + struct wpabuf *client_info; + u8 dev_capab; +}; + +/** + * struct p2p_group - Internal P2P module per-group data + */ +struct p2p_group { + struct p2p_data *p2p; + struct p2p_group_config *cfg; + struct p2p_group_member *members; + unsigned int num_members; + int group_formation; + int beacon_update; + struct wpabuf *noa; + struct wpabuf *wfd_ie; +}; + + +struct p2p_group * p2p_group_init(struct p2p_data *p2p, + struct p2p_group_config *config) +{ + struct p2p_group *group, **groups; + + group = os_zalloc(sizeof(*group)); + if (group == NULL) + return NULL; + + groups = os_realloc_array(p2p->groups, p2p->num_groups + 1, + sizeof(struct p2p_group *)); + if (groups == NULL) { + os_free(group); + return NULL; + } + groups[p2p->num_groups++] = group; + p2p->groups = groups; + + group->p2p = p2p; + group->cfg = config; + group->group_formation = 1; + group->beacon_update = 1; + p2p_group_update_ies(group); + group->cfg->idle_update(group->cfg->cb_ctx, 1); + + return group; +} + + +static void p2p_group_free_member(struct p2p_group_member *m) +{ + wpabuf_free(m->wfd_ie); + wpabuf_free(m->p2p_ie); + wpabuf_free(m->client_info); + os_free(m); +} + + +static void p2p_group_free_members(struct p2p_group *group) +{ + struct p2p_group_member *m, *prev; + m = group->members; + group->members = NULL; + group->num_members = 0; + while (m) { + prev = m; + m = m->next; + p2p_group_free_member(prev); + } +} + + +void p2p_group_deinit(struct p2p_group *group) +{ + size_t g; + struct p2p_data *p2p; + + if (group == NULL) + return; + + p2p = group->p2p; + + for (g = 0; g < p2p->num_groups; g++) { + if (p2p->groups[g] == group) { + while (g + 1 < p2p->num_groups) { + p2p->groups[g] = p2p->groups[g + 1]; + g++; + } + p2p->num_groups--; + break; + } + } + + p2p_group_free_members(group); + os_free(group->cfg); + wpabuf_free(group->noa); + wpabuf_free(group->wfd_ie); + os_free(group); +} + + +static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m) +{ + if (m->client_info == NULL) + return; + if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1) + return; + wpabuf_put_buf(ie, m->client_info); +} + + +static void p2p_group_add_common_ies(struct p2p_group *group, + struct wpabuf *ie) +{ + u8 dev_capab = group->p2p->dev_capab, group_capab = 0; + + /* P2P Capability */ + dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; + group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER; + if (group->cfg->persistent_group) { + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; + if (group->cfg->persistent_group == 2) + group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; + } + if (group->p2p->cfg->p2p_intra_bss) + group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; + if (group->group_formation) + group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION; + if (group->p2p->cross_connect) + group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; + if (group->num_members >= group->cfg->max_clients) + group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT; + group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION; + p2p_buf_add_capability(ie, dev_capab, group_capab); +} + + +static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa) +{ + if (noa == NULL) + return; + /* Notice of Absence */ + wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE); + wpabuf_put_le16(ie, wpabuf_len(noa)); + wpabuf_put_buf(ie, noa); +} + + +static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems) +{ + struct wpabuf *ie; + const u8 *pos, *end; + size_t len; + + if (subelems == NULL) + return NULL; + + len = wpabuf_len(subelems) + 100; + + ie = wpabuf_alloc(len); + if (ie == NULL) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + 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, P2P_IE_VENDOR_TYPE); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + return ie; +} + + +static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) +{ + struct wpabuf *ie; + u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->p2p->wfd_ie_beacon) + extra = wpabuf_len(group->p2p->wfd_ie_beacon); +#endif /* CONFIG_WIFI_DISPLAY */ + + ie = wpabuf_alloc(257 + extra); + if (ie == NULL) + return NULL; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->p2p->wfd_ie_beacon) + wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon); +#endif /* CONFIG_WIFI_DISPLAY */ + + len = p2p_buf_add_ie_hdr(ie); + p2p_group_add_common_ies(group, ie); + p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr); + p2p_group_add_noa(ie, group->noa); + p2p_buf_update_ie_hdr(ie, len); + + return ie; +} + + +#ifdef CONFIG_WIFI_DISPLAY + +struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g) +{ + return g->wfd_ie; +} + + +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems) +{ + struct wpabuf *ie; + const u8 *pos, *end; + + if (subelems == NULL) + return NULL; + + ie = wpabuf_alloc(wpabuf_len(subelems) + 100); + if (ie == NULL) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + 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, WFD_IE_VENDOR_TYPE); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + return ie; +} + + +static int wifi_display_add_dev_info_descr(struct wpabuf *buf, + struct p2p_group_member *m) +{ + const u8 *pos, *end; + const u8 *dev_info = NULL; + const u8 *assoc_bssid = NULL; + const u8 *coupled_sink = NULL; + u8 zero_addr[ETH_ALEN]; + + if (m->wfd_ie == NULL) + return 0; + + os_memset(zero_addr, 0, ETH_ALEN); + pos = wpabuf_head_u8(m->wfd_ie); + end = pos + wpabuf_len(m->wfd_ie); + while (pos + 1 < end) { + u8 id; + u16 len; + + id = *pos++; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) + break; + + switch (id) { + case WFD_SUBELEM_DEVICE_INFO: + if (len < 6) + break; + dev_info = pos; + break; + case WFD_SUBELEM_ASSOCIATED_BSSID: + if (len < ETH_ALEN) + break; + assoc_bssid = pos; + break; + case WFD_SUBELEM_COUPLED_SINK: + if (len < 1 + ETH_ALEN) + break; + coupled_sink = pos; + break; + } + + pos += len; + } + + if (dev_info == NULL) + return 0; + + wpabuf_put_u8(buf, 23); + wpabuf_put_data(buf, m->dev_addr, ETH_ALEN); + if (assoc_bssid) + wpabuf_put_data(buf, assoc_bssid, ETH_ALEN); + else + wpabuf_put_data(buf, zero_addr, ETH_ALEN); + wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */ + wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */ + if (coupled_sink) { + wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN); + } else { + wpabuf_put_u8(buf, 0); + wpabuf_put_data(buf, zero_addr, ETH_ALEN); + } + + return 1; +} + + +static struct wpabuf * +wifi_display_build_go_ie(struct p2p_group *group) +{ + struct wpabuf *wfd_subelems, *wfd_ie; + struct p2p_group_member *m; + u8 *len; + unsigned int count = 0; + + if (!group->p2p->wfd_ie_probe_resp) + return NULL; + + wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) + + group->num_members * 24 + 100); + if (wfd_subelems == NULL) + return NULL; + if (group->p2p->wfd_dev_info) + wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info); + if (group->p2p->wfd_assoc_bssid) + wpabuf_put_buf(wfd_subelems, + group->p2p->wfd_assoc_bssid); + if (group->p2p->wfd_coupled_sink_info) + wpabuf_put_buf(wfd_subelems, + group->p2p->wfd_coupled_sink_info); + + /* Build WFD Session Info */ + wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO); + len = wpabuf_put(wfd_subelems, 2); + m = group->members; + while (m) { + if (wifi_display_add_dev_info_descr(wfd_subelems, m)) + count++; + m = m->next; + } + + if (count == 0) { + /* No Wi-Fi Display clients - do not include subelement */ + wfd_subelems->used -= 3; + } else { + WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len - + 2); + p2p_dbg(group->p2p, "WFD: WFD Session Info: %u descriptors", + count); + } + + wfd_ie = wifi_display_encaps(wfd_subelems); + wpabuf_free(wfd_subelems); + + return wfd_ie; +} + +static void wifi_display_group_update(struct p2p_group *group) +{ + wpabuf_free(group->wfd_ie); + group->wfd_ie = wifi_display_build_go_ie(group); +} + +#endif /* CONFIG_WIFI_DISPLAY */ + + +void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, + int max_clients) +{ + u8 *group_info; + int count = 0; + struct p2p_group_member *m; + + p2p_dbg(group->p2p, "* P2P Group Info"); + group_info = wpabuf_put(buf, 0); + wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO); + wpabuf_put_le16(buf, 0); /* Length to be filled */ + for (m = group->members; m; m = m->next) { + p2p_client_info(buf, m); + count++; + if (max_clients >= 0 && count >= max_clients) + break; + } + WPA_PUT_LE16(group_info + 1, + (u8 *) wpabuf_put(buf, 0) - group_info - 3); +} + + +void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf) +{ + p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid, + group->cfg->ssid_len); +} + + +static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) +{ + struct wpabuf *p2p_subelems, *ie; + + p2p_subelems = wpabuf_alloc(500); + if (p2p_subelems == NULL) + return NULL; + + p2p_group_add_common_ies(group, p2p_subelems); + p2p_group_add_noa(p2p_subelems, group->noa); + + /* P2P Device Info */ + p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL); + + /* P2P Group Info: Only when at least one P2P Client is connected */ + if (group->members) + p2p_buf_add_group_info(group, p2p_subelems, -1); + + ie = p2p_group_encaps_probe_resp(p2p_subelems); + wpabuf_free(p2p_subelems); + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) { + struct wpabuf *wfd = wpabuf_dup(group->wfd_ie); + ie = wpabuf_concat(wfd, ie); + } +#endif /* CONFIG_WIFI_DISPLAY */ + + return ie; +} + + +void p2p_group_update_ies(struct p2p_group *group) +{ + struct wpabuf *beacon_ie; + struct wpabuf *probe_resp_ie; + +#ifdef CONFIG_WIFI_DISPLAY + wifi_display_group_update(group); +#endif /* CONFIG_WIFI_DISPLAY */ + + probe_resp_ie = p2p_group_build_probe_resp_ie(group); + if (probe_resp_ie == NULL) + return; + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE", + probe_resp_ie); + + if (group->beacon_update) { + beacon_ie = p2p_group_build_beacon_ie(group); + if (beacon_ie) + group->beacon_update = 0; + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE", + beacon_ie); + } else + beacon_ie = NULL; + + group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie); +} + + +/** + * p2p_build_client_info - Build P2P Client Info Descriptor + * @addr: MAC address of the peer device + * @p2p_ie: P2P IE from (Re)Association Request + * @dev_capab: Buffer for returning Device Capability + * @dev_addr: Buffer for returning P2P Device Address + * Returns: P2P Client Info Descriptor or %NULL on failure + * + * This function builds P2P Client Info Descriptor based on the information + * available from (Re)Association Request frame. Group owner can use this to + * build the P2P Group Info attribute for Probe Response frames. + */ +static struct wpabuf * p2p_build_client_info(const u8 *addr, + struct wpabuf *p2p_ie, + u8 *dev_capab, u8 *dev_addr) +{ + const u8 *spos; + struct p2p_message msg; + u8 *len_pos; + struct wpabuf *buf; + + if (p2p_ie == NULL) + return NULL; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg) || + msg.capability == NULL || msg.p2p_device_info == NULL) + return NULL; + + buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len); + if (buf == NULL) + return NULL; + + *dev_capab = msg.capability[0]; + os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN); + + spos = msg.p2p_device_info; /* P2P Device address */ + + /* P2P Client Info Descriptor */ + /* Length to be set */ + len_pos = wpabuf_put(buf, 1); + /* P2P Device address */ + wpabuf_put_data(buf, spos, ETH_ALEN); + /* P2P Interface address */ + wpabuf_put_data(buf, addr, ETH_ALEN); + /* Device Capability Bitmap */ + wpabuf_put_u8(buf, msg.capability[0]); + /* + * Config Methods, Primary Device Type, Number of Secondary Device + * Types, Secondary Device Type List, Device Name copied from + * Device Info + */ + wpabuf_put_data(buf, spos + ETH_ALEN, + msg.p2p_device_info_len - ETH_ALEN); + + *len_pos = wpabuf_len(buf) - 1; + + + return buf; +} + + +static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr) +{ + struct p2p_group_member *m, *prev; + + if (group == NULL) + return 0; + + m = group->members; + prev = NULL; + while (m) { + if (os_memcmp(m->addr, addr, ETH_ALEN) == 0) + break; + prev = m; + m = m->next; + } + + if (m == NULL) + return 0; + + if (prev) + prev->next = m->next; + else + group->members = m->next; + p2p_group_free_member(m); + group->num_members--; + + return 1; +} + + +int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, + const u8 *ie, size_t len) +{ + struct p2p_group_member *m; + + if (group == NULL) + return -1; + + p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0); + + m = os_zalloc(sizeof(*m)); + if (m == NULL) + return -1; + os_memcpy(m->addr, addr, ETH_ALEN); + m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE); + if (m->p2p_ie) { + m->client_info = p2p_build_client_info(addr, m->p2p_ie, + &m->dev_capab, + m->dev_addr); + } +#ifdef CONFIG_WIFI_DISPLAY + m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE); +#endif /* CONFIG_WIFI_DISPLAY */ + + p2p_group_remove_member(group, addr); + + m->next = group->members; + group->members = m; + group->num_members++; + p2p_dbg(group->p2p, "Add client " MACSTR + " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u", + MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0, + m->client_info ? 1 : 0, + group->num_members, group->cfg->max_clients); + if (group->num_members == group->cfg->max_clients) + group->beacon_update = 1; + p2p_group_update_ies(group); + if (group->num_members == 1) + group->cfg->idle_update(group->cfg->cb_ctx, 0); + + return 0; +} + + +struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) +{ + struct wpabuf *resp; + u8 *rlen; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + extra = wpabuf_len(group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + /* + * (Re)Association Response - P2P IE + * Status attribute (shall be present when association request is + * denied) + * Extended Listen Timing (may be present) + */ + resp = wpabuf_alloc(20 + extra); + if (resp == NULL) + return NULL; + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) + wpabuf_put_buf(resp, group->wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + rlen = p2p_buf_add_ie_hdr(resp); + if (status != P2P_SC_SUCCESS) + p2p_buf_add_status(resp, status); + p2p_buf_update_ie_hdr(resp, rlen); + + return resp; +} + + +void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr) +{ + if (p2p_group_remove_member(group, addr)) { + p2p_dbg(group->p2p, "Remove client " MACSTR + " from group; num_members=%u/%u", + MAC2STR(addr), group->num_members, + group->cfg->max_clients); + if (group->num_members == group->cfg->max_clients - 1) + group->beacon_update = 1; + p2p_group_update_ies(group); + if (group->num_members == 0) + group->cfg->idle_update(group->cfg->cb_ctx, 1); + } +} + + +/** + * p2p_match_dev_type_member - Match client device type with requested type + * @m: Group member + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs) + * Returns: 1 on match, 0 on mismatch + * + * This function can be used to match the Requested Device Type attribute in + * WPS IE with the device types of a group member for deciding whether a GO + * should reply to a Probe Request frame. + */ +static int p2p_match_dev_type_member(struct p2p_group_member *m, + struct wpabuf *wps) +{ + const u8 *pos, *end; + struct wps_parse_attr attr; + u8 num_sec; + + if (m->client_info == NULL || wps == NULL) + return 0; + + pos = wpabuf_head(m->client_info); + end = pos + wpabuf_len(m->client_info); + + pos += 1 + 2 * ETH_ALEN + 1 + 2; + if (end - pos < WPS_DEV_TYPE_LEN + 1) + return 0; + + if (wps_parse_msg(wps, &attr)) + return 1; /* assume no Requested Device Type attributes */ + + if (attr.num_req_dev_type == 0) + return 1; /* no Requested Device Type attributes -> match */ + + if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type)) + return 1; /* Match with client Primary Device Type */ + + pos += WPS_DEV_TYPE_LEN; + num_sec = *pos++; + if (end - pos < num_sec * WPS_DEV_TYPE_LEN) + return 0; + while (num_sec > 0) { + num_sec--; + if (dev_type_list_match(pos, attr.req_dev_type, + attr.num_req_dev_type)) + return 1; /* Match with client Secondary Device Type */ + pos += WPS_DEV_TYPE_LEN; + } + + /* No matching device type found */ + return 0; +} + + +int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps) +{ + struct p2p_group_member *m; + + if (p2p_match_dev_type(group->p2p, wps)) + return 1; /* Match with own device type */ + + for (m = group->members; m; m = m->next) { + if (p2p_match_dev_type_member(m, wps)) + return 1; /* Match with group client device type */ + } + + /* No match with Requested Device Type */ + return 0; +} + + +int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p) +{ + struct p2p_group_member *m; + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p, &msg)) + return 1; /* Failed to parse - assume no filter on Device ID */ + + if (!msg.device_id) + return 1; /* No filter on Device ID */ + + if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0) + return 1; /* Match with our P2P Device Address */ + + for (m = group->members; m; m = m->next) { + if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0) + return 1; /* Match with group client P2P Device Address */ + } + + /* No match with Device ID */ + return 0; +} + + +void p2p_group_notif_formation_done(struct p2p_group *group) +{ + if (group == NULL) + return; + group->group_formation = 0; + group->beacon_update = 1; + p2p_group_update_ies(group); +} + + +int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa, + size_t noa_len) +{ + if (noa == NULL) { + wpabuf_free(group->noa); + group->noa = NULL; + } else { + if (group->noa) { + if (wpabuf_size(group->noa) >= noa_len) { + group->noa->used = 0; + wpabuf_put_data(group->noa, noa, noa_len); + } else { + wpabuf_free(group->noa); + group->noa = NULL; + } + } + + if (!group->noa) { + group->noa = wpabuf_alloc_copy(noa, noa_len); + if (group->noa == NULL) + return -1; + } + } + + group->beacon_update = 1; + p2p_group_update_ies(group); + return 0; +} + + +static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group, + const u8 *dev_id) +{ + struct p2p_group_member *m; + + for (m = group->members; m; m = m->next) { + if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0) + return m; + } + + return NULL; +} + + +static struct p2p_group_member * p2p_group_get_client_iface( + struct p2p_group *group, const u8 *interface_addr) +{ + struct p2p_group_member *m; + + for (m = group->members; m; m = m->next) { + if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0) + return m; + } + + return NULL; +} + + +const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr) +{ + struct p2p_group_member *m; + + if (group == NULL) + return NULL; + m = p2p_group_get_client_iface(group, addr); + if (m && !is_zero_ether_addr(m->dev_addr)) + return m->dev_addr; + return NULL; +} + + +static struct wpabuf * p2p_build_go_disc_req(void) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(100); + if (buf == NULL) + return NULL; + + p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0); + + return buf; +} + + +int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, + const u8 *searching_dev, int rx_freq) +{ + struct p2p_group_member *m; + struct wpabuf *req; + struct p2p_data *p2p = group->p2p; + int freq; + + m = p2p_group_get_client(group, dev_id); + if (m == NULL || m->client_info == NULL) { + p2p_dbg(group->p2p, "Requested client was not in this group " + MACSTR, MAC2STR(group->cfg->interface_addr)); + return -1; + } + + if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + p2p_dbg(group->p2p, "Requested client does not support client discoverability"); + return -1; + } + + p2p_dbg(group->p2p, "Schedule GO Discoverability Request to be sent to " + MACSTR, MAC2STR(dev_id)); + + req = p2p_build_go_disc_req(); + if (req == NULL) + return -1; + + /* TODO: Should really use group operating frequency here */ + freq = rx_freq; + + p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ; + if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr, + group->cfg->interface_addr, + group->cfg->interface_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) + { + p2p_dbg(p2p, "Failed to send Action frame"); + } + + wpabuf_free(req); + + return 0; +} + + +const u8 * p2p_group_get_interface_addr(struct p2p_group *group) +{ + return group->cfg->interface_addr; +} + + +u8 p2p_group_presence_req(struct p2p_group *group, + const u8 *client_interface_addr, + const u8 *noa, size_t noa_len) +{ + struct p2p_group_member *m; + u8 curr_noa[50]; + int curr_noa_len; + + m = p2p_group_get_client_iface(group, client_interface_addr); + if (m == NULL || m->client_info == NULL) { + p2p_dbg(group->p2p, "Client was not in this group"); + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + } + + wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len); + + if (group->p2p->cfg->get_noa) + curr_noa_len = group->p2p->cfg->get_noa( + group->p2p->cfg->cb_ctx, group->cfg->interface_addr, + curr_noa, sizeof(curr_noa)); + else + curr_noa_len = -1; + if (curr_noa_len < 0) + p2p_dbg(group->p2p, "Failed to fetch current NoA"); + else if (curr_noa_len == 0) + p2p_dbg(group->p2p, "No NoA being advertized"); + else + wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa, + curr_noa_len); + + /* TODO: properly process request and store copy */ + if (curr_noa_len > 0 || curr_noa_len == -1) + return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; + + return P2P_SC_SUCCESS; +} + + +unsigned int p2p_get_group_num_members(struct p2p_group *group) +{ + return group->num_members; +} + + +const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next) +{ + struct p2p_group_member *iter = *next; + + if (!iter) + iter = group->members; + else + iter = iter->next; + + *next = iter; + + if (!iter) + return NULL; + + return iter->addr; +} + + +int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr) +{ + struct p2p_group_member *m; + + for (m = group->members; m; m = m->next) { + if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0) + return 1; + } + + return 0; +} + + +int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, + size_t group_id_len) +{ + if (group_id_len != ETH_ALEN + group->cfg->ssid_len) + return 0; + if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0) + return 0; + return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid, + group->cfg->ssid_len) == 0; +} + + +void p2p_group_force_beacon_update_ies(struct p2p_group *group) +{ + group->beacon_update = 1; + p2p_group_update_ies(group); +} + + +int p2p_group_get_freq(struct p2p_group *group) +{ + return group->cfg->freq; +} diff --git a/contrib/hostapd/src/p2p/p2p_i.h b/contrib/hostapd/src/p2p/p2p_i.h new file mode 100644 index 0000000000..6ebaa846d6 --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p_i.h @@ -0,0 +1,759 @@ +/* + * P2P - Internal definitions for P2P module + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef P2P_I_H +#define P2P_I_H + +#include "utils/list.h" +#include "p2p.h" + +enum p2p_role_indication; + +enum p2p_go_state { + UNKNOWN_GO, + LOCAL_GO, + REMOTE_GO +}; + +/** + * struct p2p_device - P2P Device data (internal to P2P module) + */ +struct p2p_device { + struct dl_list list; + struct os_reltime last_seen; + int listen_freq; + int oob_go_neg_freq; + enum p2p_wps_method wps_method; + u16 oob_pw_id; + + struct p2p_peer_info info; + + /* + * If the peer was discovered based on an interface address (e.g., GO + * from Beacon/Probe Response), the interface address is stored here. + * p2p_device_addr must still be set in such a case to the unique + * identifier for the P2P Device. + */ + u8 interface_addr[ETH_ALEN]; + + /* + * P2P Device Address of the GO in whose group this P2P Device is a + * client. + */ + u8 member_in_go_dev[ETH_ALEN]; + + /* + * P2P Interface Address of the GO in whose group this P2P Device is a + * client. + */ + u8 member_in_go_iface[ETH_ALEN]; + + int go_neg_req_sent; + enum p2p_go_state go_state; + u8 dialog_token; + u8 tie_breaker; + u8 intended_addr[ETH_ALEN]; + + char country[3]; + struct p2p_channels channels; + int oper_freq; + u8 oper_ssid[32]; + size_t oper_ssid_len; + + /** + * req_config_methods - Pending provision discovery methods + */ + u16 req_config_methods; + + /** + * wps_prov_info - Stored provisioning WPS config method + * + * This is used to store pending WPS config method between Provisioning + * Discovery and connection to a running group. + */ + u16 wps_prov_info; + +#define P2P_DEV_PROBE_REQ_ONLY BIT(0) +#define P2P_DEV_REPORTED BIT(1) +#define P2P_DEV_NOT_YET_READY BIT(2) +#define P2P_DEV_SD_INFO BIT(3) +#define P2P_DEV_SD_SCHEDULE BIT(4) +#define P2P_DEV_PD_PEER_DISPLAY BIT(5) +#define P2P_DEV_PD_PEER_KEYPAD BIT(6) +#define P2P_DEV_USER_REJECTED BIT(7) +#define P2P_DEV_PEER_WAITING_RESPONSE BIT(8) +#define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9) +#define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10) +#define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11) +#define P2P_DEV_GROUP_CLIENT_ONLY BIT(12) +#define P2P_DEV_FORCE_FREQ BIT(13) +#define P2P_DEV_PD_FOR_JOIN BIT(14) +#define P2P_DEV_REPORTED_ONCE BIT(15) +#define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16) +#define P2P_DEV_PD_BEFORE_GO_NEG BIT(17) +#define P2P_DEV_NO_PREF_CHAN BIT(18) + unsigned int flags; + + int status; /* enum p2p_status_code */ + unsigned int wait_count; + unsigned int connect_reqs; + unsigned int invitation_reqs; + + u16 ext_listen_period; + u16 ext_listen_interval; + + u8 go_timeout; + u8 client_timeout; +}; + +struct p2p_sd_query { + struct p2p_sd_query *next; + u8 peer[ETH_ALEN]; + int for_all_peers; + int wsd; /* Wi-Fi Display Service Discovery Request */ + struct wpabuf *tlvs; +}; + +struct p2p_pending_action_tx { + unsigned int freq; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + size_t len; + unsigned int wait_time; + /* Followed by len octets of the frame */ +}; + +/** + * struct p2p_data - P2P module data (internal to P2P module) + */ +struct p2p_data { + /** + * cfg - P2P module configuration + * + * This is included in the same memory allocation with the + * struct p2p_data and as such, must not be freed separately. + */ + struct p2p_config *cfg; + + /** + * state - The current P2P state + */ + enum p2p_state { + /** + * P2P_IDLE - Idle + */ + P2P_IDLE, + + /** + * P2P_SEARCH - Search (Device Discovery) + */ + P2P_SEARCH, + + /** + * P2P_CONNECT - Trying to start GO Negotiation + */ + P2P_CONNECT, + + /** + * P2P_CONNECT_LISTEN - Listen during GO Negotiation start + */ + P2P_CONNECT_LISTEN, + + /** + * P2P_GO_NEG - In GO Negotiation + */ + P2P_GO_NEG, + + /** + * P2P_LISTEN_ONLY - Listen only + */ + P2P_LISTEN_ONLY, + + /** + * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg + */ + P2P_WAIT_PEER_CONNECT, + + /** + * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg + */ + P2P_WAIT_PEER_IDLE, + + /** + * P2P_SD_DURING_FIND - Service Discovery during find + */ + P2P_SD_DURING_FIND, + + /** + * P2P_PROVISIONING - Provisioning (during group formation) + */ + P2P_PROVISIONING, + + /** + * P2P_PD_DURING_FIND - Provision Discovery during find + */ + P2P_PD_DURING_FIND, + + /** + * P2P_INVITE - Trying to start Invite + */ + P2P_INVITE, + + /** + * P2P_INVITE_LISTEN - Listen during Invite + */ + P2P_INVITE_LISTEN, + } state; + + /** + * min_disc_int - minDiscoverableInterval + */ + int min_disc_int; + + /** + * max_disc_int - maxDiscoverableInterval + */ + int max_disc_int; + + /** + * max_disc_tu - Maximum number of TUs for discoverable interval + */ + int max_disc_tu; + + /** + * devices - List of known P2P Device peers + */ + struct dl_list devices; + + /** + * go_neg_peer - Pointer to GO Negotiation peer + */ + struct p2p_device *go_neg_peer; + + /** + * invite_peer - Pointer to Invite peer + */ + struct p2p_device *invite_peer; + + const u8 *invite_go_dev_addr; + u8 invite_go_dev_addr_buf[ETH_ALEN]; + int invite_dev_pw_id; + + /** + * sd_peer - Pointer to Service Discovery peer + */ + struct p2p_device *sd_peer; + + /** + * sd_query - Pointer to Service Discovery query + */ + struct p2p_sd_query *sd_query; + + /* GO Negotiation data */ + + /** + * intended_addr - Local Intended P2P Interface Address + * + * This address is used during group owner negotiation as the Intended + * P2P Interface Address and the group interface will be created with + * address as the local address in case of successfully completed + * negotiation. + */ + u8 intended_addr[ETH_ALEN]; + + /** + * go_intent - Local GO Intent to be used during GO Negotiation + */ + u8 go_intent; + + /** + * next_tie_breaker - Next tie-breaker value to use in GO Negotiation + */ + u8 next_tie_breaker; + + /** + * ssid - Selected SSID for GO Negotiation (if local end will be GO) + */ + u8 ssid[32]; + + /** + * ssid_len - ssid length in octets + */ + size_t ssid_len; + + /** + * ssid_set - Whether SSID is already set for GO Negotiation + */ + int ssid_set; + + /** + * Regulatory class for own operational channel + */ + u8 op_reg_class; + + /** + * op_channel - Own operational channel + */ + u8 op_channel; + + /** + * channels - Own supported regulatory classes and channels + * + * List of supposerted channels per regulatory class. The regulatory + * classes are defined in IEEE Std 802.11-2007 Annex J and the + * numbering of the clases depends on the configured country code. + */ + struct p2p_channels channels; + + struct wpa_freq_range_list no_go_freq; + + enum p2p_pending_action_state { + P2P_NO_PENDING_ACTION, + P2P_PENDING_GO_NEG_REQUEST, + P2P_PENDING_GO_NEG_RESPONSE, + P2P_PENDING_GO_NEG_RESPONSE_FAILURE, + P2P_PENDING_GO_NEG_CONFIRM, + P2P_PENDING_SD, + P2P_PENDING_PD, + P2P_PENDING_INVITATION_REQUEST, + P2P_PENDING_INVITATION_RESPONSE, + P2P_PENDING_DEV_DISC_REQUEST, + P2P_PENDING_DEV_DISC_RESPONSE, + P2P_PENDING_GO_DISC_REQ + } pending_action_state; + + unsigned int pending_listen_freq; + unsigned int pending_listen_sec; + unsigned int pending_listen_usec; + + u8 dev_capab; + + int in_listen; + int drv_in_listen; + + /** + * sd_queries - Pending service discovery queries + */ + struct p2p_sd_query *sd_queries; + + /** + * srv_update_indic - Service Update Indicator for local services + */ + u16 srv_update_indic; + + struct wpabuf *sd_resp; /* Fragmented SD response */ + u8 sd_resp_addr[ETH_ALEN]; + u8 sd_resp_dialog_token; + size_t sd_resp_pos; /* Offset in sd_resp */ + u8 sd_frag_id; + + struct wpabuf *sd_rx_resp; /* Reassembled SD response */ + u16 sd_rx_update_indic; + + /* P2P Invitation data */ + enum p2p_invite_role inv_role; + u8 inv_bssid[ETH_ALEN]; + int inv_bssid_set; + u8 inv_ssid[32]; + size_t inv_ssid_len; + u8 inv_sa[ETH_ALEN]; + u8 inv_group_bssid[ETH_ALEN]; + u8 *inv_group_bssid_ptr; + u8 inv_go_dev_addr[ETH_ALEN]; + u8 inv_status; + int inv_op_freq; + int inv_persistent; + + enum p2p_discovery_type find_type; + unsigned int last_p2p_find_timeout; + u8 last_prog_scan_class; + u8 last_prog_scan_chan; + int p2p_scan_running; + enum p2p_after_scan { + P2P_AFTER_SCAN_NOTHING, + P2P_AFTER_SCAN_LISTEN, + P2P_AFTER_SCAN_CONNECT + } start_after_scan; + u8 after_scan_peer[ETH_ALEN]; + struct p2p_pending_action_tx *after_scan_tx; + unsigned int after_scan_tx_in_progress:1; + + /* Requested device types for find/search */ + unsigned int num_req_dev_types; + u8 *req_dev_types; + u8 *find_dev_id; + u8 find_dev_id_buf[ETH_ALEN]; + + struct os_reltime find_start; /* time of last p2p_find start */ + + struct p2p_group **groups; + size_t num_groups; + + struct p2p_device *pending_client_disc_go; + u8 pending_client_disc_addr[ETH_ALEN]; + u8 pending_dev_disc_dialog_token; + u8 pending_dev_disc_addr[ETH_ALEN]; + int pending_dev_disc_freq; + unsigned int pending_client_disc_freq; + + int ext_listen_only; + unsigned int ext_listen_period; + unsigned int ext_listen_interval; + unsigned int ext_listen_interval_sec; + unsigned int ext_listen_interval_usec; + + u8 peer_filter[ETH_ALEN]; + + int cross_connect; + + int best_freq_24; + int best_freq_5; + int best_freq_overall; + int own_freq_preference; + + /** + * wps_vendor_ext - WPS Vendor Extensions to add + */ + struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + + /* + * user_initiated_pd - Whether a PD request is user initiated or not. + */ + u8 user_initiated_pd; + + /* + * Keep track of which peer a given PD request was sent to. + * Used to raise a timeout alert in case there is no response. + */ + u8 pending_pd_devaddr[ETH_ALEN]; + + /* + * Retry counter for provision discovery requests when issued + * in IDLE state. + */ + int pd_retries; + + /** + * pd_force_freq - Forced frequency for PD retries or 0 to auto-select + * + * This is is used during PD retries for join-a-group case to use the + * correct operating frequency determined from a BSS entry for the GO. + */ + int pd_force_freq; + + u8 go_timeout; + u8 client_timeout; + + /* Extra delay in milliseconds between search iterations */ + unsigned int search_delay; + int in_search_delay; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie_beacon; + struct wpabuf *wfd_ie_probe_req; + struct wpabuf *wfd_ie_probe_resp; + struct wpabuf *wfd_ie_assoc_req; + struct wpabuf *wfd_ie_invitation; + struct wpabuf *wfd_ie_prov_disc_req; + struct wpabuf *wfd_ie_prov_disc_resp; + struct wpabuf *wfd_ie_go_neg; + struct wpabuf *wfd_dev_info; + struct wpabuf *wfd_assoc_bssid; + struct wpabuf *wfd_coupled_sink_info; +#endif /* CONFIG_WIFI_DISPLAY */ + + u16 authorized_oob_dev_pw_id; +}; + +/** + * struct p2p_message - Parsed P2P message (or P2P IE) + */ +struct p2p_message { + struct wpabuf *p2p_attributes; + struct wpabuf *wps_attributes; + struct wpabuf *wfd_subelems; + + u8 dialog_token; + + const u8 *capability; + const u8 *go_intent; + const u8 *status; + const u8 *listen_channel; + const u8 *operating_channel; + const u8 *channel_list; + u8 channel_list_len; + const u8 *config_timeout; + const u8 *intended_addr; + const u8 *group_bssid; + const u8 *invitation_flags; + + const u8 *group_info; + size_t group_info_len; + + const u8 *group_id; + size_t group_id_len; + + const u8 *device_id; + + const u8 *manageability; + + const u8 *noa; + size_t noa_len; + + const u8 *ext_listen_timing; + + const u8 *minor_reason_code; + + const u8 *oob_go_neg_channel; + + /* P2P Device Info */ + const u8 *p2p_device_info; + size_t p2p_device_info_len; + const u8 *p2p_device_addr; + const u8 *pri_dev_type; + u8 num_sec_dev_types; + char device_name[33]; + u16 config_methods; + + /* WPS IE */ + u16 dev_password_id; + int dev_password_id_present; + u16 wps_config_methods; + const u8 *wps_pri_dev_type; + const u8 *wps_sec_dev_type_list; + size_t wps_sec_dev_type_list_len; + const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; + size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT]; + 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 *oob_dev_password; + size_t oob_dev_password_len; + + /* DS Parameter Set IE */ + const u8 *ds_params; + + /* SSID IE */ + const u8 *ssid; +}; + + +#define P2P_MAX_GROUP_ENTRIES 50 + +struct p2p_group_info { + unsigned int num_clients; + struct p2p_client_info { + const u8 *p2p_device_addr; + const u8 *p2p_interface_addr; + u8 dev_capab; + u16 config_methods; + const u8 *pri_dev_type; + u8 num_sec_dev_types; + const u8 *sec_dev_types; + const char *dev_name; + size_t dev_name_len; + } client[P2P_MAX_GROUP_ENTRIES]; +}; + + +/* p2p_utils.c */ +int p2p_random(char *buf, size_t len); +int p2p_channel_to_freq(int op_class, int channel); +int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel); +void p2p_channels_intersect(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res); +void p2p_channels_union(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res); +void p2p_channels_remove_freqs(struct p2p_channels *chan, + const struct wpa_freq_range_list *list); +int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, + u8 channel); +void p2p_channels_dump(struct p2p_data *p2p, const char *title, + const struct p2p_channels *chan); +int p2p_channel_select(struct p2p_channels *chans, const int *classes, + u8 *op_class, u8 *op_channel); + +/* p2p_parse.c */ +int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg); +int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg); +int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg); +int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, + size_t p2p_len, struct p2p_message *msg); +void p2p_parse_free(struct p2p_message *msg); +int p2p_attr_text(struct wpabuf *data, char *buf, char *end); +int p2p_group_info_parse(const u8 *gi, size_t gi_len, + struct p2p_group_info *info); + +/* p2p_build.c */ + +struct p2p_noa_desc { + u8 count_type; + u32 duration; + u32 interval; + u32 start_time; +}; + +/* p2p_group.c */ +const u8 * p2p_group_get_interface_addr(struct p2p_group *group); +u8 p2p_group_presence_req(struct p2p_group *group, + const u8 *client_interface_addr, + const u8 *noa, size_t noa_len); +int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, + size_t group_id_len); +void p2p_group_update_ies(struct p2p_group *group); +void p2p_group_force_beacon_update_ies(struct p2p_group *group); +struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g); +void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, + int max_clients); +void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf); +int p2p_group_get_freq(struct p2p_group *group); + + +void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); +void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, + u8 dialog_token); +u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf); +void p2p_buf_add_status(struct wpabuf *buf, u8 status); +void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, + struct p2p_device *peer); +void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr); +void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len); +void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab); +void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent); +void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel); +void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country, + u8 reg_class, u8 channel); +void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country, + struct p2p_channels *chan); +void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout, + u8 client_timeout); +void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr); +void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid); +void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, + const u8 *ssid, size_t ssid_len); +void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags); +void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, + struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2); +void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, + u16 interval); +void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p); +void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, + u8 oper_class, u8 channel, + enum p2p_role_indication role); +int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, + int all_attr); + +/* p2p_sd.c */ +struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, + struct p2p_device *dev); +void p2p_free_sd_queries(struct p2p_data *p2p); +void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev); + +/* p2p_go_neg.c */ +int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, + struct p2p_device *dev, + const u8 *channel_list, size_t channel_list_len); +void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev); +u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method); +void p2p_reselect_channel(struct p2p_data *p2p, + struct p2p_channels *intersection); + +/* p2p_pd.c */ +void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, + int join, int force_freq); +void p2p_reset_pending_pd(struct p2p_data *p2p); + +/* p2p_invitation.c */ +void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *go_dev_addr, int dev_pw_id); +void p2p_invitation_req_cb(struct p2p_data *p2p, int success); +void p2p_invitation_resp_cb(struct p2p_data *p2p, int success); + +/* p2p_dev_disc.c */ +void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq); +void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success); +int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev); +void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success); +void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len); +void p2p_go_disc_req_cb(struct p2p_data *p2p, int success); +void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, + const u8 *data, size_t len, int rx_freq); + +/* p2p.c */ +void p2p_set_state(struct p2p_data *p2p, int new_state); +void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, + unsigned int usec); +void p2p_clear_timeout(struct p2p_data *p2p); +void p2p_continue_find(struct p2p_data *p2p); +struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, + const u8 *addr, + struct p2p_message *msg); +void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, + struct p2p_device *dev, struct p2p_message *msg); +int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, + struct os_reltime *rx_time, int level, const u8 *ies, + size_t ies_len, int scan_res); +struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr); +struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, + const u8 *addr); +void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, + int status); +void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); +int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); +int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], + size_t num_req_dev_type); +struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p); +void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len); +int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, const u8 *buf, + size_t len, unsigned int wait_time); +void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq); +int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, + unsigned int force_freq, unsigned int pref_freq, + int go); +void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) +PRINTF_FORMAT(2, 3); +void p2p_info(struct p2p_data *p2p, const char *fmt, ...) +PRINTF_FORMAT(2, 3); +void p2p_err(struct p2p_data *p2p, const char *fmt, ...) +PRINTF_FORMAT(2, 3); + +#endif /* P2P_I_H */ diff --git a/contrib/hostapd/src/p2p/p2p_invitation.c b/contrib/hostapd/src/p2p/p2p_invitation.c new file mode 100644 index 0000000000..98cfb3303c --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p_invitation.c @@ -0,0 +1,605 @@ +/* + * Wi-Fi Direct - P2P Invitation procedure + * Copyright (c) 2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "p2p_i.h" +#include "p2p.h" + + +static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, + struct p2p_device *peer, + const u8 *go_dev_addr, + int dev_pw_id) +{ + struct wpabuf *buf; + u8 *len; + const u8 *dev_addr; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; + if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (os_memcmp(p2p_group_get_interface_addr(g), + p2p->inv_bssid, ETH_ALEN) != 0) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + peer->dialog_token++; + if (peer->dialog_token == 0) + peer->dialog_token = 1; + p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ, + peer->dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent) + p2p_buf_add_config_timeout(buf, 0, 0); + else + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); + p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? + P2P_INVITATION_FLAGS_TYPE : 0); + if (p2p->inv_role != P2P_INVITE_ROLE_CLIENT || + !(peer->flags & P2P_DEV_NO_PREF_CHAN)) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); + if (p2p->inv_bssid_set) + p2p_buf_add_group_bssid(buf, p2p->inv_bssid); + p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); + if (go_dev_addr) + dev_addr = go_dev_addr; + else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT) + dev_addr = peer->info.p2p_device_addr; + else + dev_addr = p2p->cfg->dev_addr; + p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len); + p2p_buf_add_device_info(buf, p2p, peer); + p2p_buf_update_ie_hdr(buf, len); + +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + if (dev_pw_id >= 0) { + /* WSC IE in Invitation Request for NFC static handover */ + p2p_build_wps_ie(p2p, buf, dev_pw_id, 0); + } + + return buf; +} + + +static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p, + struct p2p_device *peer, + u8 dialog_token, u8 status, + const u8 *group_bssid, + u8 reg_class, u8 channel, + struct p2p_channels *channels) +{ + struct wpabuf *buf; + u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_invitation; + if (wfd_ie && group_bssid) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (os_memcmp(p2p_group_get_interface_addr(g), + group_bssid, ETH_ALEN) != 0) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP, + dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_status(buf, status); + p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */ + if (reg_class && channel) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + reg_class, channel); + if (group_bssid) + p2p_buf_add_group_bssid(buf, group_bssid); + if (channels) + p2p_buf_add_channel_list(buf, p2p->cfg->country, channels); + p2p_buf_update_ie_hdr(buf, len); + +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_device *dev; + struct p2p_message msg; + struct wpabuf *resp = NULL; + u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + int freq; + int go = 0; + u8 group_bssid[ETH_ALEN], *bssid; + int op_freq = 0; + u8 reg_class = 0, channel = 0; + struct p2p_channels intersection, *channels = NULL; + int persistent; + + os_memset(group_bssid, 0, sizeof(group_bssid)); + + p2p_dbg(p2p, "Received Invitation Request from " MACSTR " (freq=%d)", + MAC2STR(sa), rx_freq); + + if (p2p_parse(data, len, &msg)) + return; + + dev = p2p_get_device(p2p, sa); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + p2p_dbg(p2p, "Invitation Request from unknown peer " MACSTR, + MAC2STR(sa)); + + if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, + 0)) { + p2p_dbg(p2p, "Invitation Request add device failed " + MACSTR, MAC2STR(sa)); + status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + goto fail; + } + + dev = p2p_get_device(p2p, sa); + if (dev == NULL) { + p2p_dbg(p2p, "Reject Invitation Request from unknown peer " + MACSTR, MAC2STR(sa)); + status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; + goto fail; + } + } + + if (!msg.group_id || !msg.channel_list) { + p2p_dbg(p2p, "Mandatory attribute missing in Invitation Request from " + MACSTR, MAC2STR(sa)); + status = P2P_SC_FAIL_INVALID_PARAMS; + goto fail; + } + + if (msg.invitation_flags) + persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE; + else { + /* Invitation Flags is a mandatory attribute starting from P2P + * spec 1.06. As a backwards compatibility mechanism, assume + * the request was for a persistent group if the attribute is + * missing. + */ + p2p_dbg(p2p, "Mandatory Invitation Flags attribute missing from Invitation Request"); + persistent = 1; + } + + if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev, + msg.channel_list, msg.channel_list_len) < + 0) { + p2p_dbg(p2p, "No common channels found"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, + &intersection); + + if (p2p->cfg->invitation_process) { + status = p2p->cfg->invitation_process( + p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id, + msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN, + &go, group_bssid, &op_freq, persistent, &intersection, + msg.dev_password_id_present ? msg.dev_password_id : -1); + } + + if (op_freq) { + p2p_dbg(p2p, "Invitation processing forced frequency %d MHz", + op_freq); + if (p2p_freq_to_channel(op_freq, ®_class, &channel) < 0) { + p2p_dbg(p2p, "Unknown forced freq %d MHz from invitation_process()", + op_freq); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + if (!p2p_channels_includes(&intersection, reg_class, channel)) + { + p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction", + op_freq); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + + if (status == P2P_SC_SUCCESS) + channels = &intersection; + } else { + p2p_dbg(p2p, "No forced channel from invitation processing - figure out best one to use"); + + /* Default to own configuration as a starting point */ + p2p->op_reg_class = p2p->cfg->op_reg_class; + p2p->op_channel = p2p->cfg->op_channel; + p2p_dbg(p2p, "Own default op_class %d channel %d", + p2p->op_reg_class, p2p->op_channel); + + /* Use peer preference if specified and compatible */ + if (msg.operating_channel) { + int req_freq; + req_freq = p2p_channel_to_freq( + msg.operating_channel[3], + msg.operating_channel[4]); + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", + req_freq); + if (req_freq > 0 && + p2p_channels_includes(&intersection, + msg.operating_channel[3], + msg.operating_channel[4])) { + p2p->op_reg_class = msg.operating_channel[3]; + p2p->op_channel = msg.operating_channel[4]; + p2p_dbg(p2p, "Use peer preference op_class %d channel %d", + p2p->op_reg_class, p2p->op_channel); + } else { + p2p_dbg(p2p, "Cannot use peer channel preference"); + } + } + + if (!p2p_channels_includes(&intersection, p2p->op_reg_class, + p2p->op_channel)) { + p2p_dbg(p2p, "Initially selected channel (op_class %d channel %d) not in channel intersection - try to reselect", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + p2p_dbg(p2p, "Re-selection result: op_class %d channel %d", + p2p->op_reg_class, p2p->op_channel); + if (!p2p_channels_includes(&intersection, + p2p->op_reg_class, + p2p->op_channel)) { + p2p_dbg(p2p, "Peer does not support selected operating channel (reg_class=%u channel=%u)", + p2p->op_reg_class, p2p->op_channel); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) && + !p2p->cfg->cfg_op_channel) { + p2p_dbg(p2p, "Try to reselect channel selection with peer information received; previously selected op_class %u channel %u", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); + } + + op_freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (op_freq < 0) { + p2p_dbg(p2p, "Unknown operational channel (country=%c%c reg_class=%u channel=%u)", + p2p->cfg->country[0], p2p->cfg->country[1], + p2p->op_reg_class, p2p->op_channel); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + p2p_dbg(p2p, "Selected operating channel - %d MHz", op_freq); + + if (status == P2P_SC_SUCCESS) { + reg_class = p2p->op_reg_class; + channel = p2p->op_channel; + channels = &intersection; + } + } + +fail: + if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid)) + bssid = group_bssid; + else + bssid = NULL; + resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status, + bssid, reg_class, channel, channels); + + if (resp == NULL) + goto out; + + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown regulatory class/channel"); + goto out; + } + + /* + * Store copy of invitation data to be used when processing TX status + * callback for the Acton frame. + */ + os_memcpy(p2p->inv_sa, sa, ETH_ALEN); + if (msg.group_bssid) { + os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN); + p2p->inv_group_bssid_ptr = p2p->inv_group_bssid; + } else + p2p->inv_group_bssid_ptr = NULL; + if (msg.group_id_len - ETH_ALEN <= 32) { + os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, + msg.group_id_len - ETH_ALEN); + p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; + } + os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN); + p2p->inv_status = status; + p2p->inv_op_freq = op_freq; + + p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + } + +out: + wpabuf_free(resp); + p2p_parse_free(&msg); +} + + +void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_device *dev; + struct p2p_message msg; + struct p2p_channels intersection, *channels = NULL; + + p2p_dbg(p2p, "Received Invitation Response from " MACSTR, + MAC2STR(sa)); + + dev = p2p_get_device(p2p, sa); + if (dev == NULL) { + p2p_dbg(p2p, "Ignore Invitation Response from unknown peer " + MACSTR, MAC2STR(sa)); + return; + } + + if (dev != p2p->invite_peer) { + p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer " + MACSTR, MAC2STR(sa)); + return; + } + + if (p2p_parse(data, len, &msg)) + return; + + if (!msg.status) { + p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from " + MACSTR, MAC2STR(sa)); + p2p_parse_free(&msg); + return; + } + + if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) { + p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from " + MACSTR, MAC2STR(sa)); +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + /* Try to survive without peer channel list */ + channels = &p2p->channels; + } else if (!msg.channel_list) { + /* Non-success cases are not required to include Channel List */ + channels = &p2p->channels; + } else if (p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, "No common channels found"); + p2p_parse_free(&msg); + return; + } else { + p2p_channels_intersect(&p2p->channels, &dev->channels, + &intersection); + channels = &intersection; + } + + if (p2p->cfg->invitation_result) { + int freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (freq < 0) + freq = 0; + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, + msg.group_bssid, channels, sa, + freq); + } + + p2p_parse_free(&msg); + + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); + p2p->invite_peer = NULL; +} + + +int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, + const u8 *go_dev_addr, int dev_pw_id) +{ + struct wpabuf *req; + int freq; + + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) + freq = dev->oob_go_neg_freq; + if (freq <= 0) { + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send Invitation Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id); + if (req == NULL) + return -1; + if (p2p->state != P2P_IDLE) + p2p_stop_listen_for_freq(p2p, freq); + p2p_dbg(p2p, "Sending Invitation Request"); + p2p_set_state(p2p, P2P_INVITE); + p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST; + p2p->invite_peer = dev; + dev->invitation_reqs++; + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + /* Use P2P find to recover and retry */ + p2p_set_timeout(p2p, 0, 0); + } + + wpabuf_free(req); + + return 0; +} + + +void p2p_invitation_req_cb(struct p2p_data *p2p, int success) +{ + p2p_dbg(p2p, "Invitation Request TX callback: success=%d", success); + + if (p2p->invite_peer == NULL) { + p2p_dbg(p2p, "No pending Invite"); + return; + } + + /* + * Use P2P find, if needed, to find the other device from its listen + * channel. + */ + p2p_set_state(p2p, P2P_INVITE); + p2p_set_timeout(p2p, 0, success ? 500000 : 100000); +} + + +void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) +{ + p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + + if (!success) + p2p_dbg(p2p, "Assume Invitation Response was actually received by the peer even though Ack was not reported"); + + if (p2p->cfg->invitation_received) { + p2p->cfg->invitation_received(p2p->cfg->cb_ctx, + p2p->inv_sa, + p2p->inv_group_bssid_ptr, + p2p->inv_ssid, p2p->inv_ssid_len, + p2p->inv_go_dev_addr, + p2p->inv_status, + p2p->inv_op_freq); + } +} + + +int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + unsigned int force_freq, const u8 *go_dev_addr, + int persistent_group, unsigned int pref_freq, int dev_pw_id) +{ + struct p2p_device *dev; + + p2p_dbg(p2p, "Request to invite peer " MACSTR " role=%d persistent=%d " + "force_freq=%u", + MAC2STR(peer), role, persistent_group, force_freq); + if (bssid) + p2p_dbg(p2p, "Invitation for BSSID " MACSTR, MAC2STR(bssid)); + if (go_dev_addr) { + p2p_dbg(p2p, "Invitation for GO Device Address " MACSTR, + MAC2STR(go_dev_addr)); + os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN); + p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf; + } else + p2p->invite_go_dev_addr = NULL; + wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID", + ssid, ssid_len); + if (dev_pw_id >= 0) { + p2p_dbg(p2p, "Invitation to use Device Password ID %d", + dev_pw_id); + } + p2p->invite_dev_pw_id = dev_pw_id; + + dev = p2p_get_device(p2p, peer); + if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 && + dev->oob_go_neg_freq <= 0)) { + p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR, + MAC2STR(peer)); + return -1; + } + + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, + role != P2P_INVITE_ROLE_CLIENT) < 0) + return -1; + + if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq && + !pref_freq) + dev->flags |= P2P_DEV_NO_PREF_CHAN; + else + dev->flags &= ~P2P_DEV_NO_PREF_CHAN; + + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { + if (!(dev->info.dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + p2p_dbg(p2p, "Cannot invite a P2P Device " MACSTR + " that is in a group and is not discoverable", + MAC2STR(peer)); + } + /* TODO: use device discoverability request through GO */ + } + + dev->invitation_reqs = 0; + + if (p2p->state != P2P_IDLE) + p2p_stop_find(p2p); + + p2p->inv_role = role; + p2p->inv_bssid_set = bssid != NULL; + if (bssid) + os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN); + os_memcpy(p2p->inv_ssid, ssid, ssid_len); + p2p->inv_ssid_len = ssid_len; + p2p->inv_persistent = persistent_group; + return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id); +} diff --git a/contrib/hostapd/src/p2p/p2p_parse.c b/contrib/hostapd/src/p2p/p2p_parse.c new file mode 100644 index 0000000000..d6144a0ebc --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p_parse.c @@ -0,0 +1,767 @@ +/* + * P2P - IE parser + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "wps/wps_i.h" +#include "p2p_i.h" + + +static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, + struct p2p_message *msg) +{ + const u8 *pos; + size_t i, nlen; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + + switch (id) { + case P2P_ATTR_CAPABILITY: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short Capability " + "attribute (length %d)", len); + return -1; + } + msg->capability = data; + wpa_printf(MSG_DEBUG, "P2P: * Device Capability %02x " + "Group Capability %02x", + data[0], data[1]); + break; + case P2P_ATTR_DEVICE_ID: + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "P2P: Too short Device ID " + "attribute (length %d)", len); + return -1; + } + msg->device_id = data; + wpa_printf(MSG_DEBUG, "P2P: * Device ID " MACSTR, + MAC2STR(msg->device_id)); + break; + case P2P_ATTR_GROUP_OWNER_INTENT: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short GO Intent " + "attribute (length %d)", len); + return -1; + } + msg->go_intent = data; + wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u " + "Tie breaker %u", data[0] >> 1, data[0] & 0x01); + break; + case P2P_ATTR_STATUS: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Status " + "attribute (length %d)", len); + return -1; + } + msg->status = data; + wpa_printf(MSG_DEBUG, "P2P: * Status: %d", data[0]); + break; + case P2P_ATTR_LISTEN_CHANNEL: + if (len == 0) { + wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Ignore " + "null channel"); + break; + } + if (len < 5) { + wpa_printf(MSG_DEBUG, "P2P: Too short Listen Channel " + "attribute (length %d)", len); + return -1; + } + msg->listen_channel = data; + wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: " + "Country %c%c(0x%02x) Regulatory " + "Class %d Channel Number %d", data[0], data[1], + data[2], data[3], data[4]); + break; + case P2P_ATTR_OPERATING_CHANNEL: + if (len == 0) { + wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: " + "Ignore null channel"); + break; + } + if (len < 5) { + wpa_printf(MSG_DEBUG, "P2P: Too short Operating " + "Channel attribute (length %d)", len); + return -1; + } + msg->operating_channel = data; + wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: " + "Country %c%c(0x%02x) Regulatory " + "Class %d Channel Number %d", data[0], data[1], + data[2], data[3], data[4]); + break; + case P2P_ATTR_CHANNEL_LIST: + if (len < 3) { + wpa_printf(MSG_DEBUG, "P2P: Too short Channel List " + "attribute (length %d)", len); + return -1; + } + msg->channel_list = data; + msg->channel_list_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String " + "'%c%c(0x%02x)'", data[0], data[1], data[2]); + wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List", + msg->channel_list, msg->channel_list_len); + break; + case P2P_ATTR_GROUP_INFO: + msg->group_info = data; + msg->group_info_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Group Info"); + break; + case P2P_ATTR_DEVICE_INFO: + if (len < ETH_ALEN + 2 + 8 + 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Device Info " + "attribute (length %d)", len); + return -1; + } + msg->p2p_device_info = data; + msg->p2p_device_info_len = len; + pos = data; + msg->p2p_device_addr = pos; + pos += ETH_ALEN; + msg->config_methods = WPA_GET_BE16(pos); + pos += 2; + msg->pri_dev_type = pos; + pos += 8; + msg->num_sec_dev_types = *pos++; + if (msg->num_sec_dev_types * 8 > data + len - pos) { + wpa_printf(MSG_DEBUG, "P2P: Device Info underflow"); + return -1; + } + pos += msg->num_sec_dev_types * 8; + if (data + len - pos < 4) { + wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " + "length %d", (int) (data + len - pos)); + return -1; + } + if (WPA_GET_BE16(pos) != ATTR_DEV_NAME) { + wpa_hexdump(MSG_DEBUG, "P2P: Unexpected Device Name " + "header", pos, 4); + return -1; + } + pos += 2; + nlen = WPA_GET_BE16(pos); + pos += 2; + if (data + len - pos < (int) nlen || nlen > 32) { + wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " + "length %d (buf len %d)", (int) nlen, + (int) (data + len - pos)); + return -1; + } + os_memcpy(msg->device_name, pos, nlen); + msg->device_name[nlen] = '\0'; + for (i = 0; i < nlen; i++) { + if (msg->device_name[i] == '\0') + break; + if (msg->device_name[i] > 0 && + msg->device_name[i] < 32) + msg->device_name[i] = '_'; + } + wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR + " primary device type %s device name '%s' " + "config methods 0x%x", + MAC2STR(msg->p2p_device_addr), + wps_dev_type_bin2str(msg->pri_dev_type, devtype, + sizeof(devtype)), + msg->device_name, msg->config_methods); + break; + case P2P_ATTR_CONFIGURATION_TIMEOUT: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short Configuration " + "Timeout attribute (length %d)", len); + return -1; + } + msg->config_timeout = data; + wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout"); + break; + case P2P_ATTR_INTENDED_INTERFACE_ADDR: + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "P2P: Too short Intended P2P " + "Interface Address attribute (length %d)", + len); + return -1; + } + msg->intended_addr = data; + wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address: " + MACSTR, MAC2STR(msg->intended_addr)); + break; + case P2P_ATTR_GROUP_BSSID: + if (len < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "P2P: Too short P2P Group BSSID " + "attribute (length %d)", len); + return -1; + } + msg->group_bssid = data; + wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID: " MACSTR, + MAC2STR(msg->group_bssid)); + break; + case P2P_ATTR_GROUP_ID: + if (len < ETH_ALEN || len > ETH_ALEN + 32) { + wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID " + "attribute length %d", len); + return -1; + } + msg->group_id = data; + msg->group_id_len = len; + wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID: Device Address " + MACSTR, MAC2STR(msg->group_id)); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: * P2P Group ID: SSID", + msg->group_id + ETH_ALEN, + msg->group_id_len - ETH_ALEN); + break; + case P2P_ATTR_INVITATION_FLAGS: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Invitation " + "Flag attribute (length %d)", len); + return -1; + } + msg->invitation_flags = data; + wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", + data[0]); + break; + case P2P_ATTR_MANAGEABILITY: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Manageability " + "attribute (length %d)", len); + return -1; + } + msg->manageability = data; + wpa_printf(MSG_DEBUG, "P2P: * Manageability: bitmap 0x%x", + data[0]); + break; + case P2P_ATTR_NOTICE_OF_ABSENCE: + if (len < 2) { + wpa_printf(MSG_DEBUG, "P2P: Too short Notice of " + "Absence attribute (length %d)", len); + return -1; + } + msg->noa = data; + msg->noa_len = len; + wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence"); + break; + case P2P_ATTR_EXT_LISTEN_TIMING: + if (len < 4) { + wpa_printf(MSG_DEBUG, "P2P: Too short Extended Listen " + "Timing attribute (length %d)", len); + return -1; + } + msg->ext_listen_timing = data; + wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing " + "(period %u msec interval %u msec)", + WPA_GET_LE16(msg->ext_listen_timing), + WPA_GET_LE16(msg->ext_listen_timing + 2)); + break; + case P2P_ATTR_MINOR_REASON_CODE: + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: Too short Minor Reason " + "Code attribute (length %d)", len); + return -1; + } + msg->minor_reason_code = data; + wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u", + *msg->minor_reason_code); + break; + case P2P_ATTR_OOB_GO_NEG_CHANNEL: + if (len < 6) { + wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg " + "Channel attribute (length %d)", len); + return -1; + } + msg->oob_go_neg_channel = data; + wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: " + "Country %c%c(0x%02x) Operating Class %d " + "Channel Number %d Role %d", + data[0], data[1], data[2], data[3], data[4], + data[5]); + break; + default: + wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d " + "(length %d)", id, len); + break; + } + + return 0; +} + + +/** + * p2p_parse_p2p_ie - Parse P2P IE + * @buf: Concatenated P2P IE(s) payload + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: Caller is responsible for clearing the msg data structure before + * calling this function. + */ +int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg) +{ + const u8 *pos = wpabuf_head_u8(buf); + const u8 *end = pos + wpabuf_len(buf); + + wpa_printf(MSG_DEBUG, "P2P: Parsing P2P IE"); + + while (pos < end) { + u16 attr_len; + if (pos + 2 >= end) { + wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute"); + return -1; + } + attr_len = WPA_GET_LE16(pos + 1); + wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u", + pos[0], attr_len); + if (pos + 3 + attr_len > end) { + wpa_printf(MSG_DEBUG, "P2P: Attribute underflow " + "(len=%u left=%d)", + attr_len, (int) (end - pos - 3)); + wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos); + return -1; + } + if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg)) + return -1; + pos += 3 + attr_len; + } + + return 0; +} + + +static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg) +{ + struct wps_parse_attr attr; + int i; + + wpa_printf(MSG_DEBUG, "P2P: Parsing WPS IE"); + if (wps_parse_msg(buf, &attr)) + return -1; + if (attr.dev_name && attr.dev_name_len < sizeof(msg->device_name) && + !msg->device_name[0]) + os_memcpy(msg->device_name, attr.dev_name, attr.dev_name_len); + if (attr.config_methods) { + msg->wps_config_methods = + WPA_GET_BE16(attr.config_methods); + wpa_printf(MSG_DEBUG, "P2P: Config Methods (WPS): 0x%x", + msg->wps_config_methods); + } + if (attr.dev_password_id) { + msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id); + wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d", + msg->dev_password_id); + msg->dev_password_id_present = 1; + } + if (attr.primary_dev_type) { + char devtype[WPS_DEV_TYPE_BUFSIZE]; + msg->wps_pri_dev_type = attr.primary_dev_type; + wpa_printf(MSG_DEBUG, "P2P: Primary Device Type (WPS): %s", + wps_dev_type_bin2str(msg->wps_pri_dev_type, devtype, + sizeof(devtype))); + } + if (attr.sec_dev_type_list) { + msg->wps_sec_dev_type_list = attr.sec_dev_type_list; + msg->wps_sec_dev_type_list_len = attr.sec_dev_type_list_len; + } + + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + msg->wps_vendor_ext[i] = attr.vendor_ext[i]; + msg->wps_vendor_ext_len[i] = attr.vendor_ext_len[i]; + } + + msg->manufacturer = attr.manufacturer; + msg->manufacturer_len = attr.manufacturer_len; + msg->model_name = attr.model_name; + msg->model_name_len = attr.model_name_len; + msg->model_number = attr.model_number; + msg->model_number_len = attr.model_number_len; + msg->serial_number = attr.serial_number; + msg->serial_number_len = attr.serial_number_len; + + msg->oob_dev_password = attr.oob_dev_password; + msg->oob_dev_password_len = attr.oob_dev_password_len; + + return 0; +} + + +/** + * p2p_parse_ies - Parse P2P message IEs (both WPS and P2P IE) + * @data: IEs from the message + * @len: Length of data buffer in octets + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: Caller is responsible for clearing the msg data structure before + * calling this function. + * + * Note: Caller must free temporary memory allocations by calling + * p2p_parse_free() when the parsed data is not needed anymore. + */ +int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg) +{ + struct ieee802_11_elems elems; + + ieee802_11_parse_elems(data, len, &elems, 0); + if (elems.ds_params && elems.ds_params_len >= 1) + msg->ds_params = elems.ds_params; + if (elems.ssid) + msg->ssid = elems.ssid - 2; + + msg->wps_attributes = ieee802_11_vendor_ie_concat(data, len, + WPS_DEV_OUI_WFA); + if (msg->wps_attributes && + p2p_parse_wps_ie(msg->wps_attributes, msg)) { + p2p_parse_free(msg); + return -1; + } + + msg->p2p_attributes = ieee802_11_vendor_ie_concat(data, len, + P2P_IE_VENDOR_TYPE); + if (msg->p2p_attributes && + p2p_parse_p2p_ie(msg->p2p_attributes, msg)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data"); + if (msg->p2p_attributes) + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data", + msg->p2p_attributes); + p2p_parse_free(msg); + return -1; + } + +#ifdef CONFIG_WIFI_DISPLAY + if (elems.wfd) { + msg->wfd_subelems = ieee802_11_vendor_ie_concat( + data, len, WFD_IE_VENDOR_TYPE); + } +#endif /* CONFIG_WIFI_DISPLAY */ + + return 0; +} + + +/** + * p2p_parse - Parse a P2P Action frame contents + * @data: Action frame payload after Category and Code fields + * @len: Length of data buffer in octets + * @msg: Buffer for returning parsed attributes + * Returns: 0 on success, -1 on failure + * + * Note: Caller must free temporary memory allocations by calling + * p2p_parse_free() when the parsed data is not needed anymore. + */ +int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg) +{ + os_memset(msg, 0, sizeof(*msg)); + wpa_printf(MSG_DEBUG, "P2P: Parsing the received message"); + if (len < 1) { + wpa_printf(MSG_DEBUG, "P2P: No Dialog Token in the message"); + return -1; + } + msg->dialog_token = data[0]; + wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", msg->dialog_token); + + return p2p_parse_ies(data + 1, len - 1, msg); +} + + +int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, + size_t p2p_len, struct p2p_message *msg) +{ + os_memset(msg, 0, sizeof(*msg)); + + msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len); + if (msg->wps_attributes && + p2p_parse_wps_ie(msg->wps_attributes, msg)) { + p2p_parse_free(msg); + return -1; + } + + msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len); + if (msg->p2p_attributes && + p2p_parse_p2p_ie(msg->p2p_attributes, msg)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data"); + if (msg->p2p_attributes) + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data", + msg->p2p_attributes); + p2p_parse_free(msg); + return -1; + } + + return 0; +} + + +/** + * p2p_parse_free - Free temporary data from P2P parsing + * @msg: Parsed attributes + */ +void p2p_parse_free(struct p2p_message *msg) +{ + wpabuf_free(msg->p2p_attributes); + msg->p2p_attributes = NULL; + wpabuf_free(msg->wps_attributes); + msg->wps_attributes = NULL; +#ifdef CONFIG_WIFI_DISPLAY + wpabuf_free(msg->wfd_subelems); + msg->wfd_subelems = NULL; +#endif /* CONFIG_WIFI_DISPLAY */ +} + + +int p2p_group_info_parse(const u8 *gi, size_t gi_len, + struct p2p_group_info *info) +{ + const u8 *g, *gend; + + os_memset(info, 0, sizeof(*info)); + if (gi == NULL) + return 0; + + g = gi; + gend = gi + gi_len; + while (g < gend) { + struct p2p_client_info *cli; + const u8 *t, *cend; + int count; + + cli = &info->client[info->num_clients]; + cend = g + 1 + g[0]; + if (cend > gend) + return -1; /* invalid data */ + /* g at start of P2P Client Info Descriptor */ + /* t at Device Capability Bitmap */ + t = g + 1 + 2 * ETH_ALEN; + if (t > cend) + return -1; /* invalid data */ + cli->p2p_device_addr = g + 1; + cli->p2p_interface_addr = g + 1 + ETH_ALEN; + cli->dev_capab = t[0]; + + if (t + 1 + 2 + 8 + 1 > cend) + return -1; /* invalid data */ + + cli->config_methods = WPA_GET_BE16(&t[1]); + cli->pri_dev_type = &t[3]; + + t += 1 + 2 + 8; + /* t at Number of Secondary Device Types */ + cli->num_sec_dev_types = *t++; + if (t + 8 * cli->num_sec_dev_types > cend) + return -1; /* invalid data */ + cli->sec_dev_types = t; + t += 8 * cli->num_sec_dev_types; + + /* t at Device Name in WPS TLV format */ + if (t + 2 + 2 > cend) + return -1; /* invalid data */ + if (WPA_GET_BE16(t) != ATTR_DEV_NAME) + return -1; /* invalid Device Name TLV */ + t += 2; + count = WPA_GET_BE16(t); + t += 2; + if (count > cend - t) + return -1; /* invalid Device Name TLV */ + if (count >= 32) + count = 32; + cli->dev_name = (const char *) t; + cli->dev_name_len = count; + + g = cend; + + info->num_clients++; + if (info->num_clients == P2P_MAX_GROUP_ENTRIES) + return -1; + } + + return 0; +} + + +static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, + char *end) +{ + char *pos = buf; + int ret; + struct p2p_group_info info; + unsigned int i; + + if (p2p_group_info_parse(gi, gi_len, &info) < 0) + return 0; + + for (i = 0; i < info.num_clients; i++) { + struct p2p_client_info *cli; + char name[33]; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + u8 s; + int count; + + cli = &info.client[i]; + ret = os_snprintf(pos, end - pos, "p2p_group_client: " + "dev=" MACSTR " iface=" MACSTR, + MAC2STR(cli->p2p_device_addr), + MAC2STR(cli->p2p_interface_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, + " dev_capab=0x%x config_methods=0x%x " + "dev_type=%s", + cli->dev_capab, cli->config_methods, + wps_dev_type_bin2str(cli->pri_dev_type, + devtype, + sizeof(devtype))); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + for (s = 0; s < cli->num_sec_dev_types; s++) { + ret = os_snprintf(pos, end - pos, " dev_type=%s", + wps_dev_type_bin2str( + &cli->sec_dev_types[s * 8], + devtype, sizeof(devtype))); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + os_memcpy(name, cli->dev_name, cli->dev_name_len); + name[cli->dev_name_len] = '\0'; + count = (int) cli->dev_name_len - 1; + while (count >= 0) { + if (name[count] > 0 && name[count] < 32) + name[count] = '_'; + count--; + } + + ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +/** + * p2p_attr_text - Build text format description of P2P IE attributes + * @data: P2P IE contents + * @buf: Buffer for returning text + * @end: Pointer to the end of the buf area + * Returns: Number of octets written to the buffer or -1 on faikure + * + * This function can be used to parse P2P IE contents into text format + * field=value lines. + */ +int p2p_attr_text(struct wpabuf *data, char *buf, char *end) +{ + struct p2p_message msg; + char *pos = buf; + int ret; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(data, &msg)) + return -1; + + if (msg.capability) { + ret = os_snprintf(pos, end - pos, + "p2p_dev_capab=0x%x\n" + "p2p_group_capab=0x%x\n", + msg.capability[0], msg.capability[1]); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (msg.pri_dev_type) { + char devtype[WPS_DEV_TYPE_BUFSIZE]; + ret = os_snprintf(pos, end - pos, + "p2p_primary_device_type=%s\n", + wps_dev_type_bin2str(msg.pri_dev_type, + devtype, + sizeof(devtype))); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n", + msg.device_name); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + if (msg.p2p_device_addr) { + ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR + "\n", + MAC2STR(msg.p2p_device_addr)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n", + msg.config_methods); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + ret = p2p_group_info_text(msg.group_info, msg.group_info_len, + pos, end); + if (ret < 0) + return pos - buf; + pos += ret; + + return pos - buf; +} + + +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return 0; + + if (!msg.manageability) + return 0; + + return !(msg.manageability[0] & P2P_MAN_CROSS_CONNECTION_PERMITTED); +} + + +u8 p2p_get_group_capab(const struct wpabuf *p2p_ie) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return 0; + + if (!msg.capability) + return 0; + + return msg.capability[1]; +} + + +const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie) +{ + struct p2p_message msg; + + os_memset(&msg, 0, sizeof(msg)); + if (p2p_parse_p2p_ie(p2p_ie, &msg)) + return NULL; + + if (msg.p2p_device_addr) + return msg.p2p_device_addr; + if (msg.device_id) + return msg.device_id; + + return NULL; +} diff --git a/contrib/hostapd/src/p2p/p2p_pd.c b/contrib/hostapd/src/p2p/p2p_pd.c new file mode 100644 index 0000000000..409405fb26 --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p_pd.c @@ -0,0 +1,477 @@ +/* + * Wi-Fi Direct - P2P provision discovery + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "wps/wps_defs.h" +#include "p2p_i.h" +#include "p2p.h" + + +/* + * Number of retries to attempt for provision discovery requests + * in case the peer is not listening. + */ +#define MAX_PROV_DISC_REQ_RETRIES 120 + + +static void p2p_build_wps_ie_config_methods(struct wpabuf *buf, + u16 config_methods) +{ + u8 *len; + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(buf, 1); + wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); + + /* Config Methods */ + wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); + wpabuf_put_be16(buf, 2); + wpabuf_put_be16(buf, config_methods); + + p2p_buf_update_ie_hdr(buf, len); +} + + +static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, + u8 dialog_token, + u16 config_methods, + struct p2p_device *go) +{ + struct wpabuf *buf; + u8 *len; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_prov_disc_req) + extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(1000 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); + + len = p2p_buf_add_ie_hdr(buf); + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + p2p_buf_add_device_info(buf, p2p, NULL); + if (go) { + p2p_buf_add_group_id(buf, go->info.p2p_device_addr, + go->oper_ssid, go->oper_ssid_len); + } + p2p_buf_update_ie_hdr(buf, len); + + /* WPS IE with Config Methods attribute */ + p2p_build_wps_ie_config_methods(buf, config_methods); + +#ifdef CONFIG_WIFI_DISPLAY + if (p2p->wfd_ie_prov_disc_req) + wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, + u8 dialog_token, + u16 config_methods, + const u8 *group_id, + size_t group_id_len) +{ + struct wpabuf *buf; + size_t extra = 0; + +#ifdef CONFIG_WIFI_DISPLAY + struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; + if (wfd_ie && group_id) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + struct p2p_group *g = p2p->groups[i]; + struct wpabuf *ie; + if (!p2p_group_is_group_id_match(g, group_id, + group_id_len)) + continue; + ie = p2p_group_get_wfd_ie(g); + if (ie) { + wfd_ie = ie; + break; + } + } + } + if (wfd_ie) + extra = wpabuf_len(wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + buf = wpabuf_alloc(100 + extra); + if (buf == NULL) + return NULL; + + p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); + + /* WPS IE with Config Methods attribute */ + p2p_build_wps_ie_config_methods(buf, config_methods); + +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_ie) + wpabuf_put_buf(buf, wfd_ie); +#endif /* CONFIG_WIFI_DISPLAY */ + + return buf; +} + + +void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct p2p_message msg; + struct p2p_device *dev; + int freq; + int reject = 1; + struct wpabuf *resp; + + if (p2p_parse(data, len, &msg)) + return; + + p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR + " with config methods 0x%x (freq=%d)", + MAC2STR(sa), msg.wps_config_methods, rx_freq); + + dev = p2p_get_device(p2p, sa); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + p2p_dbg(p2p, "Provision Discovery Request from unknown peer " + MACSTR, MAC2STR(sa)); + + if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, + 0)) { + p2p_dbg(p2p, "Provision Discovery Request add device failed " + MACSTR, MAC2STR(sa)); + } + } else if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + + if (!(msg.wps_config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | + WPS_CONFIG_PUSHBUTTON))) { + p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); + goto out; + } + + if (msg.group_id) { + size_t i; + for (i = 0; i < p2p->num_groups; i++) { + if (p2p_group_is_group_id_match(p2p->groups[i], + msg.group_id, + msg.group_id_len)) + break; + } + if (i == p2p->num_groups) { + p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject"); + goto out; + } + } + + if (dev) + dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | + P2P_DEV_PD_PEER_KEYPAD); + if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { + p2p_dbg(p2p, "Peer " MACSTR + " requested us to show a PIN on display", MAC2STR(sa)); + if (dev) + dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + p2p_dbg(p2p, "Peer " MACSTR + " requested us to write its PIN using keypad", + MAC2STR(sa)); + if (dev) + dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + } + + reject = 0; + +out: + resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token, + reject ? 0 : msg.wps_config_methods, + msg.group_id, msg.group_id_len); + if (resp == NULL) { + p2p_parse_free(&msg); + return; + } + p2p_dbg(p2p, "Sending Provision Discovery Response"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown regulatory class/channel"); + wpabuf_free(resp); + p2p_parse_free(&msg); + return; + } + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + } + + wpabuf_free(resp); + + if (!reject && p2p->cfg->prov_disc_req) { + const u8 *dev_addr = sa; + if (msg.p2p_device_addr) + dev_addr = msg.p2p_device_addr; + p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, + msg.wps_config_methods, + dev_addr, msg.pri_dev_type, + msg.device_name, msg.config_methods, + msg.capability ? msg.capability[0] : 0, + msg.capability ? msg.capability[1] : + 0, + msg.group_id, msg.group_id_len); + } + p2p_parse_free(&msg); +} + + +void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len) +{ + struct p2p_message msg; + struct p2p_device *dev; + u16 report_config_methods = 0, req_config_methods; + int success = 0; + + if (p2p_parse(data, len, &msg)) + return; + + p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR + " with config methods 0x%x", + MAC2STR(sa), msg.wps_config_methods); + + dev = p2p_get_device(p2p, sa); + if (dev == NULL || !dev->req_config_methods) { + p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR + " with no pending request", MAC2STR(sa)); + p2p_parse_free(&msg); + return; + } + + if (dev->dialog_token != msg.dialog_token) { + p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); + return; + } + + if (p2p->pending_action_state == P2P_PENDING_PD) { + os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + } + + /* + * Use a local copy of the requested config methods since + * p2p_reset_pending_pd() can clear this in the peer entry. + */ + req_config_methods = dev->req_config_methods; + + /* + * If the response is from the peer to whom a user initiated request + * was sent earlier, we reset that state info here. + */ + if (p2p->user_initiated_pd && + os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) + p2p_reset_pending_pd(p2p); + + if (msg.wps_config_methods != req_config_methods) { + p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x", + msg.wps_config_methods, req_config_methods); + if (p2p->cfg->prov_disc_fail) + p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, + P2P_PROV_DISC_REJECTED); + p2p_parse_free(&msg); + goto out; + } + + report_config_methods = req_config_methods; + dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | + P2P_DEV_PD_PEER_KEYPAD); + if (req_config_methods & WPS_CONFIG_DISPLAY) { + p2p_dbg(p2p, "Peer " MACSTR + " accepted to show a PIN on display", MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { + p2p_dbg(p2p, "Peer " MACSTR + " accepted to write our PIN using keypad", + MAC2STR(sa)); + dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + } + + /* Store the provisioning info */ + dev->wps_prov_info = msg.wps_config_methods; + + p2p_parse_free(&msg); + success = 1; + +out: + dev->req_config_methods = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { + p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with " + MACSTR, MAC2STR(dev->info.p2p_device_addr)); + dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; + p2p_connect_send(p2p, dev); + return; + } + if (success && p2p->cfg->prov_disc_resp) + p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, + report_config_methods); + + if (p2p->state == P2P_PD_DURING_FIND) { + p2p_clear_timeout(p2p); + p2p_continue_find(p2p); + } +} + + +int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, + int join, int force_freq) +{ + struct wpabuf *req; + int freq; + + if (force_freq > 0) + freq = force_freq; + else + freq = dev->listen_freq > 0 ? dev->listen_freq : + dev->oper_freq; + if (freq <= 0) { + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send Provision Discovery Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { + if (!(dev->info.dev_capab & + P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { + p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR + " that is in a group and is not discoverable", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + /* TODO: use device discoverability request through GO */ + } + + req = p2p_build_prov_disc_req(p2p, dev->dialog_token, + dev->req_config_methods, + join ? dev : NULL); + if (req == NULL) + return -1; + + if (p2p->state != P2P_IDLE) + p2p_stop_listen_for_freq(p2p, freq); + p2p->pending_action_state = P2P_PENDING_PD; + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 200) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + wpabuf_free(req); + return -1; + } + + os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN); + + wpabuf_free(req); + return 0; +} + + +int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, + u16 config_methods, int join, int force_freq, + int user_initiated_pd) +{ + struct p2p_device *dev; + + dev = p2p_get_device(p2p, peer_addr); + if (dev == NULL) + dev = p2p_get_device_interface(p2p, peer_addr); + if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { + p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR + " not yet known", MAC2STR(peer_addr)); + return -1; + } + + p2p_dbg(p2p, "Provision Discovery Request with " MACSTR + " (config methods 0x%x)", + MAC2STR(peer_addr), config_methods); + if (config_methods == 0) + return -1; + + /* Reset provisioning info */ + dev->wps_prov_info = 0; + + dev->req_config_methods = config_methods; + if (join) + dev->flags |= P2P_DEV_PD_FOR_JOIN; + else + dev->flags &= ~P2P_DEV_PD_FOR_JOIN; + + if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && + p2p->state != P2P_LISTEN_ONLY) { + p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with " + MACSTR " (config methods 0x%x)", + MAC2STR(peer_addr), config_methods); + return 0; + } + + p2p->user_initiated_pd = user_initiated_pd; + p2p->pd_force_freq = force_freq; + + if (p2p->user_initiated_pd) + p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; + + /* + * Assign dialog token here to use the same value in each retry within + * the same PD exchange. + */ + dev->dialog_token++; + if (dev->dialog_token == 0) + dev->dialog_token = 1; + + return p2p_send_prov_disc_req(p2p, dev, join, force_freq); +} + + +void p2p_reset_pending_pd(struct p2p_data *p2p) +{ + struct p2p_device *dev; + + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (os_memcmp(p2p->pending_pd_devaddr, + dev->info.p2p_device_addr, ETH_ALEN)) + continue; + if (!dev->req_config_methods) + continue; + if (dev->flags & P2P_DEV_PD_FOR_JOIN) + continue; + /* Reset the config methods of the device */ + dev->req_config_methods = 0; + } + + p2p->user_initiated_pd = 0; + os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); + p2p->pd_retries = 0; + p2p->pd_force_freq = 0; +} diff --git a/contrib/hostapd/src/p2p/p2p_sd.c b/contrib/hostapd/src/p2p/p2p_sd.c new file mode 100644 index 0000000000..0e0c7f121e --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p_sd.c @@ -0,0 +1,879 @@ +/* + * Wi-Fi Direct - P2P service discovery + * Copyright (c) 2009, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "p2p_i.h" +#include "p2p.h" + + +#ifdef CONFIG_WIFI_DISPLAY +static int wfd_wsd_supported(struct wpabuf *wfd) +{ + const u8 *pos, *end; + u8 subelem; + u16 len; + + if (wfd == NULL) + return 0; + + pos = wpabuf_head(wfd); + end = pos + wpabuf_len(wfd); + + while (pos + 3 <= end) { + subelem = *pos++; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) + break; + + if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) { + u16 info = WPA_GET_BE16(pos); + return !!(info & 0x0040); + } + + pos += len; + } + + return 0; +} +#endif /* CONFIG_WIFI_DISPLAY */ + +struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, + struct p2p_device *dev) +{ + struct p2p_sd_query *q; + int wsd = 0; + + if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY)) + return NULL; /* peer does not support SD */ +#ifdef CONFIG_WIFI_DISPLAY + if (wfd_wsd_supported(dev->info.wfd_subelems)) + wsd = 1; +#endif /* CONFIG_WIFI_DISPLAY */ + + for (q = p2p->sd_queries; q; q = q->next) { + /* Use WSD only if the peer indicates support or it */ + if (q->wsd && !wsd) + continue; + if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO)) + return q; + if (!q->for_all_peers && + os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) == + 0) + return q; + } + + return NULL; +} + + +static int p2p_unlink_sd_query(struct p2p_data *p2p, + struct p2p_sd_query *query) +{ + struct p2p_sd_query *q, *prev; + q = p2p->sd_queries; + prev = NULL; + while (q) { + if (q == query) { + if (prev) + prev->next = q->next; + else + p2p->sd_queries = q->next; + if (p2p->sd_query == query) + p2p->sd_query = NULL; + return 1; + } + prev = q; + q = q->next; + } + return 0; +} + + +static void p2p_free_sd_query(struct p2p_sd_query *q) +{ + if (q == NULL) + return; + wpabuf_free(q->tlvs); + os_free(q); +} + + +void p2p_free_sd_queries(struct p2p_data *p2p) +{ + struct p2p_sd_query *q, *prev; + q = p2p->sd_queries; + p2p->sd_queries = NULL; + while (q) { + prev = q; + q = q->next; + p2p_free_sd_query(prev); + } +} + + +static struct wpabuf * p2p_build_sd_query(u16 update_indic, + struct wpabuf *tlvs) +{ + struct wpabuf *buf; + u8 *len_pos; + + buf = gas_anqp_build_initial_req(0, 100 + wpabuf_len(tlvs)); + if (buf == NULL) + return NULL; + + /* ANQP Query Request Frame */ + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */ + wpabuf_put_buf(buf, tlvs); + gas_anqp_set_element_len(buf, len_pos); + + gas_anqp_set_len(buf); + + return buf; +} + + +static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst, + u8 dialog_token, int freq) +{ + struct wpabuf *req; + + req = gas_build_comeback_req(dialog_token); + if (req == NULL) + return; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst, + wpabuf_head(req), wpabuf_len(req), 200) < 0) + p2p_dbg(p2p, "Failed to send Action frame"); + + wpabuf_free(req); +} + + +static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code, + u16 comeback_delay, + u16 update_indic, + const struct wpabuf *tlvs) +{ + struct wpabuf *buf; + u8 *len_pos; + + buf = gas_anqp_build_initial_resp(dialog_token, status_code, + comeback_delay, + 100 + (tlvs ? wpabuf_len(tlvs) : 0)); + if (buf == NULL) + return NULL; + + if (tlvs) { + /* ANQP Query Response Frame */ + len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + /* Service Update Indicator */ + wpabuf_put_le16(buf, update_indic); + wpabuf_put_buf(buf, tlvs); + gas_anqp_set_element_len(buf, len_pos); + } + + gas_anqp_set_len(buf); + + return buf; +} + + +static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token, + u16 status_code, + u16 update_indic, + const u8 *data, size_t len, + u8 frag_id, u8 more, + u16 total_len) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id, + more, 0, 100 + len); + if (buf == NULL) + return NULL; + + if (frag_id == 0) { + /* ANQP Query Response Frame */ + wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */ + wpabuf_put_le16(buf, 3 + 1 + 2 + total_len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, P2P_OUI_TYPE); + /* Service Update Indicator */ + wpabuf_put_le16(buf, update_indic); + } + + wpabuf_put_data(buf, data, len); + gas_anqp_set_len(buf); + + return buf; +} + + +int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) +{ + struct wpabuf *req; + int ret = 0; + struct p2p_sd_query *query; + int freq; + + freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) { + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send SD Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + query = p2p_pending_sd_req(p2p, dev); + if (query == NULL) + return -1; + + p2p_dbg(p2p, "Start Service Discovery with " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); + + req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs); + if (req == NULL) + return -1; + + p2p->sd_peer = dev; + p2p->sd_query = query; + p2p->pending_action_state = P2P_PENDING_SD; + + if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, + p2p->cfg->dev_addr, dev->info.p2p_device_addr, + wpabuf_head(req), wpabuf_len(req), 5000) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + ret = -1; + } + + wpabuf_free(req); + + return ret; +} + + +void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 slen; + int freq; + u16 update_indic; + + + if (p2p->cfg->sd_request == NULL) + return; + + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) + return; + + if (len < 1 + 2) + return; + + dialog_token = *pos++; + p2p_dbg(p2p, "GAS Initial Request from " MACSTR + " (dialog token %u, freq %d)", + MAC2STR(sa), dialog_token, rx_freq); + + if (*pos != WLAN_EID_ADV_PROTO) { + p2p_dbg(p2p, "Unexpected IE in GAS Initial Request: %u", *pos); + return; + } + pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + p2p_dbg(p2p, "Invalid IE in GAS Initial Request"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", + *pos); + return; + } + + pos = next; + /* Query Request */ + if (pos + 2 > end) + return; + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end) + return; + end = pos + slen; + + /* ANQP Query Request */ + if (pos + 4 > end) + return; + if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + return; + } + pos += 2; + + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 3 + 1) { + p2p_dbg(p2p, "Invalid ANQP Query Request length"); + return; + } + + if (WPA_GET_BE24(pos) != OUI_WFA) { + p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + return; + } + pos += 3; + + if (*pos != P2P_OUI_TYPE) { + p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos); + return; + } + pos++; + + if (pos + 2 > end) + return; + update_indic = WPA_GET_LE16(pos); + p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); + pos += 2; + + p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token, + update_indic, pos, end - pos); + /* the response will be indicated with a call to p2p_sd_response() */ +} + + +void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, + u8 dialog_token, const struct wpabuf *resp_tlvs) +{ + struct wpabuf *resp; + + /* TODO: fix the length limit to match with the maximum frame length */ + if (wpabuf_len(resp_tlvs) > 1400) { + p2p_dbg(p2p, "SD response long enough to require fragmentation"); + if (p2p->sd_resp) { + /* + * TODO: Could consider storing the fragmented response + * separately for each peer to avoid having to drop old + * one if there is more than one pending SD query. + * Though, that would eat more memory, so there are + * also benefits to just using a single buffer. + */ + p2p_dbg(p2p, "Drop previous SD response"); + wpabuf_free(p2p->sd_resp); + } + p2p->sd_resp = wpabuf_dup(resp_tlvs); + if (p2p->sd_resp == NULL) { + p2p_err(p2p, "Failed to allocate SD response fragmentation area"); + return; + } + os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN); + p2p->sd_resp_dialog_token = dialog_token; + p2p->sd_resp_pos = 0; + p2p->sd_frag_id = 0; + resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, + 1, p2p->srv_update_indic, NULL); + } else { + p2p_dbg(p2p, "SD response fits in initial response"); + resp = p2p_build_sd_response(dialog_token, + WLAN_STATUS_SUCCESS, 0, + p2p->srv_update_indic, resp_tlvs); + } + if (resp == NULL) + return; + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + p2p_dbg(p2p, "Failed to send Action frame"); + + wpabuf_free(resp); +} + + +void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 status_code; + u16 comeback_delay; + u16 slen; + u16 update_indic; + + if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || + os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { + p2p_dbg(p2p, "Ignore unexpected GAS Initial Response from " + MACSTR, MAC2STR(sa)); + return; + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_clear_timeout(p2p); + + p2p_dbg(p2p, "Received GAS Initial Response from " MACSTR " (len=%d)", + MAC2STR(sa), (int) len); + + if (len < 5 + 2) { + p2p_dbg(p2p, "Too short GAS Initial Response frame"); + return; + } + + dialog_token = *pos++; + /* TODO: check dialog_token match */ + status_code = WPA_GET_LE16(pos); + pos += 2; + comeback_delay = WPA_GET_LE16(pos); + pos += 2; + p2p_dbg(p2p, "dialog_token=%u status_code=%u comeback_delay=%u", + dialog_token, status_code, comeback_delay); + if (status_code) { + p2p_dbg(p2p, "Service Discovery failed: status code %u", + status_code); + return; + } + + if (*pos != WLAN_EID_ADV_PROTO) { + p2p_dbg(p2p, "Unexpected IE in GAS Initial Response: %u", *pos); + return; + } + pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + p2p_dbg(p2p, "Invalid IE in GAS Initial Response"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", + *pos); + return; + } + + pos = next; + /* Query Response */ + if (pos + 2 > end) { + p2p_dbg(p2p, "Too short Query Response"); + return; + } + slen = WPA_GET_LE16(pos); + pos += 2; + p2p_dbg(p2p, "Query Response Length: %d", slen); + if (pos + slen > end) { + p2p_dbg(p2p, "Not enough Query Response data"); + return; + } + end = pos + slen; + + if (comeback_delay) { + p2p_dbg(p2p, "Fragmented response - request fragments"); + if (p2p->sd_rx_resp) { + p2p_dbg(p2p, "Drop old SD reassembly buffer"); + wpabuf_free(p2p->sd_rx_resp); + p2p->sd_rx_resp = NULL; + } + p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); + return; + } + + /* ANQP Query Response */ + if (pos + 4 > end) + return; + if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + return; + } + pos += 2; + + slen = WPA_GET_LE16(pos); + pos += 2; + if (pos + slen > end || slen < 3 + 1) { + p2p_dbg(p2p, "Invalid ANQP Query Response length"); + return; + } + + if (WPA_GET_BE24(pos) != OUI_WFA) { + p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + return; + } + pos += 3; + + if (*pos != P2P_OUI_TYPE) { + p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos); + return; + } + pos++; + + if (pos + 2 > end) + return; + update_indic = WPA_GET_LE16(pos); + p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); + pos += 2; + + p2p->sd_peer->flags |= P2P_DEV_SD_INFO; + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + + if (p2p->sd_query) { + if (!p2p->sd_query->for_all_peers) { + struct p2p_sd_query *q; + p2p_dbg(p2p, "Remove completed SD query %p", + p2p->sd_query); + q = p2p->sd_query; + p2p_unlink_sd_query(p2p, p2p->sd_query); + p2p_free_sd_query(q); + } + p2p->sd_query = NULL; + } + + if (p2p->cfg->sd_response) + p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, update_indic, + pos, end - pos); + p2p_continue_find(p2p); +} + + +void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + struct wpabuf *resp; + u8 dialog_token; + size_t frag_len; + int more = 0; + + wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len); + if (len < 1) + return; + dialog_token = *data; + p2p_dbg(p2p, "Dialog Token: %u", dialog_token); + if (dialog_token != p2p->sd_resp_dialog_token) { + p2p_dbg(p2p, "No pending SD response fragment for dialog token %u", + dialog_token); + return; + } + + if (p2p->sd_resp == NULL) { + p2p_dbg(p2p, "No pending SD response fragment available"); + return; + } + if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) { + p2p_dbg(p2p, "No pending SD response fragment for " MACSTR, + MAC2STR(sa)); + return; + } + + frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos; + if (frag_len > 1400) { + frag_len = 1400; + more = 1; + } + resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS, + p2p->srv_update_indic, + wpabuf_head_u8(p2p->sd_resp) + + p2p->sd_resp_pos, frag_len, + p2p->sd_frag_id, more, + wpabuf_len(p2p->sd_resp)); + if (resp == NULL) + return; + p2p_dbg(p2p, "Send GAS Comeback Response (frag_id %d more=%d frag_len=%d)", + p2p->sd_frag_id, more, (int) frag_len); + p2p->sd_frag_id++; + p2p->sd_resp_pos += frag_len; + + if (more) { + p2p_dbg(p2p, "%d more bytes remain to be sent", + (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos)); + } else { + p2p_dbg(p2p, "All fragments of SD response sent"); + wpabuf_free(p2p->sd_resp); + p2p->sd_resp = NULL; + } + + p2p->pending_action_state = P2P_NO_PENDING_ACTION; + if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr, + p2p->cfg->dev_addr, + wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + p2p_dbg(p2p, "Failed to send Action frame"); + + wpabuf_free(resp); +} + + +void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, + const u8 *data, size_t len, int rx_freq) +{ + const u8 *pos = data; + const u8 *end = data + len; + const u8 *next; + u8 dialog_token; + u16 status_code; + u8 frag_id; + u8 more_frags; + u16 comeback_delay; + u16 slen; + + wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Response", data, len); + + if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || + os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { + p2p_dbg(p2p, "Ignore unexpected GAS Comeback Response from " + MACSTR, MAC2STR(sa)); + return; + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_clear_timeout(p2p); + + p2p_dbg(p2p, "Received GAS Comeback Response from " MACSTR " (len=%d)", + MAC2STR(sa), (int) len); + + if (len < 6 + 2) { + p2p_dbg(p2p, "Too short GAS Comeback Response frame"); + return; + } + + dialog_token = *pos++; + /* TODO: check dialog_token match */ + status_code = WPA_GET_LE16(pos); + pos += 2; + frag_id = *pos & 0x7f; + more_frags = (*pos & 0x80) >> 7; + pos++; + comeback_delay = WPA_GET_LE16(pos); + pos += 2; + p2p_dbg(p2p, "dialog_token=%u status_code=%u frag_id=%d more_frags=%d " + "comeback_delay=%u", + dialog_token, status_code, frag_id, more_frags, + comeback_delay); + /* TODO: check frag_id match */ + if (status_code) { + p2p_dbg(p2p, "Service Discovery failed: status code %u", + status_code); + return; + } + + if (*pos != WLAN_EID_ADV_PROTO) { + p2p_dbg(p2p, "Unexpected IE in GAS Comeback Response: %u", + *pos); + return; + } + pos++; + + slen = *pos++; + next = pos + slen; + if (next > end || slen < 2) { + p2p_dbg(p2p, "Invalid IE in GAS Comeback Response"); + return; + } + pos++; /* skip QueryRespLenLimit and PAME-BI */ + + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", + *pos); + return; + } + + pos = next; + /* Query Response */ + if (pos + 2 > end) { + p2p_dbg(p2p, "Too short Query Response"); + return; + } + slen = WPA_GET_LE16(pos); + pos += 2; + p2p_dbg(p2p, "Query Response Length: %d", slen); + if (pos + slen > end) { + p2p_dbg(p2p, "Not enough Query Response data"); + return; + } + if (slen == 0) { + p2p_dbg(p2p, "No Query Response data"); + return; + } + end = pos + slen; + + if (p2p->sd_rx_resp) { + /* + * ANQP header is only included in the first fragment; rest of + * the fragments start with continue TLVs. + */ + goto skip_nqp_header; + } + + /* ANQP Query Response */ + if (pos + 4 > end) + return; + if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + return; + } + pos += 2; + + slen = WPA_GET_LE16(pos); + pos += 2; + p2p_dbg(p2p, "ANQP Query Response length: %u", slen); + if (slen < 3 + 1) { + p2p_dbg(p2p, "Invalid ANQP Query Response length"); + return; + } + if (pos + 4 > end) + return; + + if (WPA_GET_BE24(pos) != OUI_WFA) { + p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + return; + } + pos += 3; + + if (*pos != P2P_OUI_TYPE) { + p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos); + return; + } + pos++; + + if (pos + 2 > end) + return; + p2p->sd_rx_update_indic = WPA_GET_LE16(pos); + p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic); + pos += 2; + +skip_nqp_header: + if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0) + return; + wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos); + p2p_dbg(p2p, "Current SD reassembly buffer length: %u", + (unsigned int) wpabuf_len(p2p->sd_rx_resp)); + + if (more_frags) { + p2p_dbg(p2p, "More fragments remains"); + /* TODO: what would be a good size limit? */ + if (wpabuf_len(p2p->sd_rx_resp) > 64000) { + wpabuf_free(p2p->sd_rx_resp); + p2p->sd_rx_resp = NULL; + p2p_dbg(p2p, "Too long SD response - drop it"); + return; + } + p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); + return; + } + + p2p->sd_peer->flags |= P2P_DEV_SD_INFO; + p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; + p2p->sd_peer = NULL; + + if (p2p->sd_query) { + if (!p2p->sd_query->for_all_peers) { + struct p2p_sd_query *q; + p2p_dbg(p2p, "Remove completed SD query %p", + p2p->sd_query); + q = p2p->sd_query; + p2p_unlink_sd_query(p2p, p2p->sd_query); + p2p_free_sd_query(q); + } + p2p->sd_query = NULL; + } + + if (p2p->cfg->sd_response) + p2p->cfg->sd_response(p2p->cfg->cb_ctx, sa, + p2p->sd_rx_update_indic, + wpabuf_head(p2p->sd_rx_resp), + wpabuf_len(p2p->sd_rx_resp)); + wpabuf_free(p2p->sd_rx_resp); + p2p->sd_rx_resp = NULL; + + p2p_continue_find(p2p); +} + + +void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs) +{ + struct p2p_sd_query *q; + + q = os_zalloc(sizeof(*q)); + if (q == NULL) + return NULL; + + if (dst) + os_memcpy(q->peer, dst, ETH_ALEN); + else + q->for_all_peers = 1; + + q->tlvs = wpabuf_dup(tlvs); + if (q->tlvs == NULL) { + p2p_free_sd_query(q); + return NULL; + } + + q->next = p2p->sd_queries; + p2p->sd_queries = q; + p2p_dbg(p2p, "Added SD Query %p", q); + + if (dst == NULL) { + struct p2p_device *dev; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) + dev->flags &= ~P2P_DEV_SD_INFO; + } + + return q; +} + + +#ifdef CONFIG_WIFI_DISPLAY +void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst, + const struct wpabuf *tlvs) +{ + struct p2p_sd_query *q; + q = p2p_sd_request(p2p, dst, tlvs); + if (q) + q->wsd = 1; + return q; +} +#endif /* CONFIG_WIFI_DISPLAY */ + + +void p2p_sd_service_update(struct p2p_data *p2p) +{ + p2p->srv_update_indic++; +} + + +int p2p_sd_cancel_request(struct p2p_data *p2p, void *req) +{ + if (p2p_unlink_sd_query(p2p, req)) { + p2p_dbg(p2p, "Cancel pending SD query %p", req); + p2p_free_sd_query(req); + return 0; + } + return -1; +} diff --git a/contrib/hostapd/src/p2p/p2p_utils.c b/contrib/hostapd/src/p2p/p2p_utils.c new file mode 100644 index 0000000000..161a402ef3 --- /dev/null +++ b/contrib/hostapd/src/p2p/p2p_utils.c @@ -0,0 +1,471 @@ +/* + * P2P - generic helper functions + * Copyright (c) 2009, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "p2p_i.h" + + +/** + * p2p_random - Generate random string for SSID and passphrase + * @buf: Buffer for returning the result + * @len: Number of octets to write to the buffer + * Returns: 0 on success, -1 on failure + * + * This function generates a random string using the following character set: + * 'A'-'Z', 'a'-'z', '0'-'9'. + */ +int p2p_random(char *buf, size_t len) +{ + u8 val; + size_t i; + u8 letters = 'Z' - 'A' + 1; + u8 numbers = 10; + + if (os_get_random((unsigned char *) buf, len)) + return -1; + /* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */ + for (i = 0; i < len; i++) { + val = buf[i]; + val %= 2 * letters + numbers; + if (val < letters) + buf[i] = 'A' + val; + else if (val < 2 * letters) + buf[i] = 'a' + (val - letters); + else + buf[i] = '0' + (val - 2 * letters); + } + + return 0; +} + + +/** + * p2p_channel_to_freq - Convert channel info to frequency + * @op_class: Operating class + * @channel: Channel number + * Returns: Frequency in MHz or -1 if the specified channel is unknown + */ +int p2p_channel_to_freq(int op_class, int channel) +{ + /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */ + /* TODO: more operating classes */ + switch (op_class) { + case 81: + /* channels 1..13 */ + if (channel < 1 || channel > 13) + return -1; + return 2407 + 5 * channel; + case 82: + /* channel 14 */ + if (channel != 14) + return -1; + return 2414 + 5 * channel; + case 83: /* channels 1..9; 40 MHz */ + case 84: /* channels 5..13; 40 MHz */ + if (channel < 1 || channel > 13) + return -1; + return 2407 + 5 * channel; + case 115: /* channels 36,40,44,48; indoor only */ + case 118: /* channels 52,56,60,64; dfs */ + if (channel < 36 || channel > 64) + return -1; + return 5000 + 5 * channel; + case 124: /* channels 149,153,157,161 */ + case 125: /* channels 149,153,157,161,165,169 */ + if (channel < 149 || channel > 161) + return -1; + return 5000 + 5 * channel; + case 116: /* channels 36,44; 40 MHz; indoor only */ + case 117: /* channels 40,48; 40 MHz; indoor only */ + case 119: /* channels 52,60; 40 MHz; dfs */ + case 120: /* channels 56,64; 40 MHz; dfs */ + if (channel < 36 || channel > 64) + return -1; + return 5000 + 5 * channel; + case 126: /* channels 149,157; 40 MHz */ + case 127: /* channels 153,161; 40 MHz */ + if (channel < 149 || channel > 161) + return -1; + return 5000 + 5 * channel; + case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ + if (channel < 36 || channel > 161) + return -1; + return 5000 + 5 * channel; + } + return -1; +} + + +/** + * p2p_freq_to_channel - Convert frequency into channel info + * @op_class: Buffer for returning operating class + * @channel: Buffer for returning channel number + * Returns: 0 on success, -1 if the specified frequency is unknown + */ +int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) +{ + /* TODO: more operating classes */ + if (freq >= 2412 && freq <= 2472) { + if ((freq - 2407) % 5) + return -1; + + *op_class = 81; /* 2.407 GHz, channels 1..13 */ + *channel = (freq - 2407) / 5; + return 0; + } + + if (freq == 2484) { + *op_class = 82; /* channel 14 */ + *channel = 14; + return 0; + } + + if (freq >= 5180 && freq <= 5240) { + if ((freq - 5000) % 5) + return -1; + + *op_class = 115; /* 5 GHz, channels 36..48 */ + *channel = (freq - 5000) / 5; + return 0; + } + + if (freq >= 5745 && freq <= 5805) { + if ((freq - 5000) % 5) + return -1; + + *op_class = 124; /* 5 GHz, channels 149..161 */ + *channel = (freq - 5000) / 5; + return 0; + } + + return -1; +} + + +static void p2p_reg_class_intersect(const struct p2p_reg_class *a, + const struct p2p_reg_class *b, + struct p2p_reg_class *res) +{ + size_t i, j; + + res->reg_class = a->reg_class; + + for (i = 0; i < a->channels; i++) { + for (j = 0; j < b->channels; j++) { + if (a->channel[i] != b->channel[j]) + continue; + res->channel[res->channels] = a->channel[i]; + res->channels++; + if (res->channels == P2P_MAX_REG_CLASS_CHANNELS) + return; + } + } +} + + +/** + * p2p_channels_intersect - Intersection of supported channel lists + * @a: First set of supported channels + * @b: Second set of supported channels + * @res: Data structure for returning the intersection of support channels + * + * This function can be used to find a common set of supported channels. Both + * input channels sets are assumed to use the same country code. If different + * country codes are used, the regulatory class numbers may not be matched + * correctly and results are undefined. + */ +void p2p_channels_intersect(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res) +{ + size_t i, j; + + os_memset(res, 0, sizeof(*res)); + + for (i = 0; i < a->reg_classes; i++) { + const struct p2p_reg_class *a_reg = &a->reg_class[i]; + for (j = 0; j < b->reg_classes; j++) { + const struct p2p_reg_class *b_reg = &b->reg_class[j]; + if (a_reg->reg_class != b_reg->reg_class) + continue; + p2p_reg_class_intersect( + a_reg, b_reg, + &res->reg_class[res->reg_classes]); + if (res->reg_class[res->reg_classes].channels) { + res->reg_classes++; + if (res->reg_classes == P2P_MAX_REG_CLASSES) + return; + } + } + } +} + + +static void p2p_op_class_union(struct p2p_reg_class *cl, + const struct p2p_reg_class *b_cl) +{ + size_t i, j; + + for (i = 0; i < b_cl->channels; i++) { + for (j = 0; j < cl->channels; j++) { + if (b_cl->channel[i] == cl->channel[j]) + break; + } + if (j == cl->channels) { + if (cl->channels == P2P_MAX_REG_CLASS_CHANNELS) + return; + cl->channel[cl->channels++] = b_cl->channel[i]; + } + } +} + + +/** + * p2p_channels_union - Union of channel lists + * @a: First set of channels + * @b: Second set of channels + * @res: Data structure for returning the union of channels + */ +void p2p_channels_union(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res) +{ + size_t i, j; + + if (a != res) + os_memcpy(res, a, sizeof(*res)); + + for (i = 0; i < res->reg_classes; i++) { + struct p2p_reg_class *cl = &res->reg_class[i]; + for (j = 0; j < b->reg_classes; j++) { + const struct p2p_reg_class *b_cl = &b->reg_class[j]; + if (cl->reg_class != b_cl->reg_class) + continue; + p2p_op_class_union(cl, b_cl); + } + } + + for (j = 0; j < b->reg_classes; j++) { + const struct p2p_reg_class *b_cl = &b->reg_class[j]; + + for (i = 0; i < res->reg_classes; i++) { + struct p2p_reg_class *cl = &res->reg_class[i]; + if (cl->reg_class == b_cl->reg_class) + break; + } + + if (i == res->reg_classes) { + if (res->reg_classes == P2P_MAX_REG_CLASSES) + return; + os_memcpy(&res->reg_class[res->reg_classes++], + b_cl, sizeof(struct p2p_reg_class)); + } + } +} + + +void p2p_channels_remove_freqs(struct p2p_channels *chan, + const struct wpa_freq_range_list *list) +{ + size_t o, c; + + if (list == NULL) + return; + + o = 0; + while (o < chan->reg_classes) { + struct p2p_reg_class *op = &chan->reg_class[o]; + + c = 0; + while (c < op->channels) { + int freq = p2p_channel_to_freq(op->reg_class, + op->channel[c]); + if (freq > 0 && freq_range_list_includes(list, freq)) { + op->channels--; + os_memmove(&op->channel[c], + &op->channel[c + 1], + op->channels - c); + } else + c++; + } + + if (op->channels == 0) { + chan->reg_classes--; + os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1], + (chan->reg_classes - o) * + sizeof(struct p2p_reg_class)); + } else + o++; + } +} + + +/** + * p2p_channels_includes - Check whether a channel is included in the list + * @channels: List of supported channels + * @reg_class: Regulatory class of the channel to search + * @channel: Channel number of the channel to search + * Returns: 1 if channel was found or 0 if not + */ +int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, + u8 channel) +{ + size_t i, j; + for (i = 0; i < channels->reg_classes; i++) { + const struct p2p_reg_class *reg = &channels->reg_class[i]; + if (reg->reg_class != reg_class) + continue; + for (j = 0; j < reg->channels; j++) { + if (reg->channel[j] == channel) + return 1; + } + } + return 0; +} + + +int p2p_channels_includes_freq(const struct p2p_channels *channels, + unsigned int freq) +{ + size_t i, j; + for (i = 0; i < channels->reg_classes; i++) { + const struct p2p_reg_class *reg = &channels->reg_class[i]; + for (j = 0; j < reg->channels; j++) { + if (p2p_channel_to_freq(reg->reg_class, + reg->channel[j]) == (int) freq) + return 1; + } + } + return 0; +} + + +int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel); +} + + +int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel) && + !freq_range_list_includes(&p2p->no_go_freq, freq); +} + + +int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel) || + p2p_channels_includes(&p2p->cfg->cli_channels, op_reg_class, + op_channel); +} + + +unsigned int p2p_get_pref_freq(struct p2p_data *p2p, + const struct p2p_channels *channels) +{ + unsigned int i; + int freq = 0; + + if (channels == NULL) { + if (p2p->cfg->num_pref_chan) { + freq = p2p_channel_to_freq( + p2p->cfg->pref_chan[0].op_class, + p2p->cfg->pref_chan[0].chan); + if (freq < 0) + freq = 0; + } + return freq; + } + + for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { + freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class, + p2p->cfg->pref_chan[i].chan); + if (p2p_channels_includes_freq(channels, freq)) + return freq; + } + + return 0; +} + + +void p2p_channels_dump(struct p2p_data *p2p, const char *title, + const struct p2p_channels *chan) +{ + char buf[500], *pos, *end; + size_t i, j; + int ret; + + pos = buf; + end = pos + sizeof(buf); + + for (i = 0; i < chan->reg_classes; i++) { + const struct p2p_reg_class *c; + c = &chan->reg_class[i]; + ret = os_snprintf(pos, end - pos, " %u:", c->reg_class); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + + for (j = 0; j < c->channels; j++) { + ret = os_snprintf(pos, end - pos, "%s%u", + j == 0 ? "" : ",", + c->channel[j]); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + } + } + *pos = '\0'; + + p2p_dbg(p2p, "%s:%s", title, buf); +} + + +int p2p_channel_select(struct p2p_channels *chans, const int *classes, + u8 *op_class, u8 *op_channel) +{ + unsigned int i, j, r; + + for (j = 0; classes[j]; j++) { + for (i = 0; i < chans->reg_classes; i++) { + struct p2p_reg_class *c = &chans->reg_class[i]; + + if (c->channels == 0) + continue; + + if (c->reg_class == classes[j]) { + /* + * Pick one of the available channels in the + * operating class at random. + */ + os_get_random((u8 *) &r, sizeof(r)); + r %= c->channels; + *op_class = c->reg_class; + *op_channel = c->channel[r]; + return 0; + } + } + } + + return -1; +} diff --git a/contrib/hostapd/src/radius/radius.c b/contrib/hostapd/src/radius/radius.c index 71bbfb52ee..494f92d19e 100644 --- a/contrib/hostapd/src/radius/radius.c +++ b/contrib/hostapd/src/radius/radius.c @@ -1,73 +1,87 @@ /* - * hostapd / RADIUS message processing - * Copyright (c) 2002-2008, Jouni Malinen + * RADIUS message processing + * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ -#include "includes.h" +#include "utils/includes.h" -#include "common.h" +#include "utils/common.h" +#include "utils/wpabuf.h" +#include "crypto/md5.h" +#include "crypto/crypto.h" #include "radius.h" -#include "md5.h" -#include "crypto.h" -static struct radius_attr_hdr * -radius_get_attr_hdr(struct radius_msg *msg, int idx) -{ - return (struct radius_attr_hdr *) (msg->buf + msg->attr_pos[idx]); -} +/** + * 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_msg *radius_msg_new(u8 code, u8 identifier) +struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg) { - struct radius_msg *msg; + return msg->hdr; +} - msg = os_malloc(sizeof(*msg)); - if (msg == NULL) - return NULL; - if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) { - os_free(msg); - return NULL; - } +struct wpabuf * radius_msg_get_buf(struct radius_msg *msg) +{ + return msg->buf; +} - radius_msg_set_hdr(msg, code, identifier); - return msg; +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]); } -int radius_msg_initialize(struct radius_msg *msg, size_t init_len) +static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) { - if (msg == NULL || init_len < sizeof(struct radius_hdr)) - return -1; - - os_memset(msg, 0, sizeof(*msg)); - msg->buf = os_zalloc(init_len); - if (msg->buf == NULL) - return -1; + msg->hdr->code = code; + msg->hdr->identifier = identifier; +} - msg->buf_size = init_len; - msg->hdr = (struct radius_hdr *) msg->buf; - msg->buf_used = sizeof(*msg->hdr); - msg->attr_pos = - os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos)); - if (msg->attr_pos == NULL) { - os_free(msg->buf); - msg->buf = NULL; - msg->hdr = NULL; +static int radius_msg_initialize(struct radius_msg *msg) +{ + msg->attr_pos = os_calloc(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; @@ -76,23 +90,48 @@ int radius_msg_initialize(struct radius_msg *msg, size_t init_len) } -void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) +/** + * 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) { - msg->hdr->code = code; - msg->hdr->identifier = 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) { - os_free(msg->buf); - msg->buf = NULL; - msg->hdr = NULL; - msg->buf_size = msg->buf_used = 0; + if (msg == NULL) + return; + wpabuf_free(msg->buf); os_free(msg->attr_pos); - msg->attr_pos = NULL; - msg->attr_size = msg->attr_used = 0; + os_free(msg); } @@ -108,6 +147,12 @@ static const char *radius_code_string(u8 code) case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; case RADIUS_CODE_RESERVED: return "Reserved"; + case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request"; + case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK"; + case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK"; + case RADIUS_CODE_COA_REQUEST: return "CoA-Request"; + case RADIUS_CODE_COA_ACK: return "CoA-ACK"; + case RADIUS_CODE_COA_NAK: return "CoA-NAK"; default: return "?Unknown?"; } } @@ -173,6 +218,8 @@ static struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password", + RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", @@ -181,11 +228,12 @@ static struct radius_attr_type radius_attrs[] = RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity", + { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, + { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 } }; -#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) +#define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) static struct radius_attr_type *radius_get_attr_type(u8 type) @@ -221,7 +269,7 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) printf(" Attribute %d (%s) length=%d\n", hdr->type, attr ? attr->name : "?Unknown?", hdr->length); - if (attr == NULL) + if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr)) return; len = hdr->length - sizeof(struct radius_attr_hdr); @@ -284,7 +332,7 @@ void radius_msg_dump(struct radius_msg *msg) 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)); + msg->hdr->identifier, be_to_host16(msg->hdr->length)); for (i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); @@ -305,19 +353,19 @@ int radius_msg_finish(struct radius_msg *msg, const u8 *secret, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, auth, MD5_MAC_LEN); if (attr == NULL) { - printf("WARNING: Could not add " - "Message-Authenticator\n"); + wpa_printf(MSG_WARNING, "RADIUS: Could not add " + "Message-Authenticator"); return -1; } - msg->hdr->length = htons(msg->buf_used); - hmac_md5(secret, secret_len, msg->buf, msg->buf_used, - (u8 *) (attr + 1)); + msg->hdr->length = host_to_be16(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(msg->buf_used); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); - if (msg->buf_used > 0xffff) { - printf("WARNING: too long RADIUS message (%lu)\n", - (unsigned long) msg->buf_used); + 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; @@ -339,26 +387,65 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, printf("WARNING: Could not add Message-Authenticator\n"); return -1; } - msg->hdr->length = htons(msg->buf_used); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memcpy(msg->hdr->authenticator, req_authenticator, sizeof(msg->hdr->authenticator)); - hmac_md5(secret, secret_len, msg->buf, msg->buf_used, - (u8 *) (attr + 1)); + 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] = (u8 *) (msg->hdr + 1); - len[2] = msg->buf_used - sizeof(*msg->hdr); + 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 (msg->buf_used > 0xffff) { - printf("WARNING: too long RADIUS message (%lu)\n", - (unsigned long) msg->buf_used); + 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_das_resp(struct radius_msg *msg, const u8 *secret, + size_t secret_len, + const struct radius_hdr *req_hdr) +{ + const u8 *addr[2]; + size_t len[2]; + 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, "Could not add Message-Authenticator"); + return -1; + } + + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); + os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16); + 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] = wpabuf_head_u8(msg->buf); + len[0] = wpabuf_len(msg->buf); + addr[1] = secret; + len[1] = secret_len; + if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0) + return -1; + + 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; @@ -371,18 +458,100 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, const u8 *addr[2]; size_t len[2]; - msg->hdr->length = htons(msg->buf_used); + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); - addr[0] = msg->buf; - len[0] = msg->buf_used; + 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 (msg->buf_used > 0xffff) { - printf("WARNING: too long RADIUS messages (%lu)\n", - (unsigned long) msg->buf_used); + if (wpabuf_len(msg->buf) > 0xffff) { + wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", + (unsigned long) wpabuf_len(msg->buf)); + } +} + + +int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len) +{ + const u8 *addr[4]; + size_t len[4]; + u8 zero[MD5_MAC_LEN]; + u8 hash[MD5_MAC_LEN]; + + os_memset(zero, 0, sizeof(zero)); + addr[0] = (u8 *) msg->hdr; + len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; + addr[1] = zero; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, hash); + return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0; +} + + +int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len) +{ + const u8 *addr[4]; + size_t len[4]; + u8 zero[MD5_MAC_LEN]; + u8 hash[MD5_MAC_LEN]; + u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; + u8 orig_authenticator[16]; + + struct radius_attr_hdr *attr = NULL, *tmp; + size_t i; + + os_memset(zero, 0, sizeof(zero)); + addr[0] = (u8 *) msg->hdr; + len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN; + addr[1] = zero; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + 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(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0) + return 1; + + 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) { + wpa_printf(MSG_WARNING, "Multiple " + "Message-Authenticator attributes " + "in RADIUS message"); + return 1; + } + attr = tmp; + } + } + + if (attr == NULL) { + /* Message-Authenticator is MAY; not required */ + return 0; } + + os_memcpy(orig, attr + 1, MD5_MAC_LEN); + os_memset(attr + 1, 0, MD5_MAC_LEN); + os_memcpy(orig_authenticator, msg->hdr->authenticator, + sizeof(orig_authenticator)); + os_memset(msg->hdr->authenticator, 0, + 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); + os_memcpy(msg->hdr->authenticator, orig_authenticator, + sizeof(orig_authenticator)); + + return os_memcmp(orig, auth, MD5_MAC_LEN) != 0; } @@ -393,8 +562,8 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg, size_t *nattr_pos; int nlen = msg->attr_size * 2; - nattr_pos = os_realloc(msg->attr_pos, - nlen * sizeof(*msg->attr_pos)); + nattr_pos = os_realloc_array(msg->attr_pos, nlen, + sizeof(*msg->attr_pos)); if (nattr_pos == NULL) return -1; @@ -402,7 +571,8 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg, msg->attr_size = nlen; } - msg->attr_pos[msg->attr_used++] = (unsigned char *) attr - msg->buf; + msg->attr_pos[msg->attr_used++] = + (unsigned char *) attr - wpabuf_head_u8(msg->buf); return 0; } @@ -420,31 +590,19 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, return NULL; } - buf_needed = msg->buf_used + sizeof(*attr) + data_len; + buf_needed = sizeof(*attr) + data_len; - if (msg->buf_size < buf_needed) { + if (wpabuf_tailroom(msg->buf) < buf_needed) { /* allocate more space for message buffer */ - unsigned char *nbuf; - size_t nlen = msg->buf_size; - - while (nlen < buf_needed) - nlen *= 2; - nbuf = os_realloc(msg->buf, nlen); - if (nbuf == NULL) + if (wpabuf_resize(&msg->buf, buf_needed) < 0) return NULL; - msg->buf = nbuf; - msg->hdr = (struct radius_hdr *) msg->buf; - os_memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size); - msg->buf_size = nlen; + msg->hdr = wpabuf_mhead(msg->buf); } - attr = (struct radius_attr_hdr *) (msg->buf + msg->buf_used); + attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr)); attr->type = type; attr->length = sizeof(*attr) + data_len; - if (data_len > 0) - os_memcpy(attr + 1, data, data_len); - - msg->buf_used += sizeof(*attr) + data_len; + wpabuf_put_data(msg->buf, data, data_len); if (radius_msg_add_attr_to_array(msg, attr)) return NULL; @@ -453,7 +611,16 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, } -struct radius_msg *radius_msg_parse(const u8 *data, size_t len) +/** + * 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; @@ -466,32 +633,31 @@ struct radius_msg *radius_msg_parse(const u8 *data, size_t len) hdr = (struct radius_hdr *) data; - msg_len = ntohs(hdr->length); + msg_len = be_to_host16(hdr->length); if (msg_len < sizeof(*hdr) || msg_len > len) { - printf("Invalid RADIUS message length\n"); + wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); return NULL; } if (msg_len < len) { - printf("Ignored %lu extra bytes after RADIUS message\n", - (unsigned long) len - msg_len); + wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after " + "RADIUS message", (unsigned long) len - msg_len); } - msg = os_malloc(sizeof(*msg)); + msg = os_zalloc(sizeof(*msg)); if (msg == NULL) return NULL; - if (radius_msg_initialize(msg, msg_len)) { - os_free(msg); + msg->buf = wpabuf_alloc_copy(data, msg_len); + if (msg->buf == NULL || radius_msg_initialize(msg)) { + radius_msg_free(msg); return NULL; } - - os_memcpy(msg->buf, data, msg_len); - msg->buf_size = msg->buf_used = msg_len; + msg->hdr = wpabuf_mhead(msg->buf); /* parse attributes */ - pos = (unsigned char *) (msg->hdr + 1); - end = msg->buf + msg->buf_used; + 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; @@ -513,7 +679,6 @@ struct radius_msg *radius_msg_parse(const u8 *data, size_t len) fail: radius_msg_free(msg); - os_free(msg); return NULL; } @@ -542,9 +707,9 @@ 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 *eap_len) +struct wpabuf * radius_msg_get_eap(struct radius_msg *msg) { - u8 *eap, *pos; + struct wpabuf *eap; size_t len, i; struct radius_attr_hdr *attr; @@ -554,30 +719,27 @@ u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) len = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); - if (attr->type == RADIUS_ATTR_EAP_MESSAGE) + if (attr->type == RADIUS_ATTR_EAP_MESSAGE && + attr->length > sizeof(struct radius_attr_hdr)) len += attr->length - sizeof(struct radius_attr_hdr); } if (len == 0) return NULL; - eap = os_malloc(len); + eap = wpabuf_alloc(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) { + if (attr->type == RADIUS_ATTR_EAP_MESSAGE && + attr->length > sizeof(struct radius_attr_hdr)) { int flen = attr->length - sizeof(*attr); - os_memcpy(pos, attr + 1, flen); - pos += flen; + wpabuf_put_data(eap, attr + 1, flen); } } - if (eap_len) - *eap_len = len; - return eap; } @@ -615,7 +777,8 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, os_memcpy(msg->hdr->authenticator, req_auth, sizeof(msg->hdr->authenticator)); } - hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth); + 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, @@ -654,8 +817,8 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret, len[0] = 1 + 1 + 2; addr[1] = sent_msg->hdr->authenticator; len[1] = MD5_MAC_LEN; - addr[2] = (u8 *) (msg->hdr + 1); - len[2] = msg->buf_used - sizeof(*msg->hdr); + addr[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); @@ -677,7 +840,7 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, for (i = 0; i < src->attr_used; i++) { attr = radius_get_attr_hdr(src, i); - if (attr->type == type) { + if (attr->type == type && attr->length >= sizeof(*attr)) { if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), attr->length - sizeof(*attr))) return -1; @@ -734,7 +897,8 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, u32 vendor_id; struct radius_attr_vendor *vhdr; - if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC) + if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || + attr->length < sizeof(*attr)) continue; left = attr->length - sizeof(*attr); @@ -1048,8 +1212,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, const u8 *secret, size_t secret_len) { u8 buf[128]; - int padlen, i; - size_t buf_len, pos; + size_t padlen, i, buf_len, pos; const u8 *addr[2]; size_t len[2]; u8 hash[16]; @@ -1061,7 +1224,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, buf_len = data_len; padlen = data_len % 16; - if (padlen) { + if (padlen && data_len < sizeof(buf)) { padlen = 16 - padlen; os_memset(buf + data_len, 0, padlen); buf_len += padlen; @@ -1108,7 +1271,7 @@ int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) } } - if (!attr) + if (!attr || attr->length < sizeof(*attr)) return -1; dlen = attr->length - sizeof(*attr); @@ -1133,7 +1296,7 @@ int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, } } - if (!attr) + if (!attr || attr->length < sizeof(*attr)) return -1; *buf = (u8 *) (attr + 1); @@ -1184,6 +1347,8 @@ int radius_msg_get_vlanid(struct radius_msg *msg) for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); + if (attr->length < sizeof(*attr)) + return -1; data = (const u8 *) (attr + 1); dlen = attr->length - sizeof(*attr); if (attr->length < 3) @@ -1232,3 +1397,182 @@ int radius_msg_get_vlanid(struct radius_msg *msg) return -1; } + + +/** + * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password + * @msg: Received RADIUS message + * @keylen: Length of returned password + * @secret: RADIUS shared secret + * @secret_len: Length of secret + * @sent_msg: Sent RADIUS message + * @n: Number of password attribute to return (starting with 0) + * Returns: Pointer to n-th password (free with os_free) or %NULL + */ +char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, + const u8 *secret, size_t secret_len, + struct radius_msg *sent_msg, size_t n) +{ + u8 *buf = NULL; + size_t buflen; + const u8 *salt; + u8 *str; + const u8 *addr[3]; + size_t len[3]; + u8 hash[16]; + u8 *pos; + size_t i, j = 0; + struct radius_attr_hdr *attr; + const u8 *data; + size_t dlen; + const u8 *fdata = NULL; /* points to found item */ + size_t fdlen = -1; + char *ret = NULL; + + /* find n-th valid Tunnel-Password attribute */ + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + if (attr == NULL || + attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) { + continue; + } + if (attr->length <= 5) + continue; + data = (const u8 *) (attr + 1); + dlen = attr->length - sizeof(*attr); + if (dlen <= 3 || dlen % 16 != 3) + continue; + j++; + if (j <= n) + continue; + + fdata = data; + fdlen = dlen; + break; + } + if (fdata == NULL) + goto out; + + /* alloc writable memory for decryption */ + buf = os_malloc(fdlen); + if (buf == NULL) + goto out; + os_memcpy(buf, fdata, fdlen); + buflen = fdlen; + + /* init pointers */ + salt = buf + 1; + str = buf + 3; + + /* decrypt blocks */ + pos = buf + buflen - 16; /* last block */ + while (pos >= str + 16) { /* all but the first block */ + addr[0] = secret; + len[0] = secret_len; + addr[1] = pos - 16; + len[1] = 16; + md5_vector(2, addr, len, hash); + + for (i = 0; i < 16; i++) + pos[i] ^= hash[i]; + + pos -= 16; + } + + /* decrypt first block */ + if (str != pos) + goto out; + addr[0] = secret; + len[0] = secret_len; + addr[1] = sent_msg->hdr->authenticator; + len[1] = 16; + addr[2] = salt; + len[2] = 2; + md5_vector(3, addr, len, hash); + + for (i = 0; i < 16; i++) + pos[i] ^= hash[i]; + + /* derive plaintext length from first subfield */ + *keylen = (unsigned char) str[0]; + if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) { + /* decryption error - invalid key length */ + goto out; + } + if (*keylen == 0) { + /* empty password */ + goto out; + } + + /* copy passphrase into new buffer */ + ret = os_malloc(*keylen); + if (ret) + os_memcpy(ret, str + 1, *keylen); + +out: + /* return new buffer */ + os_free(buf); + return ret; +} + + +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_calloc(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; +} + + +u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs) +{ + size_t i, j; + struct radius_attr_hdr *attr; + + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + + for (j = 0; attrs[j]; j++) { + if (attr->type == attrs[j]) + break; + } + + if (attrs[j] == 0) + return attr->type; /* unlisted attr */ + } + + return 0; +} diff --git a/contrib/hostapd/src/radius/radius.h b/contrib/hostapd/src/radius/radius.h index c30621dc2e..2031054b1d 100644 --- a/contrib/hostapd/src/radius/radius.h +++ b/contrib/hostapd/src/radius/radius.h @@ -1,15 +1,9 @@ /* - * hostapd / RADIUS message processing - * Copyright (c) 2002-2007, Jouni Malinen + * RADIUS message processing + * Copyright (c) 2002-2009, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RADIUS_H @@ -24,7 +18,7 @@ struct radius_hdr { u8 code; u8 identifier; - u16 length; /* including this header */ + be16 length; /* including this header */ u8 authenticator[16]; /* followed by length-20 octets of attributes */ } STRUCT_PACKED; @@ -37,6 +31,12 @@ enum { RADIUS_CODE_ACCESS_REQUEST = 1, RADIUS_CODE_ACCESS_CHALLENGE = 11, RADIUS_CODE_STATUS_SERVER = 12, RADIUS_CODE_STATUS_CLIENT = 13, + RADIUS_CODE_DISCONNECT_REQUEST = 40, + RADIUS_CODE_DISCONNECT_ACK = 41, + RADIUS_CODE_DISCONNECT_NAK = 42, + RADIUS_CODE_COA_REQUEST = 43, + RADIUS_CODE_COA_ACK = 44, + RADIUS_CODE_COA_NAK = 45, RADIUS_CODE_RESERVED = 255 }; @@ -82,13 +82,15 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_NAS_PORT_TYPE = 61, RADIUS_ATTR_TUNNEL_TYPE = 64, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65, + RADIUS_ATTR_TUNNEL_PASSWORD = 69, 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 + RADIUS_ATTR_NAS_IPV6_ADDRESS = 95, + RADIUS_ATTR_ERROR_CAUSE = 101 }; @@ -173,21 +175,7 @@ struct radius_ms_mppe_keys { }; -/* RADIUS message structure for new and parsed messages */ -struct radius_msg { - unsigned char *buf; - size_t buf_size; /* total size allocated for buf */ - size_t buf_used; /* bytes used in buf */ - - struct radius_hdr *hdr; - - size_t *attr_pos; /* array of indexes to attributes (number of bytes - * from buf to the beginning of - * struct radius_attr_hdr). */ - size_t attr_size; /* total size of the attribute pointer array */ - size_t attr_used; /* total number of attributes in the array */ -}; - +struct radius_msg; /* Default size to be allocated for new RADIUS messages */ #define RADIUS_DEFAULT_MSG_SIZE 1024 @@ -202,23 +190,30 @@ struct radius_msg { /* MAC address ASCII format for non-802.1X use */ #define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x" -struct radius_msg *radius_msg_new(u8 code, u8 identifier); -int radius_msg_initialize(struct radius_msg *msg, size_t init_len); -void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier); +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); +int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret, + size_t secret_len, + const struct radius_hdr *req_hdr); 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_verify_acct_req(struct radius_msg *msg, const u8 *secret, + size_t secret_len); +int radius_msg_verify_das_req(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); +struct wpabuf * radius_msg_get_eap(struct radius_msg *msg); int radius_msg_verify(struct radius_msg *msg, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, int auth); @@ -245,6 +240,9 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, 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); +char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, + const u8 *secret, size_t secret_len, + struct radius_msg *sent_msg, size_t n); static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type, u32 value) @@ -269,4 +267,21 @@ 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); + +u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs); + #endif /* RADIUS_H */ diff --git a/contrib/hostapd/src/radius/radius_client.c b/contrib/hostapd/src/radius/radius_client.c index 826acad678..762599668f 100644 --- a/contrib/hostapd/src/radius/radius_client.c +++ b/contrib/hostapd/src/radius/radius_client.c @@ -1,15 +1,9 @@ /* - * hostapd / RADIUS client - * Copyright (c) 2002-2005, Jouni Malinen + * RADIUS client + * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -20,68 +14,217 @@ #include "eloop.h" /* Defaults for RADIUS retransmit values (exponential backoff) */ -#define RADIUS_CLIENT_FIRST_WAIT 3 /* seconds */ -#define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */ -#define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts - * before entry is removed from retransmit - * list */ -#define RADIUS_CLIENT_MAX_ENTRIES 30 /* maximum number of entries in retransmit - * list (oldest will be removed, if this - * limit is exceeded) */ -#define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this - * many failed retry attempts */ +/** + * 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; }; -/* RADIUS message retransmit list */ +/** + * 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 { - u8 addr[ETH_ALEN]; /* STA/client address; used to find RADIUS messages - * for the same STA. */ + /** + * 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; - struct os_time last_attempt; - u8 *shared_secret; + /** + * last_attempt - Time of the last transmission attempt + */ + struct os_reltime 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; - int auth_serv_sock; /* socket for authentication RADIUS messages */ - int acct_serv_sock; /* socket for accounting RADIUS messages */ + /** + * 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; - int auth_sock; /* currently used socket */ - int acct_sock; /* currently used socket */ + /** + * 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; }; @@ -98,11 +241,26 @@ 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->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, @@ -123,8 +281,8 @@ int radius_client_register(struct radius_client_data *radius, num = &radius->num_auth_handlers; } - newh = os_realloc(*handlers, - (*num + 1) * sizeof(struct radius_rx_handler)); + newh = os_realloc_array(*handlers, *num + 1, + sizeof(struct radius_rx_handler)); if (newh == NULL) return -1; @@ -142,7 +300,7 @@ static void radius_client_handle_send_error(struct radius_client_data *radius, { #ifndef CONFIG_NATIVE_WINDOWS int _errno = errno; - perror("send[RADIUS]"); + wpa_printf(MSG_INFO, "send[RADIUS]: %s", strerror(errno)); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || _errno == EBADF) { hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, @@ -166,6 +324,7 @@ static int radius_client_retransmit(struct radius_client_data *radius, { struct hostapd_radius_servers *conf = radius->conf; int s; + struct wpabuf *buf; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { @@ -190,10 +349,11 @@ static int radius_client_retransmit(struct radius_client_data *radius, entry->attempts++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", - entry->msg->hdr->identifier); + radius_msg_get_hdr(entry->msg)->identifier); - os_get_time(&entry->last_attempt); - if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0) + os_get_reltime(&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; @@ -201,8 +361,7 @@ static int radius_client_retransmit(struct radius_client_data *radius, 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"); + wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); return 1; } @@ -214,7 +373,7 @@ 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; + struct os_reltime now; os_time_t first; struct radius_msg_list *entry, *prev, *tmp; int auth_failover = 0, acct_failover = 0; @@ -224,7 +383,7 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) if (!entry) return; - os_get_time(&now); + os_get_reltime(&now); first = 0; prev = NULL; @@ -322,7 +481,7 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) static void radius_client_update_timeout(struct radius_client_data *radius) { - struct os_time now; + struct os_reltime now; os_time_t first; struct radius_msg_list *entry; @@ -338,20 +497,21 @@ static void radius_client_update_timeout(struct radius_client_data *radius) first = entry->next_try; } - os_get_time(&now); + os_get_reltime(&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)); + " %ld seconds", (long int) (first - now.sec)); } static void radius_client_list_add(struct radius_client_data *radius, struct radius_msg *msg, - RadiusType msg_type, u8 *shared_secret, + RadiusType msg_type, + const u8 *shared_secret, size_t shared_secret_len, const u8 *addr) { struct radius_msg_list *entry, *prev; @@ -360,15 +520,13 @@ static void radius_client_list_add(struct radius_client_data *radius, /* No point in adding entries to retransmit queue since event * loop has already been terminated. */ radius_msg_free(msg); - os_free(msg); return; } entry = os_zalloc(sizeof(*entry)); if (entry == NULL) { - printf("Failed to add RADIUS packet into retransmit list\n"); + wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list"); radius_msg_free(msg); - os_free(msg); return; } @@ -378,7 +536,7 @@ static void radius_client_list_add(struct radius_client_data *radius, entry->msg_type = msg_type; entry->shared_secret = shared_secret; entry->shared_secret_len = shared_secret_len; - os_get_time(&entry->last_attempt); + os_get_reltime(&entry->last_attempt); entry->first_try = entry->last_attempt.sec; entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; entry->attempts = 1; @@ -388,8 +546,7 @@ static void radius_client_list_add(struct radius_client_data *radius, 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"); + wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits"); prev = NULL; while (entry->next) { prev = entry; @@ -437,15 +594,38 @@ static void radius_client_list_del(struct radius_client_data *radius, } +/** + * 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; - u8 *shared_secret; + 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. */ @@ -488,14 +668,15 @@ int radius_client_send(struct radius_client_data *radius, if (conf->msg_dumps) radius_msg_dump(msg); - res = send(s, msg->buf, msg->buf_used, 0); + 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; + return 0; } @@ -507,10 +688,11 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *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 os_reltime now; struct hostapd_radius_server *rconf; int invalid_authenticator = 0; @@ -526,31 +708,31 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); if (len < 0) { - perror("recv[RADIUS]"); + wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno)); 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"); + wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it"); return; } msg = radius_msg_parse(buf, len); if (msg == NULL) { - printf("Parsing incoming RADIUS frame failed\n"); + wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed"); 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 (msg->hdr->code) { + switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: rconf->access_accepts++; break; @@ -573,7 +755,8 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) if ((req->msg_type == msg_type || (req->msg_type == RADIUS_ACCT_INTERIM && msg_type == RADIUS_ACCT)) && - req->msg->hdr->identifier == msg->hdr->identifier) + radius_msg_get_hdr(req->msg)->identifier == + hdr->identifier) break; prev_req = req; @@ -585,11 +768,11 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) HOSTAPD_LEVEL_DEBUG, "No matching RADIUS request found (type=%d " "id=%d) - dropping packet", - msg_type, msg->hdr->identifier); + msg_type, hdr->identifier); goto fail; } - os_get_time(&now); + os_get_reltime(&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, @@ -614,7 +797,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) switch (res) { case RADIUS_RX_PROCESSED: radius_msg_free(msg); - os_free(msg); /* continue */ case RADIUS_RX_QUEUED: radius_client_msg_free(req); @@ -635,17 +817,24 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " "(type=%d code=%d id=%d)%s - dropping packet", - msg_type, msg->hdr->code, msg->hdr->identifier, + msg_type, hdr->code, hdr->identifier, invalid_authenticator ? " [INVALID AUTHENTICATOR]" : ""); radius_client_msg_free(req); fail: radius_msg_free(msg); - os_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; @@ -656,7 +845,7 @@ u8 radius_client_get_id(struct radius_client_data *radius) entry = radius->msgs; prev = NULL; while (entry) { - if (entry->msg->hdr->identifier == id) { + if (radius_msg_get_hdr(entry->msg)->identifier == id) { hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, @@ -681,6 +870,11 @@ u8 radius_client_get_id(struct radius_client_data *radius) } +/** + * 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; @@ -714,7 +908,7 @@ void radius_client_flush(struct radius_client_data *radius, int only_auth) static void radius_client_update_acct_msgs(struct radius_client_data *radius, - u8 *shared_secret, + const u8 *shared_secret, size_t shared_secret_len) { struct radius_msg_list *entry; @@ -844,13 +1038,14 @@ radius_change_server(struct radius_client_data *radius, } if (bind(sel_sock, cl_addr, claddrlen) < 0) { - perror("bind[radius]"); + wpa_printf(MSG_INFO, "bind[radius]: %s", + strerror(errno)); return -1; } } if (connect(sel_sock, addr, addrlen) < 0) { - perror("connect[radius]"); + wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); return -1; } @@ -926,8 +1121,8 @@ static int radius_client_disable_pmtu_discovery(int s) 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)); + wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s", + strerror(errno)); #endif return r; } @@ -940,7 +1135,8 @@ static int radius_client_init_auth(struct radius_client_data *radius) radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if (radius->auth_serv_sock < 0) - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); else { radius_client_disable_pmtu_discovery(radius->auth_serv_sock); ok++; @@ -949,7 +1145,8 @@ static int radius_client_init_auth(struct radius_client_data *radius) #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]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", + strerror(errno)); else ok++; #endif /* CONFIG_IPV6 */ @@ -965,8 +1162,7 @@ static int radius_client_init_auth(struct radius_client_data *radius) 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"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); return -1; } @@ -975,8 +1171,7 @@ static int radius_client_init_auth(struct radius_client_data *radius) 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"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); return -1; } #endif /* CONFIG_IPV6 */ @@ -992,7 +1187,8 @@ static int radius_client_init_acct(struct radius_client_data *radius) radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); if (radius->acct_serv_sock < 0) - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); else { radius_client_disable_pmtu_discovery(radius->acct_serv_sock); ok++; @@ -1001,7 +1197,8 @@ static int radius_client_init_acct(struct radius_client_data *radius) #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]"); + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", + strerror(errno)); else ok++; #endif /* CONFIG_IPV6 */ @@ -1017,8 +1214,7 @@ static int radius_client_init_acct(struct radius_client_data *radius) 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"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); return -1; } @@ -1027,8 +1223,7 @@ static int radius_client_init_acct(struct radius_client_data *radius) 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"); + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); return -1; } #endif /* CONFIG_IPV6 */ @@ -1037,6 +1232,16 @@ static int radius_client_init_acct(struct radius_client_data *radius) } +/** + * 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) { @@ -1071,6 +1276,10 @@ radius_client_init(void *ctx, struct hostapd_radius_servers *conf) } +/** + * 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) @@ -1096,7 +1305,18 @@ void radius_client_deinit(struct radius_client_data *radius) } -void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr) +/** + * 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; @@ -1224,6 +1444,13 @@ static int radius_client_dump_acct_server(char *buf, size_t buflen, } +/** + * 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) { @@ -1256,46 +1483,9 @@ int radius_client_get_mib(struct radius_client_data *radius, char *buf, } -static int radius_servers_diff(struct hostapd_radius_server *nserv, - struct hostapd_radius_server *oserv, - int num) +void radius_client_reconfig(struct radius_client_data *radius, + struct hostapd_radius_servers *conf) { - int i; - - for (i = 0; i < num; i++) { - if (hostapd_ip_diff(&nserv[i].addr, &oserv[i].addr) || - nserv[i].port != oserv[i].port || - nserv[i].shared_secret_len != oserv[i].shared_secret_len || - os_memcmp(nserv[i].shared_secret, oserv[i].shared_secret, - nserv[i].shared_secret_len) != 0) - return 1; - } - - return 0; -} - - -struct radius_client_data * -radius_client_reconfig(struct radius_client_data *old, void *ctx, - struct hostapd_radius_servers *oldconf, - struct hostapd_radius_servers *newconf) -{ - radius_client_flush(old, 0); - - if (newconf->retry_primary_interval != - oldconf->retry_primary_interval || - newconf->num_auth_servers != oldconf->num_auth_servers || - newconf->num_acct_servers != oldconf->num_acct_servers || - radius_servers_diff(newconf->auth_servers, oldconf->auth_servers, - newconf->num_auth_servers) || - radius_servers_diff(newconf->acct_servers, oldconf->acct_servers, - newconf->num_acct_servers)) { - hostapd_logger(ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, - "Reconfiguring RADIUS client"); - radius_client_deinit(old); - return radius_client_init(ctx, newconf); - } - - return old; + if (radius) + radius->conf = conf; } diff --git a/contrib/hostapd/src/radius/radius_client.h b/contrib/hostapd/src/radius/radius_client.h index 4fe9ba9a42..3db16aa282 100644 --- a/contrib/hostapd/src/radius/radius_client.h +++ b/contrib/hostapd/src/radius/radius_client.h @@ -1,15 +1,9 @@ /* - * hostapd / RADIUS client - * Copyright (c) 2002-2005, Jouni Malinen + * RADIUS client + * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RADIUS_CLIENT_H @@ -19,63 +13,222 @@ 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 { - /* MIB prefix for shared variables: - * @ = radiusAuth or radiusAcc depending on the type of the server */ - struct hostapd_ip_addr addr; /* @ServerAddress */ - int port; /* @ClientServerPortNumber */ + /** + * 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 */ - int index; /* @ServerIndex */ - int round_trip_time; /* @ClientRoundTripTime; in hundredths of a - * second */ - u32 requests; /* @Client{Access,}Requests */ - u32 retransmissions; /* @Client{Access,}Retransmissions */ - u32 access_accepts; /* radiusAuthClientAccessAccepts */ - u32 access_rejects; /* radiusAuthClientAccessRejects */ - u32 access_challenges; /* radiusAuthClientAccessChallenges */ - u32 responses; /* radiusAccClientResponses */ - u32 malformed_responses; /* @ClientMalformed{Access,}Responses */ - u32 bad_authenticators; /* @ClientBadAuthenticators */ - u32 timeouts; /* @ClientTimeouts */ - u32 unknown_types; /* @ClientUnknownTypes */ - u32 packets_dropped; /* @ClientPacketsDropped */ - /* @ClientPendingRequests: length of hapd->radius->msgs for matching - * msg_type */ + + /** + * 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 { - /* RADIUS Authentication and Accounting servers in priority order */ - struct hostapd_radius_server *auth_servers, *auth_server; + /** + * 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; - struct hostapd_radius_server *acct_servers, *acct_server; + + /** + * 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; - int acct_interim_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 /* used only with radius_client_send(); just like - * RADIUS_ACCT, but removes any pending interim - * RADIUS Accounting packages for the same STA - * before sending the new interim update */ + + /** + * 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; @@ -92,17 +245,15 @@ 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, u8 *addr); +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); -struct radius_client_data * -radius_client_reconfig(struct radius_client_data *old, void *ctx, - struct hostapd_radius_servers *oldconf, - struct hostapd_radius_servers *newconf); +void radius_client_reconfig(struct radius_client_data *radius, + struct hostapd_radius_servers *conf); #endif /* RADIUS_CLIENT_H */ diff --git a/contrib/hostapd/src/radius/radius_das.c b/contrib/hostapd/src/radius/radius_das.c new file mode 100644 index 0000000000..b2a27735b3 --- /dev/null +++ b/contrib/hostapd/src/radius/radius_das.c @@ -0,0 +1,362 @@ +/* + * RADIUS Dynamic Authorization Server (DAS) (RFC 5176) + * Copyright (c) 2012-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/ip_addr.h" +#include "radius.h" +#include "radius_das.h" + + +struct radius_das_data { + int sock; + u8 *shared_secret; + size_t shared_secret_len; + struct hostapd_ip_addr client_addr; + unsigned int time_window; + int require_event_timestamp; + void *ctx; + enum radius_das_res (*disconnect)(void *ctx, + struct radius_das_attrs *attr); +}; + + +static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, + struct radius_msg *msg, + const char *abuf, + int from_port) +{ + struct radius_hdr *hdr; + struct radius_msg *reply; + u8 allowed[] = { + RADIUS_ATTR_USER_NAME, + RADIUS_ATTR_CALLING_STATION_ID, + RADIUS_ATTR_ACCT_SESSION_ID, + RADIUS_ATTR_EVENT_TIMESTAMP, + RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + 0 + }; + int error = 405; + u8 attr; + enum radius_das_res res; + struct radius_das_attrs attrs; + u8 *buf; + size_t len; + char tmp[100]; + u8 sta_addr[ETH_ALEN]; + + hdr = radius_msg_get_hdr(msg); + + attr = radius_msg_find_unlisted_attr(msg, allowed); + if (attr) { + wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in " + "Disconnect-Request from %s:%d", attr, + abuf, from_port); + error = 401; + goto fail; + } + + os_memset(&attrs, 0, sizeof(attrs)); + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, + &buf, &len, NULL) == 0) { + if (len >= sizeof(tmp)) + len = sizeof(tmp) - 1; + os_memcpy(tmp, buf, len); + tmp[len] = '\0'; + if (hwaddr_aton2(tmp, sta_addr) < 0) { + wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id " + "'%s' from %s:%d", tmp, abuf, from_port); + error = 407; + goto fail; + } + attrs.sta_addr = sta_addr; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, + &buf, &len, NULL) == 0) { + attrs.user_name = buf; + attrs.user_name_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + &buf, &len, NULL) == 0) { + attrs.acct_session_id = buf; + attrs.acct_session_id_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) == 0) { + attrs.cui = buf; + attrs.cui_len = len; + } + + res = das->disconnect(das->ctx, &attrs); + switch (res) { + case RADIUS_DAS_NAS_MISMATCH: + wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d", + abuf, from_port); + error = 403; + break; + case RADIUS_DAS_SESSION_NOT_FOUND: + wpa_printf(MSG_INFO, "DAS: Session not found for request from " + "%s:%d", abuf, from_port); + error = 503; + break; + case RADIUS_DAS_SUCCESS: + error = 0; + break; + } + +fail: + reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK : + RADIUS_CODE_DISCONNECT_ACK, hdr->identifier); + if (reply == NULL) + return NULL; + + if (error) { + if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, + error)) { + radius_msg_free(reply); + return NULL; + } + } + + return reply; +} + + +static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct radius_das_data *das = eloop_ctx; + u8 buf[1500]; + union { + struct sockaddr_storage ss; + struct sockaddr_in sin; +#ifdef CONFIG_IPV6 + struct sockaddr_in6 sin6; +#endif /* CONFIG_IPV6 */ + } from; + char abuf[50]; + int from_port = 0; + socklen_t fromlen; + int len; + struct radius_msg *msg, *reply = NULL; + struct radius_hdr *hdr; + struct wpabuf *rbuf; + u32 val; + int res; + struct os_time now; + + fromlen = sizeof(from); + len = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *) &from.ss, &fromlen); + if (len < 0) { + wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno)); + return; + } + + os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); + from_port = ntohs(from.sin.sin_port); + + wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d", + len, abuf, from_port); + if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { + wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client"); + return; + } + + msg = radius_msg_parse(buf, len); + if (msg == NULL) { + wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet " + "from %s:%d failed", abuf, from_port); + return; + } + + if (wpa_debug_level <= MSG_MSGDUMP) + radius_msg_dump(msg); + + if (radius_msg_verify_das_req(msg, das->shared_secret, + das->shared_secret_len)) { + wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet " + "from %s:%d - drop", abuf, from_port); + goto fail; + } + + os_get_time(&now); + res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + (u8 *) &val, 4); + if (res == 4) { + u32 timestamp = ntohl(val); + if ((unsigned int) abs(now.sec - timestamp) > + das->time_window) { + wpa_printf(MSG_DEBUG, "DAS: Unacceptable " + "Event-Timestamp (%u; local time %u) in " + "packet from %s:%d - drop", + timestamp, (unsigned int) now.sec, + abuf, from_port); + goto fail; + } + } else if (das->require_event_timestamp) { + wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet " + "from %s:%d - drop", abuf, from_port); + goto fail; + } + + hdr = radius_msg_get_hdr(msg); + + switch (hdr->code) { + case RADIUS_CODE_DISCONNECT_REQUEST: + reply = radius_das_disconnect(das, msg, abuf, from_port); + break; + case RADIUS_CODE_COA_REQUEST: + /* TODO */ + reply = radius_msg_new(RADIUS_CODE_COA_NAK, + hdr->identifier); + if (reply == NULL) + break; + + /* Unsupported Service */ + if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, + 405)) { + radius_msg_free(reply); + reply = NULL; + break; + } + break; + default: + wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in " + "packet from %s:%d", + hdr->code, abuf, from_port); + } + + if (reply) { + wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port); + + if (!radius_msg_add_attr_int32(reply, + RADIUS_ATTR_EVENT_TIMESTAMP, + now.sec)) { + wpa_printf(MSG_DEBUG, "DAS: Failed to add " + "Event-Timestamp attribute"); + } + + if (radius_msg_finish_das_resp(reply, das->shared_secret, + das->shared_secret_len, hdr) < + 0) { + wpa_printf(MSG_DEBUG, "DAS: Failed to add " + "Message-Authenticator attribute"); + } + + if (wpa_debug_level <= MSG_MSGDUMP) + radius_msg_dump(reply); + + rbuf = radius_msg_get_buf(reply); + res = sendto(das->sock, wpabuf_head(rbuf), + wpabuf_len(rbuf), 0, + (struct sockaddr *) &from.ss, fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s", + abuf, from_port, strerror(errno)); + } + } + +fail: + radius_msg_free(msg); + radius_msg_free(reply); +} + + +static int radius_das_open_socket(int port) +{ + int s; + struct sockaddr_in addr; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno)); + return -1; + } + + 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) { + wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno)); + close(s); + return -1; + } + + return s; +} + + +struct radius_das_data * +radius_das_init(struct radius_das_conf *conf) +{ + struct radius_das_data *das; + + if (conf->port == 0 || conf->shared_secret == NULL || + conf->client_addr == NULL) + return NULL; + + das = os_zalloc(sizeof(*das)); + if (das == NULL) + return NULL; + + das->time_window = conf->time_window; + das->require_event_timestamp = conf->require_event_timestamp; + das->ctx = conf->ctx; + das->disconnect = conf->disconnect; + + os_memcpy(&das->client_addr, conf->client_addr, + sizeof(das->client_addr)); + + das->shared_secret = os_malloc(conf->shared_secret_len); + if (das->shared_secret == NULL) { + radius_das_deinit(das); + return NULL; + } + os_memcpy(das->shared_secret, conf->shared_secret, + conf->shared_secret_len); + das->shared_secret_len = conf->shared_secret_len; + + das->sock = radius_das_open_socket(conf->port); + if (das->sock < 0) { + wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS " + "DAS"); + radius_das_deinit(das); + return NULL; + } + + if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL)) + { + radius_das_deinit(das); + return NULL; + } + + return das; +} + + +void radius_das_deinit(struct radius_das_data *das) +{ + if (das == NULL) + return; + + if (das->sock >= 0) { + eloop_unregister_read_sock(das->sock); + close(das->sock); + } + + os_free(das->shared_secret); + os_free(das); +} diff --git a/contrib/hostapd/src/radius/radius_das.h b/contrib/hostapd/src/radius/radius_das.h new file mode 100644 index 0000000000..738b18b059 --- /dev/null +++ b/contrib/hostapd/src/radius/radius_das.h @@ -0,0 +1,47 @@ +/* + * RADIUS Dynamic Authorization Server (DAS) + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RADIUS_DAS_H +#define RADIUS_DAS_H + +struct radius_das_data; + +enum radius_das_res { + RADIUS_DAS_SUCCESS, + RADIUS_DAS_NAS_MISMATCH, + RADIUS_DAS_SESSION_NOT_FOUND +}; + +struct radius_das_attrs { + const u8 *sta_addr; + const u8 *user_name; + size_t user_name_len; + const u8 *acct_session_id; + size_t acct_session_id_len; + const u8 *cui; + size_t cui_len; +}; + +struct radius_das_conf { + int port; + const u8 *shared_secret; + size_t shared_secret_len; + const struct hostapd_ip_addr *client_addr; + unsigned int time_window; + int require_event_timestamp; + void *ctx; + enum radius_das_res (*disconnect)(void *ctx, + struct radius_das_attrs *attr); +}; + +struct radius_das_data * +radius_das_init(struct radius_das_conf *conf); + +void radius_das_deinit(struct radius_das_data *data); + +#endif /* RADIUS_DAS_H */ diff --git a/contrib/hostapd/src/radius/radius_server.c b/contrib/hostapd/src/radius/radius_server.c index 4f399bcfd1..1063d65ace 100644 --- a/contrib/hostapd/src/radius/radius_server.c +++ b/contrib/hostapd/src/radius/radius_server.c @@ -1,15 +1,9 @@ /* - * hostapd / RADIUS authentication server - * Copyright (c) 2005-2008, Jouni Malinen + * RADIUS authentication server + * Copyright (c) 2005-2009, 2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -18,12 +12,22 @@ #include "common.h" #include "radius.h" #include "eloop.h" -#include "defs.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; @@ -31,6 +35,9 @@ 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; @@ -44,6 +51,9 @@ struct radius_server_counters { u32 unknown_types; }; +/** + * struct radius_session - Internal RADIUS server data for a session + */ struct radius_session { struct radius_session *next; struct radius_client *client; @@ -62,6 +72,9 @@ struct radius_session { 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; @@ -76,35 +89,214 @@ struct radius_client { 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; + + /** + * pwd_group - The D-H group assigned for EAP-pwd + * + * If EAP-pwd is not used it can be set to zero. + */ + u16 pwd_group; + + /** + * server_id - Server identity + */ + const char *server_id; + + /** + * 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; - struct os_time start_time; + + /** + * start_time - Timestamp of server start + */ + struct os_reltime 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; + +#ifdef CONFIG_RADIUS_TEST + char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ +}; -extern int wpa_debug_level; #define RADIUS_DEBUG(args...) \ wpa_printf(MSG_DEBUG, "RADIUS SRV: " args) @@ -182,15 +374,9 @@ static void radius_server_session_free(struct radius_server_data *data, 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); - if (sess->last_msg) { - radius_msg_free(sess->last_msg); - os_free(sess->last_msg); - } + radius_msg_free(sess->last_msg); os_free(sess->last_from_addr); - if (sess->last_reply) { - radius_msg_free(sess->last_reply); - os_free(sess->last_reply); - } + radius_msg_free(sess->last_reply); os_free(sess); data->num_sess--; } @@ -313,6 +499,7 @@ radius_server_get_new_session(struct radius_server_data *data, 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; @@ -326,6 +513,9 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind; eap_conf.tnc = data->tnc; eap_conf.wps = data->wps; + eap_conf.pwd_group = data->pwd_group; + eap_conf.server_id = (const u8 *) data->server_id; + eap_conf.server_id_len = os_strlen(data->server_id); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { @@ -353,6 +543,7 @@ radius_server_encapsulate_eap(struct radius_server_data *data, 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; @@ -365,7 +556,7 @@ radius_server_encapsulate_eap(struct radius_server_data *data, code = RADIUS_CODE_ACCESS_CHALLENGE; } - msg = radius_msg_new(code, request->hdr->identifier); + msg = radius_msg_new(code, hdr->identifier); if (msg == NULL) { RADIUS_DEBUG("Failed to allocate reply message"); return NULL; @@ -386,12 +577,30 @@ radius_server_encapsulate_eap(struct radius_server_data *data, if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { int len; +#ifdef CONFIG_RADIUS_TEST + if (data->dump_msk_file) { + FILE *f; + char buf[2 * 64 + 1]; + f = fopen(data->dump_msk_file, "a"); + if (f) { + len = sess->eap_if->eapKeyDataLen; + if (len > 64) + len = 64; + len = wpa_snprintf_hex( + buf, sizeof(buf), + sess->eap_if->eapKeyData, len); + buf[len] = '\0'; + fprintf(f, "%s\n", buf); + fclose(f); + } + } +#endif /* CONFIG_RADIUS_TEST */ if (sess->eap_if->eapKeyDataLen > 64) { len = 32; } else { len = sess->eap_if->eapKeyDataLen / 2; } - if (!radius_msg_add_mppe_keys(msg, request->hdr->authenticator, + if (!radius_msg_add_mppe_keys(msg, hdr->authenticator, (u8 *) client->shared_secret, client->shared_secret_len, sess->eap_if->eapKeyData + len, @@ -404,13 +613,12 @@ radius_server_encapsulate_eap(struct radius_server_data *data, 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); - os_free(msg); return NULL; } if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, - request->hdr->authenticator) < 0) { + hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); } @@ -427,12 +635,13 @@ static int radius_server_reject(struct radius_server_data *data, 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, - request->hdr->identifier); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier); if (msg == NULL) { return -1; } @@ -449,13 +658,13 @@ static int radius_server_reject(struct radius_server_data *data, 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); - os_free(msg); return -1; } if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, - request->hdr->authenticator) < 0) { + hdr->authenticator) < + 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); } @@ -465,14 +674,14 @@ static int radius_server_reject(struct radius_server_data *data, data->counters.access_rejects++; client->counters.access_rejects++; - if (sendto(data->auth_sock, msg->buf, msg->buf_used, 0, + 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]"); + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno)); ret = -1; } radius_msg_free(msg); - os_free(msg); return ret; } @@ -485,8 +694,7 @@ static int radius_server_request(struct radius_server_data *data, const char *from_addr, int from_port, struct radius_session *force_sess) { - u8 *eap = NULL; - size_t eap_len; + struct wpabuf *eap = NULL; int res, state_included = 0; u8 statebuf[4]; unsigned int state; @@ -526,19 +734,22 @@ static int radius_server_request(struct radius_server_data *data, } if (sess->last_from_port == from_port && - sess->last_identifier == msg->hdr->identifier && - os_memcmp(sess->last_authenticator, msg->hdr->authenticator, 16) == - 0) { + 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) { - res = sendto(data->auth_sock, sess->last_reply->buf, - sess->last_reply->buf_used, 0, + 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]"); + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", + strerror(errno)); } return 0; } @@ -548,7 +759,7 @@ static int radius_server_request(struct radius_server_data *data, return -1; } - eap = radius_msg_get_eap(msg, &eap_len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", from_addr); @@ -557,7 +768,7 @@ static int radius_server_request(struct radius_server_data *data, return -1; } - RADIUS_DUMP("Received EAP data", eap, eap_len); + RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap)); /* FIX: if Code is Request, Success, or Failure, send Access-Reject; * RFC3579 Sect. 2.6.2. @@ -567,10 +778,7 @@ static int radius_server_request(struct radius_server_data *data, * 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->eapRespData = eap; sess->eap_if->eapResp = TRUE; eap_server_sm_step(sess->eap); @@ -583,10 +791,7 @@ static int radius_server_request(struct radius_server_data *data, RADIUS_DEBUG("No EAP data from the state machine, but eapFail " "set"); } else if (eap_sm_method_pending(sess->eap)) { - if (sess->last_msg) { - radius_msg_free(sess->last_msg); - os_free(sess->last_msg); - } + radius_msg_free(sess->last_msg); sess->last_msg = msg; sess->last_from_port = from_port; os_free(sess->last_from_addr); @@ -609,12 +814,15 @@ static int radius_server_request(struct radius_server_data *data, 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 (reply->hdr->code) { + switch (radius_msg_get_hdr(reply)->code) { case RADIUS_CODE_ACCESS_ACCEPT: data->counters.access_accepts++; client->counters.access_accepts++; @@ -628,20 +836,20 @@ static int radius_server_request(struct radius_server_data *data, client->counters.access_challenges++; break; } - res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0, + 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]"); - } - if (sess->last_reply) { - radius_msg_free(sess->last_reply); - os_free(sess->last_reply); + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", + strerror(errno)); } + radius_msg_free(sess->last_reply); sess->last_reply = reply; sess->last_from_port = from_port; - sess->last_identifier = msg->hdr->identifier; - os_memcpy(sess->last_authenticator, msg->hdr->authenticator, - 16); + 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++; @@ -689,7 +897,8 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, (struct sockaddr *) &from.ss, &fromlen); if (len < 0) { - perror("recvfrom[radius_server]"); + wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s", + strerror(errno)); goto fail; } @@ -740,8 +949,9 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, radius_msg_dump(msg); } - if (msg->hdr->code != RADIUS_CODE_ACCESS_REQUEST) { - RADIUS_DEBUG("Unexpected RADIUS code %d", msg->hdr->code); + 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; @@ -764,10 +974,7 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, return; /* msg was stored with the session */ fail: - if (msg) { - radius_msg_free(msg); - os_free(msg); - } + radius_msg_free(msg); os_free(buf); } @@ -795,7 +1002,7 @@ static int radius_server_open_socket(int port) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno)); return -1; } @@ -805,7 +1012,7 @@ static int radius_server_open_socket(int port) addr.sin_family = AF_INET; addr.sin_port = htons(port); if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); + wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); close(s); return -1; } @@ -822,7 +1029,8 @@ static int radius_server_open_socket6(int port) s = socket(PF_INET6, SOCK_DGRAM, 0); if (s < 0) { - perror("socket[IPv6]"); + wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s", + strerror(errno)); return -1; } @@ -831,7 +1039,7 @@ static int radius_server_open_socket6(int port) 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"); + wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); close(s); return -1; } @@ -1026,6 +1234,15 @@ radius_server_read_clients(const char *client_file, int ipv6) } +/** + * 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) { @@ -1033,8 +1250,7 @@ radius_server_init(struct radius_server_conf *conf) #ifndef CONFIG_IPV6 if (conf->ipv6) { - fprintf(stderr, "RADIUS server compiled without IPv6 " - "support.\n"); + wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support"); return NULL; } #endif /* CONFIG_IPV6 */ @@ -1043,10 +1259,11 @@ radius_server_init(struct radius_server_conf *conf) if (data == NULL) return NULL; - os_get_time(&data->start_time); + os_get_reltime(&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); @@ -1070,6 +1287,8 @@ radius_server_init(struct radius_server_conf *conf) data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; data->tnc = conf->tnc; data->wps = conf->wps; + data->pwd_group = conf->pwd_group; + data->server_id = conf->server_id; 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) { @@ -1079,10 +1298,15 @@ radius_server_init(struct radius_server_conf *conf) } } +#ifdef CONFIG_RADIUS_TEST + if (conf->dump_msk_file) + data->dump_msk_file = os_strdup(conf->dump_msk_file); +#endif /* CONFIG_RADIUS_TEST */ + data->clients = radius_server_read_clients(conf->client_file, conf->ipv6); if (data->clients == NULL) { - printf("No RADIUS clients configured.\n"); + wpa_printf(MSG_ERROR, "No RADIUS clients configured"); radius_server_deinit(data); return NULL; } @@ -1094,8 +1318,7 @@ radius_server_init(struct radius_server_conf *conf) #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"); + wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server"); radius_server_deinit(data); return NULL; } @@ -1110,6 +1333,10 @@ radius_server_init(struct radius_server_conf *conf) } +/** + * 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) @@ -1126,17 +1353,27 @@ void radius_server_deinit(struct radius_server_data *data) os_free(data->eap_fast_a_id); os_free(data->eap_fast_a_id_info); os_free(data->eap_req_id_text); +#ifdef CONFIG_RADIUS_TEST + os_free(data->dump_msk_file); +#endif /* CONFIG_RADIUS_TEST */ 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 os_reltime now; struct radius_client *cli; /* RFC 2619 - RADIUS Authentication Server MIB */ @@ -1147,7 +1384,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, pos = buf; end = buf + buflen; - os_get_time(&now); + os_get_reltime(&now); uptime = (now.sec - data->start_time.sec) * 100 + ((now.usec - data->start_time.usec) / 10000) % 100; ret = os_snprintf(pos, end - pos, @@ -1269,6 +1506,14 @@ static struct eapol_callbacks radius_server_eapol_cb = }; +/** + * 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; @@ -1307,5 +1552,4 @@ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) return; /* msg was stored with the session */ radius_msg_free(msg); - os_free(msg); } diff --git a/contrib/hostapd/src/radius/radius_server.h b/contrib/hostapd/src/radius/radius_server.h index d5fb6a1e3f..284bd59d72 100644 --- a/contrib/hostapd/src/radius/radius_server.h +++ b/contrib/hostapd/src/radius/radius_server.h @@ -1,15 +1,9 @@ /* - * hostapd / RADIUS authentication server - * Copyright (c) 2005-2007, Jouni Malinen + * RADIUS authentication server + * Copyright (c) 2005-2009, 2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RADIUS_SERVER_H @@ -18,31 +12,200 @@ 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; + + /** + * pwd_group - EAP-pwd D-H group + * + * This is used to select which D-H group to use with EAP-pwd. + */ + u16 pwd_group; + + /** + * server_id - Server identity + */ + const char *server_id; + + /** + * 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; + +#ifdef CONFIG_RADIUS_TEST + const char *dump_msk_file; +#endif /* CONFIG_RADIUS_TEST */ +}; -#ifdef RADIUS_SERVER struct radius_server_data * radius_server_init(struct radius_server_conf *conf); @@ -54,29 +217,4 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx); -#else /* RADIUS_SERVER */ - -static inline struct radius_server_data * -radius_server_init(struct radius_server_conf *conf) -{ - return NULL; -} - -static inline void radius_server_deinit(struct radius_server_data *data) -{ -} - -static inline int radius_server_get_mib(struct radius_server_data *data, - char *buf, size_t buflen) -{ - return 0; -} - -static inline void -radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) -{ -} - -#endif /* RADIUS_SERVER */ - #endif /* RADIUS_SERVER_H */ diff --git a/contrib/hostapd/src/rsn_supp/peerkey.c b/contrib/hostapd/src/rsn_supp/peerkey.c index 45c256a027..cb86dfbcc4 100644 --- a/contrib/hostapd/src/rsn_supp/peerkey.c +++ b/contrib/hostapd/src/rsn_supp/peerkey.c @@ -2,14 +2,8 @@ * WPA Supplicant - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,13 +11,14 @@ #ifdef CONFIG_PEERKEY #include "common.h" -#include "sha1.h" -#include "sha256.h" #include "eloop.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" #include "wpa.h" #include "wpa_i.h" #include "wpa_ie.h" -#include "ieee802_11_defs.h" #include "peerkey.h" @@ -222,20 +217,17 @@ static int wpa_supplicant_process_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 { + cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & + sm->allowed_pairwise_cipher, 0); + if (cipher < 0) { 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; } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); /* 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 @@ -254,8 +246,8 @@ static int wpa_supplicant_process_smk_m2( peerkey->use_sha256 = 1; #endif /* CONFIG_IEEE80211W */ - if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, + if (random_get_bytes(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; @@ -272,10 +264,7 @@ static int wpa_supplicant_process_smk_m2( /* 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); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher)); pos += RSN_SELECTOR_LEN; hdr->len = (pos - peerkey->rsnie_p) - 2; @@ -349,7 +338,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, msg->type = EAPOL_KEY_TYPE_RSN; - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -357,7 +346,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, 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) + if (peerkey->cipher != WPA_CIPHER_TKIP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); @@ -370,8 +359,8 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, 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->ctx, MSG_WARNING, + if (random_get_bytes(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; @@ -408,7 +397,7 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, msg->type = EAPOL_KEY_TYPE_RSN; - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -417,7 +406,7 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; WPA_PUT_BE16(msg->key_info, key_info); - if (peerkey->cipher == WPA_CIPHER_CCMP) + if (peerkey->cipher != WPA_CIPHER_TKIP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); @@ -501,14 +490,9 @@ static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, 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 { + cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & + sm->allowed_pairwise_cipher, 0); + if (cipher < 0) { 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, @@ -517,6 +501,9 @@ static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, /* TODO: abort negotiation */ return -1; } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); + peerkey->cipher = cipher; return 0; } @@ -529,7 +516,6 @@ static int wpa_supplicant_process_smk_m45( 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 " @@ -581,10 +567,8 @@ static int wpa_supplicant_process_smk_m45( 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 */ + lifetime = 1000000000; /* avoid overflowing eloop time */ peerkey->lifetime = lifetime; - os_get_time(&now); - peerkey->expiration = now.sec + lifetime; eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, sm, peerkey); @@ -634,9 +618,11 @@ static int wpa_supplicant_process_smk_error( 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->ctx, MSG_INFO, + 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, @@ -695,8 +681,8 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, return; } - if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, + if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for PNonce"); return; } @@ -747,7 +733,6 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, struct wpa_eapol_ie_parse *kde) { u32 lifetime; - struct os_time now; if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime)) return; @@ -766,8 +751,6 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, 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); @@ -1019,7 +1002,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) return -1; } - if (sm->pairwise_cipher == WPA_CIPHER_CCMP) + if (sm->pairwise_cipher != WPA_CIPHER_TKIP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; @@ -1058,17 +1041,8 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) 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++; - } + count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher); + pos += count * RSN_SELECTOR_LEN; WPA_PUT_LE16(count_pos, count); hdr->len = (pos - peerkey->rsnie_i) - 2; @@ -1095,8 +1069,8 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) 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->ctx, MSG_WARNING, + if (random_get_bytes(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); @@ -1136,8 +1110,9 @@ void peerkey_deinit(struct wpa_sm *sm) while (peerkey) { prev = peerkey; peerkey = peerkey->next; - os_free(prev); + wpa_supplicant_peerkey_free(sm, prev); } + sm->peerkey = NULL; } diff --git a/contrib/hostapd/src/rsn_supp/peerkey.h b/contrib/hostapd/src/rsn_supp/peerkey.h index 2613127c3e..f420691ac7 100644 --- a/contrib/hostapd/src/rsn_supp/peerkey.h +++ b/contrib/hostapd/src/rsn_supp/peerkey.h @@ -2,14 +2,8 @@ * WPA Supplicant - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PEERKEY_H @@ -30,7 +24,6 @@ struct wpa_peerkey { 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; diff --git a/contrib/hostapd/src/rsn_supp/pmksa_cache.c b/contrib/hostapd/src/rsn_supp/pmksa_cache.c index f8373de6dd..09608153f1 100644 --- a/contrib/hostapd/src/rsn_supp/pmksa_cache.c +++ b/contrib/hostapd/src/rsn_supp/pmksa_cache.c @@ -1,29 +1,21 @@ /* * WPA Supplicant - RSN PMKSA cache - * Copyright (c) 2004-2008, Jouni Malinen + * Copyright (c) 2004-2009, 2011-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "wpa.h" #include "eloop.h" -#include "sha1.h" -#include "sha256.h" -#include "wpa_i.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) +#ifdef IEEE8021X_EAPOL static const int pmksa_cache_max_entries = 32; @@ -33,44 +25,11 @@ struct rsn_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); + enum pmksa_free_reason reason); void *ctx; }; -/** - * rsn_pmkid - Calculate PMK identifier - * @pmk: Pairwise master key - * @pmk_len: Length of pmk in bytes - * @aa: Authenticator address - * @spa: Supplicant address - * @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) - */ -static void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, - const u8 *spa, u8 *pmkid, int use_sha256) -{ - char *title = "PMK Name"; - const u8 *addr[3]; - const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA256_MAC_LEN]; - - addr[0] = (u8 *) title; - addr[1] = aa; - addr[2] = spa; - -#ifdef CONFIG_IEEE80211W - if (use_sha256) - hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); - else -#endif /* CONFIG_IEEE80211W */ - hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); - os_memcpy(pmkid, hash, PMKID_LEN); -} - - static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); @@ -82,10 +41,11 @@ static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry, - int replace) + enum pmksa_free_reason reason) { + wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); pmksa->pmksa_count--; - pmksa->free_cb(entry, pmksa->ctx, replace); + pmksa->free_cb(entry, pmksa->ctx, reason); _pmksa_cache_free_entry(entry); } @@ -93,15 +53,15 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&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_free_entry(pmksa, entry, PMKSA_EXPIRE); } pmksa_cache_set_expiration(pmksa); @@ -120,20 +80,20 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) { int sec; struct rsn_pmksa_cache_entry *entry; - struct os_time now; + struct os_reltime 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); + os_get_reltime(&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); + pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL); if (entry) { sec = pmksa->pmksa->reauth_time - now.sec; if (sec < 0) @@ -165,9 +125,9 @@ 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; + struct os_reltime now; - if (pmksa->sm->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN) + if (pmk_len > PMK_LEN) return NULL; entry = os_zalloc(sizeof(*entry)); @@ -177,7 +137,7 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t 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); + os_get_reltime(&now); entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; @@ -204,22 +164,23 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, 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; - } + + /* + * If OKC is used, there may be other PMKSA cache + * entries based on the same PMK. These needs to be + * flushed so that a new entry can be created based on + * the new PMK. Only clear other entries if they have a + * matching PMK and this PMK has been used successfully + * with the current AP, i.e., if opportunistic flag has + * been cleared in wpa_supplicant_key_neg_complete(). + */ wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " - "the current AP"); - pmksa_cache_free_entry(pmksa, pos, 1); + "the current AP and any PMKSA cache entry " + "that was based on the old PMK"); + if (!pos->opportunistic) + pmksa_cache_flush(pmksa, network_ctx, pos->pmk, + pos->pmk_len); + pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); break; } prev = pos; @@ -229,12 +190,25 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, 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); + + if (pos == pmksa->sm->cur_pmksa) { + /* + * Never remove the current PMKSA cache entry, since + * it's in use, and removing it triggers a needless + * deauthentication. + */ + pos = pos->next; + pmksa->pmksa->next = pos ? pos->next : NULL; + } else + pmksa->pmksa = pos->next; + + if (pos) { + wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " + "PMKSA cache entry (for " MACSTR ") to " + "make room for new one", + MAC2STR(pos->aa)); + pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); + } } /* Add the new entry; order by expiration time */ @@ -255,14 +229,54 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, prev->next = entry; } pmksa->pmksa_count++; - wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, - MAC2STR(entry->aa)); + wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR + " network_ctx=%p", MAC2STR(entry->aa), network_ctx); wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); return entry; } +/** + * pmksa_cache_flush - Flush PMKSA cache entries for a specific network + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @network_ctx: Network configuration context or %NULL to flush all entries + * @pmk: PMK to match for or %NYLL to match all PMKs + * @pmk_len: PMK length + */ +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len) +{ + struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; + int removed = 0; + + entry = pmksa->pmksa; + while (entry) { + if ((entry->network_ctx == network_ctx || + network_ctx == NULL) && + (pmk == NULL || + (pmk_len == entry->pmk_len && + os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { + wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " + "for " MACSTR, MAC2STR(entry->aa)); + if (prev) + prev->next = entry->next; + else + pmksa->pmksa = entry->next; + tmp = entry; + entry = entry->next; + pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); + removed++; + } else { + prev = entry; + entry = entry->next; + } + } + if (removed) + pmksa_cache_set_expiration(pmksa); +} + + /** * pmksa_cache_deinit - Free all entries in PMKSA cache * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() @@ -291,16 +305,19 @@ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) * @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 + * @network_ctx: Network context 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) + const u8 *aa, const u8 *pmkid, + const void *network_ctx) { 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)) + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && + (network_ctx == NULL || network_ctx == entry->network_ctx)) return entry; entry = entry->next; } @@ -308,22 +325,6 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, } -/** - * 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, @@ -362,6 +363,7 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); if (network_ctx == NULL) return NULL; while (entry) { @@ -419,27 +421,39 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, int try_opportunistic) { struct rsn_pmksa_cache *pmksa = sm->pmksa; + wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " + "try_opportunistic=%d", network_ctx, try_opportunistic); + if (pmkid) + wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", + pmkid, PMKID_LEN); + if (bssid) + wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, + MAC2STR(bssid)); + sm->cur_pmksa = NULL; if (pmkid) - sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid); + sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, + network_ctx); if (sm->cur_pmksa == NULL && bssid) - sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL); + sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, + network_ctx); 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", + wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", sm->cur_pmksa->pmkid, PMKID_LEN); return 0; } + wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); return -1; } /** * pmksa_cache_list - Dump text list of entries in PMKSA cache - * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @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 @@ -447,14 +461,14 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, * 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 wpa_sm *sm, char *buf, size_t len) +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; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); ret = os_snprintf(pos, buf + len - pos, "Index / AA / PMKID / expiration (in seconds) / " "opportunistic\n"); @@ -462,7 +476,7 @@ int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) return pos - buf; pos += ret; i = 0; - entry = sm->pmksa->pmksa; + entry = pmksa->pmksa; while (entry) { i++; ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", @@ -493,7 +507,7 @@ int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) */ struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace), + void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { struct rsn_pmksa_cache *pmksa; @@ -508,4 +522,4 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, return pmksa; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ diff --git a/contrib/hostapd/src/rsn_supp/pmksa_cache.h b/contrib/hostapd/src/rsn_supp/pmksa_cache.h index a329b25b08..6cbf89aa43 100644 --- a/contrib/hostapd/src/rsn_supp/pmksa_cache.h +++ b/contrib/hostapd/src/rsn_supp/pmksa_cache.h @@ -1,15 +1,9 @@ /* * wpa_supplicant - WPA2/RSN PMKSA cache functions - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PMKSA_CACHE_H @@ -44,20 +38,26 @@ struct rsn_pmksa_cache_entry { struct rsn_pmksa_cache; -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +enum pmksa_free_reason { + PMKSA_FREE, + PMKSA_REPLACE, + PMKSA_EXPIRE, +}; + +#ifdef IEEE8021X_EAPOL struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace), + void *ctx, enum pmksa_free_reason reason), 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 wpa_sm *sm, char *buf, size_t len); + const u8 *aa, const u8 *pmkid, + const void *network_ctx); +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, @@ -66,12 +66,14 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *aa); +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len); -#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#else /* IEEE8021X_EAPOL */ static inline struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace), + void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { return (void *) -1; @@ -82,7 +84,8 @@ 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) +pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, + const void *network_ctx) { return NULL; } @@ -93,7 +96,8 @@ pmksa_cache_get_current(struct wpa_sm *sm) return NULL; } -static inline int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) +static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, + size_t len) { return -1; } @@ -105,10 +109,6 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, 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) { } @@ -121,6 +121,12 @@ static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, return -1; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, + void *network_ctx, + const u8 *pmk, size_t pmk_len) +{ +} + +#endif /* IEEE8021X_EAPOL */ #endif /* PMKSA_CACHE_H */ diff --git a/contrib/hostapd/src/rsn_supp/preauth.c b/contrib/hostapd/src/rsn_supp/preauth.c index b00c004cf6..915f85e70e 100644 --- a/contrib/hostapd/src/rsn_supp/preauth.c +++ b/contrib/hostapd/src/rsn_supp/preauth.c @@ -1,38 +1,30 @@ /* - * WPA Supplicant - RSN pre-authentication - * Copyright (c) 2003-2008, Jouni Malinen + * RSN pre-authentication (supplicant) + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "wpa.h" -#include "drivers/driver.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 "ieee802_11_defs.h" -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL #define PMKID_CANDIDATE_PRIO_SCAN 1000 struct rsn_pmksa_candidate { - struct rsn_pmksa_candidate *next; + struct dl_list list; u8 bssid[ETH_ALEN]; int priority; }; @@ -44,17 +36,15 @@ struct rsn_pmksa_candidate { */ void pmksa_candidate_free(struct wpa_sm *sm) { - struct rsn_pmksa_candidate *entry, *prev; + struct rsn_pmksa_candidate *entry, *n; if (sm == NULL) return; - entry = sm->pmksa_candidates; - sm->pmksa_candidates = NULL; - while (entry) { - prev = entry; - entry = entry->next; - os_free(prev); + dl_list_for_each_safe(entry, n, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + dl_list_del(&entry->list); + os_free(entry); } } @@ -80,13 +70,14 @@ static void rsn_preauth_receive(void *ctx, const u8 *src_addr, } -static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, +static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, + enum eapol_supp_result result, void *ctx) { struct wpa_sm *sm = ctx; u8 pmk[PMK_LEN]; - if (success) { + if (result == EAPOL_SUPP_RESULT_SUCCESS) { int res, pmk_len; pmk_len = PMK_LEN; res = eapol_sm_get_key(eapol, pmk, PMK_LEN); @@ -107,16 +98,17 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, sm->network_ctx, WPA_KEY_MGMT_IEEE8021X); } else { - wpa_msg(sm->ctx->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: failed to get master session key from " + "pre-auth EAPOL state machines"); + result = EAPOL_SUPP_RESULT_FAILURE; } } - wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR - " %s", MAC2STR(sm->preauth_bssid), - success ? "completed successfully" : "failed"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " + MACSTR " %s", MAC2STR(sm->preauth_bssid), + result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" : + "failed"); rsn_preauth_deinit(sm); rsn_preauth_candidate_process(sm); @@ -127,8 +119,8 @@ static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; - wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR - " timed out", MAC2STR(sm->preauth_bssid)); + 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); } @@ -183,8 +175,8 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, if (sm->preauth_eapol) return -1; - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: starting pre-authentication " - "with " MACSTR, MAC2STR(dst)); + 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, @@ -293,42 +285,42 @@ void rsn_preauth_deinit(struct wpa_sm *sm) */ void rsn_preauth_candidate_process(struct wpa_sm *sm) { - struct rsn_pmksa_candidate *candidate; + struct rsn_pmksa_candidate *candidate, *n; - if (sm->pmksa_candidates == NULL) + if (dl_list_empty(&sm->pmksa_candidates)) return; /* TODO: drop priority for old candidate entries */ - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: processing PMKSA candidate " + 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->ctx, MSG_DEBUG, "RSN: not in suitable state " - "for new pre-authentication"); + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable " + "state for new pre-authentication"); return; /* invalid state for new pre-auth */ } - while (sm->pmksa_candidates) { + dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { struct rsn_pmksa_cache_entry *p = NULL; - candidate = sm->pmksa_candidates; - p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL); + p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL); if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && (p == NULL || p->opportunistic)) { - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: PMKSA " + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " "candidate " MACSTR " selected for pre-authentication", MAC2STR(candidate->bssid)); - sm->pmksa_candidates = candidate->next; + dl_list_del(&candidate->list); rsn_preauth_init(sm, candidate->bssid, sm->eap_conf_ctx); os_free(candidate); return; } - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: PMKSA candidate " + 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 @@ -337,10 +329,10 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid); } - sm->pmksa_candidates = candidate->next; + dl_list_del(&candidate->list); os_free(candidate); } - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: no more pending PMKSA " + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA " "candidates"); } @@ -359,7 +351,7 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, int prio, int preauth) { - struct rsn_pmksa_candidate *cand, *prev, *pos; + struct rsn_pmksa_candidate *cand, *pos; if (sm->network_ctx && sm->proactive_key_caching) pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx, @@ -373,21 +365,17 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, /* If BSSID already on candidate list, update the priority of the old * entry. Do not override priority based on normal scan results. */ - prev = NULL; - cand = sm->pmksa_candidates; - while (cand) { - if (os_memcmp(cand->bssid, bssid, ETH_ALEN) == 0) { - if (prev) - prev->next = cand->next; - else - sm->pmksa_candidates = cand->next; + 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; } - prev = cand; - cand = cand->next; } if (cand) { + dl_list_del(&cand->list); if (prio < PMKID_CANDIDATE_PRIO_SCAN) cand->priority = prio; } else { @@ -400,21 +388,18 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, /* Add candidate to the list; order by increasing priority value. i.e., * highest priority (smallest value) first. */ - prev = NULL; - pos = sm->pmksa_candidates; - while (pos) { - if (cand->priority <= pos->priority) + 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; - prev = pos; - pos = pos->next; + } } - cand->next = pos; - if (prev) - prev->next = cand; - else - sm->pmksa_candidates = cand; + if (cand) + dl_list_add_tail(&sm->pmksa_candidates, &cand->list); - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: added PMKSA cache " + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache " "candidate " MACSTR " prio %d", MAC2STR(bssid), prio); rsn_preauth_candidate_process(sm); } @@ -423,23 +408,18 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, /* TODO: schedule periodic scans if current AP supports preauth */ /** - * rsn_preauth_scan_results - Process scan results to find PMKSA candidates + * rsn_preauth_scan_results - Start processing scan results for canditates * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @results: Scan results + * Returns: 0 if ready to process results or -1 to skip processing * - * This functions goes through the scan results and adds all suitable APs - * (Authenticators) into PMKSA candidate list. + * 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. */ -void rsn_preauth_scan_results(struct wpa_sm *sm, - struct wpa_scan_results *results) +int rsn_preauth_scan_results(struct wpa_sm *sm) { - struct wpa_scan_res *r; - struct wpa_ie_data ie; - int i; - struct rsn_pmksa_cache_entry *pmksa; - if (sm->ssid_len == 0) - return; + return -1; /* * TODO: is it ok to free all candidates? What about the entries @@ -447,37 +427,41 @@ void rsn_preauth_scan_results(struct wpa_sm *sm, */ pmksa_candidate_free(sm); - for (i = results->num - 1; i >= 0; i--) { - const u8 *ssid, *rsn; + return 0; +} - r = results->res[i]; - ssid = wpa_scan_get_ie(r, WLAN_EID_SSID); - if (ssid == NULL || ssid[1] != sm->ssid_len || - os_memcmp(ssid + 2, sm->ssid, ssid[1]) != 0) - continue; +/** + * 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 (os_memcmp(r->bssid, sm->bssid, ETH_ALEN) == 0) - continue; + if (ssid[1] != sm->ssid_len || + os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0) + return; /* Not for the current SSID */ - rsn = wpa_scan_get_ie(r, WLAN_EID_RSN); - if (rsn == NULL || wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) - continue; + if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0) + return; /* Ignore current AP */ - pmksa = pmksa_cache_get(sm->pmksa, r->bssid, NULL); - if (pmksa && - (!pmksa->opportunistic || - !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) - continue; + if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) + return; - /* - * Give less priority to candidates found from normal - * scan results. - */ - pmksa_candidate_add(sm, r->bssid, - PMKID_CANDIDATE_PRIO_SCAN, - ie.capabilities & WPA_CAPABILITY_PREAUTH); - } + pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, 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); } @@ -526,4 +510,4 @@ int rsn_preauth_in_progress(struct wpa_sm *sm) return sm->preauth_eapol != NULL; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ diff --git a/contrib/hostapd/src/rsn_supp/preauth.h b/contrib/hostapd/src/rsn_supp/preauth.h index b9ac57b531..277f0663b0 100644 --- a/contrib/hostapd/src/rsn_supp/preauth.h +++ b/contrib/hostapd/src/rsn_supp/preauth.h @@ -1,15 +1,9 @@ /* * wpa_supplicant - WPA2/RSN pre-authentication functions - * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2003-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef PREAUTH_H @@ -17,14 +11,15 @@ struct wpa_scan_results; -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL 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); -void rsn_preauth_scan_results(struct wpa_sm *sm, - struct wpa_scan_results *results); +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); @@ -32,7 +27,7 @@ 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 */ +#else /* IEEE8021X_EAPOL */ static inline void pmksa_candidate_free(struct wpa_sm *sm) { @@ -51,8 +46,14 @@ static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, static inline void rsn_preauth_deinit(struct wpa_sm *sm) { } -static inline void rsn_preauth_scan_results(struct wpa_sm *sm, - struct wpa_scan_results *results) + +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) { } @@ -73,6 +74,6 @@ static inline int rsn_preauth_in_progress(struct wpa_sm *sm) return 0; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ #endif /* PREAUTH_H */ diff --git a/contrib/hostapd/src/rsn_supp/tdls.c b/contrib/hostapd/src/rsn_supp/tdls.c new file mode 100644 index 0000000000..8a978f7475 --- /dev/null +++ b/contrib/hostapd/src/rsn_supp/tdls.c @@ -0,0 +1,2638 @@ +/* + * wpa_supplicant - TDLS + * Copyright (c) 2010-2011, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/os.h" +#include "common/ieee802_11_defs.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" +#include "crypto/aes_wrap.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/wpa_ie.h" +#include "rsn_supp/wpa_i.h" +#include "drivers/driver.h" +#include "l2_packet/l2_packet.h" + +#ifdef CONFIG_TDLS_TESTING +#define TDLS_TESTING_LONG_FRAME BIT(0) +#define TDLS_TESTING_ALT_RSN_IE BIT(1) +#define TDLS_TESTING_DIFF_BSSID BIT(2) +#define TDLS_TESTING_SHORT_LIFETIME BIT(3) +#define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4) +#define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5) +#define TDLS_TESTING_LONG_LIFETIME BIT(6) +#define TDLS_TESTING_CONCURRENT_INIT BIT(7) +#define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8) +#define TDLS_TESTING_DECLINE_RESP BIT(9) +#define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10) +unsigned int tdls_testing = 0; +#endif /* CONFIG_TDLS_TESTING */ + +#define TPK_LIFETIME 43200 /* 12 hours */ +#define TPK_M1_RETRY_COUNT 3 +#define TPK_M1_TIMEOUT 5000 /* in milliseconds */ +#define TPK_M2_RETRY_COUNT 10 +#define TPK_M2_TIMEOUT 500 /* in milliseconds */ + +#define TDLS_MIC_LEN 16 + +#define TDLS_TIMEOUT_LEN 4 + +struct wpa_tdls_ftie { + u8 ie_type; /* FTIE */ + u8 ie_len; + u8 mic_ctrl[2]; + u8 mic[TDLS_MIC_LEN]; + u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */ + u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */ + /* followed by optional elements */ +} STRUCT_PACKED; + +struct wpa_tdls_timeoutie { + u8 ie_type; /* Timeout IE */ + u8 ie_len; + u8 interval_type; + u8 value[TDLS_TIMEOUT_LEN]; +} STRUCT_PACKED; + +struct wpa_tdls_lnkid { + u8 ie_type; /* Link Identifier IE */ + u8 ie_len; + u8 bssid[ETH_ALEN]; + u8 init_sta[ETH_ALEN]; + u8 resp_sta[ETH_ALEN]; +} STRUCT_PACKED; + +/* TDLS frame headers as per IEEE Std 802.11z-2010 */ +struct wpa_tdls_frame { + u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */ + u8 category; /* Category */ + u8 action; /* Action (enum tdls_frame_type) */ +} STRUCT_PACKED; + +static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs); +static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer); +static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, + struct wpa_tdls_peer *peer); + + +#define TDLS_MAX_IE_LEN 80 +#define IEEE80211_MAX_SUPP_RATES 32 + +struct wpa_tdls_peer { + struct wpa_tdls_peer *next; + unsigned int reconfig_key:1; + int initiator; /* whether this end was initiator for TDLS setup */ + u8 addr[ETH_ALEN]; /* other end MAC address */ + u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ + u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */ + u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */ + size_t rsnie_i_len; + u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */ + size_t rsnie_p_len; + u32 lifetime; + int cipher; /* Selected cipher (WPA_CIPHER_*) */ + u8 dtoken; + + struct tpk { + u8 kck[16]; /* TPK-KCK */ + u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */ + } tpk; + int tpk_set; + int tpk_success; + int tpk_in_progress; + + struct tpk_timer { + u8 dest[ETH_ALEN]; + int count; /* Retry Count */ + int timer; /* Timeout in milliseconds */ + u8 action_code; /* TDLS frame type */ + u8 dialog_token; + u16 status_code; + int buf_len; /* length of TPK message for retransmission */ + u8 *buf; /* buffer for TPK message */ + } sm_tmr; + + u16 capability; + + u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; + size_t supp_rates_len; + + struct ieee80211_ht_capabilities *ht_capabilities; + struct ieee80211_vht_capabilities *vht_capabilities; + + u8 qos_info; + + u16 aid; + + u8 *ext_capab; + size_t ext_capab_len; + + u8 *supp_channels; + size_t supp_channels_len; + + u8 *supp_oper_classes; + size_t supp_oper_classes_len; +}; + + +static int wpa_tdls_get_privacy(struct wpa_sm *sm) +{ + /* + * Get info needed from supplicant to check if the current BSS supports + * security. Other than OPEN mode, rest are considered secured + * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake. + */ + return sm->pairwise_cipher != WPA_CIPHER_NONE; +} + + +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 int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr, + 0, 0, NULL, 0, NULL, 0) < 0) { + wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from " + "the driver"); + return -1; + } + + return 0; +} + + +static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + u8 key_len; + u8 rsc[6]; + enum wpa_alg alg; + + os_memset(rsc, 0, 6); + + switch (peer->cipher) { + case WPA_CIPHER_CCMP: + alg = WPA_ALG_CCMP; + key_len = 16; + break; + case WPA_CIPHER_NONE: + wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: " + "NONE - do not use pairwise keys"); + return -1; + default: + wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d", + sm->pairwise_cipher); + return -1; + } + + if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1, + rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) { + wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the " + "driver"); + return -1; + } + return 0; +} + + +static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len) +{ + return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token, + status_code, buf, len); +} + + +static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, + u8 dialog_token, u16 status_code, + const u8 *msg, size_t msg_len) +{ + struct wpa_tdls_peer *peer; + + wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u " + "dialog_token=%u status_code=%u msg_len=%u", + MAC2STR(dest), action_code, dialog_token, status_code, + (unsigned int) msg_len); + + if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token, + status_code, msg, msg_len)) { + wpa_printf(MSG_INFO, "TDLS: Failed to send message " + "(action_code=%u)", action_code); + return -1; + } + + if (action_code == WLAN_TDLS_SETUP_CONFIRM || + action_code == WLAN_TDLS_TEARDOWN || + action_code == WLAN_TDLS_DISCOVERY_REQUEST || + action_code == WLAN_TDLS_DISCOVERY_RESPONSE) + return 0; /* No retries */ + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching entry found for " + "retry " MACSTR, MAC2STR(dest)); + return 0; + } + + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + + if (action_code == WLAN_TDLS_SETUP_RESPONSE) { + peer->sm_tmr.count = TPK_M2_RETRY_COUNT; + peer->sm_tmr.timer = TPK_M2_TIMEOUT; + } else { + peer->sm_tmr.count = TPK_M1_RETRY_COUNT; + peer->sm_tmr.timer = TPK_M1_TIMEOUT; + } + + /* Copy message to resend on timeout */ + os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN); + peer->sm_tmr.action_code = action_code; + peer->sm_tmr.dialog_token = dialog_token; + peer->sm_tmr.status_code = status_code; + peer->sm_tmr.buf_len = msg_len; + os_free(peer->sm_tmr.buf); + peer->sm_tmr.buf = os_malloc(msg_len); + if (peer->sm_tmr.buf == NULL) + return -1; + os_memcpy(peer->sm_tmr.buf, msg, msg_len); + + wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " + "(action_code=%u)", action_code); + eloop_register_timeout(peer->sm_tmr.timer / 1000, + (peer->sm_tmr.timer % 1000) * 1000, + wpa_tdls_tpk_retry_timeout, sm, peer); + return 0; +} + + +static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer, + u16 reason_code) +{ + int ret; + + ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); + /* disable the link after teardown was sent */ + wpa_tdls_disable_peer_link(sm, peer); + + return ret; +} + + +static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) +{ + + struct wpa_sm *sm = eloop_ctx; + struct wpa_tdls_peer *peer = timeout_ctx; + + if (peer->sm_tmr.count) { + peer->sm_tmr.count--; + + wpa_printf(MSG_INFO, "TDLS: Retrying sending of message " + "(action_code=%u)", + peer->sm_tmr.action_code); + + if (peer->sm_tmr.buf == NULL) { + wpa_printf(MSG_INFO, "TDLS: No retry buffer available " + "for action_code=%u", + peer->sm_tmr.action_code); + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, + peer); + return; + } + + /* resend TPK Handshake Message to Peer */ + if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest, + peer->sm_tmr.action_code, + peer->sm_tmr.dialog_token, + peer->sm_tmr.status_code, + peer->sm_tmr.buf, + peer->sm_tmr.buf_len)) { + wpa_printf(MSG_INFO, "TDLS: Failed to retry " + "transmission"); + } + + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + eloop_register_timeout(peer->sm_tmr.timer / 1000, + (peer->sm_tmr.timer % 1000) * 1000, + wpa_tdls_tpk_retry_timeout, sm, peer); + } else { + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + + wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request"); + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + } +} + + +static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm, + struct wpa_tdls_peer *peer, + u8 action_code) +{ + if (action_code == peer->sm_tmr.action_code) { + wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for " + "action_code=%u", action_code); + + /* Cancel Timeout registered */ + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + + /* free all resources meant for retry */ + os_free(peer->sm_tmr.buf); + peer->sm_tmr.buf = NULL; + + peer->sm_tmr.count = 0; + peer->sm_tmr.timer = 0; + peer->sm_tmr.buf_len = 0; + peer->sm_tmr.action_code = 0xff; + } else { + wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout " + "(Unknown action_code=%u)", action_code); + } +} + + +static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer, + const u8 *own_addr, const u8 *bssid) +{ + u8 key_input[SHA256_MAC_LEN]; + const u8 *nonce[2]; + size_t len[2]; + u8 data[3 * ETH_ALEN]; + + /* IEEE Std 802.11z-2010 8.5.9.1: + * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce)) + */ + len[0] = WPA_NONCE_LEN; + len[1] = WPA_NONCE_LEN; + if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) { + nonce[0] = peer->inonce; + nonce[1] = peer->rnonce; + } else { + nonce[0] = peer->rnonce; + nonce[1] = peer->inonce; + } + wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN); + sha256_vector(2, nonce, len, key_input); + wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input", + key_input, SHA256_MAC_LEN); + + /* + * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK", + * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY) + * TODO: is N_KEY really included in KDF Context and if so, in which + * presentation format (little endian 16-bit?) is it used? It gets + * added by the KDF anyway.. + */ + + if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) { + os_memcpy(data, own_addr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN); + } else { + os_memcpy(data, peer->addr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN); + } + os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data)); + + sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data), + (u8 *) &peer->tpk, sizeof(peer->tpk)); + wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK", + peer->tpk.kck, sizeof(peer->tpk.kck)); + wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK", + peer->tpk.tk, sizeof(peer->tpk.tk)); + peer->tpk_set = 1; +} + + +/** + * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC + * @kck: TPK-KCK + * @lnkid: Pointer to the beginning of Link Identifier IE + * @rsnie: Pointer to the beginning of RSN IE used for handshake + * @timeoutie: Pointer to the beginning of Timeout IE used for handshake + * @ftie: Pointer to the beginning of FT IE + * @mic: Pointer for writing MIC + * + * Calculate MIC for TDLS frame. + */ +static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid, + const u8 *rsnie, const u8 *timeoutie, + const u8 *ftie, u8 *mic) +{ + u8 *buf, *pos; + struct wpa_tdls_ftie *_ftie; + const struct wpa_tdls_lnkid *_lnkid; + int ret; + int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] + + 2 + timeoutie[1] + 2 + ftie[1]; + buf = os_zalloc(len); + if (!buf) { + wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation"); + return -1; + } + + pos = buf; + _lnkid = (const struct wpa_tdls_lnkid *) lnkid; + /* 1) TDLS initiator STA MAC address */ + os_memcpy(pos, _lnkid->init_sta, ETH_ALEN); + pos += ETH_ALEN; + /* 2) TDLS responder STA MAC address */ + os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN); + pos += ETH_ALEN; + /* 3) Transaction Sequence number */ + *pos++ = trans_seq; + /* 4) Link Identifier IE */ + os_memcpy(pos, lnkid, 2 + lnkid[1]); + pos += 2 + lnkid[1]; + /* 5) RSN IE */ + os_memcpy(pos, rsnie, 2 + rsnie[1]); + pos += 2 + rsnie[1]; + /* 6) Timeout Interval IE */ + os_memcpy(pos, timeoutie, 2 + timeoutie[1]); + pos += 2 + timeoutie[1]; + /* 7) FTIE, with the MIC field of the FTIE set to 0 */ + os_memcpy(pos, ftie, 2 + ftie[1]); + _ftie = (struct wpa_tdls_ftie *) pos; + os_memset(_ftie->mic, 0, TDLS_MIC_LEN); + pos += 2 + ftie[1]; + + wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf); + wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16); + ret = omac1_aes_128(kck, buf, pos - buf, mic); + os_free(buf); + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16); + return ret; +} + + +/** + * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame + * @kck: TPK-KCK + * @trans_seq: Transaction Sequence Number (4 - Teardown) + * @rcode: Reason code for Teardown + * @dtoken: Dialog Token used for that particular link + * @lnkid: Pointer to the beginning of Link Identifier IE + * @ftie: Pointer to the beginning of FT IE + * @mic: Pointer for writing MIC + * + * Calculate MIC for TDLS frame. + */ +static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode, + u8 dtoken, const u8 *lnkid, + const u8 *ftie, u8 *mic) +{ + u8 *buf, *pos; + struct wpa_tdls_ftie *_ftie; + int ret; + int len; + + if (lnkid == NULL) + return -1; + + len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) + + sizeof(trans_seq) + 2 + ftie[1]; + + buf = os_zalloc(len); + if (!buf) { + wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation"); + return -1; + } + + pos = buf; + /* 1) Link Identifier IE */ + os_memcpy(pos, lnkid, 2 + lnkid[1]); + pos += 2 + lnkid[1]; + /* 2) Reason Code */ + WPA_PUT_LE16(pos, rcode); + pos += sizeof(rcode); + /* 3) Dialog token */ + *pos++ = dtoken; + /* 4) Transaction Sequence number */ + *pos++ = trans_seq; + /* 7) FTIE, with the MIC field of the FTIE set to 0 */ + os_memcpy(pos, ftie, 2 + ftie[1]); + _ftie = (struct wpa_tdls_ftie *) pos; + os_memset(_ftie->mic, 0, TDLS_MIC_LEN); + pos += 2 + ftie[1]; + + wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf); + wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16); + ret = omac1_aes_128(kck, buf, pos - buf, mic); + os_free(buf); + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16); + return ret; +} + + +static int wpa_supplicant_verify_tdls_mic(u8 trans_seq, + struct wpa_tdls_peer *peer, + const u8 *lnkid, const u8 *timeoutie, + const struct wpa_tdls_ftie *ftie) +{ + u8 mic[16]; + + if (peer->tpk_set) { + wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid, + peer->rsnie_p, timeoutie, (u8 *) ftie, + mic); + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - " + "dropping packet"); + wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC", + ftie->mic, 16); + wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC", + mic, 16); + return -1; + } + } else { + wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, " + "TPK not set - dropping packet"); + return -1; + } + return 0; +} + + +static int wpa_supplicant_verify_tdls_mic_teardown( + u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer, + const u8 *lnkid, const struct wpa_tdls_ftie *ftie) +{ + u8 mic[16]; + + if (peer->tpk_set) { + wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode, + dtoken, lnkid, (u8 *) ftie, mic); + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - " + "dropping packet"); + return -1; + } + } else { + wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown " + "MIC, TPK not set - dropping packet"); + return -1; + } + return 0; +} + + +static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + struct wpa_tdls_peer *peer = timeout_ctx; + + /* + * On TPK lifetime expiration, we have an option of either tearing down + * the direct link or trying to re-initiate it. The selection of what + * to do is not strictly speaking controlled by our role in the expired + * link, but for now, use that to select whether to renew or tear down + * the link. + */ + + if (peer->initiator) { + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR + " - try to renew", MAC2STR(peer->addr)); + wpa_tdls_start(sm, peer->addr); + } else { + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR + " - tear down", MAC2STR(peer->addr)); + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + } +} + + +static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR, + MAC2STR(peer->addr)); + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + peer->reconfig_key = 0; + peer->initiator = 0; + peer->tpk_in_progress = 0; + os_free(peer->sm_tmr.buf); + peer->sm_tmr.buf = NULL; + os_free(peer->ht_capabilities); + peer->ht_capabilities = NULL; + os_free(peer->vht_capabilities); + peer->vht_capabilities = NULL; + os_free(peer->ext_capab); + peer->ext_capab = NULL; + os_free(peer->supp_channels); + peer->supp_channels = NULL; + os_free(peer->supp_oper_classes); + peer->supp_oper_classes = NULL; + peer->rsnie_i_len = peer->rsnie_p_len = 0; + peer->cipher = 0; + peer->tpk_set = peer->tpk_success = 0; + os_memset(&peer->tpk, 0, sizeof(peer->tpk)); + os_memset(peer->inonce, 0, WPA_NONCE_LEN); + os_memset(peer->rnonce, 0, WPA_NONCE_LEN); +} + + +static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer, + struct wpa_tdls_lnkid *lnkid) +{ + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = 3 * ETH_ALEN; + os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN); + if (peer->initiator) { + os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN); + os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN); + } else { + os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN); + os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN); + } +} + + +int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code) +{ + struct wpa_tdls_peer *peer; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_lnkid lnkid; + u8 dialog_token; + u8 *rbuf, *pos; + int ielen; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + /* Find the node and free from the list */ + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching entry found for " + "Teardown " MACSTR, MAC2STR(addr)); + return 0; + } + + dialog_token = peer->dtoken; + + wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR, + MAC2STR(addr)); + + ielen = 0; + if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) { + /* To add FTIE for Teardown request and compute MIC */ + ielen += sizeof(*ftie); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) + ielen += 170; +#endif /* CONFIG_TDLS_TESTING */ + } + + rbuf = os_zalloc(ielen + 1); + if (rbuf == NULL) + return -1; + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) + goto skip_ies; + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + /* Using the recent nonce which should be for CONFIRM frame */ + os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + pos = (u8 *) (ftie + 1); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake", + (u8 *) ftie, pos - (u8 *) ftie); + + /* compute MIC before sending */ + wpa_tdls_linkid(sm, peer, &lnkid); + wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code, + dialog_token, (u8 *) &lnkid, (u8 *) ftie, + ftie->mic); + +skip_ies: + /* TODO: register for a Timeout handler, if Teardown is not received at + * the other end, then try again another time */ + + /* request driver to send Teardown using this FTIE */ + wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0, + reason_code, rbuf, pos - rbuf); + os_free(rbuf); + + /* clear the Peerkey statemachine */ + wpa_tdls_peer_free(sm, peer); + + return 0; +} + + +int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR + " for link Teardown", MAC2STR(addr)); + return -1; + } + + if (!peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR + " not connected - cannot Teardown link", MAC2STR(addr)); + return -1; + } + + return wpa_tdls_do_teardown(sm, peer, reason_code); +} + + +static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, + struct wpa_tdls_peer *peer) +{ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_tdls_peer_free(sm, peer); +} + + +void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer) + wpa_tdls_disable_peer_link(sm, peer); +} + + +const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return "disabled"; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) + return "peer does not exist"; + + if (!peer->tpk_success) + return "peer not connected"; + + return "connected"; +} + + +static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer = NULL; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_lnkid *lnkid; + struct wpa_eapol_ie_parse kde; + u16 reason_code; + const u8 *pos; + int ielen; + + /* Find the node and free from the list */ + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching entry found for " + "Teardown " MACSTR, MAC2STR(src_addr)); + return 0; + } + + pos = buf; + pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + + reason_code = WPA_GET_LE16(pos); + pos += 2; + + wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR + " (reason code %u)", MAC2STR(src_addr), reason_code); + + ielen = len - (pos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown"); + return -1; + } + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS " + "Teardown"); + return -1; + } + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + + if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success) + goto skip_ftie; + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown"); + return -1; + } + + ftie = (struct wpa_tdls_ftie *) kde.ftie; + + /* Process MIC check to see if TDLS Teardown is right */ + if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code, + peer->dtoken, peer, + (u8 *) lnkid, ftie) < 0) { + wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS " + "Teardown Request from " MACSTR, MAC2STR(src_addr)); + return -1; + } + +skip_ftie: + /* + * Request the driver to disable the direct link and clear associated + * keys. + */ + wpa_tdls_disable_peer_link(sm, peer); + return 0; +} + + +/** + * wpa_tdls_send_error - To send suitable TDLS status response with + * appropriate status code mentioning reason for error/failure. + * @dst - MAC addr of Peer station + * @tdls_action - TDLS frame type for which error code is sent + * @status - status code mentioning reason + */ + +static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst, + u8 tdls_action, u8 dialog_token, u16 status) +{ + wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR + " (action=%u status=%u)", + MAC2STR(dst), tdls_action, status); + return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status, + NULL, 0); +} + + +static struct wpa_tdls_peer * +wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing) +{ + struct wpa_tdls_peer *peer; + + if (existing) + *existing = 0; + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) { + if (existing) + *existing = 1; + return peer; /* re-use existing entry */ + } + } + + wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR, + MAC2STR(addr)); + + peer = os_zalloc(sizeof(*peer)); + if (peer == NULL) + return NULL; + + os_memcpy(peer->addr, addr, ETH_ALEN); + peer->next = sm->tdls; + sm->tdls = peer; + + return peer; +} + + +static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm, + struct wpa_tdls_peer *peer) +{ + size_t buf_len; + struct wpa_tdls_timeoutie timeoutie; + u16 rsn_capab; + struct wpa_tdls_ftie *ftie; + u8 *rbuf, *pos, *count_pos; + u16 count; + struct rsn_ie_hdr *hdr; + int status; + + if (!wpa_tdls_get_privacy(sm)) { + wpa_printf(MSG_DEBUG, "TDLS: No security used on the link"); + peer->rsnie_i_len = 0; + goto skip_rsnie; + } + + /* + * TPK Handshake Message 1: + * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I, + * Timeout Interval IE)) + */ + + /* Filling RSN IE */ + hdr = (struct rsn_ie_hdr *) peer->rsnie_i; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + + pos = (u8 *) (hdr + 1); + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; + count_pos = pos; + pos += 2; + + count = 0; + + /* + * AES-CCMP is the default Encryption preferred for TDLS, so + * RSN IE is filled only with CCMP CIPHER + * Note: TKIP is not used to encrypt TDLS link. + * + * Regardless of the cipher used on the AP connection, select CCMP + * here. + */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + count++; + + WPA_PUT_LE16(count_pos, count); + + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); + pos += RSN_SELECTOR_LEN; + + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) { + wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for " + "testing"); + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + } +#endif /* CONFIG_TDLS_TESTING */ + WPA_PUT_LE16(pos, rsn_capab); + pos += 2; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) { + /* Number of PMKIDs */ + *pos++ = 0x00; + *pos++ = 0x00; + } +#endif /* CONFIG_TDLS_TESTING */ + + hdr->len = (pos - peer->rsnie_i) - 2; + peer->rsnie_i_len = pos - peer->rsnie_i; + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake", + peer->rsnie_i, peer->rsnie_i_len); + +skip_rsnie: + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) + buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + + sizeof(struct wpa_tdls_timeoutie); +#ifdef CONFIG_TDLS_TESTING + if (wpa_tdls_get_privacy(sm) && + (tdls_testing & TDLS_TESTING_LONG_FRAME)) + buf_len += 170; + if (tdls_testing & TDLS_TESTING_DIFF_BSSID) + buf_len += sizeof(struct wpa_tdls_lnkid); +#endif /* CONFIG_TDLS_TESTING */ + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) { + wpa_tdls_peer_free(sm, peer); + return -1; + } + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + + /* Initiator RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len); + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + + if (os_get_random(peer->inonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "TDLS: Failed to get random data for initiator Nonce"); + os_free(rbuf); + wpa_tdls_peer_free(sm, peer); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake", + peer->inonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1", + (u8 *) ftie, sizeof(struct wpa_tdls_ftie)); + + pos = (u8 *) (ftie + 1); + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + + /* Lifetime */ + peer->lifetime = TPK_LIFETIME; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK " + "lifetime"); + peer->lifetime = 301; + } + if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK " + "lifetime"); + peer->lifetime = 0xffffffff; + } +#endif /* CONFIG_TDLS_TESTING */ + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), peer->lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime); + +skip_ies: + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_DIFF_BSSID) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in " + "Link Identifier"); + struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos; + wpa_tdls_linkid(sm, peer, l); + l->bssid[5] ^= 0x01; + pos += sizeof(*l); + } +#endif /* CONFIG_TDLS_TESTING */ + + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK " + "Handshake Message 1 (peer " MACSTR ")", + MAC2STR(peer->addr)); + + status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, + 1, 0, rbuf, pos - rbuf); + os_free(rbuf); + + return status; +} + + +static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, + const unsigned char *src_addr, u8 dtoken, + struct wpa_tdls_lnkid *lnkid, + const struct wpa_tdls_peer *peer) +{ + u8 *rbuf, *pos; + size_t buf_len; + u32 lifetime; + struct wpa_tdls_timeoutie timeoutie; + struct wpa_tdls_ftie *ftie; + int status; + + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) { + /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce), + * Lifetime */ + buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + + sizeof(struct wpa_tdls_timeoutie); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) + buf_len += 170; +#endif /* CONFIG_TDLS_TESTING */ + } + + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) + return -1; + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + + /* Peer RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len); + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + /* TODO: ftie->mic_control to set 2-RESPONSE */ + os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2", + (u8 *) ftie, sizeof(*ftie)); + + pos = (u8 *) (ftie + 1); + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + + /* Lifetime */ + lifetime = peer->lifetime; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK " + "lifetime in response"); + lifetime++; + } +#endif /* CONFIG_TDLS_TESTING */ + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator", + lifetime); + + /* compute MIC before sending */ + wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p, + (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); + +skip_ies: + status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, + dtoken, 0, rbuf, pos - rbuf); + os_free(rbuf); + + return status; +} + + +static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, + const unsigned char *src_addr, u8 dtoken, + struct wpa_tdls_lnkid *lnkid, + const struct wpa_tdls_peer *peer) +{ + u8 *rbuf, *pos; + size_t buf_len; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_timeoutie timeoutie; + u32 lifetime; + int status; + + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) { + /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce), + * Lifetime */ + buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) + + sizeof(struct wpa_tdls_timeoutie); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) + buf_len += 170; +#endif /* CONFIG_TDLS_TESTING */ + } + + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) + return -1; + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + + /* Peer RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len); + + ftie = (struct wpa_tdls_ftie *) pos; + ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION; + /*TODO: ftie->mic_control to set 3-CONFIRM */ + os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN); + os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN); + ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2; + + pos = (u8 *) (ftie + 1); + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_LONG_FRAME) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to " + "FTIE"); + ftie->ie_len += 170; + *pos++ = 255; /* FTIE subelem */ + *pos++ = 168; /* FTIE subelem length */ + pos += 168; + } +#endif /* CONFIG_TDLS_TESTING */ + + /* Lifetime */ + lifetime = peer->lifetime; +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK " + "lifetime in confirm"); + lifetime++; + } +#endif /* CONFIG_TDLS_TESTING */ + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", + lifetime); + + /* compute MIC before sending */ + wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p, + (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); + +skip_ies: + status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, + dtoken, 0, rbuf, pos - rbuf); + os_free(rbuf); + + return status; +} + + +static int wpa_tdls_send_discovery_response(struct wpa_sm *sm, + struct wpa_tdls_peer *peer, + u8 dialog_token) +{ + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response " + "(peer " MACSTR ")", MAC2STR(peer->addr)); + + return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, + dialog_token, 0, NULL, 0); +} + + +static int +wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr, + const u8 *buf, size_t len) +{ + struct wpa_eapol_ie_parse kde; + const struct wpa_tdls_lnkid *lnkid; + struct wpa_tdls_peer *peer; + size_t min_req_len = sizeof(struct wpa_tdls_frame) + + 1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid); + u8 dialog_token; + + wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR, + MAC2STR(addr)); + + if (len < min_req_len) { + wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: " + "%d", (int) len); + return -1; + } + + dialog_token = buf[sizeof(struct wpa_tdls_frame)]; + + if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1, + len - (sizeof(struct wpa_tdls_frame) + 1), + &kde) < 0) + return -1; + + if (!kde.lnkid) { + wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery " + "Request"); + return -1; + } + + lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid; + + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different " + " BSS " MACSTR, MAC2STR(lnkid->bssid)); + return -1; + } + + peer = wpa_tdls_add_peer(sm, addr, NULL); + if (peer == NULL) + return -1; + + return wpa_tdls_send_discovery_response(sm, peer, dialog_token); +} + + +int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr) +{ + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer " + MACSTR, MAC2STR(addr)); + return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST, + 1, 0, NULL, 0); +} + + +static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_rates) { + wpa_printf(MSG_DEBUG, "TDLS: No supported rates received"); + return -1; + } + peer->supp_rates_len = merge_byte_arrays( + peer->supp_rates, sizeof(peer->supp_rates), + kde->supp_rates + 2, kde->supp_rates_len - 2, + kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL, + kde->ext_supp_rates_len - 2); + return 0; +} + + +static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->ht_capabilities || + kde->ht_capabilities_len < + sizeof(struct ieee80211_ht_capabilities) ) { + wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities " + "received"); + return 0; + } + + if (!peer->ht_capabilities) { + peer->ht_capabilities = + os_zalloc(sizeof(struct ieee80211_ht_capabilities)); + if (peer->ht_capabilities == NULL) + return -1; + } + + os_memcpy(peer->ht_capabilities, kde->ht_capabilities, + sizeof(struct ieee80211_ht_capabilities)); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities", + (u8 *) peer->ht_capabilities, + sizeof(struct ieee80211_ht_capabilities)); + + return 0; +} + + +static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->vht_capabilities || + kde->vht_capabilities_len < + sizeof(struct ieee80211_vht_capabilities) ) { + wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities " + "received"); + return 0; + } + + if (!peer->vht_capabilities) { + peer->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (peer->vht_capabilities == NULL) + return -1; + } + + os_memcpy(peer->vht_capabilities, kde->vht_capabilities, + sizeof(struct ieee80211_vht_capabilities)); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities", + (u8 *) peer->vht_capabilities, + sizeof(struct ieee80211_vht_capabilities)); + + return 0; +} + + +static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->ext_capab) { + wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities " + "received"); + return 0; + } + + if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) { + /* Need to allocate buffer to fit the new information */ + os_free(peer->ext_capab); + peer->ext_capab = os_zalloc(kde->ext_capab_len - 2); + if (peer->ext_capab == NULL) + return -1; + } + + peer->ext_capab_len = kde->ext_capab_len - 2; + os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len); + + return 0; +} + + +static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_channels) { + wpa_printf(MSG_DEBUG, "TDLS: No supported channels received"); + return 0; + } + + if (!peer->supp_channels || + peer->supp_channels_len < kde->supp_channels_len) { + os_free(peer->supp_channels); + peer->supp_channels = os_zalloc(kde->supp_channels_len); + if (peer->supp_channels == NULL) + return -1; + } + + peer->supp_channels_len = kde->supp_channels_len; + + os_memcpy(peer->supp_channels, kde->supp_channels, + peer->supp_channels_len); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels", + (u8 *) peer->supp_channels, peer->supp_channels_len); + return 0; +} + + +static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_oper_classes) { + wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received"); + return 0; + } + + if (!peer->supp_oper_classes || + peer->supp_oper_classes_len < kde->supp_oper_classes_len) { + os_free(peer->supp_oper_classes); + peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len); + if (peer->supp_oper_classes == NULL) + return -1; + } + + peer->supp_oper_classes_len = kde->supp_oper_classes_len; + os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes, + peer->supp_oper_classes_len); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes", + (u8 *) peer->supp_oper_classes, + peer->supp_oper_classes_len); + return 0; +} + + +static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer; + struct wpa_eapol_ie_parse kde; + struct wpa_ie_data ie; + int cipher; + const u8 *cpos; + struct wpa_tdls_ftie *ftie = NULL; + struct wpa_tdls_timeoutie *timeoutie; + struct wpa_tdls_lnkid *lnkid; + u32 lifetime = 0; +#if 0 + struct rsn_ie_hdr *hdr; + u8 *pos; + u16 rsn_capab; + u16 rsn_ver; +#endif + u8 dtoken; + u16 ielen; + u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE; + int tdls_prohibited = sm->tdls_prohibited; + int existing_peer = 0; + + if (len < 3 + 3) + return -1; + + cpos = buf; + cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + + /* driver had already verified the frame format */ + dtoken = *cpos++; /* dialog token */ + + wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken); + + peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer); + if (peer == NULL) + goto error; + + /* If found, use existing entry instead of adding a new one; + * how to handle the case where both ends initiate at the + * same time? */ + if (existing_peer) { + if (peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " + "direct link is enabled - tear down the " + "old link first"); + wpa_tdls_disable_peer_link(sm, peer); + } + + /* + * An entry is already present, so check if we already sent a + * TDLS Setup Request. If so, compare MAC addresses and let the + * STA with the lower MAC address continue as the initiator. + * The other negotiation is terminated. + */ + if (peer->initiator) { + if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discard request " + "from peer with higher address " + MACSTR, MAC2STR(src_addr)); + return -1; + } else { + wpa_printf(MSG_DEBUG, "TDLS: Accept request " + "from peer with lower address " + MACSTR " (terminate previously " + "initiated negotiation", + MAC2STR(src_addr)); + wpa_tdls_disable_peer_link(sm, peer); + } + } + } + + /* capability information */ + peer->capability = WPA_GET_LE16(cpos); + cpos += 2; + + ielen = len - (cpos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1"); + goto error; + } + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " + "TPK M1"); + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1", + kde.lnkid, kde.lnkid_len); + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS"); + status = WLAN_STATUS_NOT_IN_SAME_BSS; + goto error; + } + + wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR, + MAC2STR(src_addr)); + + if (copy_supp_rates(&kde, peer) < 0) + goto error; + + if (copy_peer_ht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_vht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_ext_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_channels(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_oper_classes(&kde, peer) < 0) + goto error; + + peer->qos_info = kde.qosinfo; + + peer->aid = kde.aid; + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { + peer = wpa_tdls_add_peer(sm, src_addr, NULL); + if (peer == NULL) + goto error; + wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of " + "TDLS setup - send own request"); + peer->initiator = 1; + wpa_tdls_send_tpk_m1(sm, peer); + } + + if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) && + tdls_prohibited) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition " + "on TDLS"); + tdls_prohibited = 0; + } +#endif /* CONFIG_TDLS_TESTING */ + + if (tdls_prohibited) { + wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS"); + status = WLAN_STATUS_REQUEST_DECLINED; + goto error; + } + + if (!wpa_tdls_get_privacy(sm)) { + if (kde.rsn_ie) { + wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while " + "security is disabled"); + status = WLAN_STATUS_SECURITY_DISABLED; + goto error; + } + goto skip_rsn; + } + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) || + kde.rsn_ie == NULL) { + wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto error; + } + + if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) { + wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in " + "TPK M1"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + cipher = ie.pairwise_cipher; + if (cipher & WPA_CIPHER_CCMP) { + wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link"); + cipher = WPA_CIPHER_CCMP; + } else { + wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1"); + status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + goto error; + } + + if ((ie.capabilities & + (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) != + WPA_CAPABILITY_PEERKEY_ENABLED) { + wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in " + "TPK M1"); + status = WLAN_STATUS_INVALID_RSN_IE_CAPAB; + goto error; + } + + /* Lifetime */ + if (kde.key_lifetime == NULL) { + wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1"); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; + lifetime = WPA_GET_LE32(timeoutie->value); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime); + if (lifetime < 300) { + wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime"); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + +skip_rsn: +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { + if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) { + /* + * The request frame from us is going to win, so do not + * replace information based on this request frame from + * the peer. + */ + goto skip_rsn_check; + } + } +#endif /* CONFIG_TDLS_TESTING */ + + peer->initiator = 0; /* Need to check */ + peer->dtoken = dtoken; + + if (!wpa_tdls_get_privacy(sm)) { + peer->rsnie_i_len = 0; + peer->rsnie_p_len = 0; + peer->cipher = WPA_CIPHER_NONE; + goto skip_rsn_check; + } + + ftie = (struct wpa_tdls_ftie *) kde.ftie; + os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); + peer->rsnie_i_len = kde.rsn_ie_len; + peer->cipher = cipher; + + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { + /* + * There is no point in updating the RNonce for every obtained + * TPK M1 frame (e.g., retransmission due to timeout) with the + * same INonce (SNonce in FTIE). However, if the TPK M1 is + * retransmitted with a different INonce, update the RNonce + * since this is for a new TDLS session. + */ + wpa_printf(MSG_DEBUG, + "TDLS: New TPK M1 INonce - generate new RNonce"); + os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); + if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "TDLS: Failed to get random data for responder nonce"); + wpa_tdls_peer_free(sm, peer); + goto error; + } + } + +#if 0 + /* get version info from RSNIE received from Peer */ + hdr = (struct rsn_ie_hdr *) kde.rsn_ie; + rsn_ver = WPA_GET_LE16(hdr->version); + + /* use min(peer's version, out version) */ + if (rsn_ver > RSN_VERSION) + rsn_ver = RSN_VERSION; + + hdr = (struct rsn_ie_hdr *) peer->rsnie_p; + + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, rsn_ver); + pos = (u8 *) (hdr + 1); + + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + 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); + pos += RSN_SELECTOR_LEN; + + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); + pos += RSN_SELECTOR_LEN; + + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; + WPA_PUT_LE16(pos, rsn_capab); + pos += 2; + + hdr->len = (pos - peer->rsnie_p) - 2; + peer->rsnie_p_len = pos - peer->rsnie_p; +#endif + + /* temp fix: validation of RSNIE later */ + os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len); + peer->rsnie_p_len = peer->rsnie_i_len; + + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake", + peer->rsnie_p, peer->rsnie_p_len); + + peer->lifetime = lifetime; + + wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); + +skip_rsn_check: + /* add the peer to the driver as a "setup in progress" peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0, + NULL, 0, NULL, 0, NULL, 0); + peer->tpk_in_progress = 1; + + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2"); + if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) { + wpa_tdls_disable_peer_link(sm, peer); + goto error; + } + + return 0; + +error: + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, + status); + return -1; +} + + +static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + peer->tpk_success = 1; + peer->tpk_in_progress = 0; + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + if (wpa_tdls_get_privacy(sm)) { + u32 lifetime = peer->lifetime; + /* + * Start the initiator process a bit earlier to avoid race + * condition with the responder sending teardown request. + */ + if (lifetime > 3 && peer->initiator) + lifetime -= 3; + eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout, + sm, peer); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK " + "expiration"); + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + } +#endif /* CONFIG_TDLS_TESTING */ + } + + /* add supported rates, capabilities, and qos_info to the TDLS peer */ + if (wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->aid, + peer->capability, + peer->supp_rates, peer->supp_rates_len, + peer->ht_capabilities, + peer->vht_capabilities, + peer->qos_info, peer->ext_capab, + peer->ext_capab_len, + peer->supp_channels, + peer->supp_channels_len, + peer->supp_oper_classes, + peer->supp_oper_classes_len) < 0) + return -1; + + if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) { + wpa_printf(MSG_INFO, "TDLS: Could not configure key to the " + "driver"); + return -1; + } + peer->reconfig_key = 0; + + return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); +} + + +static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer; + struct wpa_eapol_ie_parse kde; + struct wpa_ie_data ie; + int cipher; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_timeoutie *timeoutie; + struct wpa_tdls_lnkid *lnkid; + u32 lifetime; + u8 dtoken; + int ielen; + u16 status; + const u8 *pos; + int ret = 0; + + wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 " + "(Peer " MACSTR ")", MAC2STR(src_addr)); + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching peer found for " + "TPK M2: " MACSTR, MAC2STR(src_addr)); + return -1; + } + if (!peer->initiator) { + /* + * This may happen if both devices try to initiate TDLS at the + * same time and we accept the TPK M1 from the peer in + * wpa_tdls_process_tpk_m1() and clear our previous state. + */ + wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so " + "ignore TPK M2 from " MACSTR, MAC2STR(src_addr)); + return -1; + } + wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST); + + if (len < 3 + 2 + 1) { + wpa_tdls_disable_peer_link(sm, peer); + return -1; + } + + pos = buf; + pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + status = WPA_GET_LE16(pos); + pos += 2 /* status code */; + + if (status != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u", + status); + wpa_tdls_disable_peer_link(sm, peer); + return -1; + } + + status = WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* TODO: need to verify dialog token matches here or in kernel */ + dtoken = *pos++; /* dialog token */ + + wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken); + + if (len < 3 + 2 + 1 + 2) { + wpa_tdls_disable_peer_link(sm, peer); + return -1; + } + + /* capability information */ + peer->capability = WPA_GET_LE16(pos); + pos += 2; + + ielen = len - (pos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2"); + goto error; + } + +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_DECLINE_RESP) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response"); + status = WLAN_STATUS_REQUEST_DECLINED; + goto error; + } +#endif /* CONFIG_TDLS_TESTING */ + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " + "TPK M2"); + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2", + kde.lnkid, kde.lnkid_len); + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS"); + status = WLAN_STATUS_NOT_IN_SAME_BSS; + goto error; + } + + if (copy_supp_rates(&kde, peer) < 0) + goto error; + + if (copy_peer_ht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_vht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_ext_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_channels(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_oper_classes(&kde, peer) < 0) + goto error; + + peer->qos_info = kde.qosinfo; + + peer->aid = kde.aid; + + if (!wpa_tdls_get_privacy(sm)) { + peer->rsnie_p_len = 0; + peer->cipher = WPA_CIPHER_NONE; + goto skip_rsn; + } + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) || + kde.rsn_ie == NULL) { + wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2"); + status = WLAN_STATUS_INVALID_PARAMETERS; + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", + kde.rsn_ie, kde.rsn_ie_len); + + /* + * FIX: bitwise comparison of RSN IE is not the correct way of + * validation this. It can be different, but certain fields must + * match. Since we list only a single pairwise cipher in TPK M1, the + * memcmp is likely to work in most cases, though. + */ + if (kde.rsn_ie_len != peer->rsnie_i_len || + os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) { + wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does " + "not match with RSN IE used in TPK M1"); + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1", + peer->rsnie_i, peer->rsnie_i_len); + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", + kde.rsn_ie, kde.rsn_ie_len); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + + cipher = ie.pairwise_cipher; + if (cipher == WPA_CIPHER_CCMP) { + wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link"); + cipher = WPA_CIPHER_CCMP; + } else { + wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2"); + status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + goto error; + } + + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2", + kde.ftie, sizeof(*ftie)); + ftie = (struct wpa_tdls_ftie *) kde.ftie; + + if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { + wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does " + "not match with FTIE SNonce used in TPK M1"); + /* Silently discard the frame */ + return -1; + } + + /* Responder Nonce and RSN IE */ + os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN); + os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len); + peer->rsnie_p_len = kde.rsn_ie_len; + peer->cipher = cipher; + + /* Lifetime */ + if (kde.key_lifetime == NULL) { + wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2"); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; + lifetime = WPA_GET_LE32(timeoutie->value); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2", + lifetime); + if (lifetime != peer->lifetime) { + wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " + "TPK M2 (expected %u)", lifetime, peer->lifetime); + status = WLAN_STATUS_UNACCEPTABLE_LIFETIME; + goto error; + } + + wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); + + /* Process MIC check to see if TPK M2 is right */ + if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid, + (u8 *) timeoutie, ftie) < 0) { + /* Discard the frame */ + wpa_tdls_del_key(sm, peer); + wpa_tdls_disable_peer_link(sm, peer); + return -1; + } + + if (wpa_tdls_set_key(sm, peer) < 0) { + /* + * Some drivers may not be able to config the key prior to full + * STA entry having been configured. + */ + wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " + "STA entry is complete"); + peer->reconfig_key = 1; + } + +skip_rsn: + peer->dtoken = dtoken; + + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " + "TPK Handshake Message 3"); + if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) { + wpa_tdls_disable_peer_link(sm, peer); + return -1; + } + + if (!peer->tpk_success) { + /* + * Enable Link only when tpk_success is 0, signifying that this + * processing of TPK M2 frame is not because of a retransmission + * during TDLS setup handshake. + */ + ret = wpa_tdls_enable_link(sm, peer); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); + wpa_tdls_do_teardown( + sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + } + } + return ret; + +error: + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, + status); + wpa_tdls_disable_peer_link(sm, peer); + return -1; +} + + +static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_tdls_peer *peer; + struct wpa_eapol_ie_parse kde; + struct wpa_tdls_ftie *ftie; + struct wpa_tdls_timeoutie *timeoutie; + struct wpa_tdls_lnkid *lnkid; + int ielen; + u16 status; + const u8 *pos; + u32 lifetime; + int ret = 0; + + wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 " + "(Peer " MACSTR ")", MAC2STR(src_addr)); + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) + break; + } + if (peer == NULL) { + wpa_printf(MSG_INFO, "TDLS: No matching peer found for " + "TPK M3: " MACSTR, MAC2STR(src_addr)); + return -1; + } + wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE); + + if (len < 3 + 3) + goto error; + pos = buf; + pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; + + status = WPA_GET_LE16(pos); + + if (status != 0) { + wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u", + status); + goto error; + } + pos += 2 /* status code */ + 1 /* dialog token */; + + ielen = len - (pos - buf); /* start of IE in buf */ + if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { + wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3"); + goto error; + } + + if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { + wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3"); + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3", + (u8 *) kde.lnkid, kde.lnkid_len); + lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; + + if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS"); + goto error; + } + + if (!wpa_tdls_get_privacy(sm)) + goto skip_rsn; + + if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3"); + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3", + kde.ftie, sizeof(*ftie)); + ftie = (struct wpa_tdls_ftie *) kde.ftie; + + if (kde.rsn_ie == NULL) { + wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3"); + goto error; + } + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3", + kde.rsn_ie, kde.rsn_ie_len); + if (kde.rsn_ie_len != peer->rsnie_p_len || + os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) { + wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match " + "with the one sent in TPK M2"); + goto error; + } + + if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) { + wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does " + "not match with FTIE ANonce used in TPK M2"); + goto error; + } + + if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { + wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not " + "match with FTIE SNonce used in TPK M1"); + goto error; + } + + if (kde.key_lifetime == NULL) { + wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3"); + goto error; + } + timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; + wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3", + (u8 *) timeoutie, sizeof(*timeoutie)); + lifetime = WPA_GET_LE32(timeoutie->value); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3", + lifetime); + if (lifetime != peer->lifetime) { + wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " + "TPK M3 (expected %u)", lifetime, peer->lifetime); + goto error; + } + + if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid, + (u8 *) timeoutie, ftie) < 0) { + wpa_tdls_del_key(sm, peer); + goto error; + } + + if (wpa_tdls_set_key(sm, peer) < 0) { + /* + * Some drivers may not be able to config the key prior to full + * STA entry having been configured. + */ + wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " + "STA entry is complete"); + peer->reconfig_key = 1; + } + +skip_rsn: + if (!peer->tpk_success) { + /* + * Enable Link only when tpk_success is 0, signifying that this + * processing of TPK M3 frame is not because of a retransmission + * during TDLS setup handshake. + */ + ret = wpa_tdls_enable_link(sm, peer); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); + wpa_tdls_do_teardown( + sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + } + } + return ret; +error: + wpa_tdls_disable_peer_link(sm, peer); + return -1; +} + + +static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs) +{ + struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie; + + os_memset(lifetime, 0, ie_len); + lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL; + lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2; + lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME; + WPA_PUT_LE32(lifetime->value, tsecs); + os_memcpy(pos, ie, ie_len); + return pos + ie_len; +} + + +/** + * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1) + * @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 TPK Handshake Message 1 info to driver to start TDLS + * handshake with the peer. + */ +int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + int tdls_prohibited = sm->tdls_prohibited; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + +#ifdef CONFIG_TDLS_TESTING + if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) && + tdls_prohibited) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition " + "on TDLS"); + tdls_prohibited = 0; + } +#endif /* CONFIG_TDLS_TESTING */ + + if (tdls_prohibited) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - " + "reject request to start setup"); + return -1; + } + + peer = wpa_tdls_add_peer(sm, addr, NULL); + if (peer == NULL) + return -1; + + if (peer->tpk_in_progress) { + wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer"); + return 0; + } + + peer->initiator = 1; + + /* add the peer to the driver as a "setup in progress" peer */ + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0, + NULL, 0, NULL, 0, NULL, 0); + + peer->tpk_in_progress = 1; + + if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { + wpa_tdls_disable_peer_link(sm, peer); + return -1; + } + + return 0; +} + + +void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL || !peer->tpk_success) + return; + + if (sm->tdls_external_setup) { + /* + * Disable previous link to allow renegotiation to be completed + * on AP path. + */ + wpa_tdls_disable_peer_link(sm, peer); + } +} + + +/** + * wpa_supplicant_rx_tdls - Receive TDLS data frame + * + * This function is called to receive TDLS (ethertype = 0x890d) data frames. + */ +static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_sm *sm = ctx; + struct wpa_tdls_frame *tf; + + wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation", + buf, len); + + if (sm->tdls_disabled || !sm->tdls_supported) { + wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled " + "or unsupported by driver"); + return; + } + + if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message"); + return; + } + + if (len < sizeof(*tf)) { + wpa_printf(MSG_INFO, "TDLS: Drop too short frame"); + return; + } + + /* Check to make sure its a valid encapsulated TDLS frame */ + tf = (struct wpa_tdls_frame *) buf; + if (tf->payloadtype != 2 /* TDLS_RFTYPE */ || + tf->category != WLAN_ACTION_TDLS) { + wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u " + "category=%u action=%u", + tf->payloadtype, tf->category, tf->action); + return; + } + + switch (tf->action) { + case WLAN_TDLS_SETUP_REQUEST: + wpa_tdls_process_tpk_m1(sm, src_addr, buf, len); + break; + case WLAN_TDLS_SETUP_RESPONSE: + wpa_tdls_process_tpk_m2(sm, src_addr, buf, len); + break; + case WLAN_TDLS_SETUP_CONFIRM: + wpa_tdls_process_tpk_m3(sm, src_addr, buf, len); + break; + case WLAN_TDLS_TEARDOWN: + wpa_tdls_recv_teardown(sm, src_addr, buf, len); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + wpa_tdls_process_discovery_request(sm, src_addr, buf, len); + break; + default: + /* Kernel code will process remaining frames */ + wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u", + tf->action); + break; + } +} + + +/** + * wpa_tdls_init - Initialize driver interface parameters for TDLS + * @wpa_s: Pointer to wpa_supplicant data + * Returns: 0 on success, -1 on failure + * + * This function is called to initialize driver interface parameters for TDLS. + * wpa_drv_init() must have been called before this function to initialize the + * driver interface. + */ +int wpa_tdls_init(struct wpa_sm *sm) +{ + if (sm == NULL) + return -1; + + sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname : + sm->ifname, + sm->own_addr, + ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls, + sm, 0); + if (sm->l2_tdls == NULL) { + wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet " + "connection"); + return -1; + } + + /* + * Drivers that support TDLS but don't implement the get_capa callback + * are assumed to perform everything internally + */ + if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported, + &sm->tdls_external_setup) < 0) { + sm->tdls_supported = 1; + sm->tdls_external_setup = 0; + } + + wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by " + "driver", sm->tdls_supported ? "" : " not"); + wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup", + sm->tdls_external_setup ? "external" : "internal"); + + return 0; +} + + +void wpa_tdls_teardown_peers(struct wpa_sm *sm) +{ + struct wpa_tdls_peer *peer; + + peer = sm->tdls; + + wpa_printf(MSG_DEBUG, "TDLS: Tear down peers"); + + while (peer) { + wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR, + MAC2STR(peer->addr)); + if (sm->tdls_external_setup) + wpa_tdls_send_teardown(sm, peer->addr, + WLAN_REASON_DEAUTH_LEAVING); + else + wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); + + peer = peer->next; + } +} + + +static void wpa_tdls_remove_peers(struct wpa_sm *sm) +{ + struct wpa_tdls_peer *peer, *tmp; + + peer = sm->tdls; + sm->tdls = NULL; + + while (peer) { + int res; + tmp = peer->next; + res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)", + MAC2STR(peer->addr), res); + wpa_tdls_peer_free(sm, peer); + os_free(peer); + peer = tmp; + } +} + + +/** + * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS + * + * This function is called to recover driver interface parameters for TDLS + * and frees resources allocated for it. + */ +void wpa_tdls_deinit(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + + if (sm->l2_tdls) + l2_packet_deinit(sm->l2_tdls); + sm->l2_tdls = NULL; + + wpa_tdls_remove_peers(sm); +} + + +void wpa_tdls_assoc(struct wpa_sm *sm) +{ + wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association"); + wpa_tdls_remove_peers(sm); +} + + +void wpa_tdls_disassoc(struct wpa_sm *sm) +{ + wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation"); + wpa_tdls_remove_peers(sm); +} + + +static int wpa_tdls_prohibited(const u8 *ies, size_t len) +{ + struct wpa_eapol_ie_parse elems; + + if (ies == NULL) + return 0; + + if (wpa_supplicant_parse_ies(ies, len, &elems) < 0) + return 0; + + if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + return 0; + + /* bit 38 - TDLS Prohibited */ + return !!(elems.ext_capab[2 + 4] & 0x40); +} + + +void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) +{ + sm->tdls_prohibited = wpa_tdls_prohibited(ies, len); + wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS", + sm->tdls_prohibited ? "prohibited" : "allowed"); +} + + +void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) +{ + if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on " + "(Re)Association Response IEs"); + sm->tdls_prohibited = 1; + } +} + + +void wpa_tdls_enable(struct wpa_sm *sm, int enabled) +{ + wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled"); + sm->tdls_disabled = !enabled; +} + + +int wpa_tdls_is_external_setup(struct wpa_sm *sm) +{ + return sm->tdls_external_setup; +} diff --git a/contrib/hostapd/src/rsn_supp/wpa.c b/contrib/hostapd/src/rsn_supp/wpa.c index e611fc5e78..4474c3b117 100644 --- a/contrib/hostapd/src/rsn_supp/wpa.c +++ b/contrib/hostapd/src/rsn_supp/wpa.c @@ -1,92 +1,26 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "rc4.h" -#include "aes_wrap.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" +#include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" #include "eloop.h" -#include "eapol_supp/eapol_supp_sm.h" #include "preauth.h" #include "pmksa_cache.h" #include "wpa_i.h" #include "wpa_ie.h" #include "peerkey.h" -#include "ieee802_11_defs.h" - - -/** - * 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 - */ -static const char * wpa_cipher_txt(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_NONE: - return "NONE"; - case WPA_CIPHER_WEP40: - return "WEP-40"; - case WPA_CIPHER_WEP104: - return "WEP-104"; - case WPA_CIPHER_TKIP: - return "TKIP"; - case WPA_CIPHER_CCMP: - return "CCMP"; - default: - return "UNKNOWN"; - } -} - - -/** - * 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 - */ -static const char * wpa_key_mgmt_txt(int key_mgmt, int proto) -{ - switch (key_mgmt) { - case WPA_KEY_MGMT_IEEE8021X: - return proto == WPA_PROTO_RSN ? - "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP"; - case WPA_KEY_MGMT_PSK: - return proto == WPA_PROTO_RSN ? - "WPA2-PSK" : "WPA-PSK"; - case WPA_KEY_MGMT_NONE: - return "NONE"; - case WPA_KEY_MGMT_IEEE8021X_NO_WPA: - return "IEEE 802.1X (no WPA)"; -#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"; - } -} /** @@ -110,20 +44,30 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, * 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"); + wpa_dbg(sm->ctx->msg_ctx, 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)); + wpa_dbg(sm->ctx->msg_ctx, 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); + if (key_mic && + wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: Failed to generate EAPOL-Key " + "version %d MIC", ver); + goto out; + } + wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16); + wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16); 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); } @@ -147,14 +91,14 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) 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) + else if (sm->pairwise_cipher != WPA_CIPHER_TKIP) 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "Failed to read BSSID for EAPOL-Key request"); return; } @@ -180,9 +124,10 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) 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_msg(sm->ctx->msg_ctx, 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); @@ -200,12 +145,14 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * 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); + sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, + NULL); if (sm->cur_pmksa) { - wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from " - "PMKSA cache"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: found matching PMKID from PMKSA cache"); } else { - wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: no matching PMKID found"); abort_cached = 1; } } @@ -243,27 +190,38 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, #endif /* CONFIG_IEEE80211R */ } if (res == 0) { + struct rsn_pmksa_cache_entry *sa = NULL; wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; - pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, src_addr, - sm->own_addr, sm->network_ctx, - sm->key_mgmt); + if (sm->proto == WPA_PROTO_RSN && + !wpa_key_mgmt_ft(sm->key_mgmt)) { + sa = 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"); + pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) + { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: the new PMK matches with the " + "PMKID"); abort_cached = 0; } + + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; } else { - wpa_msg(sm->ctx->ctx, MSG_WARNING, + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " - "EAPOL state machines"); - wpa_msg(sm->ctx->ctx, MSG_WARNING, - "WPA: Key handshake aborted"); + "EAPOL state machines - key handshake " + "aborted"); if (sm->cur_pmksa) { - wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA " - "caching attempt"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelled PMKSA caching " + "attempt"); sm->cur_pmksa = NULL; abort_cached = 1; } else if (!abort_cached) { @@ -272,19 +230,22 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, } } - if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) { + if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && + !wpa_key_mgmt_ft(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"); + wpa_dbg(sm->ctx->msg_ctx, 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; @@ -315,20 +276,54 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, 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"); + wpa_msg(sm->ctx->msg_ctx, 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) + 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; @@ -340,13 +335,16 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, os_memcpy(reply->key_length, key->key_length, 2); os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->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_dbg(sm->ctx->msg_ctx, 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); @@ -358,7 +356,7 @@ 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; + size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 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); @@ -380,39 +378,47 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; u8 buf[8]; + int res; + u8 *kde, *kde_buf = NULL; + size_t kde_len; if (wpa_sm_get_network_ctx(sm) == NULL) { - wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of " - "4)."); + wpa_msg(sm->ctx->msg_ctx, 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); + wpa_dbg(sm->ctx->msg_ctx, 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 (wpa_supplicant_parse_ies(_buf, len, &ie) < 0) + goto failed; if (ie.pmkid) { wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " "Authenticator", ie.pmkid, PMKID_LEN); } } -#endif /* CONFIG_NO_WPA2 */ - if (wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid)) + res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); + if (res == -2) { + wpa_dbg(sm->ctx->msg_ctx, 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->ctx, MSG_WARNING, + if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for SNonce"); goto failed; } @@ -431,15 +437,39 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, os_memcpy(ptk->u.auth.rx_mic_key, buf, 8); sm->tptk_set = 1; + kde = sm->assoc_wpa_ie; + kde_len = sm->assoc_wpa_ie_len; + +#ifdef CONFIG_P2P + if (sm->p2p) { + kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1); + if (kde_buf) { + u8 *pos; + wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE " + "into EAPOL-Key 2/4"); + os_memcpy(kde_buf, kde, kde_len); + kde = kde_buf; + pos = kde + kde_len; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + 1; + RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ); + pos += RSN_SELECTOR_LEN; + *pos++ = 0x01; + kde_len = pos - kde; + } + } +#endif /* CONFIG_P2P */ + if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, - sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, - ptk)) + kde, kde_len, ptk)) goto failed; + os_free(kde_buf); os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); return; failed: + os_free(kde_buf); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -454,7 +484,8 @@ static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx) static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, const u8 *addr, int secure) { - wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Key negotiation completed with " + 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)); @@ -472,22 +503,23 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, * 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 + * likelihood 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"); + wpa_dbg(sm->ctx->msg_ctx, 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); + wpa_ft_prepare_auth_request(sm, NULL); } #endif /* CONFIG_IEEE80211R */ } @@ -496,7 +528,7 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, 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_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying"); wpa_sm_key_request(sm, 0, 1); } @@ -505,33 +537,30 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, const struct wpa_eapol_key *key) { int keylen, rsclen; - wpa_alg alg; + 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."); + wpa_dbg(sm->ctx->msg_ctx, 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"); + if (sm->pairwise_cipher == WPA_CIPHER_NONE) { + wpa_dbg(sm->ctx->msg_ctx, 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); + } + + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported pairwise cipher %d", + sm->pairwise_cipher); return -1; } + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); + if (sm->proto == WPA_PROTO_RSN) { key_rsc = null_rsc; } else { @@ -541,9 +570,10 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, 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)); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set PTK to the " + "driver (alg=%d keylen=%d bssid=" MACSTR ")", + alg, keylen, MAC2STR(sm->bssid)); return -1; } @@ -557,63 +587,36 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, } -static int wpa_supplicant_check_group_cipher(int group_cipher, +static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, + int group_cipher, int keylen, int maxkeylen, - int *key_rsc_len, wpa_alg *alg) + int *key_rsc_len, + enum wpa_alg *alg) { - int ret = 0; + int klen; - 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); + *alg = wpa_cipher_to_alg(group_cipher); + if (*alg == WPA_ALG_NONE) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported Group Cipher %d", + group_cipher); return -1; } + *key_rsc_len = wpa_cipher_rsc_len(group_cipher); - if (ret < 0 ) { - wpa_printf(MSG_WARNING, "WPA: Unsupported %s Group Cipher key " - "length %d (%d).", - wpa_cipher_txt(group_cipher), keylen, maxkeylen); + klen = wpa_cipher_key_len(group_cipher); + if (keylen != klen || maxkeylen < klen) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported %s Group Cipher key length %d (%d)", + wpa_cipher_txt(group_cipher), keylen, maxkeylen); + return -1; } - - return ret; + return 0; } struct wpa_gtk_data { - wpa_alg alg; + enum wpa_alg alg; int tx, key_rsc_len, keyidx; u8 gtk[32]; int gtk_len; @@ -628,9 +631,9 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, 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_dbg(sm->ctx->msg_ctx, 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 */ @@ -640,21 +643,21 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, _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", + if (wpa_sm_set_key(sm, gd->alg, NULL, 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)."); + wpa_msg(sm->ctx->msg_ctx, 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", + } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr, 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); + wpa_msg(sm->ctx->msg_ctx, 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; } @@ -671,8 +674,9 @@ static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, * 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Tx bit set for GTK, but pairwise " + "keys are used - ignore Tx bit"); return 0; } return tx; @@ -684,7 +688,6 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const u8 *gtk, size_t gtk_len, int key_info) { -#ifndef CONFIG_NO_WPA2 struct wpa_gtk_data gd; /* @@ -711,20 +714,19 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, 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"); + if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && + (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gtk_len, gtk_len, + &gd.key_rsc_len, &gd.alg) || + wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) { + wpa_dbg(sm->ctx->msg_ctx, 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 */ } @@ -742,22 +744,21 @@ static int ieee80211w_set_keys(struct wpa_sm *sm, 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_dbg(sm->ctx->msg_ctx, 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); + wpa_msg(sm->ctx->msg_ctx, 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", + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure IGTK to the driver"); return -1; } } @@ -774,7 +775,7 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *rsn_ie, size_t rsn_ie_len) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", reason, MAC2STR(src_addr)); if (sm->ap_wpa_ie) { @@ -783,8 +784,8 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm, } if (wpa_ie) { if (!sm->ap_wpa_ie) { - wpa_printf(MSG_INFO, "WPA: No WPA IE in " - "Beacon/ProbeResp"); + wpa_msg(sm->ctx->msg_ctx, 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); @@ -796,30 +797,164 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm, } if (rsn_ie) { if (!sm->ap_rsn_ie) { - wpa_printf(MSG_INFO, "WPA: No RSN IE in " - "Beacon/ProbeResp"); + wpa_msg(sm->ctx->msg_ctx, 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); + wpa_sm_deauthenticate(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_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, 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"); + wpa_dbg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Found the current AP from " + "updated scan results"); } } @@ -836,8 +971,9 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, (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 && - (ie->rsn_ie_len != sm->ap_rsn_ie_len || - os_memcmp(ie->rsn_ie, sm->ap_rsn_ie, ie->rsn_ie_len) != 0))) { + 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, @@ -857,19 +993,9 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, } #ifdef CONFIG_IEEE80211R - if (wpa_key_mgmt_ft(sm->key_mgmt)) { - struct rsn_mdie *mdie; - /* TODO: verify that full MDIE matches with the one from scan - * results, not only mobility domain */ - 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 (wpa_key_mgmt_ft(sm->key_mgmt) && + wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0) + return -1; #endif /* CONFIG_IEEE80211R */ return 0; @@ -923,7 +1049,7 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, if (kde) os_memcpy(reply + 1, kde, kde_len); - wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); + wpa_dbg(sm->ctx->msg_ctx, 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); @@ -940,29 +1066,32 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, 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); + wpa_dbg(sm->ctx->msg_ctx, 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 (wpa_supplicant_parse_ies(pos, len, &ie) < 0) + goto failed; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { - wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, 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); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KDE length %lu", + (unsigned long) ie.igtk_len); goto failed; } #endif /* CONFIG_IEEE80211W */ @@ -971,31 +1100,29 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, 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)); + wpa_msg(sm->ctx->msg_ctx, 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 (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid %s key length %d (src=" MACSTR + ")", wpa_cipher_txt(sm->pairwise_cipher), keylen, + MAC2STR(sm->bssid)); + goto failed; + } + +#ifdef CONFIG_P2P + if (ie.ip_addr_alloc) { + os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4); + wpa_hexdump(MSG_DEBUG, "P2P: IP address info", + sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr)); } +#endif /* CONFIG_P2P */ if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, NULL, 0, &sm->ptk)) { @@ -1020,18 +1147,26 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); - if (ie.gtk && + if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) { + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + } else 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"); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure IGTK"); goto failed; } + if (ie.gtk) + wpa_sm_set_rekey_offload(sm); + return; failed: @@ -1049,18 +1184,21 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, 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 (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) + return -1; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { - wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data"); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, 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, + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; @@ -1071,14 +1209,16 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, 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); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure IGTK"); return 0; } @@ -1096,22 +1236,23 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, 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); + wpa_msg(sm->ctx->msg_ctx, 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); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Too short maxkeylen (%lu)", + (unsigned long) maxkeylen); return -1; } maxkeylen -= 8; } - if (wpa_supplicant_check_group_cipher(sm->group_cipher, + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, gd->gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; @@ -1122,35 +1263,42 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, 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); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 key data too long (%lu)", + (unsigned long) keydatalen); return -1; } os_memcpy(gd->gtk, key + 1, keydatalen); - rc4_skip(ek, 32, 256, gd->gtk, keydatalen); + if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) { + wpa_msg(sm->ctx->msg_ctx, 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); + wpa_msg(sm->ctx->msg_ctx, 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); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES unwrap failed - could not decrypt " + "GTK"); return -1; } } else { - wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d", - ver); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported key_info type %d", ver); return -1; } gd->tx = wpa_supplicant_gtk_tx_bit_workaround( @@ -1186,7 +1334,7 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, WPA_PUT_BE16(reply->key_data_length, 0); - wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); + wpa_dbg(sm->ctx->msg_ctx, 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); @@ -1206,8 +1354,8 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, 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); + wpa_dbg(sm->ctx->msg_ctx, 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); @@ -1233,7 +1381,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, goto failed; if (rekey) { - wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Group rekeying " + 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); @@ -1243,6 +1391,9 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, key_info & WPA_KEY_INFO_SECURE); } + + wpa_sm_set_rekey_offload(sm); + return; failed: @@ -1264,8 +1415,9 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid EAPOL-Key MIC " + "when using TPTK - ignoring TPTK"); } else { ok = 1; sm->tptk_set = 0; @@ -1279,16 +1431,18 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, 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"); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Could not verify EAPOL-Key MIC - " + "dropping packet"); return -1; } @@ -1308,8 +1462,9 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, 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."); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: PTK not available, cannot decrypt EAPOL-Key Key " + "Data"); return -1; } @@ -1319,35 +1474,41 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, u8 ek[32]; os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, 16); - rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen); + if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) { + wpa_msg(sm->ctx->msg_ctx, 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); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, 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); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported key_info type %d", ver); return -1; } wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", @@ -1363,35 +1524,38 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, void wpa_sm_aborted_cached(struct wpa_sm *sm) { if (sm && sm->cur_pmksa) { - wpa_printf(MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelling PMKSA caching attempt"); sm->cur_pmksa = NULL; } } -static void wpa_eapol_key_dump(const struct wpa_eapol_key *key) +static void wpa_eapol_key_dump(struct wpa_sm *sm, + 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_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type); + wpa_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, 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); @@ -1435,10 +1599,11 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #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)); + wpa_dbg(sm->ctx->msg_ctx, 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; } @@ -1451,40 +1616,45 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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); + wpa_dbg(sm->ctx->msg_ctx, 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, " + wpa_dbg(sm->ctx->msg_ctx, 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); + wpa_dbg(sm->ctx->msg_ctx, 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); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL-Key type (%d) unknown, discarded", + key->type); ret = 0; goto out; } - wpa_eapol_key_dump(key); + wpa_eapol_key_dump(sm, 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); + wpa_dbg(sm->ctx->msg_ctx, 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; @@ -1493,8 +1663,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Unsupported EAPOL-Key descriptor version %d", + ver); goto out; } @@ -1502,8 +1673,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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."); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "FT: AP did not use AES-128-CMAC"); goto out; } } else @@ -1511,28 +1682,37 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #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."); + wpa_msg(sm->ctx->msg_ctx, 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); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Backwards compatibility: allow invalid " + "version for non-CCMP group keys"); } else goto out; } + if (sm->pairwise_cipher == WPA_CIPHER_GCMP && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: GCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2", ver); + goto out; + } #ifdef CONFIG_PEERKEY for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { @@ -1544,9 +1724,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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"); + wpa_msg(sm->ctx->msg_ctx, 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]; @@ -1555,16 +1735,18 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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"); + wpa_dbg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Ack bit in key_info from STK peer"); goto out; } #endif /* CONFIG_PEERKEY */ @@ -1572,8 +1754,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key Replay Counter did not increase - " + "dropping packet"); goto out; } @@ -1582,13 +1765,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, && (peerkey == NULL || !peerkey->initiator) #endif /* CONFIG_PEERKEY */ ) { - wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info"); + wpa_msg(sm->ctx->msg_ctx, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: EAPOL-Key with Request bit - dropped"); goto out; } @@ -1605,7 +1789,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, extra_len = data_len - sizeof(*hdr) - sizeof(*key); if (WPA_GET_BE16(key->key_data_length) > extra_len) { - wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " + 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); @@ -1622,8 +1806,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Ignored EAPOL-Key (Pairwise) with " + "non-zero key index"); goto out; } if (peerkey) { @@ -1647,8 +1832,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key (Group) without Mic bit - " + "dropped"); } } @@ -1661,23 +1847,6 @@ out: #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) { @@ -1701,6 +1870,10 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) case WPA_KEY_MGMT_PSK_SHA256: return RSN_AUTH_KEY_MGMT_PSK_SHA256; #endif /* CONFIG_IEEE80211W */ + case WPA_KEY_MGMT_CCKM: + return (sm->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_CCKM: + WPA_AUTH_KEY_MGMT_CCKM); case WPA_KEY_MGMT_WPA_NONE: return WPA_AUTH_KEY_MGMT_NONE; default: @@ -1709,30 +1882,6 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) } -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 @@ -1780,7 +1929,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) rsna ? "TRUE" : "FALSE", rsna ? "TRUE" : "FALSE", RSN_VERSION, - wpa_cipher_bits(sm->group_cipher), + wpa_cipher_key_len(sm->group_cipher) * 8, sm->dot11RSNAConfigPMKLifetime, sm->dot11RSNAConfigPMKReauthThreshold, sm->dot11RSNAConfigSATimeout); @@ -1800,12 +1949,16 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) "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)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + 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)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->group_cipher)), sm->dot11RSNA4WayHandshakeFailures); if (ret >= 0 && (size_t) ret < buflen) len += ret; @@ -1816,24 +1969,40 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, - void *ctx, int replace) + void *ctx, enum pmksa_free_reason reason) { struct wpa_sm *sm = ctx; + int deauth = 0; + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: " + MACSTR " reason=%d", MAC2STR(entry->aa), reason); + + if (sm->cur_pmksa == entry) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: %s current PMKSA entry", + reason == PMKSA_REPLACE ? "replaced" : "removed"); + pmksa_cache_clear_current(sm); + + /* + * If an entry is simply being replaced, there's no need to + * deauthenticate because it will be immediately re-added. + * This happens when EAP authentication is completed again + * (reauth or failed PMKSA caching attempt). + */ + if (reason != PMKSA_REPLACE) + deauth = 1; + } - if (sm->cur_pmksa == entry || + if (reason == PMKSA_EXPIRE && (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; - } + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: deauthenticating due to expired PMK"); + pmksa_cache_clear_current(sm); + deauth = 1; + } + if (deauth) { os_memset(sm->pmk, 0, sizeof(sm->pmk)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -1855,6 +2024,7 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) sm = os_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; + dl_list_init(&sm->pmksa_candidates); sm->renew_snonce = 1; sm->ctx = ctx; @@ -1864,8 +2034,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) 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"); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "RSN: PMKSA cache initialization failed"); os_free(sm); return NULL; } @@ -1890,6 +2060,9 @@ void wpa_sm_deinit(struct wpa_sm *sm) 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); } @@ -1909,7 +2082,8 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) if (sm == NULL) return; - wpa_printf(MSG_DEBUG, "WPA: Association event - clear replay counter"); + wpa_dbg(sm->ctx->msg_ctx, 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; @@ -1919,10 +2093,15 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) #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); + wpa_ft_prepare_auth_request(sm, NULL); clear_ptk = 0; } @@ -1933,10 +2112,18 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) * 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"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK"); sm->ptk_set = 0; sm->tptk_set = 0; } + +#ifdef CONFIG_TDLS + wpa_tdls_assoc(sm); +#endif /* CONFIG_TDLS */ + +#ifdef CONFIG_P2P + os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr)); +#endif /* CONFIG_P2P */ } @@ -1949,9 +2136,14 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) */ void wpa_sm_notify_disassoc(struct wpa_sm *sm) { + peerkey_deinit(sm); rsn_preauth_deinit(sm); + pmksa_cache_clear_current(sm); if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) sm->dot11RSNA4WayHandshakeFailures++; +#ifdef CONFIG_TDLS + wpa_tdls_disassoc(sm); +#endif /* CONFIG_TDLS */ } @@ -2055,6 +2247,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) } else sm->ssid_len = 0; sm->wpa_ptk_rekey = config->wpa_ptk_rekey; + sm->p2p = config->p2p; } else { sm->network_ctx = NULL; sm->peerkey_enabled = 0; @@ -2064,9 +2257,8 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->eap_conf_ctx = NULL; sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; + sm->p2p = 0; } - if (config == NULL || config->network_ctx != sm->network_ctx) - pmksa_cache_notify_reconfig(sm->pmksa); } @@ -2164,6 +2356,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, case WPA_PARAM_RSN_ENABLED: sm->rsn_enabled = value; break; + case WPA_PARAM_MFP: + sm->mfp = value; + break; default: break; } @@ -2238,10 +2433,41 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; + + if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) { + struct wpa_ie_data rsn; + if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) + >= 0 && + rsn.capabilities & (WPA_CAPABILITY_MFPR | + WPA_CAPABILITY_MFPC)) { + ret = os_snprintf(pos, end - pos, "pmf=%d\n", + (rsn.capabilities & + WPA_CAPABILITY_MFPR) ? 2 : 1); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + } + return pos - buf; } +int wpa_sm_pmf_enabled(struct wpa_sm *sm) +{ + struct wpa_ie_data rsn; + + if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie) + return 0; + + if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && + rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) + return 1; + + return 0; +} + + /** * 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() @@ -2301,7 +2527,8 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) os_free(sm->assoc_wpa_ie); if (ie == NULL || len == 0) { - wpa_printf(MSG_DEBUG, "WPA: clearing own WPA/RSN IE"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing own WPA/RSN IE"); sm->assoc_wpa_ie = NULL; sm->assoc_wpa_ie_len = 0; } else { @@ -2335,7 +2562,8 @@ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) os_free(sm->ap_wpa_ie); if (ie == NULL || len == 0) { - wpa_printf(MSG_DEBUG, "WPA: clearing AP WPA IE"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing AP WPA IE"); sm->ap_wpa_ie = NULL; sm->ap_wpa_ie_len = 0; } else { @@ -2369,7 +2597,8 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) os_free(sm->ap_rsn_ie); if (ie == NULL || len == 0) { - wpa_printf(MSG_DEBUG, "WPA: clearing AP RSN IE"); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing AP RSN IE"); sm->ap_rsn_ie = NULL; sm->ap_rsn_ie_len = 0; } else { @@ -2397,12 +2626,161 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) */ 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"); + if (sm == NULL) + return -1; + + if (sm->assoc_wpa_ie == NULL) { + wpa_dbg(sm->ctx->msg_ctx, 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) +{ + return pmksa_cache_list(sm->pmksa, buf, len); +} + + +void wpa_sm_drop_sa(struct wpa_sm *sm) +{ + wpa_dbg(sm->ctx->msg_ctx, 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; +} + + +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) +{ + os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN); +} + + +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) +{ + pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0); +} + + +#ifdef CONFIG_WNM +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) +{ + struct wpa_gtk_data gd; +#ifdef CONFIG_IEEE80211W + struct wpa_igtk_kde igd; + u16 keyidx; +#endif /* CONFIG_IEEE80211W */ + u16 keyinfo; + u8 keylen; /* plaintext key len */ + u8 *key_rsc; + + os_memset(&gd, 0, sizeof(gd)); +#ifdef CONFIG_IEEE80211W + os_memset(&igd, 0, sizeof(igd)); +#endif /* CONFIG_IEEE80211W */ + + keylen = wpa_cipher_key_len(sm->group_cipher); + gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher); + gd.alg = wpa_cipher_to_alg(sm->group_cipher); + if (gd.alg == WPA_ALG_NONE) { + wpa_printf(MSG_DEBUG, "Unsupported group cipher suite"); + return -1; + } + + if (subelem_id == WNM_SLEEP_SUBELEM_GTK) { + key_rsc = buf + 5; + keyinfo = WPA_GET_LE16(buf + 2); + gd.gtk_len = keylen; + if (gd.gtk_len != buf[4]) { + wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d", + gd.gtk_len, buf[4]); + return -1; + } + gd.keyidx = keyinfo & 0x03; /* B0 - B1 */ + gd.tx = wpa_supplicant_gtk_tx_bit_workaround( + sm, !!(keyinfo & WPA_KEY_INFO_TXRX)); + + os_memcpy(gd.gtk, buf + 13, gd.gtk_len); + + wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", + gd.gtk, gd.gtk_len); + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) { + wpa_printf(MSG_DEBUG, "Failed to install the GTK in " + "WNM mode"); + return -1; + } +#ifdef CONFIG_IEEE80211W + } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { + os_memcpy(igd.keyid, buf + 2, 2); + os_memcpy(igd.pn, buf + 4, 6); + + keyidx = WPA_GET_LE16(igd.keyid); + os_memcpy(igd.igtk, buf + 10, WPA_IGTK_LEN); + + wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", + igd.igtk, WPA_IGTK_LEN); + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, + keyidx, 0, igd.pn, sizeof(igd.pn), + igd.igtk, WPA_IGTK_LEN) < 0) { + wpa_printf(MSG_DEBUG, "Failed to install the IGTK in " + "WNM mode"); + return -1; + } +#endif /* CONFIG_IEEE80211W */ + } else { + wpa_printf(MSG_DEBUG, "Unknown element id"); + return -1; + } + + return 0; +} +#endif /* CONFIG_WNM */ + + +#ifdef CONFIG_PEERKEY +int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_peerkey *peerkey; + + for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { + if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) + break; + } + + if (!peerkey) + return 0; + + wpa_sm_rx_eapol(sm, src_addr, buf, len); + + return 1; +} +#endif /* CONFIG_PEERKEY */ + + +#ifdef CONFIG_P2P + +int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf) +{ + if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0) + return -1; + os_memcpy(buf, sm->p2p_ip_addr, 3 * 4); + return 0; +} + +#endif /* CONFIG_P2P */ diff --git a/contrib/hostapd/src/rsn_supp/wpa.h b/contrib/hostapd/src/rsn_supp/wpa.h index bdf778544f..20b3f6211f 100644 --- a/contrib/hostapd/src/rsn_supp/wpa.h +++ b/contrib/hostapd/src/rsn_supp/wpa.h @@ -2,30 +2,17 @@ * wpa_supplicant - WPA definitions * Copyright (c) 2003-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_H #define WPA_H -#include "defs.h" -#include "eapol_common.h" -#include "wpa_common.h" - -#ifndef ETH_P_EAPOL -#define ETH_P_EAPOL 0x888e -#endif - -#ifndef ETH_P_RSN_PREAUTH -#define ETH_P_RSN_PREAUTH 0x88c7 -#endif +#include "common/defs.h" +#include "common/eapol_common.h" +#include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" struct wpa_sm; struct eapol_sm; @@ -33,12 +20,12 @@ 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, wpa_states state); - wpa_states (*get_state)(void *ctx); + 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, wpa_alg alg, + 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); @@ -61,6 +48,27 @@ struct wpa_sm_ctx { 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); +#ifdef CONFIG_TDLS + int (*tdls_get_capa)(void *ctx, int *tdls_supported, + int *tdls_ext_setup); + int (*send_tdls_mgmt)(void *ctx, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, size_t len); + int (*tdls_oper)(void *ctx, int oper, const u8 *peer); + int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid, + u16 capability, const u8 *supp_rates, + size_t supp_rates_len, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u8 qosinfo, const u8 *ext_capab, + size_t ext_capab_len, const u8 *supp_channels, + size_t supp_channels_len, + const u8 *supp_oper_classes, + size_t supp_oper_classes_len); +#endif /* CONFIG_TDLS */ + void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck, + const u8 *replay_ctr); }; @@ -73,7 +81,8 @@ enum wpa_sm_conf_params { WPA_PARAM_GROUP, WPA_PARAM_KEY_MGMT, WPA_PARAM_MGMT_GROUP, - WPA_PARAM_RSN_ENABLED + WPA_PARAM_RSN_ENABLED, + WPA_PARAM_MFP }; struct rsn_supp_config { @@ -86,6 +95,7 @@ struct rsn_supp_config { const u8 *ssid; size_t ssid_len; int wpa_ptk_rekey; + int p2p; }; #ifndef CONFIG_NO_WPA @@ -117,6 +127,7 @@ unsigned int wpa_sm_get_param(struct wpa_sm *sm, int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); +int wpa_sm_pmf_enabled(struct wpa_sm *sm); void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); @@ -127,6 +138,15 @@ 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); + +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr); + +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); + +int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf); #else /* CONFIG_NO_WPA */ @@ -231,6 +251,11 @@ static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf, return 0; } +static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) +{ + return 0; +} + static inline void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { @@ -258,40 +283,74 @@ static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, 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; +} + +static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm, + const u8 *replay_ctr) +{ +} + +static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, + void *network_ctx) +{ +} + #endif /* CONFIG_NO_WPA */ #ifdef CONFIG_PEERKEY int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer); +int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len); #else /* CONFIG_PEERKEY */ static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) { return -1; } + +static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + return 0; +} #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211R -int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, - const u8 *r0kh_id, size_t r0kh_id_len, - const u8 *r1kh_id); -int wpa_ft_prepare_auth_request(struct wpa_sm *sm); +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); + 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); +void wpa_reset_ft_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); +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 *mobility_domain, - const u8 *r0kh_id, const u8 *r1kh_id) +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) +static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm, + const u8 *mdie) { return 0; } @@ -308,6 +367,10 @@ static inline int wpa_ft_is_completed(struct wpa_sm *sm) return 0; } +static inline void wpa_reset_ft_completed(struct wpa_sm *sm) +{ +} + static inline int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr) @@ -317,4 +380,23 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, #endif /* CONFIG_IEEE80211R */ + +/* tdls.c */ +void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len); +void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len); +int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr); +void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code); +int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code); +int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_init(struct wpa_sm *sm); +void wpa_tdls_teardown_peers(struct wpa_sm *sm); +void wpa_tdls_deinit(struct wpa_sm *sm); +void wpa_tdls_enable(struct wpa_sm *sm, int enabled); +void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr); +const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_is_external_setup(struct wpa_sm *sm); + +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); + #endif /* WPA_H */ diff --git a/contrib/hostapd/src/rsn_supp/wpa_ft.c b/contrib/hostapd/src/rsn_supp/wpa_ft.c index 557b3114d2..c8d8cfc8b6 100644 --- a/contrib/hostapd/src/rsn_supp/wpa_ft.c +++ b/contrib/hostapd/src/rsn_supp/wpa_ft.c @@ -2,24 +2,19 @@ * WPA Supplicant - IEEE 802.11r - Fast BSS Transition * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "wpa.h" #include "wpa_i.h" -#include "wpa_ie.h" -#include "aes_wrap.h" -#include "ieee802_11_defs.h" #ifdef CONFIG_IEEE80211R @@ -27,7 +22,6 @@ 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 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *anonce = key->key_nonce; @@ -45,11 +39,12 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, 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, pmk_r1_name); + 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", pmk_r1_name, WPA_PMK_NAME_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, pmk_r1_name, + 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); @@ -61,31 +56,40 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, /** * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @mobility_domain: Mobility domain identifier (2 octets) - * @r0kh_id: PMK-R0 key holder identity (1-48 octets) - * @r0kh_id_len: R0KH-ID length (1-48) - * @r1kh_id: PMK-R1 key holder identity (16 octets) + * @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 *mobility_domain, - const u8 *r0kh_id, size_t r0kh_id_len, - const u8 *r1kh_id) +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) { - if (sm && mobility_domain) { + 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", - mobility_domain, MOBILITY_DOMAIN_ID_LEN); - os_memcpy(sm->mobility_domain, mobility_domain, + ft.mdie, MOBILITY_DOMAIN_ID_LEN); + os_memcpy(sm->mobility_domain, ft.mdie, MOBILITY_DOMAIN_ID_LEN); - } else if (sm) + 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 (sm && r0kh_id) { - if (r0kh_id_len > FT_R0KH_ID_MAX_LEN) - return -1; - wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", r0kh_id, r0kh_id_len); - os_memcpy(sm->r0kh_id, r0kh_id, r0kh_id_len); - sm->r0kh_id_len = r0kh_id_len; - } else if (sm) { + 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. */ /* @@ -94,31 +98,55 @@ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, */ } - if (sm && r1kh_id) { - wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN); - os_memcpy(sm->r1kh_id, r1kh_id, FT_R1KH_ID_LEN); - } else if (sm) + 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 Request + * 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 *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; @@ -130,29 +158,29 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, sm->ft_completed = 0; buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + - 2 + sm->r0kh_id_len + 100; + 2 + sm->r0kh_id_len + ric_ies_len + 100; buf = os_zalloc(buf_len); if (buf == NULL) return NULL; pos = buf; - /* RSNIE[PMKR0Name] */ + /* 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 { + if (sm->group_cipher != WPA_CIPHER_CCMP && + sm->group_cipher != WPA_CIPHER_GCMP && + sm->group_cipher != WPA_CIPHER_TKIP) { wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", sm->group_cipher); os_free(buf); return NULL; } + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->group_cipher)); pos += RSN_SELECTOR_LEN; /* Pairwise Suite Count */ @@ -160,16 +188,14 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, 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 { + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", sm->pairwise_cipher); os_free(buf); return NULL; } + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->pairwise_cipher)); pos += RSN_SELECTOR_LEN; /* Authenticated Key Management Suite Count */ @@ -181,6 +207,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, 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 if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); else { wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", sm->key_mgmt); @@ -223,9 +251,10 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos += sizeof(*mdie); os_memcpy(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN); - mdie->ft_capab = 0; /* FIX: copy from the target AP's MDIE */ + mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : + sm->mdie_ft_capab; - /* FTIE[SNonce, R0KH-ID] */ + /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */ ftie_pos = pos; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ftie_len = pos++; @@ -234,6 +263,13 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, 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; @@ -241,6 +277,12 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *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 @@ -253,12 +295,14 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, * FTIE (with MIC field set to 0) * RIC-Request (if present) */ - ftie->mic_control[1] = 3; /* Information element count */ + /* 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, NULL, 0, - ftie->mic) < 0) { + (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; @@ -271,148 +315,23 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, } -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; -}; - - -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; - - os_memset(parse, 0, sizeof(*parse)); - if (ies == NULL) - return 0; - - pos = ies; - end = ies + ies_len; - while (pos + 2 <= end && pos + 2 + pos[1] <= end) { - switch (pos[0]) { - case WLAN_EID_RSN: - parse->rsn = pos + 2; - parse->rsn_len = pos[1]; - ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, - parse->rsn_len + 2, - &data); - if (ret < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to parse " - "RSN IE: %d", ret); - return -1; - } - if (data.num_pmkid == 1 && data.pmkid) - parse->rsn_pmkid = data.pmkid; - break; - case WLAN_EID_MOBILITY_DOMAIN: - parse->mdie = pos + 2; - parse->mdie_len = pos[1]; - break; - case WLAN_EID_FAST_BSS_TRANSITION: - if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) - return -1; - break; - case WLAN_EID_TIMEOUT_INTERVAL: - parse->tie = pos + 2; - parse->tie_len = pos[1]; - break; - } - - pos += 2 + pos[1]; - } - - return 0; -} - - static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) { int keylen; - wpa_alg alg; + 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: + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d", sm->pairwise_cipher); return -1; } + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + 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"); @@ -426,21 +345,22 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) /** * 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) +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)) { + if (random_get_bytes(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, sm->bssid, NULL, 0, mdie); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); @@ -452,7 +372,8 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm) int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, - int ft_action, const u8 *target_ap) + 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; @@ -464,6 +385,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, 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) { @@ -480,8 +402,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, } } - if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + if (!wpa_key_mgmt_ft(sm->key_mgmt)) { wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " "enabled for this connection"); return -1; @@ -506,6 +427,15 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, 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; @@ -538,6 +468,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_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); @@ -545,7 +476,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; - ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; + ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 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); @@ -554,26 +485,40 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_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); + 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; - if (ret == 0) { - sm->ft_completed = 1; - if (ft_action) { - /* TODO: trigger re-association to the Target AP; - * MLME is now doing this automatically, but it should - * really be done only if we get here successfully. */ - os_memcpy(sm->bssid, target_ap, ETH_ALEN); - } + 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 ret; + return 0; } @@ -582,20 +527,26 @@ 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) + if (!wpa_key_mgmt_ft(sm->key_mgmt)) return 0; return sm->ft_completed; } +void wpa_reset_ft_completed(struct wpa_sm *sm) +{ + if (sm != NULL) + sm->ft_completed = 0; +} + + 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; - wpa_alg alg; + enum wpa_alg alg; size_t gtk_len, keylen, rsc_len; if (gtk_elem == NULL) { @@ -606,41 +557,23 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp", gtk_elem, gtk_elem_len); - if (gtk_elem_len < 10 + 24 || (gtk_elem_len - 10) % 8 || - gtk_elem_len - 18 > sizeof(gtk)) { + 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 - 18; - if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 10, gtk)) { + 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: + keylen = wpa_cipher_key_len(sm->group_cipher); + rsc_len = wpa_cipher_rsc_len(sm->group_cipher); + alg = wpa_cipher_to_alg(sm->group_cipher); + if (alg == WPA_ALG_NONE) { wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d", sm->group_cipher); return -1; @@ -651,21 +584,27 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, return -1; } - /* Key Info[1] | Key Length[1] | RSC[8] | Key[5..32]. */ + /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */ - keyidx = gtk_elem[0] & 0x03; + keyidx = WPA_GET_LE16(gtk_elem) & 0x03; - if (gtk_elem[1] != keylen) { + if (gtk_elem[2] != keylen) { wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d " "negotiated %lu", - gtk_elem[1], (unsigned long) keylen); + 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 + 2, rsc_len, gtk, keylen) < - 0) { + if (sm->group_cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + u8 tmp[8]; + os_memcpy(tmp, gtk + 16, 8); + os_memcpy(gtk + 16, gtk + 24, 8); + os_memcpy(gtk + 24, tmp, 8); + } + if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0, + gtk_elem + 3, rsc_len, gtk, keylen) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " "driver."); return -1; @@ -716,9 +655,8 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *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) { + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, 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; @@ -735,13 +673,12 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; - size_t count; + 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) { + if (!wpa_key_mgmt_ft(sm->key_mgmt)) { wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " "enabled for this connection"); return -1; @@ -766,6 +703,24 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, 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; @@ -801,19 +756,20 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, } count = 3; - if (parse.tie) - count++; - + if (parse.ric) + count += ieee802_11_ie_count(parse.ric, parse.ric_len); if (ftie->mic_control[1] != count) { - wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in FTIE (%d)", - ftie->mic_control[1]); + 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, NULL, 0, + 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; @@ -834,6 +790,23 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, 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 when + * using wpa_supplicant SME */ + } + + wpa_printf(MSG_DEBUG, "FT: Completed successfully"); + return 0; } @@ -841,9 +814,12 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, /** * 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) +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; @@ -852,13 +828,13 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap) MAC2STR(target_ap)); /* Generate a new SNonce */ - if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { + if (random_get_bytes(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, target_ap, NULL, 0, mdie); if (ft_ies) { sm->over_the_ds_in_progress = 1; os_memcpy(sm->target_ap, target_ap, ETH_ALEN); diff --git a/contrib/hostapd/src/rsn_supp/wpa_i.h b/contrib/hostapd/src/rsn_supp/wpa_i.h index e0dc6bd414..75cfb479e0 100644 --- a/contrib/hostapd/src/rsn_supp/wpa_i.h +++ b/contrib/hostapd/src/rsn_supp/wpa_i.h @@ -1,22 +1,18 @@ /* - * wpa_supplicant - Internal WPA state machine definitions - * Copyright (c) 2004-2007, Jouni Malinen + * Internal WPA/RSN supplicant state machine definitions + * Copyright (c) 2004-2010, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_I_H #define WPA_I_H -struct rsn_pmksa_candidate; +#include "utils/list.h" + struct wpa_peerkey; +struct wpa_tdls_peer; struct wpa_eapol_key; /** @@ -38,10 +34,11 @@ struct wpa_sm { struct rsn_pmksa_cache *pmksa; /* PMKSA cache */ struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */ - struct rsn_pmksa_candidate *pmksa_candidates; + struct dl_list pmksa_candidates; struct l2_packet_data *l2_preauth; struct l2_packet_data *l2_preauth_br; + struct l2_packet_data *l2_tdls; u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or * 00:00:00:00:00:00 if no pre-auth is * in progress */ @@ -61,6 +58,7 @@ struct wpa_sm { u8 ssid[32]; size_t ssid_len; int wpa_ptk_rekey; + int p2p; u8 own_addr[ETH_ALEN]; const char *ifname; @@ -81,6 +79,7 @@ struct wpa_sm { 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; @@ -90,6 +89,20 @@ struct wpa_sm { #ifdef CONFIG_PEERKEY struct wpa_peerkey *peerkey; #endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_TDLS + struct wpa_tdls_peer *tdls; + int tdls_prohibited; + int tdls_disabled; + + /* The driver supports TDLS */ + int tdls_supported; + + /* + * The driver requires explicit discovery/setup/teardown frames sent + * to it via tdls_mgmt. + */ + int tdls_external_setup; +#endif /* CONFIG_TDLS */ #ifdef CONFIG_IEEE80211R u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ @@ -105,17 +118,25 @@ struct wpa_sm { 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 */ + +#ifdef CONFIG_P2P + u8 p2p_ip_addr[3 * 4]; +#endif /* CONFIG_P2P */ }; -static inline void wpa_sm_set_state(struct wpa_sm *sm, wpa_states state) +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 wpa_states wpa_sm_get_state(struct wpa_sm *sm) +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); @@ -127,13 +148,7 @@ static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code) 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, wpa_alg alg, +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) @@ -223,6 +238,77 @@ static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action, 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; +} + +static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm) +{ + if (!sm->ctx->set_rekey_offload) + return; + sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, + sm->ptk.kck, sm->rx_replay_counter); +} + +#ifdef CONFIG_TDLS +static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm, + int *tdls_supported, + int *tdls_ext_setup) +{ + if (sm->ctx->tdls_get_capa) + return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported, + tdls_ext_setup); + return -1; +} + +static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *buf, + size_t len) +{ + if (sm->ctx->send_tdls_mgmt) + return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code, + dialog_token, status_code, + buf, len); + return -1; +} + +static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, + const u8 *peer) +{ + if (sm->ctx->tdls_oper) + return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer); + return -1; +} + +static inline int +wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, + u16 aid, u16 capability, const u8 *supp_rates, + size_t supp_rates_len, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len, + const u8 *supp_channels, size_t supp_channels_len, + const u8 *supp_oper_classes, + size_t supp_oper_classes_len) +{ + if (sm->ctx->tdls_peer_addset) + return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, + aid, capability, supp_rates, + supp_rates_len, ht_capab, + vht_capab, qosinfo, + ext_capab, ext_capab_len, + supp_channels, + supp_channels_len, + supp_oper_classes, + supp_oper_classes_len); + return -1; +} +#endif /* CONFIG_TDLS */ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, int ver, const u8 *dest, u16 proto, @@ -242,4 +328,7 @@ 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); +void wpa_tdls_assoc(struct wpa_sm *sm); +void wpa_tdls_disassoc(struct wpa_sm *sm); + #endif /* WPA_I_H */ diff --git a/contrib/hostapd/src/rsn_supp/wpa_ie.c b/contrib/hostapd/src/rsn_supp/wpa_ie.c index 84f2811b88..e58bdc477c 100644 --- a/contrib/hostapd/src/rsn_supp/wpa_ie.c +++ b/contrib/hostapd/src/rsn_supp/wpa_ie.c @@ -2,14 +2,8 @@ * wpa_supplicant - WPA/RSN IE and KDE processing * Copyright (c) 2003-2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -17,149 +11,11 @@ #include "common.h" #include "wpa.h" #include "pmksa_cache.h" -#include "ieee802_11_defs.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 @@ -185,6 +41,7 @@ static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, { u8 *pos; struct wpa_ie_hdr *hdr; + u32 suite; if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) @@ -196,34 +53,26 @@ static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, 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 { + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher); + if (suite == 0) { wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", group_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); 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 { + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", pairwise_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += WPA_SELECTOR_LEN; *pos++ = 1; @@ -234,6 +83,8 @@ static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, 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 if (key_mgmt == WPA_KEY_MGMT_CCKM) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM); } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); @@ -256,10 +107,10 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, int key_mgmt, int mgmt_group_cipher, struct wpa_sm *sm) { -#ifndef CONFIG_NO_WPA2 u8 *pos; struct rsn_ie_hdr *hdr; u16 capab; + u32 suite; if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 + @@ -274,34 +125,26 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, 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 { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); + if (suite == 0) { wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", group_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); 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 { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", pairwise_cipher); return -1; } + RSN_SELECTOR_PUT(pos, suite); pos += RSN_SELECTOR_LEN; *pos++ = 1; @@ -310,6 +153,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, 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); + } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM); #ifdef CONFIG_IEEE80211R } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); @@ -322,6 +167,12 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + } else if (key_mgmt == WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); +#endif /* CONFIG_SAE */ } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); @@ -332,8 +183,10 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, /* RSN Capabilities */ capab = 0; #ifdef CONFIG_IEEE80211W - if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) + 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; @@ -366,9 +219,6 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); return pos - rsn_ie; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -414,6 +264,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, 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; } @@ -421,6 +273,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *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; } @@ -428,6 +282,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, 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; } @@ -435,6 +291,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, 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; } @@ -443,6 +301,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, 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; } @@ -450,6 +310,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, 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; } @@ -457,6 +319,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, 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; } @@ -464,6 +328,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, 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 */ @@ -473,10 +339,31 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, 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 */ +#ifdef CONFIG_P2P + if (pos[1] >= RSN_SELECTOR_LEN + 1 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { + ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", + ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); + return 0; + } + + if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { + ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, + "WPA: IP Address Allocation in EAPOL-Key", + ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); + return 0; + } +#endif /* CONFIG_P2P */ + return 0; } @@ -513,11 +400,65 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, if (*pos == WLAN_EID_RSN) { ie->rsn_ie = pos; ie->rsn_ie_len = pos[1] + 2; -#ifdef CONFIG_IEEE80211R + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", + ie->rsn_ie, ie->rsn_ie_len); } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; -#endif /* CONFIG_IEEE80211R */ + 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]); + } + } else if (*pos == WLAN_EID_LINK_ID) { + if (pos[1] >= 18) { + ie->lnkid = pos; + ie->lnkid_len = pos[1] + 2; + } + } else if (*pos == WLAN_EID_EXT_CAPAB) { + ie->ext_capab = pos; + ie->ext_capab_len = pos[1] + 2; + } else if (*pos == WLAN_EID_SUPP_RATES) { + ie->supp_rates = pos; + ie->supp_rates_len = pos[1] + 2; + } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { + ie->ext_supp_rates = pos; + ie->ext_supp_rates_len = pos[1] + 2; + } else if (*pos == WLAN_EID_HT_CAP) { + ie->ht_capabilities = pos + 2; + ie->ht_capabilities_len = pos[1]; + } else if (*pos == WLAN_EID_VHT_AID) { + if (pos[1] >= 2) + ie->aid = WPA_GET_LE16(pos + 2); + } else if (*pos == WLAN_EID_VHT_CAP) { + ie->vht_capabilities = pos + 2; + ie->vht_capabilities_len = pos[1]; + } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { + ie->qosinfo = pos[2]; + } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { + ie->supp_channels = pos + 2; + ie->supp_channels_len = pos[1]; + } else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) { + ie->supp_oper_classes = pos + 2; + ie->supp_oper_classes_len = pos[1]; } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { ret = wpa_parse_generic(pos, end, ie); if (ret < 0) diff --git a/contrib/hostapd/src/rsn_supp/wpa_ie.h b/contrib/hostapd/src/rsn_supp/wpa_ie.h index 17e375aa21..82b6fa3fba 100644 --- a/contrib/hostapd/src/rsn_supp/wpa_ie.h +++ b/contrib/hostapd/src/rsn_supp/wpa_ie.h @@ -2,19 +2,15 @@ * wpa_supplicant - WPA/RSN IE and KDE definitions * Copyright (c) 2004-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_IE_H #define WPA_IE_H +struct wpa_sm; + struct wpa_eapol_ie_parse { const u8 *wpa_ie; size_t wpa_ie_len; @@ -39,10 +35,34 @@ struct wpa_eapol_ie_parse { const u8 *igtk; size_t igtk_len; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R const u8 *mdie; size_t mdie_len; -#endif /* CONFIG_IEEE80211R */ + const u8 *ftie; + size_t ftie_len; + const u8 *reassoc_deadline; + const u8 *key_lifetime; + const u8 *lnkid; + size_t lnkid_len; + const u8 *ext_capab; + size_t ext_capab_len; + const u8 *supp_rates; + size_t supp_rates_len; + const u8 *ext_supp_rates; + size_t ext_supp_rates_len; + const u8 *ht_capabilities; + size_t ht_capabilities_len; + const u8 *vht_capabilities; + size_t vht_capabilities_len; + const u8 *supp_channels; + size_t supp_channels_len; + const u8 *supp_oper_classes; + size_t supp_oper_classes_len; + u8 qosinfo; + u16 aid; +#ifdef CONFIG_P2P + const u8 *ip_addr_req; + const u8 *ip_addr_alloc; +#endif /* CONFIG_P2P */ }; int wpa_supplicant_parse_ies(const u8 *buf, size_t len, diff --git a/contrib/hostapd/src/tls/asn1.c b/contrib/hostapd/src/tls/asn1.c index 96bc1ac78a..53acd530d9 100644 --- a/contrib/hostapd/src/tls/asn1.c +++ b/contrib/hostapd/src/tls/asn1.c @@ -2,22 +2,13 @@ * ASN.1 DER parsing * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" - -#ifdef CONFIG_INTERNAL_X509 - #include "asn1.h" int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) @@ -85,28 +76,16 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) } -int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, - const u8 **next) +int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid) { - struct asn1_hdr hdr; const u8 *pos, *end; unsigned long val; u8 tmp; os_memset(oid, 0, sizeof(*oid)); - 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; - } - - pos = hdr.payload; - end = hdr.payload + hdr.length; - *next = end; + pos = buf; + end = buf + len; while (pos < end) { val = 0; @@ -141,6 +120,26 @@ int asn1_get_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) +{ + 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; @@ -205,5 +204,3 @@ unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len) return val; } - -#endif /* CONFIG_INTERNAL_X509 */ diff --git a/contrib/hostapd/src/tls/asn1.h b/contrib/hostapd/src/tls/asn1.h index c02ada8278..6342c4cc79 100644 --- a/contrib/hostapd/src/tls/asn1.h +++ b/contrib/hostapd/src/tls/asn1.h @@ -2,14 +2,8 @@ * ASN.1 DER parsing * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef ASN1_H @@ -63,6 +57,7 @@ struct asn1_oid { 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); diff --git a/contrib/hostapd/src/tls/asn1_test.c b/contrib/hostapd/src/tls/asn1_test.c deleted file mode 100644 index a5c7753530..0000000000 --- a/contrib/hostapd/src/tls/asn1_test.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Testing tool for ASN.1/X.509v3 routines - * Copyright (c) 2006, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "asn1.h" -#include "x509v3.h" - -extern int wpa_debug_level; - - -static const char * asn1_class_str(int class) -{ - switch (class) { - case ASN1_CLASS_UNIVERSAL: - return "Universal"; - case ASN1_CLASS_APPLICATION: - return "Application"; - case ASN1_CLASS_CONTEXT_SPECIFIC: - return "Context-specific"; - case ASN1_CLASS_PRIVATE: - return "Private"; - default: - return "?"; - } -} - - -int asn1_parse(const u8 *buf, size_t len, int level) -{ - const u8 *pos, *prev, *end; - char prefix[10], str[100]; - int _level; - struct asn1_hdr hdr; - struct asn1_oid oid; - u8 tmp; - - _level = level; - if ((size_t) _level > sizeof(prefix) - 1) - _level = sizeof(prefix) - 1; - memset(prefix, ' ', _level); - prefix[_level] = '\0'; - - pos = buf; - end = buf + len; - - while (pos < end) { - if (asn1_get_next(pos, end - pos, &hdr) < 0) - return -1; - - prev = pos; - pos = hdr.payload; - - wpa_printf(MSG_MSGDUMP, "ASN.1:%s Class %d(%s) P/C %d(%s) " - "Tag %u Length %u", - prefix, hdr.class, asn1_class_str(hdr.class), - hdr.constructed, - hdr.constructed ? "Constructed" : "Primitive", - hdr.tag, hdr.length); - - if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC && - hdr.constructed) { - if (asn1_parse(pos, hdr.length, level + 1) < 0) - return -1; - pos += hdr.length; - } - - if (hdr.class != ASN1_CLASS_UNIVERSAL) - continue; - - switch (hdr.tag) { - case ASN1_TAG_EOC: - if (hdr.length) { - wpa_printf(MSG_DEBUG, "ASN.1: Non-zero " - "end-of-contents length (%u)", - hdr.length); - return -1; - } - wpa_printf(MSG_MSGDUMP, "ASN.1:%s EOC", prefix); - break; - case ASN1_TAG_BOOLEAN: - if (hdr.length != 1) { - wpa_printf(MSG_DEBUG, "ASN.1: Unexpected " - "Boolean length (%u)", hdr.length); - return -1; - } - tmp = *pos++; - wpa_printf(MSG_MSGDUMP, "ASN.1:%s Boolean %s", - prefix, tmp ? "TRUE" : "FALSE"); - break; - case ASN1_TAG_INTEGER: - wpa_hexdump(MSG_MSGDUMP, "ASN.1: INTEGER", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_BITSTRING: - wpa_hexdump(MSG_MSGDUMP, "ASN.1: BitString", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_OCTETSTRING: - wpa_hexdump(MSG_MSGDUMP, "ASN.1: OctetString", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_NULL: - if (hdr.length) { - wpa_printf(MSG_DEBUG, "ASN.1: Non-zero Null " - "length (%u)", hdr.length); - return -1; - } - wpa_printf(MSG_MSGDUMP, "ASN.1:%s Null", prefix); - break; - case ASN1_TAG_OID: - if (asn1_get_oid(prev, end - prev, &oid, &prev) < 0) { - wpa_printf(MSG_DEBUG, "ASN.1: Invalid OID"); - return -1; - } - asn1_oid_to_str(&oid, str, sizeof(str)); - wpa_printf(MSG_DEBUG, "ASN.1:%s OID %s", prefix, str); - pos += hdr.length; - break; - case ANS1_TAG_RELATIVE_OID: - wpa_hexdump(MSG_MSGDUMP, "ASN.1: Relative OID", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_SEQUENCE: - wpa_printf(MSG_MSGDUMP, "ASN.1:%s SEQUENCE", prefix); - if (asn1_parse(pos, hdr.length, level + 1) < 0) - return -1; - pos += hdr.length; - break; - case ASN1_TAG_SET: - wpa_printf(MSG_MSGDUMP, "ASN.1:%s SET", prefix); - if (asn1_parse(pos, hdr.length, level + 1) < 0) - return -1; - pos += hdr.length; - break; - case ASN1_TAG_PRINTABLESTRING: - wpa_hexdump_ascii(MSG_MSGDUMP, - "ASN.1: PrintableString", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_IA5STRING: - wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: IA5String", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_UTCTIME: - wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: UTCTIME", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_VISIBLESTRING: - wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: VisibleString", - pos, hdr.length); - pos += hdr.length; - break; - default: - wpa_printf(MSG_DEBUG, "ASN.1: Unknown tag %d", - hdr.tag); - return -1; - } - } - - return 0; -} - - -int main(int argc, char *argv[]) -{ - FILE *f; - u8 buf[3000]; - size_t len; - struct x509_certificate *cert; - - wpa_debug_level = 0; - - f = fopen(argv[1], "rb"); - if (f == NULL) - return -1; - len = fread(buf, 1, sizeof(buf), f); - fclose(f); - - if (asn1_parse(buf, len, 0) < 0) - printf("Failed to parse DER ASN.1\n"); - - printf("\n\n"); - - cert = x509_certificate_parse(buf, len); - if (cert == NULL) - printf("Failed to parse X.509 certificate\n"); - x509_certificate_free(cert); - - return 0; -} diff --git a/contrib/hostapd/src/tls/bignum.c b/contrib/hostapd/src/tls/bignum.c index 5c0fc62ede..f3baafe106 100644 --- a/contrib/hostapd/src/tls/bignum.c +++ b/contrib/hostapd/src/tls/bignum.c @@ -2,14 +2,8 @@ * Big number math * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/hostapd/src/tls/bignum.h b/contrib/hostapd/src/tls/bignum.h index f25e26783a..24acdce597 100644 --- a/contrib/hostapd/src/tls/bignum.h +++ b/contrib/hostapd/src/tls/bignum.h @@ -2,14 +2,8 @@ * Big number math * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BIGNUM_H diff --git a/contrib/hostapd/src/tls/libtommath.c b/contrib/hostapd/src/tls/libtommath.c index 137426421c..3fb8fbed25 100644 --- a/contrib/hostapd/src/tls/libtommath.c +++ b/contrib/hostapd/src/tls/libtommath.c @@ -42,6 +42,9 @@ /* Include faster sqr at the cost of about 0.5 kB in code */ #define BN_FAST_S_MP_SQR_C +/* About 0.25 kB of code, but ~1.7kB of stack space! */ +#define BN_FAST_S_MP_MUL_DIGS_C + #else /* LTM_FAST */ #define BN_MP_DIV_SMALL @@ -66,11 +69,19 @@ #define OPT_CAST(x) +#ifdef __x86_64__ +typedef unsigned long mp_digit; +typedef unsigned long mp_word __attribute__((mode(TI))); + +#define DIGIT_BIT 60 +#define MP_64BIT +#else typedef unsigned long mp_digit; typedef u64 mp_word; #define DIGIT_BIT 28 #define MP_28BIT +#endif #define XMALLOC os_malloc @@ -131,7 +142,9 @@ 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); +#ifdef BN_FAST_S_MP_MUL_DIGS_C static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); +#endif #ifdef BN_MP_INIT_MULTI_C static int mp_init_multi(mp_int *mp, ...); @@ -572,7 +585,7 @@ static int mp_mod (mp_int * a, mp_int * b, mp_int * c) /* 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 + * embedded in the normal function but that wasted a lot 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) @@ -663,6 +676,9 @@ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) #ifdef BN_MP_EXPTMOD_FAST_C } #endif + if (dr == 0) { + /* avoid compiler warnings about possibly unused variable */ + } } @@ -2207,7 +2223,7 @@ static int mp_2expt (mp_int * a, int b) /* zero a as per default */ mp_zero (a); - /* grow a to accomodate the single bit */ + /* grow a to accommodate the single bit */ if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { return res; } @@ -2319,7 +2335,7 @@ CLEANUP: } -/* multiplies |a| * |b| and only computes upto digs digits of result +/* multiplies |a| * |b| and only computes up to digs digits of result * HAC pp. 595, Algorithm 14.12 Modified so you can control how * many digits of output are created. */ @@ -2331,12 +2347,14 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) mp_word r; mp_digit tmpx, *tmpt, *tmpy; +#ifdef BN_FAST_S_MP_MUL_DIGS_C /* 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); } +#endif if ((res = mp_init_size (&t, digs)) != MP_OKAY) { return res; @@ -2389,6 +2407,7 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) } +#ifdef BN_FAST_S_MP_MUL_DIGS_C /* Fast (comba) multiplier * * This is the fast column-array [comba] multiplier. It is @@ -2474,6 +2493,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) mp_clamp (c); return MP_OKAY; } +#endif /* BN_FAST_S_MP_MUL_DIGS_C */ /* init an mp_init for a given size */ @@ -2678,7 +2698,7 @@ mp_montgomery_setup (mp_int * n, mp_digit * rho) * * Based on Algorithm 14.32 on pp.601 of HAC. */ -int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +static int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) { int ix, res, olduse; mp_word W[MP_WARRAY]; @@ -2829,7 +2849,7 @@ static int mp_mul_2(mp_int * a, mp_int * b) { int x, res, oldused; - /* grow to accomodate result */ + /* grow to accommodate result */ if (b->alloc < a->used + 1) { if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { return res; @@ -2891,8 +2911,8 @@ static int mp_mul_2(mp_int * a, mp_int * b) /* * 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. + * The method is slightly modified to shift B unconditionally up to just under + * the leading bit of b. This saves a lot of multiple precision shifting. */ static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b) { diff --git a/contrib/hostapd/src/tls/pkcs1.c b/contrib/hostapd/src/tls/pkcs1.c new file mode 100644 index 0000000000..b6fde5ee86 --- /dev/null +++ b/contrib/hostapd/src/tls/pkcs1.c @@ -0,0 +1,195 @@ +/* + * PKCS #1 (RSA Encryption) + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/tls/pkcs1.h b/contrib/hostapd/src/tls/pkcs1.h new file mode 100644 index 0000000000..ed64defaaf --- /dev/null +++ b/contrib/hostapd/src/tls/pkcs1.h @@ -0,0 +1,22 @@ +/* + * PKCS #1 (RSA Encryption) + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/tls/pkcs5.c b/contrib/hostapd/src/tls/pkcs5.c new file mode 100644 index 0000000000..8a93483781 --- /dev/null +++ b/contrib/hostapd/src/tls/pkcs5.c @@ -0,0 +1,232 @@ +/* + * PKCS #5 (Password-based Encryption) + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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; +}; + + +static 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, ¶ms) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters"); + return NULL; + } + + ctx = pkcs5_crypto_init(¶ms, 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/contrib/hostapd/src/tls/pkcs5.h b/contrib/hostapd/src/tls/pkcs5.h new file mode 100644 index 0000000000..20ddadc457 --- /dev/null +++ b/contrib/hostapd/src/tls/pkcs5.h @@ -0,0 +1,16 @@ +/* + * PKCS #5 (Password-based Encryption) + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/tls/pkcs8.c b/contrib/hostapd/src/tls/pkcs8.c new file mode 100644 index 0000000000..52e43a4403 --- /dev/null +++ b/contrib/hostapd/src/tls/pkcs8.c @@ -0,0 +1,187 @@ +/* + * PKCS #8 (Private-key information syntax) + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/tls/pkcs8.h b/contrib/hostapd/src/tls/pkcs8.h new file mode 100644 index 0000000000..bebf840ba7 --- /dev/null +++ b/contrib/hostapd/src/tls/pkcs8.h @@ -0,0 +1,16 @@ +/* + * PKCS #8 (Private-key information syntax) + * Copyright (c) 2006-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/tls/rsa.c b/contrib/hostapd/src/tls/rsa.c index 4965a2a311..125c4205b7 100644 --- a/contrib/hostapd/src/tls/rsa.c +++ b/contrib/hostapd/src/tls/rsa.c @@ -2,20 +2,13 @@ * RSA * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "crypto.h" #include "asn1.h" #include "bignum.h" #include "rsa.h" @@ -35,7 +28,6 @@ struct crypto_rsa_key { }; -#ifdef EAP_TLS_FUNCS static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end, struct bignum *num) { @@ -224,7 +216,6 @@ error: crypto_rsa_free(key); return NULL; } -#endif /* EAP_TLS_FUNCS */ /** diff --git a/contrib/hostapd/src/tls/rsa.h b/contrib/hostapd/src/tls/rsa.h index ac50dfd69d..c236a9df44 100644 --- a/contrib/hostapd/src/tls/rsa.h +++ b/contrib/hostapd/src/tls/rsa.h @@ -2,14 +2,8 @@ * RSA * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef RSA_H diff --git a/contrib/hostapd/src/tls/tlsv1_client.c b/contrib/hostapd/src/tls/tlsv1_client.c index 0bf11742ca..12148b61dd 100644 --- a/contrib/hostapd/src/tls/tlsv1_client.c +++ b/contrib/hostapd/src/tls/tlsv1_client.c @@ -1,22 +1,16 @@ /* - * TLSv1 client (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "sha1.h" -#include "tls.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" @@ -67,7 +61,8 @@ int tls_derive_keys(struct tlsv1_client *conn, 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, + if (tls_prf(conn->rl.tls_version, + 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 " @@ -80,9 +75,11 @@ int tls_derive_keys(struct tlsv1_client *conn, 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_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len); + if (conn->rl.tls_version == TLS_VERSION_1) + key_block_len += 2 * conn->rl.iv_size; + if (tls_prf(conn->rl.tls_version, + 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"); @@ -107,12 +104,21 @@ int tls_derive_keys(struct tlsv1_client *conn, 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; + if (conn->rl.tls_version == TLS_VERSION_1) { + /* 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; + } else { + /* + * Use IV field to set the mask value for TLS v1.1. A fixed + * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + os_memset(conn->rl.write_iv, 0, conn->rl.iv_size); + } return 0; } @@ -126,17 +132,23 @@ int tls_derive_keys(struct tlsv1_client *conn, * @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 + * @need_more_data: Set to 1 if more data would be needed to complete + * processing * 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) + size_t *appl_data_len, int *need_more_data) { const u8 *pos, *end; - u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; + u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct; size_t in_msg_len; int no_appl_data; + int used; + + if (need_more_data) + *need_more_data = 0; if (conn->state == CLIENT_HELLO) { if (in_len) @@ -144,6 +156,19 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, return tls_send_client_hello(conn, out_len); } + if (conn->partial_input) { + if (wpabuf_resize(&conn->partial_input, in_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for pending record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + goto failed; + } + wpabuf_put_data(conn->partial_input, in_data, in_len); + in_data = wpabuf_head(conn->partial_input); + in_len = wpabuf_len(conn->partial_input); + } + if (in_data == NULL || in_len == 0) return NULL; @@ -156,13 +181,33 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, /* 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)) { + used = tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } + if (used == 0) { + struct wpabuf *partial; + wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); + partial = wpabuf_alloc_copy(pos, end - pos); + wpabuf_free(conn->partial_input); + conn->partial_input = partial; + if (conn->partial_input == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "allocate memory for pending " + "record"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + goto failed; + } + os_free(in_msg); + if (need_more_data) + *need_more_data = 1; + return NULL; + } ct = pos[0]; in_pos = in_msg; @@ -180,7 +225,7 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, in_pos += in_msg_len; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + pos += used; } os_free(in_msg); @@ -192,6 +237,8 @@ u8 * tlsv1_client_handshake(struct tlsv1_client *conn, failed: os_free(in_msg); if (conn->alert_level) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; conn->state = FAILED; os_free(msg); msg = tlsv1_client_send_alert(conn, conn->alert_level, @@ -202,6 +249,11 @@ failed: *out_len = 0; } + if (need_more_data == NULL || !(*need_more_data)) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + } + return msg; } @@ -227,10 +279,8 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn, 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) { + out_data, out_len, in_data, 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); @@ -246,58 +296,116 @@ int tlsv1_client_encrypt(struct tlsv1_client *conn, * @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 + * @need_more_data: Set to 1 if more data would be needed to complete + * processing + * Returns: Decrypted data or %NULL 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) +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data) { const u8 *in_end, *pos; - int res; - u8 alert, *out_end, *out_pos; + int used; + u8 alert, *out_pos, ct; size_t olen; + struct wpabuf *buf = NULL; + + if (need_more_data) + *need_more_data = 0; + + if (conn->partial_input) { + if (wpabuf_resize(&conn->partial_input, in_len) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for pending record"); + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + wpabuf_put_data(conn->partial_input, in_data, in_len); + in_data = wpabuf_head(conn->partial_input); + in_len = wpabuf_len(conn->partial_input); + } 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; + ct = pos[0]; + if (wpabuf_resize(&buf, in_end - pos) < 0) { + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; } - - olen = out_end - out_pos; - res = tlsv1_record_receive(&conn->rl, pos, in_end - pos, - out_pos, &olen, &alert); - if (res < 0) { + out_pos = wpabuf_put(buf, 0); + olen = wpabuf_tailroom(buf); + used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " "failed"); - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); - return -1; + goto fail; } - 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; + if (used == 0) { + struct wpabuf *partial; + wpa_printf(MSG_DEBUG, "TLSv1: Need more data"); + partial = wpabuf_alloc_copy(pos, in_end - pos); + wpabuf_free(conn->partial_input); + conn->partial_input = partial; + if (conn->partial_input == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "allocate memory for pending " + "record"); + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + if (need_more_data) + *need_more_data = 1; + return buf; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (olen < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert " + "underflow"); + alert = TLS_ALERT_DECODE_ERROR; + goto fail; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + out_pos[0], out_pos[1]); + if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { + /* Continue processing */ + pos += used; + continue; + } + + alert = out_pos[1]; + goto fail; + } + + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " + "0x%x when decrypting application data", + pos[0]); + alert = TLS_ALERT_UNEXPECTED_MESSAGE; + goto fail; + } + + wpabuf_put(buf, olen); + + pos += used; } - return out_pos - out_data; + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + return buf; + +fail: + wpabuf_free(buf); + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return NULL; } @@ -351,15 +459,17 @@ struct tlsv1_client * tlsv1_client_init(void) count = 0; suites = conn->cipher_suites; -#ifndef CONFIG_CRYPTO_INTERNAL + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; 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; + conn->rl.tls_version = TLS_VERSION; + return conn; } @@ -378,6 +488,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn) os_free(conn->client_hello_ext); tlsv1_client_free_dh(conn); tlsv1_cred_free(conn->cred); + wpabuf_free(conn->partial_input); os_free(conn); } @@ -421,7 +532,8 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, label, seed, 2 * TLS_RANDOM_LEN, out, out_len); } @@ -453,15 +565,24 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + 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_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; case TLS_RSA_WITH_AES_128_CBC_SHA: cipher = "AES-128-SHA"; break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + cipher = "AES-128-SHA256"; + break; default: return -1; } @@ -605,7 +726,6 @@ int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn) */ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) { -#ifdef EAP_FAST size_t count; u16 *suites; @@ -613,9 +733,9 @@ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) 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_SHA256; suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; -#endif /* CONFIG_CRYPTO_INTERNAL */ + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256; 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; @@ -635,9 +755,6 @@ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) } return 0; -#else /* EAP_FAST */ - return -1; -#endif /* EAP_FAST */ } @@ -660,6 +777,12 @@ int tlsv1_client_set_cred(struct tlsv1_client *conn, } +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled) +{ + conn->disable_time_checks = !enabled; +} + + void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, tlsv1_client_session_ticket_cb cb, void *ctx) diff --git a/contrib/hostapd/src/tls/tlsv1_client.h b/contrib/hostapd/src/tls/tlsv1_client.h index 16ad57d400..8ec85f1a91 100644 --- a/contrib/hostapd/src/tls/tlsv1_client.h +++ b/contrib/hostapd/src/tls/tlsv1_client.h @@ -1,15 +1,9 @@ /* - * TLSv1 client (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_CLIENT_H @@ -29,13 +23,13 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, 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); + size_t *appl_data_len, int *need_more_data); 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); +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + int *need_more_data); int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, size_t buflen); int tlsv1_client_shutdown(struct tlsv1_client *conn); @@ -47,6 +41,7 @@ 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); +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); typedef int (*tlsv1_client_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, diff --git a/contrib/hostapd/src/tls/tlsv1_client_i.h b/contrib/hostapd/src/tls/tlsv1_client_i.h index 7fe179f10c..55fdcf8d04 100644 --- a/contrib/hostapd/src/tls/tlsv1_client_i.h +++ b/contrib/hostapd/src/tls/tlsv1_client_i.h @@ -1,15 +1,9 @@ /* * TLSv1 client - internal structures - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_CLIENT_I_H @@ -39,6 +33,7 @@ struct tlsv1_client { unsigned int session_resumed:1; unsigned int session_ticket_included:1; unsigned int use_session_ticket:1; + unsigned int disable_time_checks:1; struct crypto_public_key *server_rsa_key; @@ -67,6 +62,8 @@ struct tlsv1_client { tlsv1_client_session_ticket_cb session_ticket_cb; void *session_ticket_cb_ctx; + + struct wpabuf *partial_input; }; diff --git a/contrib/hostapd/src/tls/tlsv1_client_read.c b/contrib/hostapd/src/tls/tlsv1_client_read.c index ee20330ce2..3269ecf668 100644 --- a/contrib/hostapd/src/tls/tlsv1_client_read.c +++ b/contrib/hostapd/src/tls/tlsv1_client_read.c @@ -1,24 +1,19 @@ /* * TLSv1 client - read handshake message - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" #include "x509v3.h" -#include "tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" @@ -38,6 +33,7 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, const u8 *pos, *end; size_t left, len, i; u16 cipher_suite; + u16 tls_version; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " @@ -79,15 +75,20 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, /* ProtocolVersion server_version */ if (end - pos < 2) goto decode_error; - if (WPA_GET_BE16(pos) != TLS_VERSION) { + tls_version = WPA_GET_BE16(pos); + if (!tls_version_ok(tls_version)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ServerHello"); + "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; + wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", + tls_version_str(tls_version)); + conn->rl.tls_version = tls_version; + /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; @@ -365,7 +366,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, if (conn->cred && x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason) < 0) { + &reason, conn->disable_time_checks) + < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); @@ -815,6 +817,21 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", pos, TLS_VERIFY_DATA_LEN); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_server == NULL || + crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) + < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_server = NULL; + return -1; + } + conn->verify.sha256_server = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_server == NULL || crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { @@ -836,9 +853,15 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, return -1; } conn->verify.sha1_server = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, - "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "server finished", hash, hlen, verify_data, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, diff --git a/contrib/hostapd/src/tls/tlsv1_client_write.c b/contrib/hostapd/src/tls/tlsv1_client_write.c index e0c95cbe8e..d789efb425 100644 --- a/contrib/hostapd/src/tls/tlsv1_client_write.c +++ b/contrib/hostapd/src/tls/tlsv1_client_write.c @@ -1,24 +1,20 @@ /* * TLSv1 client - write handshake message - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" +#include "crypto/random.h" #include "x509v3.h" -#include "tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" @@ -57,7 +53,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) os_get_time(&now); WPA_PUT_BE32(conn->client_random, now.sec); - if (os_get_random(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { + if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "client_random"); return NULL; @@ -115,7 +111,8 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) 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) { + rhdr, end - rhdr, hs_start, 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); @@ -191,7 +188,8 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, 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) { + rhdr, end - rhdr, hs_start, 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); @@ -209,7 +207,6 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) { -#ifdef EAP_FAST /* ClientDiffieHellmanPublic */ u8 *csecret, *csecret_start, *dh_yc, *shared; size_t csecret_len, dh_yc_len, shared_len; @@ -223,7 +220,7 @@ static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) TLS_ALERT_INTERNAL_ERROR); return -1; } - if (os_get_random(csecret, csecret_len)) { + if (random_get_bytes(csecret, csecret_len)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " "data for Diffie-Hellman"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -321,10 +318,6 @@ static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) os_free(shared); tlsv1_client_free_dh(conn); return 0; -#else /* EAP_FAST */ - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); - return -1; -#endif /* EAP_FAST */ } @@ -417,7 +410,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn, 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) { + rhdr, end - rhdr, hs_start, 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); @@ -437,7 +431,7 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, { u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; size_t rlen, hlen, clen; - u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos; + u8 hash[100], *hpos; enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; pos = *msgpos; @@ -477,6 +471,40 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, hpos = hash; +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version == TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_cert == NULL || + crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < + 0) { + conn->verify.sha256_cert = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha256_cert = NULL; + + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + os_memmove(hash + 19, hash, hlen); + hlen += 19; + os_memcpy(hash, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65" + "\x03\x04\x02\x01\x05\x00\x04\x20", 19); + } else { +#endif /* CONFIG_TLSV12 */ + if (alg == SIGN_ALG_RSA) { hlen = MD5_MAC_LEN; if (conn->verify.md5_cert == NULL || @@ -507,8 +535,29 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, if (alg == SIGN_ALG_RSA) hlen += MD5_MAC_LEN; +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used signature and + * hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + } +#endif /* CONFIG_TLSV12 */ + /* * RFC 2246, 4.7: * In digital signing, one-way hash functions are used as input for a @@ -538,7 +587,8 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, 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) { + rhdr, end - rhdr, hs_start, 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); @@ -557,17 +607,16 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr; size_t rlen; - - pos = *msgpos; + u8 payload[1]; wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; - *pos = TLS_CHANGE_CIPHER_SPEC; + + payload[0] = TLS_CHANGE_CIPHER_SPEC; + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, - rhdr, end - rhdr, 1, &rlen) < 0) { + *msgpos, end - *msgpos, payload, sizeof(payload), + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -582,7 +631,7 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, return -1; } - *msgpos = rhdr + rlen; + *msgpos += rlen; return 0; } @@ -591,17 +640,30 @@ static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, static int tls_write_client_finished(struct tlsv1_client *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *hs_start; size_t rlen, hlen; - u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 verify_data[1 + 3 + 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 */ +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_client == NULL || + crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) + < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_client = NULL; + return -1; + } + conn->verify.sha256_client = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_client == NULL || crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { @@ -623,43 +685,44 @@ static int tls_write_client_finished(struct tlsv1_client *conn, return -1; } conn->verify.sha1_client = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - 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)) { + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "client finished", hash, hlen, + verify_data + 1 + 3, 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); + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; /* Handshake */ - hs_start = pos; + pos = hs_start = verify_data; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; - /* uint24 length (to be filled) */ - hs_length = pos; + /* uint24 length */ + WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); 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); + pos += TLS_VERIFY_DATA_LEN; /* verify_data already in place */ 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) { + *msgpos, end - *msgpos, hs_start, 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; + *msgpos += rlen; return 0; } @@ -673,7 +736,7 @@ static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn, *out_len = 0; - msglen = 1000; + msglen = 2000; if (conn->certificate_requested) msglen += tls_client_cert_chain_der_len(conn); @@ -782,7 +845,8 @@ u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, /* ContentType type */ *pos++ = TLS_CONTENT_TYPE_ALERT; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); pos += 2; /* uint16 length (to be filled) */ length = pos; diff --git a/contrib/hostapd/src/tls/tlsv1_common.c b/contrib/hostapd/src/tls/tlsv1_common.c index 2f9dd0fa88..4578b22727 100644 --- a/contrib/hostapd/src/tls/tlsv1_common.c +++ b/contrib/hostapd/src/tls/tlsv1_common.c @@ -1,20 +1,16 @@ /* * TLSv1 common routines - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" #include "x509v3.h" #include "tlsv1_common.h" @@ -50,11 +46,18 @@ static const struct tls_cipher_suite tls_cipher_suites[] = { { 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 } + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, + { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, + { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 } }; -#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0])) -#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites) +#define NUM_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites) static const struct tls_cipher_data tls_ciphers[] = { @@ -80,7 +83,7 @@ static const struct tls_cipher_data tls_ciphers[] = { CRYPTO_CIPHER_ALG_AES } }; -#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers) +#define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers) /** @@ -202,6 +205,19 @@ int tls_verify_hash_init(struct tls_verify_hash *verify) tls_verify_hash_free(verify); return -1; } +#ifdef CONFIG_TLSV12 + verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + if (verify->sha256_client == NULL || verify->sha256_server == NULL || + verify->sha256_cert == NULL) { + tls_verify_hash_free(verify); + return -1; + } +#endif /* CONFIG_TLSV12 */ return 0; } @@ -221,6 +237,14 @@ void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, crypto_hash_update(verify->md5_cert, buf, len); crypto_hash_update(verify->sha1_cert, buf, len); } +#ifdef CONFIG_TLSV12 + if (verify->sha256_client) + crypto_hash_update(verify->sha256_client, buf, len); + if (verify->sha256_server) + crypto_hash_update(verify->sha256_server, buf, len); + if (verify->sha256_cert) + crypto_hash_update(verify->sha256_cert, buf, len); +#endif /* CONFIG_TLSV12 */ } @@ -238,4 +262,60 @@ void tls_verify_hash_free(struct tls_verify_hash *verify) verify->sha1_client = NULL; verify->sha1_server = NULL; verify->sha1_cert = NULL; +#ifdef CONFIG_TLSV12 + crypto_hash_finish(verify->sha256_client, NULL, NULL); + crypto_hash_finish(verify->sha256_server, NULL, NULL); + crypto_hash_finish(verify->sha256_cert, NULL, NULL); + verify->sha256_client = NULL; + verify->sha256_server = NULL; + verify->sha256_cert = NULL; +#endif /* CONFIG_TLSV12 */ +} + + +int tls_version_ok(u16 ver) +{ + if (ver == TLS_VERSION_1) + return 1; +#ifdef CONFIG_TLSV11 + if (ver == TLS_VERSION_1_1) + return 1; +#endif /* CONFIG_TLSV11 */ +#ifdef CONFIG_TLSV12 + if (ver == TLS_VERSION_1_2) + return 1; +#endif /* CONFIG_TLSV12 */ + + return 0; +} + + +const char * tls_version_str(u16 ver) +{ + switch (ver) { + case TLS_VERSION_1: + return "1.0"; + case TLS_VERSION_1_1: + return "1.1"; + case TLS_VERSION_1_2: + return "1.2"; + } + + return "?"; +} + + +int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +{ +#ifdef CONFIG_TLSV12 + if (ver >= TLS_VERSION_1_2) { + tls_prf_sha256(secret, secret_len, label, seed, seed_len, + out, outlen); + return 0; + } +#endif /* CONFIG_TLSV12 */ + + return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out, + outlen); } diff --git a/contrib/hostapd/src/tls/tlsv1_common.h b/contrib/hostapd/src/tls/tlsv1_common.h index 77505649a2..f28c0cdc47 100644 --- a/contrib/hostapd/src/tls/tlsv1_common.h +++ b/contrib/hostapd/src/tls/tlsv1_common.h @@ -1,23 +1,28 @@ /* * TLSv1 common definitions - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_COMMON_H #define TLSV1_COMMON_H -#include "crypto.h" +#include "crypto/crypto.h" -#define TLS_VERSION 0x0301 /* TLSv1 */ +#define TLS_VERSION_1 0x0301 /* TLSv1 */ +#define TLS_VERSION_1_1 0x0302 /* TLSv1.1 */ +#define TLS_VERSION_1_2 0x0303 /* TLSv1.2 */ +#ifdef CONFIG_TLSV12 +#define TLS_VERSION TLS_VERSION_1_2 +#else /* CONFIG_TLSV12 */ +#ifdef CONFIG_TLSV11 +#define TLS_VERSION TLS_VERSION_1_1 +#else /* CONFIG_TLSV11 */ +#define TLS_VERSION TLS_VERSION_1 +#endif /* CONFIG_TLSV11 */ +#endif /* CONFIG_TLSV12 */ #define TLS_RANDOM_LEN 32 #define TLS_PRE_MASTER_SECRET_LEN 48 #define TLS_MASTER_SECRET_LEN 48 @@ -82,10 +87,42 @@ enum { #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 */ +#define TLS_RSA_WITH_NULL_SHA256 0x003B /* RFC 5246 */ +#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C /* RFC 5246 */ +#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D /* RFC 5246 */ +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E /* RFC 5246 */ +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F /* RFC 5246 */ +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 /* RFC 5246 */ +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 /* RFC 5246 */ +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 /* RFC 5246 */ +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 /* RFC 5246 */ +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A /* RFC 5246 */ +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B /* RFC 5246 */ +#define TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C /* RFC 5246 */ +#define TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D /* RFC 5246 */ /* CompressionMethod */ #define TLS_COMPRESSION_NULL 0 +/* HashAlgorithm */ +enum { + TLS_HASH_ALG_NONE = 0, + TLS_HASH_ALG_MD5 = 1, + TLS_HASH_ALG_SHA1 = 2, + TLS_HASH_ALG_SHA224 = 3, + TLS_HASH_ALG_SHA256 = 4, + TLS_HASH_ALG_SHA384 = 5, + TLS_HASH_ALG_SHA512 = 6 +}; + +/* SignatureAlgorithm */ +enum { + TLS_SIGN_ALG_ANONYMOUS = 0, + TLS_SIGN_ALG_RSA = 1, + TLS_SIGN_ALG_DSA = 2, + TLS_SIGN_ALG_ECDSA = 3, +}; + /* AlertLevel */ #define TLS_ALERT_LEVEL_WARNING 1 #define TLS_ALERT_LEVEL_FATAL 2 @@ -169,7 +206,8 @@ typedef enum { typedef enum { TLS_HASH_NULL, TLS_HASH_MD5, - TLS_HASH_SHA + TLS_HASH_SHA, + TLS_HASH_SHA256 } tls_hash; struct tls_cipher_suite { @@ -197,10 +235,13 @@ struct tls_cipher_data { struct tls_verify_hash { struct crypto_hash *md5_client; struct crypto_hash *sha1_client; + struct crypto_hash *sha256_client; struct crypto_hash *md5_server; struct crypto_hash *sha1_server; + struct crypto_hash *sha256_server; struct crypto_hash *md5_cert; struct crypto_hash *sha1_cert; + struct crypto_hash *sha256_cert; }; @@ -212,5 +253,9 @@ 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); +int tls_version_ok(u16 ver); +const char * tls_version_str(u16 ver); +int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen); #endif /* TLSV1_COMMON_H */ diff --git a/contrib/hostapd/src/tls/tlsv1_cred.c b/contrib/hostapd/src/tls/tlsv1_cred.c index d5564672c8..1ea6827b89 100644 --- a/contrib/hostapd/src/tls/tlsv1_cred.c +++ b/contrib/hostapd/src/tls/tlsv1_cred.c @@ -1,22 +1,16 @@ /* * TLSv1 credentials - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" #include "base64.h" -#include "crypto.h" +#include "crypto/crypto.h" #include "x509v3.h" #include "tlsv1_cred.h" @@ -46,7 +40,7 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred) static int tlsv1_add_cert_der(struct x509_certificate **chain, const u8 *buf, size_t len) { - struct x509_certificate *cert; + struct x509_certificate *cert, *p; char name[128]; cert = x509_certificate_parse(buf, len); @@ -56,8 +50,20 @@ static int tlsv1_add_cert_der(struct x509_certificate **chain, return -1; } - cert->next = *chain; - *chain = cert; + p = *chain; + while (p && p->next) + p = p->next; + if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) { + /* + * The new certificate is the issuer of the last certificate in + * the chain - add the new certificate to the end. + */ + p->next = cert; + } else { + /* Add to the beginning of the chain */ + cert->next = *chain; + *chain = cert; + } x509_name_string(&cert->subject, name, sizeof(name)); wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name); @@ -68,6 +74,12 @@ static int tlsv1_add_cert_der(struct x509_certificate **chain, 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) @@ -209,10 +221,81 @@ int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, } +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 { + const u8 *pos2; + pos += os_strlen(pem_key_begin); + end = search_tag(pem_key_end, pos, key + len - pos); + if (!end) + return NULL; + pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos); + if (pos2) { + wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key " + "format (Proc-Type/DEK-Info)"); + 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 u8 *key, size_t len, const char *passwd) { - cred->key = crypto_private_key_import(key, len); + 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; @@ -242,7 +325,8 @@ int tlsv1_set_private_key(struct tlsv1_credentials *cred, if (private_key_blob) return tlsv1_set_key(cred, private_key_blob, - private_key_blob_len); + private_key_blob_len, + private_key_passwd); if (private_key) { u8 *buf; @@ -256,7 +340,7 @@ int tlsv1_set_private_key(struct tlsv1_credentials *cred, return -1; } - ret = tlsv1_set_key(cred, buf, len); + ret = tlsv1_set_key(cred, buf, len, private_key_passwd); os_free(buf); return ret; } diff --git a/contrib/hostapd/src/tls/tlsv1_cred.h b/contrib/hostapd/src/tls/tlsv1_cred.h index 8425fe4541..68fbdc9230 100644 --- a/contrib/hostapd/src/tls/tlsv1_cred.h +++ b/contrib/hostapd/src/tls/tlsv1_cred.h @@ -2,14 +2,8 @@ * TLSv1 credentials * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_CRED_H diff --git a/contrib/hostapd/src/tls/tlsv1_record.c b/contrib/hostapd/src/tls/tlsv1_record.c index f226ac3f9b..3bec3be36f 100644 --- a/contrib/hostapd/src/tls/tlsv1_record.c +++ b/contrib/hostapd/src/tls/tlsv1_record.c @@ -1,22 +1,17 @@ /* * TLSv1 Record Protocol - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" #include "tlsv1_common.h" #include "tlsv1_record.h" @@ -52,6 +47,9 @@ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, } else if (suite->hash == TLS_HASH_SHA) { rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1; rl->hash_size = SHA1_MAC_LEN; + } else if (suite->hash == TLS_HASH_SHA256) { + rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA256; + rl->hash_size = SHA256_MAC_LEN; } data = tls_get_cipher_data(suite->cipher); @@ -138,10 +136,10 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) * 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: Buffer for the generated TLS message (needs to have extra space for + * header, IV (TLS v1.1), and HMAC) * @buf_size: Maximum buf size + * @payload: Payload to be sent * @payload_len: Length of the payload * @out_len: Buffer for returning the used buf length * Returns: 0 on success, -1 on failure @@ -150,29 +148,62 @@ int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) * 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) + size_t buf_size, const u8 *payload, size_t payload_len, + size_t *out_len) { - u8 *pos, *ct_start, *length, *payload; + u8 *pos, *ct_start, *length, *cpayload; struct crypto_hash *hmac; size_t clen; + int explicit_iv; pos = buf; + if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size) + return -1; + /* ContentType type */ ct_start = pos; *pos++ = content_type; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, rl->tls_version); pos += 2; /* uint16 length */ length = pos; WPA_PUT_BE16(length, payload_len); pos += 2; - /* opaque fragment[TLSPlaintext.length] */ - payload = pos; + cpayload = pos; + explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL && + rl->iv_size && rl->tls_version >= TLS_VERSION_1_1; + if (explicit_iv) { + /* opaque IV[Cipherspec.block_length] */ + if (pos + rl->iv_size > buf + buf_size) + return -1; + + /* + * Use random number R per the RFC 4346, 6.2.3.2 CBC Block + * Cipher option 2a. + */ + + if (os_get_random(pos, rl->iv_size)) + return -1; + pos += rl->iv_size; + } + + /* + * opaque fragment[TLSPlaintext.length] + * (opaque content[TLSCompressed.length] in GenericBlockCipher) + */ + if (pos + payload_len > buf + buf_size) + return -1; + os_memmove(pos, payload, payload_len); pos += payload_len; if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) { + /* + * MAC calculated over seq_num + TLSCompressed.type + + * TLSCompressed.version + TLSCompressed.length + + * TLSCompressed.fragment + */ hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size); if (hmac == NULL) { @@ -182,7 +213,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, } 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); + crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN); + crypto_hash_update(hmac, payload, payload_len); clen = buf + buf_size - pos; if (clen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " @@ -200,7 +232,7 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, pos, clen); pos += clen; if (rl->iv_size) { - size_t len = pos - payload; + size_t len = pos - cpayload; size_t pad; pad = (len + 1) % rl->iv_size; if (pad) @@ -214,8 +246,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, pos += pad + 1; } - if (crypto_cipher_encrypt(rl->write_cbc, payload, - payload, pos - payload) < 0) + if (crypto_cipher_encrypt(rl->write_cbc, cpayload, + cpayload, pos - cpayload) < 0) return -1; } @@ -237,7 +269,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, * @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 + * Returns: Number of bytes used from in_data on success, 0 if record was not + * complete (more data needed), or -1 on failure * * This function decrypts the received message, verifies HMAC and TLS record * layer header. @@ -250,40 +283,35 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, u8 padlen; struct crypto_hash *hmac; u8 len[2], hash[100]; - - wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", - in_data, in_len); + int force_mac_error = 0; + u8 ct; if (in_len < TLS_RECORD_HEADER_LEN) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)", + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu) - " + "need more data", (unsigned long) in_len); - *alert = TLS_ALERT_DECODE_ERROR; - return -1; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", + in_data, in_len); + return 0; } + ct = in_data[0]; + rlen = WPA_GET_BE16(in_data + 3); 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) { + "length %d", ct, in_data[1], in_data[2], (int) rlen); + + /* + * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the + * protocol version in record layer. As such, accept any {03,xx} value + * to remain compatible with existing implementations. + */ + if (in_data[1] != 0x03) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version " - "%d.%d", in_data[1], in_data[2]); + "%u.%u", 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)", @@ -299,7 +327,19 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, 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 0; + } + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", + in_data, rlen); + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE && + ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && + ct != TLS_CONTENT_TYPE_ALERT && + ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Ignore record with unknown " + "content type 0x%x", ct); + *alert = TLS_ALERT_UNEXPECTED_MESSAGE; return -1; } @@ -312,58 +352,86 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, 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, + size_t plen; + if (crypto_cipher_decrypt(rl->read_cbc, in_data, out_data, in_len) < 0) { *alert = TLS_ALERT_DECRYPTION_FAILED; return -1; } + plen = in_len; + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " + "data", out_data, plen); + if (rl->iv_size) { - if (in_len == 0) { + /* + * TLS v1.0 defines different alert values for various + * failures. That may information to aid in attacks, so + * use the same bad_record_mac alert regardless of the + * issues. + * + * In addition, instead of returning immediately on + * error, run through the MAC check to make timing + * attacks more difficult. + */ + + if (rl->tls_version >= TLS_VERSION_1_1) { + /* Remove opaque IV[Cipherspec.block_length] */ + if (plen < rl->iv_size) { + wpa_printf(MSG_DEBUG, "TLSv1.1: Not " + "enough room for IV"); + force_mac_error = 1; + goto check_mac; + } + os_memmove(out_data, out_data + rl->iv_size, + plen - rl->iv_size); + plen -= rl->iv_size; + } + + /* Verify and remove padding */ + if (plen == 0) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record" " (no pad)"); - *alert = TLS_ALERT_DECODE_ERROR; - return -1; + force_mac_error = 1; + goto check_mac; } - padlen = out_data[in_len - 1]; - if (padlen >= in_len) { + padlen = out_data[plen - 1]; + if (padlen >= plen) { wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " - "length (%u, in_len=%lu) in " + "length (%u, plen=%lu) in " "received record", - padlen, (unsigned long) in_len); - *alert = TLS_ALERT_DECRYPTION_FAILED; - return -1; + padlen, (unsigned long) plen); + force_mac_error = 1; + goto check_mac; } - for (i = in_len - padlen; i < in_len; i++) { + for (i = plen - padlen - 1; i < plen - 1; 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_data + plen - padlen - + 1, padlen + 1); + force_mac_error = 1; + goto check_mac; } } - *out_len -= padlen + 1; - } + plen -= padlen + 1; - wpa_hexdump(MSG_MSGDUMP, - "TLSv1: Record Layer - Decrypted data", - out_data, in_len); + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - " + "Decrypted data with IV and padding " + "removed", out_data, plen); + } - if (*out_len < rl->hash_size) { + check_mac: + if (plen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " "hash value"); - *alert = TLS_ALERT_INTERNAL_ERROR; + *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } - *out_len -= rl->hash_size; + plen -= rl->hash_size; hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size); @@ -377,22 +445,30 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, 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); + WPA_PUT_BE16(len, plen); crypto_hash_update(hmac, len, 2); - crypto_hash_update(hmac, out_data, *out_len); + crypto_hash_update(hmac, out_data, plen); hlen = sizeof(hash); if (crypto_hash_finish(hmac, hash, &hlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to calculate HMAC"); + *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } if (hlen != rl->hash_size || - os_memcmp(hash, out_data + *out_len, hlen) != 0) { + os_memcmp(hash, out_data + plen, hlen) != 0 || + force_mac_error) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " - "received message"); + "received message (force_mac_error=%d)", + force_mac_error); *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } + + *out_len = plen; + } else { + os_memcpy(out_data, in_data, in_len); + *out_len = in_len; } /* TLSCompressed must not be more than 2^14+1024 bytes */ @@ -405,5 +481,5 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); - return 0; + return TLS_RECORD_HEADER_LEN + rlen; } diff --git a/contrib/hostapd/src/tls/tlsv1_record.h b/contrib/hostapd/src/tls/tlsv1_record.h index 9170fb1a2f..48abcb0d25 100644 --- a/contrib/hostapd/src/tls/tlsv1_record.h +++ b/contrib/hostapd/src/tls/tlsv1_record.h @@ -1,23 +1,17 @@ /* * TLSv1 Record Protocol - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_RECORD_H #define TLSV1_RECORD_H -#include "crypto.h" +#include "crypto/crypto.h" -#define TLS_MAX_WRITE_MAC_SECRET_LEN 20 +#define TLS_MAX_WRITE_MAC_SECRET_LEN 32 #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 + \ @@ -35,6 +29,8 @@ enum { }; struct tlsv1_record_layer { + u16 tls_version; + 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]; @@ -66,7 +62,8 @@ int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, 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); + size_t buf_size, const u8 *payload, 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); diff --git a/contrib/hostapd/src/tls/tlsv1_server.c b/contrib/hostapd/src/tls/tlsv1_server.c index c204a4778a..2880309ebf 100644 --- a/contrib/hostapd/src/tls/tlsv1_server.c +++ b/contrib/hostapd/src/tls/tlsv1_server.c @@ -1,22 +1,16 @@ /* - * TLSv1 server (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "sha1.h" -#include "tls.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_server.h" @@ -49,7 +43,8 @@ int tlsv1_server_derive_keys(struct tlsv1_server *conn, 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, + if (tls_prf(conn->rl.tls_version, + 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 " @@ -64,7 +59,8 @@ int tlsv1_server_derive_keys(struct tlsv1_server *conn, 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, + if (tls_prf(conn->rl.tls_version, + 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"); @@ -115,6 +111,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, const u8 *pos, *end; u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; size_t in_msg_len; + int used; if (in_data == NULL || in_len == 0) { wpa_printf(MSG_DEBUG, "TLSv1: No input data to server"); @@ -130,13 +127,21 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, /* 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)) { + used = tlsv1_record_receive(&conn->rl, pos, end - pos, + in_msg, &in_msg_len, &alert); + if (used < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Processing received " "record failed"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); goto failed; } + if (used == 0) { + /* need more data */ + wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " + "yet supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + goto failed; + } ct = pos[0]; in_pos = in_msg; @@ -152,7 +157,7 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, in_pos += in_msg_len; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + pos += used; } os_free(in_msg); @@ -201,10 +206,8 @@ int tlsv1_server_encrypt(struct tlsv1_server *conn, 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) { + out_data, out_len, in_data, 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); @@ -232,8 +235,8 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, u8 *out_data, size_t out_len) { const u8 *in_end, *pos; - int res; - u8 alert, *out_end, *out_pos; + int used; + u8 alert, *out_end, *out_pos, ct; size_t olen; pos = in_data; @@ -242,7 +245,46 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, out_end = out_data + out_len; while (pos < in_end) { - if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { + ct = pos[0]; + olen = out_end - out_pos; + used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, + out_pos, &olen, &alert); + if (used < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " + "failed"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return -1; + } + if (used == 0) { + /* need more data */ + wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " + "yet supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + return -1; + } + + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (olen < 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", + out_pos[0], out_pos[1]); + if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { + /* Continue processing */ + pos += used; + continue; + } + + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + out_pos[1]); + return -1; + } + + if (ct != 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, @@ -250,15 +292,6 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, 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 " @@ -268,7 +301,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, return -1; } - pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3); + pos += used; } return out_pos - out_data; @@ -328,9 +361,7 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred) 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; @@ -412,7 +443,8 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, label, seed, 2 * TLS_RANDOM_LEN, out, out_len); } @@ -546,7 +578,6 @@ int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn) */ int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers) { -#ifdef EAP_FAST size_t count; u16 *suites; @@ -554,16 +585,12 @@ int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers) 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; @@ -572,9 +599,6 @@ int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers) } return 0; -#else /* EAP_FAST */ - return -1; -#endif /* EAP_FAST */ } diff --git a/contrib/hostapd/src/tls/tlsv1_server.h b/contrib/hostapd/src/tls/tlsv1_server.h index 00c536c3ee..a18c69e37c 100644 --- a/contrib/hostapd/src/tls/tlsv1_server.h +++ b/contrib/hostapd/src/tls/tlsv1_server.h @@ -1,15 +1,9 @@ /* - * TLSv1 server (RFC 2246) - * Copyright (c) 2006-2007, Jouni Malinen + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_SERVER_H diff --git a/contrib/hostapd/src/tls/tlsv1_server_i.h b/contrib/hostapd/src/tls/tlsv1_server_i.h index d11ea7559f..1f61533a5a 100644 --- a/contrib/hostapd/src/tls/tlsv1_server_i.h +++ b/contrib/hostapd/src/tls/tlsv1_server_i.h @@ -2,14 +2,8 @@ * TLSv1 server - internal structures * Copyright (c) 2006-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef TLSV1_SERVER_I_H diff --git a/contrib/hostapd/src/tls/tlsv1_server_read.c b/contrib/hostapd/src/tls/tlsv1_server_read.c index 397d74a197..6f6539b1bf 100644 --- a/contrib/hostapd/src/tls/tlsv1_server_read.c +++ b/contrib/hostapd/src/tls/tlsv1_server_read.c @@ -1,24 +1,19 @@ /* * TLSv1 server - read handshake message - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" #include "x509v3.h" -#include "tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_server.h" @@ -85,15 +80,30 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, 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) { + if (conn->client_version < TLS_VERSION_1) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ClientHello"); + "ClientHello %u.%u", + conn->client_version >> 8, + conn->client_version & 0xff); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; } pos += 2; + if (TLS_VERSION == TLS_VERSION_1) + conn->rl.tls_version = TLS_VERSION_1; +#ifdef CONFIG_TLSV12 + else if (conn->client_version >= TLS_VERSION_1_2) + conn->rl.tls_version = TLS_VERSION_1_2; +#endif /* CONFIG_TLSV12 */ + else if (conn->client_version > TLS_VERSION_1_1) + conn->rl.tls_version = TLS_VERSION_1_1; + else + conn->rl.tls_version = conn->client_version; + wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", + tls_version_str(conn->rl.tls_version)); + /* Random random */ if (end - pos < TLS_RANDOM_LEN) goto decode_error; @@ -424,7 +434,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, } if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason) < 0) { + &reason, 0) < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); @@ -483,6 +493,14 @@ static int tls_process_client_key_exchange_rsa( encr_len = WPA_GET_BE16(pos); pos += 2; + if (pos + encr_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange " + "format: encr_len=%u left=%u", + encr_len, (unsigned int) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } outbuflen = outlen = end - pos; out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ? @@ -512,21 +530,21 @@ static int tls_process_client_key_exchange_rsa( */ if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key, - pos, end - pos, + pos, encr_len, out, &outlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt " - "PreMasterSecret (encr_len=%d outlen=%lu)", - (int) (end - pos), (unsigned long) outlen); + "PreMasterSecret (encr_len=%u outlen=%lu)", + encr_len, (unsigned long) outlen); use_random = 1; } - if (outlen != TLS_PRE_MASTER_SECRET_LEN) { + if (!use_random && 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) { + if (!use_random && WPA_GET_BE16(out) != conn->client_version) { wpa_printf(MSG_DEBUG, "TLSv1: Client version in " "ClientKeyExchange does not match with version in " "ClientHello"); @@ -567,7 +585,6 @@ static int tls_process_client_key_exchange_rsa( static int tls_process_client_key_exchange_dh_anon( struct tlsv1_server *conn, const u8 *pos, const u8 *end) { -#ifdef EAP_FAST const u8 *dh_yc; u16 dh_yc_len; u8 *shared; @@ -665,9 +682,6 @@ static int tls_process_client_key_exchange_dh_anon( } return 0; -#else /* EAP_FAST */ - return -1; -#endif /* EAP_FAST */ } @@ -826,6 +840,47 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, hpos = hash; +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version == TLS_VERSION_1_2) { + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used signature and + * hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + if (end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + if (pos[0] != TLS_HASH_ALG_SHA256 || + pos[1] != TLS_SIGN_ALG_RSA) { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/" + "signature(%u) algorithm", + pos[0], pos[1]); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos += 2; + + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_cert == NULL || + crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < + 0) { + conn->verify.sha256_cert = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha256_cert = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + if (alg == SIGN_ALG_RSA) { hlen = MD5_MAC_LEN; if (conn->verify.md5_cert == NULL || @@ -856,6 +911,10 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, if (alg == SIGN_ALG_RSA) hlen += MD5_MAC_LEN; +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); if (end - pos < 2) { @@ -895,6 +954,41 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", buf, buflen); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || + * H + */ + if (buflen >= 19 + 32 && + os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" + "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) + { + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = " + "SHA-256"); + os_memmove(buf, buf + 19, buflen - 19); + buflen -= 19; + } else { + wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized " + "DigestInfo"); + os_free(buf); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + } +#endif /* CONFIG_TLSV12 */ + if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in " "CertificateVerify - did not match with calculated " @@ -1026,6 +1120,21 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", pos, TLS_VERIFY_DATA_LEN); +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_client == NULL || + crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) + < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.sha256_client = NULL; + return -1; + } + conn->verify.sha256_client = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_client == NULL || crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) { @@ -1047,9 +1156,15 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, return -1; } conn->verify.sha1_client = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN, - "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN, + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "client finished", hash, hlen, verify_data, TLS_VERIFY_DATA_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, diff --git a/contrib/hostapd/src/tls/tlsv1_server_write.c b/contrib/hostapd/src/tls/tlsv1_server_write.c index cf54f4265c..6d8e55ed49 100644 --- a/contrib/hostapd/src/tls/tlsv1_server_write.c +++ b/contrib/hostapd/src/tls/tlsv1_server_write.c @@ -1,24 +1,20 @@ /* * TLSv1 server - write handshake message - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" +#include "crypto/random.h" #include "x509v3.h" -#include "tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_server.h" @@ -58,7 +54,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, os_get_time(&now); WPA_PUT_BE32(conn->server_random, now.sec); - if (os_get_random(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { + if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "server_random"); return -1; @@ -67,7 +63,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, 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)) { + if (random_get_bytes(conn->session_id, conn->session_id_len)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " "session_id"); return -1; @@ -86,7 +82,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, pos += 3; /* body - ServerHello */ /* ProtocolVersion server_version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN); @@ -142,7 +138,8 @@ static int tls_write_server_hello(struct tlsv1_server *conn, 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) { + rhdr, end - rhdr, hs_start, 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); @@ -226,7 +223,8 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, 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) { + rhdr, end - rhdr, hs_start, 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); @@ -247,12 +245,10 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, { tls_key_exchange keyx; const struct tls_cipher_suite *suite; -#ifdef EAP_FAST u8 *pos, *rhdr, *hs_start, *hs_length; size_t rlen; u8 *dh_ys; size_t dh_ys_len; -#endif /* EAP_FAST */ suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite == NULL) @@ -272,7 +268,6 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, return -1; } -#ifdef EAP_FAST if (conn->cred == NULL || conn->cred->dh_p == NULL || conn->cred->dh_g == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for " @@ -290,7 +285,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, TLS_ALERT_INTERNAL_ERROR); return -1; } - if (os_get_random(conn->dh_secret, conn->dh_secret_len)) { + if (random_get_bytes(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, @@ -420,7 +415,8 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, 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) { + rhdr, end - rhdr, hs_start, 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); @@ -433,9 +429,6 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, *msgpos = pos; return 0; -#else /* EAP_FAST */ - return -1; -#endif /* EAP_FAST */ } @@ -488,7 +481,8 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn, 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) { + rhdr, end - rhdr, hs_start, 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); @@ -507,40 +501,35 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn, static int tls_write_server_hello_done(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos; size_t rlen; - - pos = *msgpos; + u8 payload[4]; wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; /* opaque fragment[TLSPlaintext.length] */ /* Handshake */ - hs_start = pos; + pos = payload; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE; - /* uint24 length (to be filled) */ - hs_length = pos; + /* uint24 length */ + WPA_PUT_BE24(pos, 0); 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) { + *msgpos, end - *msgpos, payload, pos - payload, + &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); + tls_verify_hash_add(&conn->verify, payload, pos - payload); - *msgpos = pos; + *msgpos += rlen; return 0; } @@ -549,17 +538,16 @@ static int tls_write_server_hello_done(struct tlsv1_server *conn, static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr; size_t rlen; - - pos = *msgpos; + u8 payload[1]; wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; - *pos = TLS_CHANGE_CIPHER_SPEC; + + payload[0] = TLS_CHANGE_CIPHER_SPEC; + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, - rhdr, end - rhdr, 1, &rlen) < 0) { + *msgpos, end - *msgpos, payload, sizeof(payload), + &rlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); @@ -574,7 +562,7 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, return -1; } - *msgpos = rhdr + rlen; + *msgpos += rlen; return 0; } @@ -583,9 +571,9 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, static int tls_write_server_finished(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *hs_start; size_t rlen, hlen; - u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; pos = *msgpos; @@ -594,6 +582,21 @@ static int tls_write_server_finished(struct tlsv1_server *conn, /* Encrypted Handshake Message: Finished */ +#ifdef CONFIG_TLSV12 + if (conn->rl.tls_version >= TLS_VERSION_1_2) { + hlen = SHA256_MAC_LEN; + if (conn->verify.sha256_server == NULL || + crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) + < 0) { + conn->verify.sha256_server = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha256_server = NULL; + } else { +#endif /* CONFIG_TLSV12 */ + hlen = MD5_MAC_LEN; if (conn->verify.md5_server == NULL || crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) { @@ -615,43 +618,44 @@ static int tls_write_server_finished(struct tlsv1_server *conn, return -1; } conn->verify.sha1_server = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ - 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)) { + if (tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + "server finished", hash, hlen, + verify_data + 1 + 3, 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); + verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); - rhdr = pos; - pos += TLS_RECORD_HEADER_LEN; /* Handshake */ - hs_start = pos; + pos = hs_start = verify_data; /* HandshakeType msg_type */ *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; - /* uint24 length (to be filled) */ - hs_length = pos; + /* uint24 length */ + WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); 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) { + *msgpos, end - *msgpos, hs_start, 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; + *msgpos += rlen; return 0; } @@ -776,7 +780,8 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, /* ContentType type */ *pos++ = TLS_CONTENT_TYPE_ALERT; /* ProtocolVersion version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); pos += 2; /* uint16 length (to be filled) */ length = pos; diff --git a/contrib/hostapd/src/tls/x509v3.c b/contrib/hostapd/src/tls/x509v3.c index 59bf4ff052..06540bffd6 100644 --- a/contrib/hostapd/src/tls/x509v3.c +++ b/contrib/hostapd/src/tls/x509v3.c @@ -1,39 +1,40 @@ /* * X.509v3 certificate parsing and processing (RFC 3280 profile) - * Copyright (c) 2006-2007, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" - -#ifdef CONFIG_INTERNAL_X509 - +#include "crypto/crypto.h" #include "asn1.h" -#include "crypto.h" #include "x509v3.h" static void x509_free_name(struct x509_name *name) { - os_free(name->cn); - os_free(name->c); - os_free(name->l); - os_free(name->st); - os_free(name->o); - os_free(name->ou); + 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->cn = name->c = name->l = name->st = name->o = name->ou = NULL; 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)); } @@ -146,6 +147,7 @@ static int x509_str_compare(const char *a, const char *b) int x509_name_compare(struct x509_name *a, struct x509_name *b) { int res; + size_t i; if (!a && b) return -1; @@ -153,25 +155,20 @@ int x509_name_compare(struct x509_name *a, struct x509_name *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; - res = x509_str_compare(a->cn, b->cn); - if (res) - return res; - res = x509_str_compare(a->c, b->c); - if (res) - return res; - res = x509_str_compare(a->l, b->l); - if (res) - return res; - res = x509_str_compare(a->st, b->st); - if (res) - return res; - res = x509_str_compare(a->o, b->o); - if (res) - return res; - res = x509_str_compare(a->ou, b->ou); - if (res) - return res; + 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; @@ -298,7 +295,7 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, struct asn1_hdr hdr; const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; struct asn1_oid oid; - char **fieldp; + char *val; /* * Name ::= CHOICE { RDNSequence } @@ -328,6 +325,8 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, 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) { @@ -375,34 +374,34 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, * pseudonym, generation qualifier. * MUST: domainComponent (RFC 2247). */ - fieldp = NULL; + 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 */ - fieldp = &name->cn; + type = X509_NAME_ATTR_CN; break; case 6: /* countryName */ - fieldp = &name->c; + type = X509_NAME_ATTR_C; break; case 7: /* localityName */ - fieldp = &name->l; + type = X509_NAME_ATTR_L; break; case 8: /* stateOrProvinceName */ - fieldp = &name->st; + type = X509_NAME_ATTR_ST; break; case 10: /* organizationName */ - fieldp = &name->o; + type = X509_NAME_ATTR_O; break; case 11: /* organizationalUnitName */ - fieldp = &name->ou; + type = X509_NAME_ATTR_OU; break; } } else if (oid.len == 7 && @@ -411,10 +410,25 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, oid.oid[4] == 1 && oid.oid[5] == 9 && oid.oid[6] == 1) { /* 1.2.840.113549.1.9.1 - e-mailAddress */ - fieldp = &name->email; + 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 (fieldp == NULL) { + if (type == X509_NAME_ATTR_NOT_USED) { wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID", (u8 *) oid.oid, oid.len * sizeof(oid.oid[0])); @@ -423,20 +437,59 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, continue; } - os_free(*fieldp); - *fieldp = os_malloc(hdr.length + 1); - if (*fieldp == NULL) { + if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) { + wpa_printf(MSG_INFO, "X509: Too many Name attributes"); x509_free_name(name); return -1; } - os_memcpy(*fieldp, hdr.payload, hdr.length); - (*fieldp)[hdr.length] = '\0'; + + val = dup_binstr(hdr.payload, hdr.length); + if (val == NULL) { + x509_free_name(name); + return -1; + } + if (os_strlen(val) != hdr.length) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in a string (%s[NUL])", + val); + os_free(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 @@ -447,6 +500,7 @@ 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; @@ -454,46 +508,20 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) pos = buf; end = buf + len; - if (name->c) { - ret = os_snprintf(pos, end - pos, "C=%s, ", name->c); - if (ret < 0 || ret >= end - pos) - goto done; - pos += ret; - } - if (name->st) { - ret = os_snprintf(pos, end - pos, "ST=%s, ", name->st); - if (ret < 0 || ret >= end - pos) - goto done; - pos += ret; - } - if (name->l) { - ret = os_snprintf(pos, end - pos, "L=%s, ", name->l); - if (ret < 0 || ret >= end - pos) - goto done; - pos += ret; - } - if (name->o) { - ret = os_snprintf(pos, end - pos, "O=%s, ", name->o); - if (ret < 0 || ret >= end - pos) - goto done; - pos += ret; - } - if (name->ou) { - ret = os_snprintf(pos, end - pos, "OU=%s, ", name->ou); - if (ret < 0 || ret >= end - pos) - goto done; - pos += ret; - } - if (name->cn) { - ret = os_snprintf(pos, end - pos, "CN=%s, ", name->cn); + 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-- = '\0'; - *pos-- = '\0'; + pos--; + *pos = '\0'; + pos--; + *pos = '\0'; } if (name->email) { @@ -815,6 +843,237 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, } +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) @@ -824,7 +1083,6 @@ static int x509_parse_extension_data(struct x509_certificate *cert, /* TODO: add other extensions required by RFC 3280, Ch 4.2: * certificate policies (section 4.2.1.5) - * the subject alternative name (section 4.2.1.7) * name constraints (section 4.2.1.11) * policy constraints (section 4.2.1.12) * extended key usage (section 4.2.1.13) @@ -833,6 +1091,10 @@ static int x509_parse_extension_data(struct x509_certificate *cert, 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: @@ -1495,18 +1757,12 @@ skip_digest_oid: hash, hash_len); break; case 11: /* sha256WithRSAEncryption */ -#ifdef NEED_SHA256 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; -#else /* NEED_SHA256 */ - wpa_printf(MSG_INFO, "X509: SHA256 support disabled"); - os_free(data); - return -1; -#endif /* NEED_SHA256 */ case 2: /* md2WithRSAEncryption */ case 12: /* sha384WithRSAEncryption */ case 13: /* sha512WithRSAEncryption */ @@ -1571,7 +1827,7 @@ static int x509_valid_issuer(const struct x509_certificate *cert) */ int x509_certificate_chain_validate(struct x509_certificate *trusted, struct x509_certificate *chain, - int *reason) + int *reason, int disable_time_checks) { long unsigned idx; int chain_trusted = 0; @@ -1591,10 +1847,11 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, if (chain_trusted) continue; - if ((unsigned long) now.sec < - (unsigned long) cert->not_before || - (unsigned long) now.sec > - (unsigned long) cert->not_after) { + if (!disable_time_checks && + ((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); @@ -1720,5 +1977,3 @@ int x509_certificate_self_signed(struct x509_certificate *cert) { return x509_name_compare(&cert->issuer, &cert->subject) == 0; } - -#endif /* CONFIG_INTERNAL_X509 */ diff --git a/contrib/hostapd/src/tls/x509v3.h b/contrib/hostapd/src/tls/x509v3.h index a52bcf8864..91a35baf92 100644 --- a/contrib/hostapd/src/tls/x509v3.h +++ b/contrib/hostapd/src/tls/x509v3.h @@ -1,15 +1,9 @@ /* * X.509v3 certificate parsing and processing - * Copyright (c) 2006, Jouni Malinen + * Copyright (c) 2006-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef X509V3_H @@ -21,14 +15,34 @@ 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 { - char *cn; /* commonName */ - char *c; /* countryName */ - char *l; /* localityName */ - char *st; /* stateOrProvinceName */ - char *o; /* organizationName */ - char *ou; /* organizationalUnitName */ + 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 { @@ -52,6 +66,8 @@ struct x509_certificate { #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 */ @@ -89,8 +105,6 @@ enum { X509_VALIDATE_UNKNOWN_CA }; -#ifdef CONFIG_INTERNAL_X509 - 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); @@ -100,55 +114,10 @@ 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); + int *reason, int disable_time_checks); struct x509_certificate * x509_certificate_get_subject(struct x509_certificate *chain, struct x509_name *name); int x509_certificate_self_signed(struct x509_certificate *cert); -#else /* CONFIG_INTERNAL_X509 */ - -static inline void x509_certificate_free(struct x509_certificate *cert) -{ -} - -static inline struct x509_certificate * -x509_certificate_parse(const u8 *buf, size_t len) -{ - return NULL; -} - -static inline void x509_name_string(struct x509_name *name, char *buf, - size_t len) -{ - if (len) - buf[0] = '\0'; -} - -static inline void x509_certificate_chain_free(struct x509_certificate *cert) -{ -} - -static inline int -x509_certificate_chain_validate(struct x509_certificate *trusted, - struct x509_certificate *chain, - int *reason) -{ - return -1; -} - -static inline struct x509_certificate * -x509_certificate_get_subject(struct x509_certificate *chain, - struct x509_name *name) -{ - return NULL; -} - -static inline int x509_certificate_self_signed(struct x509_certificate *cert) -{ - return -1; -} - -#endif /* CONFIG_INTERNAL_X509 */ - #endif /* X509V3_H */ diff --git a/contrib/hostapd/src/utils/base64.c b/contrib/hostapd/src/utils/base64.c index 13fc511dc6..af1307fc4e 100644 --- a/contrib/hostapd/src/utils/base64.c +++ b/contrib/hostapd/src/utils/base64.c @@ -1,15 +1,9 @@ /* * Base64 encoding/decoding (RFC1341) - * Copyright (c) 2005, Jouni Malinen + * Copyright (c) 2005-2011, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -103,8 +97,9 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, 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; + unsigned char dtable[256], *out, *pos, block[4], tmp; size_t i, count, olen; + int pad = 0; os_memset(dtable, 0x80, 256); for (i = 0; i < sizeof(base64_table) - 1; i++) @@ -131,7 +126,8 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, if (tmp == 0x80) continue; - in[count] = src[i]; + if (src[i] == '=') + pad++; block[count] = tmp; count++; if (count == 4) { @@ -139,51 +135,21 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, *pos++ = (block[1] << 4) | (block[2] >> 2); *pos++ = (block[2] << 6) | block[3]; count = 0; + if (pad) { + if (pad == 1) + pos--; + else if (pad == 2) + pos -= 2; + else { + /* Invalid padding */ + os_free(out); + return NULL; + } + break; + } } } - if (pos > out) { - if (in[2] == '=') - pos -= 2; - else if (in[3] == '=') - pos--; - } - *out_len = pos - out; return out; } - - -#ifdef TEST_MAIN - -int main(int argc, char *argv[]) -{ - FILE *f; - size_t len, elen; - unsigned char *buf, *e; - - if (argc != 4) { - printf("Usage: base64 \n"); - return -1; - } - - buf = os_readfile(argv[2], &len); - if (buf == NULL) - return -1; - - if (strcmp(argv[1], "encode") == 0) - e = base64_encode(buf, len, &elen); - else - e = base64_decode(buf, len, &elen); - if (e == NULL) - return -2; - f = fopen(argv[3], "w"); - if (f == NULL) - return -3; - fwrite(e, 1, elen, f); - fclose(f); - free(e); - - return 0; -} -#endif /* TEST_MAIN */ diff --git a/contrib/hostapd/src/utils/base64.h b/contrib/hostapd/src/utils/base64.h index 73312dde8f..aa21fd0fc1 100644 --- a/contrib/hostapd/src/utils/base64.h +++ b/contrib/hostapd/src/utils/base64.h @@ -2,18 +2,12 @@ * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BASE64_H -#define BASE64_h +#define BASE64_H unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len); diff --git a/contrib/hostapd/src/utils/bitfield.c b/contrib/hostapd/src/utils/bitfield.c new file mode 100644 index 0000000000..f90e4beb61 --- /dev/null +++ b/contrib/hostapd/src/utils/bitfield.c @@ -0,0 +1,89 @@ +/* + * Bitfield + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "bitfield.h" + + +struct bitfield { + u8 *bits; + size_t max_bits; +}; + + +struct bitfield * bitfield_alloc(size_t max_bits) +{ + struct bitfield *bf; + + bf = os_zalloc(sizeof(*bf) + (max_bits + 7) / 8); + if (bf == NULL) + return NULL; + bf->bits = (u8 *) (bf + 1); + bf->max_bits = max_bits; + return bf; +} + + +void bitfield_free(struct bitfield *bf) +{ + os_free(bf); +} + + +void bitfield_set(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return; + bf->bits[bit / 8] |= BIT(bit % 8); +} + + +void bitfield_clear(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return; + bf->bits[bit / 8] &= ~BIT(bit % 8); +} + + +int bitfield_is_set(struct bitfield *bf, size_t bit) +{ + if (bit >= bf->max_bits) + return 0; + return !!(bf->bits[bit / 8] & BIT(bit % 8)); +} + + +static int first_zero(u8 val) +{ + int i; + for (i = 0; i < 8; i++) { + if (!(val & 0x01)) + return i; + val >>= 1; + } + return -1; +} + + +int bitfield_get_first_zero(struct bitfield *bf) +{ + size_t i; + for (i = 0; i <= (bf->max_bits + 7) / 8; i++) { + if (bf->bits[i] != 0xff) + break; + } + if (i > (bf->max_bits + 7) / 8) + return -1; + i = i * 8 + first_zero(bf->bits[i]); + if (i >= bf->max_bits) + return -1; + return i; +} diff --git a/contrib/hostapd/src/utils/bitfield.h b/contrib/hostapd/src/utils/bitfield.h new file mode 100644 index 0000000000..7050a208c8 --- /dev/null +++ b/contrib/hostapd/src/utils/bitfield.h @@ -0,0 +1,21 @@ +/* + * Bitfield + * Copyright (c) 2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BITFIELD_H +#define BITFIELD_H + +struct bitfield; + +struct bitfield * bitfield_alloc(size_t max_bits); +void bitfield_free(struct bitfield *bf); +void bitfield_set(struct bitfield *bf, size_t bit); +void bitfield_clear(struct bitfield *bf, size_t bit); +int bitfield_is_set(struct bitfield *bf, size_t bit); +int bitfield_get_first_zero(struct bitfield *bf); + +#endif /* BITFIELD_H */ diff --git a/contrib/hostapd/src/utils/build_config.h b/contrib/hostapd/src/utils/build_config.h index 1e147fe369..c6f4e4379a 100644 --- a/contrib/hostapd/src/utils/build_config.h +++ b/contrib/hostapd/src/utils/build_config.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd - Build time configuration defines * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -28,7 +22,6 @@ #define CONFIG_ANSI_C_EXTRA #define CONFIG_WINPCAP #define IEEE8021X_EAPOL -#define EAP_TLS_FUNCS #define PKCS12_FUNCS #define PCSC_FUNCS #define CONFIG_CTRL_IFACE @@ -48,48 +41,10 @@ #define _CRT_SECURE_NO_DEPRECATE #ifdef USE_INTERNAL_CRYPTO -#define CONFIG_TLS_INTERNAL #define CONFIG_TLS_INTERNAL_CLIENT #define CONFIG_INTERNAL_LIBTOMMATH -#define INTERNAL_AES -#define INTERNAL_SHA1 -#define INTERNAL_SHA256 -#define INTERNAL_MD5 -#define INTERNAL_MD4 -#define INTERNAL_DES -#define CONFIG_INTERNAL_X509 #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 INTERNAL_AES -#define INTERNAL_SHA1 -#define INTERNAL_MD5 -#define INTERNAL_MD4 -#define INTERNAL_DES -#define CONFIG_INTERNAL_LIBTOMMATH -#define CONFIG_INTERNAL_X509 -#define EAP_TLS_FUNCS -#define CONFIG_TLS_INTERNAL -#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__ */ - #endif /* BUILD_CONFIG_H */ diff --git a/contrib/hostapd/src/utils/common.c b/contrib/hostapd/src/utils/common.c index 9a46ebe461..39751d408d 100644 --- a/contrib/hostapd/src/utils/common.c +++ b/contrib/hostapd/src/utils/common.c @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -29,7 +23,7 @@ static int hex2num(char c) } -static int hex2byte(const char *hex) +int hex2byte(const char *hex) { int a, b; a = hex2num(*hex++); @@ -43,7 +37,7 @@ static int hex2byte(const char *hex) /** - * hwaddr_aton - Convert ASCII string to MAC address + * 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) @@ -69,6 +63,60 @@ int hwaddr_aton(const char *txt, u8 *addr) return 0; } +/** + * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format) + * @txt: MAC address as a string (e.g., "001122334455") + * @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_compact_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; + } + + 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 @@ -296,6 +344,137 @@ TCHAR * wpa_strdup_tchar(const char *str) #endif /* CONFIG_NATIVE_WINDOWS */ +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len) +{ + char *end = txt + maxlen; + size_t i; + + for (i = 0; i < len; i++) { + if (txt + 4 > end) + break; + + switch (data[i]) { + case '\"': + *txt++ = '\\'; + *txt++ = '\"'; + break; + case '\\': + *txt++ = '\\'; + *txt++ = '\\'; + break; + case '\e': + *txt++ = '\\'; + *txt++ = 'e'; + break; + case '\n': + *txt++ = '\\'; + *txt++ = 'n'; + break; + case '\r': + *txt++ = '\\'; + *txt++ = 'r'; + break; + case '\t': + *txt++ = '\\'; + *txt++ = 't'; + break; + default: + if (data[i] >= 32 && data[i] <= 127) { + *txt++ = data[i]; + } else { + txt += os_snprintf(txt, end - txt, "\\x%02x", + data[i]); + } + break; + } + } + + *txt = '\0'; +} + + +size_t printf_decode(u8 *buf, size_t maxlen, const char *str) +{ + const char *pos = str; + size_t len = 0; + int val; + + while (*pos) { + if (len + 1 >= maxlen) + break; + switch (*pos) { + case '\\': + pos++; + switch (*pos) { + case '\\': + buf[len++] = '\\'; + pos++; + break; + case '"': + buf[len++] = '"'; + pos++; + break; + case 'n': + buf[len++] = '\n'; + pos++; + break; + case 'r': + buf[len++] = '\r'; + pos++; + break; + case 't': + buf[len++] = '\t'; + pos++; + break; + case 'e': + buf[len++] = '\e'; + pos++; + break; + case 'x': + pos++; + val = hex2byte(pos); + if (val < 0) { + val = hex2num(*pos); + if (val < 0) + break; + buf[len++] = val; + pos++; + } else { + buf[len++] = val; + pos += 2; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val = *pos++ - '0'; + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + buf[len++] = val; + break; + default: + break; + } + break; + default: + buf[len++] = *pos++; + break; + } + } + if (maxlen > len) + buf[len] = '\0'; + + return len; +} + + /** * wpa_ssid_txt - Convert SSID to a printable string * @ssid: SSID (32-octet string) @@ -312,17 +491,14 @@ TCHAR * wpa_strdup_tchar(const char *str) */ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) { - static char ssid_txt[33]; - char *pos; + static char ssid_txt[32 * 4 + 1]; - 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 = '_'; + if (ssid == NULL) { + ssid_txt[0] = '\0'; + return ssid_txt; } + + printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len); return ssid_txt; } @@ -331,3 +507,323 @@ void * __hide_aliasing_typecast(void *foo) { return foo; } + + +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 = dup_binstr(value, *len); + if (str == NULL) + return NULL; + return str; + } else if (*value == 'P' && value[1] == '"') { + const char *pos; + char *tstr, *str; + size_t tlen; + value += 2; + pos = os_strrchr(value, '"'); + if (pos == NULL || pos[1] != '\0') + return NULL; + tlen = pos - value; + tstr = dup_binstr(value, tlen); + if (tstr == NULL) + return NULL; + + str = os_malloc(tlen + 1); + if (str == NULL) { + os_free(tstr); + return NULL; + } + + *len = printf_decode((u8 *) str, tlen + 1, tstr); + os_free(tstr); + + 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; + } +} + + +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; +} + + +int find_first_bit(u32 value) +{ + int pos = 0; + + while (value) { + if (value & 0x1) + return pos; + value >>= 1; + pos++; + } + + return -1; +} + + +size_t merge_byte_arrays(u8 *res, size_t res_len, + const u8 *src1, size_t src1_len, + const u8 *src2, size_t src2_len) +{ + size_t len = 0; + + os_memset(res, 0, res_len); + + if (src1) { + if (src1_len >= res_len) { + os_memcpy(res, src1, res_len); + return res_len; + } + + os_memcpy(res, src1, src1_len); + len += src1_len; + } + + if (src2) { + if (len + src2_len >= res_len) { + os_memcpy(res + len, src2, res_len - len); + return res_len; + } + + os_memcpy(res + len, src2, src2_len); + len += src2_len; + } + + return len; +} + + +char * dup_binstr(const void *src, size_t len) +{ + char *res; + + if (src == NULL) + return NULL; + res = os_malloc(len + 1); + if (res == NULL) + return NULL; + os_memcpy(res, src, len); + res[len] = '\0'; + + return res; +} + + +int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value) +{ + struct wpa_freq_range *freq = NULL, *n; + unsigned int count = 0; + const char *pos, *pos2, *pos3; + + /* + * Comma separated list of frequency ranges. + * For example: 2412-2432,2462,5000-6000 + */ + pos = value; + while (pos && pos[0]) { + n = os_realloc_array(freq, count + 1, + sizeof(struct wpa_freq_range)); + if (n == NULL) { + os_free(freq); + return -1; + } + freq = n; + freq[count].min = atoi(pos); + pos2 = os_strchr(pos, '-'); + pos3 = os_strchr(pos, ','); + if (pos2 && (!pos3 || pos2 < pos3)) { + pos2++; + freq[count].max = atoi(pos2); + } else + freq[count].max = freq[count].min; + pos = pos3; + if (pos) + pos++; + count++; + } + + os_free(res->range); + res->range = freq; + res->num = count; + + return 0; +} + + +int freq_range_list_includes(const struct wpa_freq_range_list *list, + unsigned int freq) +{ + unsigned int i; + + if (list == NULL) + return 0; + + for (i = 0; i < list->num; i++) { + if (freq >= list->range[i].min && freq <= list->range[i].max) + return 1; + } + + return 0; +} + + +char * freq_range_list_str(const struct wpa_freq_range_list *list) +{ + char *buf, *pos, *end; + size_t maxlen; + unsigned int i; + int res; + + if (list->num == 0) + return NULL; + + maxlen = list->num * 30; + buf = os_malloc(maxlen); + if (buf == NULL) + return NULL; + pos = buf; + end = buf + maxlen; + + for (i = 0; i < list->num; i++) { + struct wpa_freq_range *range = &list->range[i]; + + if (range->min == range->max) + res = os_snprintf(pos, end - pos, "%s%u", + i == 0 ? "" : ",", range->min); + else + res = os_snprintf(pos, end - pos, "%s%u-%u", + i == 0 ? "" : ",", + range->min, range->max); + if (res < 0 || res > end - pos) { + os_free(buf); + return NULL; + } + pos += res; + } + + return buf; +} + + +int int_array_len(const int *a) +{ + int i; + for (i = 0; a && a[i]; i++) + ; + return i; +} + + +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_array(*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; +} + + +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; +} + + +void int_array_add_unique(int **res, int a) +{ + int reslen; + int *n; + + for (reslen = 0; *res && (*res)[reslen]; reslen++) { + if ((*res)[reslen] == a) + return; /* already in the list */ + } + + n = os_realloc_array(*res, reslen + 2, sizeof(int)); + if (n == NULL) { + os_free(*res); + *res = NULL; + return; + } + + n[reslen] = a; + n[reslen + 1] = 0; + + *res = n; +} diff --git a/contrib/hostapd/src/utils/common.h b/contrib/hostapd/src/utils/common.h index d649391e37..a85cc159df 100644 --- a/contrib/hostapd/src/utils/common.h +++ b/contrib/hostapd/src/utils/common.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2007, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef COMMON_H @@ -17,7 +11,7 @@ #include "os.h" -#ifdef __linux__ +#if defined(__linux__) || defined(__GLIBC__) #include #include #endif /* __linux__ */ @@ -69,12 +63,6 @@ static inline unsigned int bswap_32(unsigned int v) #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 @@ -138,16 +126,6 @@ typedef unsigned char u8; #define WPA_TYPES_DEFINED #endif /* CONFIG_TI_COMPILER */ -#ifdef __SYMBIAN32__ -#define __REMOVE_PLATSEC_DIAGNOSTICS__ -#include -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 @@ -227,6 +205,7 @@ static inline unsigned int wpa_swap_32(unsigned int v) #define be_to_host16(n) (n) #define host_to_be16(n) (n) #define le_to_host32(n) bswap_32(n) +#define host_to_le32(n) bswap_32(n) #define be_to_host32(n) (n) #define host_to_be32(n) (n) #define le_to_host64(n) bswap_64(n) @@ -246,74 +225,131 @@ static inline unsigned int wpa_swap_32(unsigned int v) /* 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])) +static inline u16 WPA_GET_BE16(const u8 *a) +{ + return (a[0] << 8) | a[1]; +} + +static inline void WPA_PUT_BE16(u8 *a, u16 val) +{ + a[0] = val >> 8; + a[1] = val & 0xff; +} + +static inline u16 WPA_GET_LE16(const u8 *a) +{ + return (a[1] << 8) | a[0]; +} + +static inline void WPA_PUT_LE16(u8 *a, u16 val) +{ + a[1] = val >> 8; + a[0] = val & 0xff; +} + +static inline u32 WPA_GET_BE24(const u8 *a) +{ + return (a[0] << 16) | (a[1] << 8) | a[2]; +} + +static inline void WPA_PUT_BE24(u8 *a, u32 val) +{ + a[0] = (val >> 16) & 0xff; + a[1] = (val >> 8) & 0xff; + a[2] = val & 0xff; +} + +static inline u32 WPA_GET_BE32(const u8 *a) +{ + return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]; +} + +static inline void WPA_PUT_BE32(u8 *a, u32 val) +{ + a[0] = (val >> 24) & 0xff; + a[1] = (val >> 16) & 0xff; + a[2] = (val >> 8) & 0xff; + a[3] = val & 0xff; +} + +static inline u32 WPA_GET_LE32(const u8 *a) +{ + return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0]; +} + +static inline void WPA_PUT_LE32(u8 *a, u32 val) +{ + a[3] = (val >> 24) & 0xff; + a[2] = (val >> 16) & 0xff; + a[1] = (val >> 8) & 0xff; + a[0] = val & 0xff; +} + +static inline u64 WPA_GET_BE64(const u8 *a) +{ + return (((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]); +} + +static inline void WPA_PUT_BE64(u8 *a, u64 val) +{ + a[0] = val >> 56; + a[1] = val >> 48; + a[2] = val >> 40; + a[3] = val >> 32; + a[4] = val >> 24; + a[5] = val >> 16; + a[6] = val >> 8; + a[7] = val & 0xff; +} + +static inline u64 WPA_GET_LE64(const u8 *a) +{ + return (((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]); +} + +static inline void WPA_PUT_LE64(u8 *a, u64 val) +{ + a[7] = val >> 56; + a[6] = val >> 48; + a[5] = val >> 40; + a[4] = val >> 32; + a[3] = val >> 24; + a[2] = val >> 16; + a[1] = val >> 8; + a[0] = val & 0xff; +} #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_80211_ENCAP +#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */ +#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__ @@ -384,6 +420,12 @@ void perror(const char *s); #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" + +/* + * Compact form for string representation of MAC address + * To be used, e.g., for constructing dbus paths for P2P Devices + */ +#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x" #endif #ifndef BIT @@ -417,7 +459,18 @@ typedef u64 __bitwise le64; #endif /* __GNUC__ */ #endif /* __must_check */ +#ifndef __maybe_unused +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define __maybe_unused __attribute__((unused)) +#else +#define __maybe_unused +#endif /* __GNUC__ */ +#endif /* __must_check */ + int hwaddr_aton(const char *txt, u8 *addr); +int hwaddr_compact_aton(const char *txt, u8 *addr); +int hwaddr_aton2(const char *txt, u8 *addr); +int hex2byte(const char *hex); 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); @@ -433,16 +486,55 @@ TCHAR * wpa_strdup_tchar(const char *str); #define wpa_strdup_tchar(s) strdup((s)) #endif /* CONFIG_NATIVE_WINDOWS */ +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len); +size_t printf_decode(u8 *buf, size_t maxlen, const char *str); + const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); +char * wpa_config_parse_string(const char *value, size_t *len); +int is_hex(const u8 *data, size_t len); +int find_first_bit(u32 value); +size_t merge_byte_arrays(u8 *res, size_t res_len, + const u8 *src1, size_t src1_len, + const u8 *src2, size_t src2_len); +char * dup_binstr(const void *src, size_t len); + static inline int is_zero_ether_addr(const u8 *a) { return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]); } +static inline int is_broadcast_ether_addr(const u8 *a) +{ + return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff; +} + +#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff" + #include "wpa_debug.h" +struct wpa_freq_range_list { + struct wpa_freq_range { + unsigned int min; + unsigned int max; + } *range; + unsigned int num; +}; + +int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value); +int freq_range_list_includes(const struct wpa_freq_range_list *list, + unsigned int freq); +char * freq_range_list_str(const struct wpa_freq_range_list *list); + +int int_array_len(const int *a); +void int_array_concat(int **res, const int *a); +void int_array_sort_unique(int *a); +void int_array_add_unique(int **res, int a); + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + + /* * 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 @@ -455,4 +547,11 @@ static inline int is_zero_ether_addr(const u8 *a) void * __hide_aliasing_typecast(void *foo); #define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a)) +#ifdef CONFIG_VALGRIND +#include +#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len)) +#else /* CONFIG_VALGRIND */ +#define WPA_MEM_DEFINED(ptr, len) do { } while (0) +#endif /* CONFIG_VALGRIND */ + #endif /* COMMON_H */ diff --git a/contrib/hostapd/src/utils/edit.c b/contrib/hostapd/src/utils/edit.c new file mode 100644 index 0000000000..177ecf41d4 --- /dev/null +++ b/contrib/hostapd/src/utils/edit.c @@ -0,0 +1,1174 @@ +/* + * Command line editing and history + * Copyright (c) 2010-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include "common.h" +#include "eloop.h" +#include "list.h" +#include "edit.h" + +#define CMD_BUF_LEN 256 +static char cmdbuf[CMD_BUF_LEN]; +static int cmdbuf_pos = 0; +static int cmdbuf_len = 0; +static char currbuf[CMD_BUF_LEN]; +static int currbuf_valid = 0; +static const char *ps2 = NULL; + +#define HISTORY_MAX 100 + +struct edit_history { + struct dl_list list; + char str[1]; +}; + +static struct dl_list history_list; +static struct edit_history *history_curr; + +static void *edit_cb_ctx; +static void (*edit_cmd_cb)(void *ctx, char *cmd); +static void (*edit_eof_cb)(void *ctx); +static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = + NULL; + +static struct termios prevt, newt; + + +#define CLEAR_END_LINE "\e[K" + + +void edit_clear_line(void) +{ + int i; + putchar('\r'); + for (i = 0; i < cmdbuf_len + 2 + (ps2 ? (int) os_strlen(ps2) : 0); i++) + putchar(' '); +} + + +static void move_start(void) +{ + cmdbuf_pos = 0; + edit_redraw(); +} + + +static void move_end(void) +{ + cmdbuf_pos = cmdbuf_len; + edit_redraw(); +} + + +static void move_left(void) +{ + if (cmdbuf_pos > 0) { + cmdbuf_pos--; + edit_redraw(); + } +} + + +static void move_right(void) +{ + if (cmdbuf_pos < cmdbuf_len) { + cmdbuf_pos++; + edit_redraw(); + } +} + + +static void move_word_left(void) +{ + while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] == ' ') + cmdbuf_pos--; + while (cmdbuf_pos > 0 && cmdbuf[cmdbuf_pos - 1] != ' ') + cmdbuf_pos--; + edit_redraw(); +} + + +static void move_word_right(void) +{ + while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] == ' ') + cmdbuf_pos++; + while (cmdbuf_pos < cmdbuf_len && cmdbuf[cmdbuf_pos] != ' ') + cmdbuf_pos++; + edit_redraw(); +} + + +static void delete_left(void) +{ + if (cmdbuf_pos == 0) + return; + + edit_clear_line(); + os_memmove(cmdbuf + cmdbuf_pos - 1, cmdbuf + cmdbuf_pos, + cmdbuf_len - cmdbuf_pos); + cmdbuf_pos--; + cmdbuf_len--; + edit_redraw(); +} + + +static void delete_current(void) +{ + if (cmdbuf_pos == cmdbuf_len) + return; + + edit_clear_line(); + os_memmove(cmdbuf + cmdbuf_pos, cmdbuf + cmdbuf_pos + 1, + cmdbuf_len - cmdbuf_pos); + cmdbuf_len--; + edit_redraw(); +} + + +static void delete_word(void) +{ + int pos; + + edit_clear_line(); + pos = cmdbuf_pos; + while (pos > 0 && cmdbuf[pos - 1] == ' ') + pos--; + while (pos > 0 && cmdbuf[pos - 1] != ' ') + pos--; + os_memmove(cmdbuf + pos, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); + cmdbuf_len -= cmdbuf_pos - pos; + cmdbuf_pos = pos; + edit_redraw(); +} + + +static void clear_left(void) +{ + if (cmdbuf_pos == 0) + return; + + edit_clear_line(); + os_memmove(cmdbuf, cmdbuf + cmdbuf_pos, cmdbuf_len - cmdbuf_pos); + cmdbuf_len -= cmdbuf_pos; + cmdbuf_pos = 0; + edit_redraw(); +} + + +static void clear_right(void) +{ + if (cmdbuf_pos == cmdbuf_len) + return; + + edit_clear_line(); + cmdbuf_len = cmdbuf_pos; + edit_redraw(); +} + + +static void history_add(const char *str) +{ + struct edit_history *h, *match = NULL, *last = NULL; + size_t len, count = 0; + + if (str[0] == '\0') + return; + + dl_list_for_each(h, &history_list, struct edit_history, list) { + if (os_strcmp(str, h->str) == 0) { + match = h; + break; + } + last = h; + count++; + } + + if (match) { + dl_list_del(&h->list); + dl_list_add(&history_list, &h->list); + history_curr = h; + return; + } + + if (count >= HISTORY_MAX && last) { + dl_list_del(&last->list); + os_free(last); + } + + len = os_strlen(str); + h = os_zalloc(sizeof(*h) + len); + if (h == NULL) + return; + dl_list_add(&history_list, &h->list); + os_strlcpy(h->str, str, len + 1); + history_curr = h; +} + + +static void history_use(void) +{ + edit_clear_line(); + cmdbuf_len = cmdbuf_pos = os_strlen(history_curr->str); + os_memcpy(cmdbuf, history_curr->str, cmdbuf_len); + edit_redraw(); +} + + +static void history_prev(void) +{ + if (history_curr == NULL) + return; + + if (history_curr == + dl_list_first(&history_list, struct edit_history, list)) { + if (!currbuf_valid) { + cmdbuf[cmdbuf_len] = '\0'; + os_memcpy(currbuf, cmdbuf, cmdbuf_len + 1); + currbuf_valid = 1; + history_use(); + return; + } + } + + if (history_curr == + dl_list_last(&history_list, struct edit_history, list)) + return; + + history_curr = dl_list_entry(history_curr->list.next, + struct edit_history, list); + history_use(); +} + + +static void history_next(void) +{ + if (history_curr == NULL || + history_curr == + dl_list_first(&history_list, struct edit_history, list)) { + if (currbuf_valid) { + currbuf_valid = 0; + edit_clear_line(); + cmdbuf_len = cmdbuf_pos = os_strlen(currbuf); + os_memcpy(cmdbuf, currbuf, cmdbuf_len); + edit_redraw(); + } + return; + } + + history_curr = dl_list_entry(history_curr->list.prev, + struct edit_history, list); + history_use(); +} + + +static void history_read(const char *fname) +{ + FILE *f; + char buf[CMD_BUF_LEN], *pos; + + f = fopen(fname, "r"); + if (f == NULL) + return; + + while (fgets(buf, CMD_BUF_LEN, f)) { + for (pos = buf; *pos; pos++) { + if (*pos == '\r' || *pos == '\n') { + *pos = '\0'; + break; + } + } + history_add(buf); + } + + fclose(f); +} + + +static void history_write(const char *fname, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + FILE *f; + struct edit_history *h; + + f = fopen(fname, "w"); + if (f == NULL) + return; + + dl_list_for_each_reverse(h, &history_list, struct edit_history, list) { + if (filter_cb && filter_cb(edit_cb_ctx, h->str)) + continue; + fprintf(f, "%s\n", h->str); + } + + fclose(f); +} + + +static void history_debug_dump(void) +{ + struct edit_history *h; + edit_clear_line(); + printf("\r"); + dl_list_for_each_reverse(h, &history_list, struct edit_history, list) + printf("%s%s\n", h == history_curr ? "[C]" : "", h->str); + if (currbuf_valid) + printf("{%s}\n", currbuf); + edit_redraw(); +} + + +static void insert_char(int c) +{ + if (cmdbuf_len >= (int) sizeof(cmdbuf) - 1) + return; + if (cmdbuf_len == cmdbuf_pos) { + cmdbuf[cmdbuf_pos++] = c; + cmdbuf_len++; + putchar(c); + fflush(stdout); + } else { + os_memmove(cmdbuf + cmdbuf_pos + 1, cmdbuf + cmdbuf_pos, + cmdbuf_len - cmdbuf_pos); + cmdbuf[cmdbuf_pos++] = c; + cmdbuf_len++; + edit_redraw(); + } +} + + +static void process_cmd(void) +{ + currbuf_valid = 0; + if (cmdbuf_len == 0) { + printf("\n%s> ", ps2 ? ps2 : ""); + fflush(stdout); + return; + } + printf("\n"); + cmdbuf[cmdbuf_len] = '\0'; + history_add(cmdbuf); + cmdbuf_pos = 0; + cmdbuf_len = 0; + edit_cmd_cb(edit_cb_ctx, cmdbuf); + printf("%s> ", ps2 ? ps2 : ""); + fflush(stdout); +} + + +static void free_completions(char **c) +{ + int i; + if (c == NULL) + return; + for (i = 0; c[i]; i++) + os_free(c[i]); + os_free(c); +} + + +static int filter_strings(char **c, char *str, size_t len) +{ + int i, j; + + for (i = 0, j = 0; c[j]; j++) { + if (os_strncasecmp(c[j], str, len) == 0) { + if (i != j) { + c[i] = c[j]; + c[j] = NULL; + } + i++; + } else { + os_free(c[j]); + c[j] = NULL; + } + } + c[i] = NULL; + return i; +} + + +static int common_len(const char *a, const char *b) +{ + int len = 0; + while (a[len] && a[len] == b[len]) + len++; + return len; +} + + +static int max_common_length(char **c) +{ + int len, i; + + len = os_strlen(c[0]); + for (i = 1; c[i]; i++) { + int same = common_len(c[0], c[i]); + if (same < len) + len = same; + } + + return len; +} + + +static int cmp_str(const void *a, const void *b) +{ + return os_strcmp(* (const char **) a, * (const char **) b); +} + +static void complete(int list) +{ + char **c; + int i, len, count; + int start, end; + int room, plen, add_space; + + if (edit_completion_cb == NULL) + return; + + cmdbuf[cmdbuf_len] = '\0'; + c = edit_completion_cb(edit_cb_ctx, cmdbuf, cmdbuf_pos); + if (c == NULL) + return; + + end = cmdbuf_pos; + start = end; + while (start > 0 && cmdbuf[start - 1] != ' ') + start--; + plen = end - start; + + count = filter_strings(c, &cmdbuf[start], plen); + if (count == 0) { + free_completions(c); + return; + } + + len = max_common_length(c); + if (len <= plen && count > 1) { + if (list) { + qsort(c, count, sizeof(char *), cmp_str); + edit_clear_line(); + printf("\r"); + for (i = 0; c[i]; i++) + printf("%s%s", i > 0 ? " " : "", c[i]); + printf("\n"); + edit_redraw(); + } + free_completions(c); + return; + } + len -= plen; + + room = sizeof(cmdbuf) - 1 - cmdbuf_len; + if (room < len) + len = room; + add_space = count == 1 && len < room; + + os_memmove(cmdbuf + cmdbuf_pos + len + add_space, cmdbuf + cmdbuf_pos, + cmdbuf_len - cmdbuf_pos); + os_memcpy(&cmdbuf[cmdbuf_pos - plen], c[0], plen + len); + if (add_space) + cmdbuf[cmdbuf_pos + len] = ' '; + + cmdbuf_pos += len + add_space; + cmdbuf_len += len + add_space; + + edit_redraw(); + + free_completions(c); +} + + +enum edit_key_code { + EDIT_KEY_NONE = 256, + EDIT_KEY_TAB, + EDIT_KEY_UP, + EDIT_KEY_DOWN, + EDIT_KEY_RIGHT, + EDIT_KEY_LEFT, + EDIT_KEY_ENTER, + EDIT_KEY_BACKSPACE, + EDIT_KEY_INSERT, + EDIT_KEY_DELETE, + EDIT_KEY_HOME, + EDIT_KEY_END, + EDIT_KEY_PAGE_UP, + EDIT_KEY_PAGE_DOWN, + EDIT_KEY_F1, + EDIT_KEY_F2, + EDIT_KEY_F3, + EDIT_KEY_F4, + EDIT_KEY_F5, + EDIT_KEY_F6, + EDIT_KEY_F7, + EDIT_KEY_F8, + EDIT_KEY_F9, + EDIT_KEY_F10, + EDIT_KEY_F11, + EDIT_KEY_F12, + EDIT_KEY_CTRL_UP, + EDIT_KEY_CTRL_DOWN, + EDIT_KEY_CTRL_RIGHT, + EDIT_KEY_CTRL_LEFT, + EDIT_KEY_CTRL_A, + EDIT_KEY_CTRL_B, + EDIT_KEY_CTRL_D, + EDIT_KEY_CTRL_E, + EDIT_KEY_CTRL_F, + EDIT_KEY_CTRL_G, + EDIT_KEY_CTRL_H, + EDIT_KEY_CTRL_J, + EDIT_KEY_CTRL_K, + EDIT_KEY_CTRL_L, + EDIT_KEY_CTRL_N, + EDIT_KEY_CTRL_O, + EDIT_KEY_CTRL_P, + EDIT_KEY_CTRL_R, + EDIT_KEY_CTRL_T, + EDIT_KEY_CTRL_U, + EDIT_KEY_CTRL_V, + EDIT_KEY_CTRL_W, + EDIT_KEY_ALT_UP, + EDIT_KEY_ALT_DOWN, + EDIT_KEY_ALT_RIGHT, + EDIT_KEY_ALT_LEFT, + EDIT_KEY_SHIFT_UP, + EDIT_KEY_SHIFT_DOWN, + EDIT_KEY_SHIFT_RIGHT, + EDIT_KEY_SHIFT_LEFT, + EDIT_KEY_ALT_SHIFT_UP, + EDIT_KEY_ALT_SHIFT_DOWN, + EDIT_KEY_ALT_SHIFT_RIGHT, + EDIT_KEY_ALT_SHIFT_LEFT, + EDIT_KEY_EOF +}; + +static void show_esc_buf(const char *esc_buf, char c, int i) +{ + edit_clear_line(); + printf("\rESC buffer '%s' c='%c' [%d]\n", esc_buf, c, i); + edit_redraw(); +} + + +static enum edit_key_code esc_seq_to_key1_no(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_UP; + case 'B': + return EDIT_KEY_DOWN; + case 'C': + return EDIT_KEY_RIGHT; + case 'D': + return EDIT_KEY_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_shift(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_SHIFT_UP; + case 'B': + return EDIT_KEY_SHIFT_DOWN; + case 'C': + return EDIT_KEY_SHIFT_RIGHT; + case 'D': + return EDIT_KEY_SHIFT_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_alt(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_ALT_UP; + case 'B': + return EDIT_KEY_ALT_DOWN; + case 'C': + return EDIT_KEY_ALT_RIGHT; + case 'D': + return EDIT_KEY_ALT_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_alt_shift(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_ALT_SHIFT_UP; + case 'B': + return EDIT_KEY_ALT_SHIFT_DOWN; + case 'C': + return EDIT_KEY_ALT_SHIFT_RIGHT; + case 'D': + return EDIT_KEY_ALT_SHIFT_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1_ctrl(char last) +{ + switch (last) { + case 'A': + return EDIT_KEY_CTRL_UP; + case 'B': + return EDIT_KEY_CTRL_DOWN; + case 'C': + return EDIT_KEY_CTRL_RIGHT; + case 'D': + return EDIT_KEY_CTRL_LEFT; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key1(int param1, int param2, char last) +{ + /* ESC-[; */ + + if (param1 < 0 && param2 < 0) + return esc_seq_to_key1_no(last); + + if (param1 == 1 && param2 == 2) + return esc_seq_to_key1_shift(last); + + if (param1 == 1 && param2 == 3) + return esc_seq_to_key1_alt(last); + + if (param1 == 1 && param2 == 4) + return esc_seq_to_key1_alt_shift(last); + + if (param1 == 1 && param2 == 5) + return esc_seq_to_key1_ctrl(last); + + if (param2 < 0) { + if (last != '~') + return EDIT_KEY_NONE; + switch (param1) { + case 2: + return EDIT_KEY_INSERT; + case 3: + return EDIT_KEY_DELETE; + case 5: + return EDIT_KEY_PAGE_UP; + case 6: + return EDIT_KEY_PAGE_DOWN; + case 15: + return EDIT_KEY_F5; + case 17: + return EDIT_KEY_F6; + case 18: + return EDIT_KEY_F7; + case 19: + return EDIT_KEY_F8; + case 20: + return EDIT_KEY_F9; + case 21: + return EDIT_KEY_F10; + case 23: + return EDIT_KEY_F11; + case 24: + return EDIT_KEY_F12; + } + } + + return EDIT_KEY_NONE; +} + + +static enum edit_key_code esc_seq_to_key2(int param1, int param2, char last) +{ + /* ESC-O; */ + + if (param1 >= 0 || param2 >= 0) + return EDIT_KEY_NONE; + + switch (last) { + case 'F': + return EDIT_KEY_END; + case 'H': + return EDIT_KEY_HOME; + case 'P': + return EDIT_KEY_F1; + case 'Q': + return EDIT_KEY_F2; + case 'R': + return EDIT_KEY_F3; + case 'S': + return EDIT_KEY_F4; + default: + return EDIT_KEY_NONE; + } +} + + +static enum edit_key_code esc_seq_to_key(char *seq) +{ + char last, *pos; + int param1 = -1, param2 = -1; + enum edit_key_code ret = EDIT_KEY_NONE; + + last = '\0'; + for (pos = seq; *pos; pos++) + last = *pos; + + if (seq[1] >= '0' && seq[1] <= '9') { + param1 = atoi(&seq[1]); + pos = os_strchr(seq, ';'); + if (pos) + param2 = atoi(pos + 1); + } + + if (seq[0] == '[') + ret = esc_seq_to_key1(param1, param2, last); + else if (seq[0] == 'O') + ret = esc_seq_to_key2(param1, param2, last); + + if (ret != EDIT_KEY_NONE) + return ret; + + edit_clear_line(); + printf("\rUnknown escape sequence '%s'\n", seq); + edit_redraw(); + return EDIT_KEY_NONE; +} + + +static enum edit_key_code edit_read_key(int sock) +{ + int c; + unsigned char buf[1]; + int res; + static int esc = -1; + static char esc_buf[7]; + + res = read(sock, buf, 1); + if (res < 0) + perror("read"); + if (res <= 0) + return EDIT_KEY_EOF; + + c = buf[0]; + + if (esc >= 0) { + if (c == 27 /* ESC */) { + esc = 0; + return EDIT_KEY_NONE; + } + + if (esc == 6) { + show_esc_buf(esc_buf, c, 0); + esc = -1; + } else { + esc_buf[esc++] = c; + esc_buf[esc] = '\0'; + } + } + + if (esc == 1) { + if (esc_buf[0] != '[' && esc_buf[0] != 'O') { + show_esc_buf(esc_buf, c, 1); + esc = -1; + return EDIT_KEY_NONE; + } else + return EDIT_KEY_NONE; /* Escape sequence continues */ + } + + if (esc > 1) { + if ((c >= '0' && c <= '9') || c == ';') + return EDIT_KEY_NONE; /* Escape sequence continues */ + + if (c == '~' || (c >= 'A' && c <= 'Z')) { + esc = -1; + return esc_seq_to_key(esc_buf); + } + + show_esc_buf(esc_buf, c, 2); + esc = -1; + return EDIT_KEY_NONE; + } + + switch (c) { + case 1: + return EDIT_KEY_CTRL_A; + case 2: + return EDIT_KEY_CTRL_B; + case 4: + return EDIT_KEY_CTRL_D; + case 5: + return EDIT_KEY_CTRL_E; + case 6: + return EDIT_KEY_CTRL_F; + case 7: + return EDIT_KEY_CTRL_G; + case 8: + return EDIT_KEY_CTRL_H; + case 9: + return EDIT_KEY_TAB; + case 10: + return EDIT_KEY_CTRL_J; + case 13: /* CR */ + return EDIT_KEY_ENTER; + case 11: + return EDIT_KEY_CTRL_K; + case 12: + return EDIT_KEY_CTRL_L; + case 14: + return EDIT_KEY_CTRL_N; + case 15: + return EDIT_KEY_CTRL_O; + case 16: + return EDIT_KEY_CTRL_P; + case 18: + return EDIT_KEY_CTRL_R; + case 20: + return EDIT_KEY_CTRL_T; + case 21: + return EDIT_KEY_CTRL_U; + case 22: + return EDIT_KEY_CTRL_V; + case 23: + return EDIT_KEY_CTRL_W; + case 27: /* ESC */ + esc = 0; + return EDIT_KEY_NONE; + case 127: + return EDIT_KEY_BACKSPACE; + default: + return c; + } +} + + +static char search_buf[21]; +static int search_skip; + +static char * search_find(void) +{ + struct edit_history *h; + size_t len = os_strlen(search_buf); + int skip = search_skip; + + if (len == 0) + return NULL; + + dl_list_for_each(h, &history_list, struct edit_history, list) { + if (os_strstr(h->str, search_buf)) { + if (skip == 0) + return h->str; + skip--; + } + } + + search_skip = 0; + return NULL; +} + + +static void search_redraw(void) +{ + char *match = search_find(); + printf("\rsearch '%s': %s" CLEAR_END_LINE, + search_buf, match ? match : ""); + printf("\rsearch '%s", search_buf); + fflush(stdout); +} + + +static void search_start(void) +{ + edit_clear_line(); + search_buf[0] = '\0'; + search_skip = 0; + search_redraw(); +} + + +static void search_clear(void) +{ + search_redraw(); + printf("\r" CLEAR_END_LINE); +} + + +static void search_stop(void) +{ + char *match = search_find(); + search_buf[0] = '\0'; + search_clear(); + if (match) { + os_strlcpy(cmdbuf, match, CMD_BUF_LEN); + cmdbuf_len = os_strlen(cmdbuf); + cmdbuf_pos = cmdbuf_len; + } + edit_redraw(); +} + + +static void search_cancel(void) +{ + search_buf[0] = '\0'; + search_clear(); + edit_redraw(); +} + + +static void search_backspace(void) +{ + size_t len; + len = os_strlen(search_buf); + if (len == 0) + return; + search_buf[len - 1] = '\0'; + search_skip = 0; + search_redraw(); +} + + +static void search_next(void) +{ + search_skip++; + search_find(); + search_redraw(); +} + + +static void search_char(char c) +{ + size_t len; + len = os_strlen(search_buf); + if (len == sizeof(search_buf) - 1) + return; + search_buf[len] = c; + search_buf[len + 1] = '\0'; + search_skip = 0; + search_redraw(); +} + + +static enum edit_key_code search_key(enum edit_key_code c) +{ + switch (c) { + case EDIT_KEY_ENTER: + case EDIT_KEY_CTRL_J: + case EDIT_KEY_LEFT: + case EDIT_KEY_RIGHT: + case EDIT_KEY_HOME: + case EDIT_KEY_END: + case EDIT_KEY_CTRL_A: + case EDIT_KEY_CTRL_E: + search_stop(); + return c; + case EDIT_KEY_DOWN: + case EDIT_KEY_UP: + search_cancel(); + return EDIT_KEY_EOF; + case EDIT_KEY_CTRL_H: + case EDIT_KEY_BACKSPACE: + search_backspace(); + break; + case EDIT_KEY_CTRL_R: + search_next(); + break; + default: + if (c >= 32 && c <= 255) + search_char(c); + break; + } + + return EDIT_KEY_NONE; +} + + +static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) +{ + static int last_tab = 0; + static int search = 0; + enum edit_key_code c; + + c = edit_read_key(sock); + + if (search) { + c = search_key(c); + if (c == EDIT_KEY_NONE) + return; + search = 0; + if (c == EDIT_KEY_EOF) + return; + } + + if (c != EDIT_KEY_TAB && c != EDIT_KEY_NONE) + last_tab = 0; + + switch (c) { + case EDIT_KEY_NONE: + break; + case EDIT_KEY_EOF: + edit_eof_cb(edit_cb_ctx); + break; + case EDIT_KEY_TAB: + complete(last_tab); + last_tab = 1; + break; + case EDIT_KEY_UP: + case EDIT_KEY_CTRL_P: + history_prev(); + break; + case EDIT_KEY_DOWN: + case EDIT_KEY_CTRL_N: + history_next(); + break; + case EDIT_KEY_RIGHT: + case EDIT_KEY_CTRL_F: + move_right(); + break; + case EDIT_KEY_LEFT: + case EDIT_KEY_CTRL_B: + move_left(); + break; + case EDIT_KEY_CTRL_RIGHT: + move_word_right(); + break; + case EDIT_KEY_CTRL_LEFT: + move_word_left(); + break; + case EDIT_KEY_DELETE: + delete_current(); + break; + case EDIT_KEY_END: + move_end(); + break; + case EDIT_KEY_HOME: + case EDIT_KEY_CTRL_A: + move_start(); + break; + case EDIT_KEY_F2: + history_debug_dump(); + break; + case EDIT_KEY_CTRL_D: + if (cmdbuf_len > 0) { + delete_current(); + return; + } + printf("\n"); + edit_eof_cb(edit_cb_ctx); + break; + case EDIT_KEY_CTRL_E: + move_end(); + break; + case EDIT_KEY_CTRL_H: + case EDIT_KEY_BACKSPACE: + delete_left(); + break; + case EDIT_KEY_ENTER: + case EDIT_KEY_CTRL_J: + process_cmd(); + break; + case EDIT_KEY_CTRL_K: + clear_right(); + break; + case EDIT_KEY_CTRL_L: + edit_clear_line(); + edit_redraw(); + break; + case EDIT_KEY_CTRL_R: + search = 1; + search_start(); + break; + case EDIT_KEY_CTRL_U: + clear_left(); + break; + case EDIT_KEY_CTRL_W: + delete_word(); + break; + default: + if (c >= 32 && c <= 255) + insert_char(c); + break; + } +} + + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file, const char *ps) +{ + currbuf[0] = '\0'; + dl_list_init(&history_list); + history_curr = NULL; + if (history_file) + history_read(history_file); + + edit_cb_ctx = ctx; + edit_cmd_cb = cmd_cb; + edit_eof_cb = eof_cb; + edit_completion_cb = completion_cb; + + tcgetattr(STDIN_FILENO, &prevt); + newt = prevt; + newt.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + + eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + + ps2 = ps; + printf("%s> ", ps2 ? ps2 : ""); + fflush(stdout); + + return 0; +} + + +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + struct edit_history *h; + if (history_file) + history_write(history_file, filter_cb); + while ((h = dl_list_first(&history_list, struct edit_history, list))) { + dl_list_del(&h->list); + os_free(h); + } + edit_clear_line(); + putchar('\r'); + fflush(stdout); + eloop_unregister_read_sock(STDIN_FILENO); + tcsetattr(STDIN_FILENO, TCSANOW, &prevt); +} + + +void edit_redraw(void) +{ + char tmp; + cmdbuf[cmdbuf_len] = '\0'; + printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); + if (cmdbuf_pos != cmdbuf_len) { + tmp = cmdbuf[cmdbuf_pos]; + cmdbuf[cmdbuf_pos] = '\0'; + printf("\r%s> %s", ps2 ? ps2 : "", cmdbuf); + cmdbuf[cmdbuf_pos] = tmp; + } + fflush(stdout); +} diff --git a/contrib/hostapd/src/utils/edit.h b/contrib/hostapd/src/utils/edit.h new file mode 100644 index 0000000000..ad27f1c7a4 --- /dev/null +++ b/contrib/hostapd/src/utils/edit.h @@ -0,0 +1,21 @@ +/* + * Command line editing and history + * Copyright (c) 2010, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EDIT_H +#define EDIT_H + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file, const char *ps); +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)); +void edit_clear_line(void); +void edit_redraw(void); + +#endif /* EDIT_H */ diff --git a/contrib/hostapd/src/utils/edit_readline.c b/contrib/hostapd/src/utils/edit_readline.c new file mode 100644 index 0000000000..c2a5bcaa17 --- /dev/null +++ b/contrib/hostapd/src/utils/edit_readline.c @@ -0,0 +1,192 @@ +/* + * Command line editing and history wrapper for readline + * Copyright (c) 2010, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include +#include + +#include "common.h" +#include "eloop.h" +#include "edit.h" + + +static void *edit_cb_ctx; +static void (*edit_cmd_cb)(void *ctx, char *cmd); +static void (*edit_eof_cb)(void *ctx); +static char ** (*edit_completion_cb)(void *ctx, const char *cmd, int pos) = + NULL; + +static char **pending_completions = NULL; + + +static void readline_free_completions(void) +{ + int i; + if (pending_completions == NULL) + return; + for (i = 0; pending_completions[i]; i++) + os_free(pending_completions[i]); + os_free(pending_completions); + pending_completions = NULL; +} + + +static char * readline_completion_func(const char *text, int state) +{ + static int pos = 0; + static size_t len = 0; + + if (pending_completions == NULL) { + rl_attempted_completion_over = 1; + return NULL; + } + + if (state == 0) { + pos = 0; + len = os_strlen(text); + } + for (; pending_completions[pos]; pos++) { + if (strncmp(pending_completions[pos], text, len) == 0) + return strdup(pending_completions[pos++]); + } + + rl_attempted_completion_over = 1; + return NULL; +} + + +static char ** readline_completion(const char *text, int start, int end) +{ + readline_free_completions(); + if (edit_completion_cb) + pending_completions = edit_completion_cb(edit_cb_ctx, + rl_line_buffer, end); + return rl_completion_matches(text, readline_completion_func); +} + + +static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) +{ + rl_callback_read_char(); +} + + +static void trunc_nl(char *str) +{ + char *pos = str; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } +} + + +static void readline_cmd_handler(char *cmd) +{ + 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(); + } + if (cmd == NULL) { + edit_eof_cb(edit_cb_ctx); + return; + } + trunc_nl(cmd); + edit_cmd_cb(edit_cb_ctx, cmd); +} + + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file, const char *ps) +{ + edit_cb_ctx = ctx; + edit_cmd_cb = cmd_cb; + edit_eof_cb = eof_cb; + edit_completion_cb = completion_cb; + + rl_attempted_completion_function = readline_completion; + if (history_file) { + read_history(history_file); + stifle_history(100); + } + + eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + + if (ps) { + size_t blen = os_strlen(ps) + 3; + char *ps2 = os_malloc(blen); + if (ps2) { + os_snprintf(ps2, blen, "%s> ", ps); + rl_callback_handler_install(ps2, readline_cmd_handler); + os_free(ps2); + return 0; + } + } + + rl_callback_handler_install("> ", readline_cmd_handler); + + return 0; +} + + +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + rl_set_prompt(""); + rl_replace_line("", 0); + rl_redisplay(); + rl_callback_handler_remove(); + readline_free_completions(); + + eloop_unregister_read_sock(STDIN_FILENO); + + if (history_file) { + /* 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 (filter_cb && filter_cb(edit_cb_ctx, p)) { + h = remove_history(where_history()); + if (h) { + free(h->line); + free(h->data); + free(h); + } else + next_history(); + } else + next_history(); + } + write_history(history_file); + } +} + + +void edit_clear_line(void) +{ +} + + +void edit_redraw(void) +{ + rl_on_new_line(); + rl_redisplay(); +} diff --git a/contrib/hostapd/src/utils/edit_simple.c b/contrib/hostapd/src/utils/edit_simple.c new file mode 100644 index 0000000000..a095ea6abe --- /dev/null +++ b/contrib/hostapd/src/utils/edit_simple.c @@ -0,0 +1,92 @@ +/* + * Minimal command line editing + * Copyright (c) 2010, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "edit.h" + + +#define CMD_BUF_LEN 256 +static char cmdbuf[CMD_BUF_LEN]; +static int cmdbuf_pos = 0; +static const char *ps2 = NULL; + +static void *edit_cb_ctx; +static void (*edit_cmd_cb)(void *ctx, char *cmd); +static void (*edit_eof_cb)(void *ctx); + + +static void edit_read_char(int sock, void *eloop_ctx, void *sock_ctx) +{ + int c; + unsigned char buf[1]; + int res; + + res = read(sock, buf, 1); + if (res < 0) + perror("read"); + if (res <= 0) { + edit_eof_cb(edit_cb_ctx); + return; + } + c = buf[0]; + + if (c == '\r' || c == '\n') { + cmdbuf[cmdbuf_pos] = '\0'; + cmdbuf_pos = 0; + edit_cmd_cb(edit_cb_ctx, cmdbuf); + printf("%s> ", ps2 ? ps2 : ""); + fflush(stdout); + return; + } + + if (c >= 32 && c <= 255) { + if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) { + cmdbuf[cmdbuf_pos++] = c; + } + } +} + + +int edit_init(void (*cmd_cb)(void *ctx, char *cmd), + void (*eof_cb)(void *ctx), + char ** (*completion_cb)(void *ctx, const char *cmd, int pos), + void *ctx, const char *history_file, const char *ps) +{ + edit_cb_ctx = ctx; + edit_cmd_cb = cmd_cb; + edit_eof_cb = eof_cb; + eloop_register_read_sock(STDIN_FILENO, edit_read_char, NULL, NULL); + ps2 = ps; + + printf("%s> ", ps2 ? ps2 : ""); + fflush(stdout); + + return 0; +} + + +void edit_deinit(const char *history_file, + int (*filter_cb)(void *ctx, const char *cmd)) +{ + eloop_unregister_read_sock(STDIN_FILENO); +} + + +void edit_clear_line(void) +{ +} + + +void edit_redraw(void) +{ + cmdbuf[cmdbuf_pos] = '\0'; + printf("\r> %s", cmdbuf); +} diff --git a/contrib/hostapd/src/utils/eloop.c b/contrib/hostapd/src/utils/eloop.c index 4edb2a7033..f83a2327ae 100644 --- a/contrib/hostapd/src/utils/eloop.c +++ b/contrib/hostapd/src/utils/eloop.c @@ -1,36 +1,43 @@ /* * Event loop based on select() loop - * Copyright (c) 2002-2005, Jouni Malinen + * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#include "trace.h" +#include "list.h" #include "eloop.h" +#ifdef CONFIG_ELOOP_POLL +#include +#include +#endif /* CONFIG_ELOOP_POLL */ + 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 os_time time; + struct dl_list list; + struct os_reltime time; void *eloop_data; void *user_data; eloop_timeout_handler handler; - struct eloop_timeout *next; + WPA_TRACE_REF(eloop); + WPA_TRACE_REF(user); + WPA_TRACE_INFO }; struct eloop_signal { @@ -47,15 +54,20 @@ struct eloop_sock_table { }; struct eloop_data { - void *user_data; - int max_sock; + int count; /* sum of all table counts */ +#ifdef CONFIG_ELOOP_POLL + int max_pollfd_map; /* number of pollfds_map currently allocated */ + int max_poll_fds; /* number of pollfds currently allocated */ + struct pollfd *pollfds; + struct pollfd **pollfds_map; +#endif /* CONFIG_ELOOP_POLL */ struct eloop_sock_table readers; struct eloop_sock_table writers; struct eloop_sock_table exceptions; - struct eloop_timeout *timeout; + struct dl_list timeout; int signal_count; struct eloop_signal *signals; @@ -69,10 +81,56 @@ struct eloop_data { static struct eloop_data eloop; -int eloop_init(void *user_data) +#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)); - eloop.user_data = user_data; + dl_list_init(&eloop.timeout); +#ifdef WPA_TRACE + signal(SIGSEGV, eloop_sigsegv_handler); +#endif /* WPA_TRACE */ return 0; } @@ -82,13 +140,44 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, void *eloop_data, void *user_data) { struct eloop_sock *tmp; + int new_max_sock; + + if (sock > eloop.max_sock) + new_max_sock = sock; + else + new_max_sock = eloop.max_sock; if (table == NULL) return -1; - tmp = (struct eloop_sock *) - os_realloc(table->table, - (table->count + 1) * sizeof(struct eloop_sock)); +#ifdef CONFIG_ELOOP_POLL + if (new_max_sock >= eloop.max_pollfd_map) { + struct pollfd **nmap; + nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50, + sizeof(struct pollfd *)); + if (nmap == NULL) + return -1; + + eloop.max_pollfd_map = new_max_sock + 50; + eloop.pollfds_map = nmap; + } + + if (eloop.count + 1 > eloop.max_poll_fds) { + struct pollfd *n; + int nmax = eloop.count + 1 + 50; + n = os_realloc_array(eloop.pollfds, nmax, + sizeof(struct pollfd)); + if (n == NULL) + return -1; + + eloop.max_poll_fds = nmax; + eloop.pollfds = n; + } +#endif /* CONFIG_ELOOP_POLL */ + + eloop_trace_sock_remove_ref(table); + tmp = os_realloc_array(table->table, table->count + 1, + sizeof(struct eloop_sock)); if (tmp == NULL) return -1; @@ -96,11 +185,13 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, 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; + eloop.max_sock = new_max_sock; + eloop.count++; table->changed = 1; + eloop_trace_sock_add_ref(table); return 0; } @@ -120,15 +211,158 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, } 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--; + eloop.count--; table->changed = 1; + eloop_trace_sock_add_ref(table); +} + + +#ifdef CONFIG_ELOOP_POLL + +static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx) +{ + if (fd < mx && fd >= 0) + return pollfds_map[fd]; + return NULL; +} + + +static int eloop_sock_table_set_fds(struct eloop_sock_table *readers, + struct eloop_sock_table *writers, + struct eloop_sock_table *exceptions, + struct pollfd *pollfds, + struct pollfd **pollfds_map, + int max_pollfd_map) +{ + int i; + int nxt = 0; + int fd; + struct pollfd *pfd; + + /* Clear pollfd lookup map. It will be re-populated below. */ + os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map); + + if (readers && readers->table) { + for (i = 0; i < readers->count; i++) { + fd = readers->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pollfds[nxt].fd = fd; + pollfds[nxt].events = POLLIN; + pollfds[nxt].revents = 0; + pollfds_map[fd] = &(pollfds[nxt]); + nxt++; + } + } + + if (writers && writers->table) { + for (i = 0; i < writers->count; i++) { + /* + * See if we already added this descriptor, update it + * if so. + */ + fd = writers->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pfd = pollfds_map[fd]; + if (!pfd) { + pfd = &(pollfds[nxt]); + pfd->events = 0; + pfd->fd = fd; + pollfds[i].revents = 0; + pollfds_map[fd] = pfd; + nxt++; + } + pfd->events |= POLLOUT; + } + } + + /* + * Exceptions are always checked when using poll, but I suppose it's + * possible that someone registered a socket *only* for exception + * handling. Set the POLLIN bit in this case. + */ + if (exceptions && exceptions->table) { + for (i = 0; i < exceptions->count; i++) { + /* + * See if we already added this descriptor, just use it + * if so. + */ + fd = exceptions->table[i].sock; + assert(fd >= 0 && fd < max_pollfd_map); + pfd = pollfds_map[fd]; + if (!pfd) { + pfd = &(pollfds[nxt]); + pfd->events = POLLIN; + pfd->fd = fd; + pollfds[i].revents = 0; + pollfds_map[fd] = pfd; + nxt++; + } + } + } + + return nxt; +} + + +static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table, + struct pollfd **pollfds_map, + int max_pollfd_map, + short int revents) +{ + int i; + struct pollfd *pfd; + + if (!table || !table->table) + return 0; + + table->changed = 0; + for (i = 0; i < table->count; i++) { + pfd = find_pollfd(pollfds_map, table->table[i].sock, + max_pollfd_map); + if (!pfd) + continue; + + if (!(pfd->revents & revents)) + continue; + + table->table[i].handler(table->table[i].sock, + table->table[i].eloop_data, + table->table[i].user_data); + if (table->changed) + return 1; + } + + return 0; +} + + +static void eloop_sock_table_dispatch(struct eloop_sock_table *readers, + struct eloop_sock_table *writers, + struct eloop_sock_table *exceptions, + struct pollfd **pollfds_map, + int max_pollfd_map) +{ + if (eloop_sock_table_dispatch_table(readers, pollfds_map, + max_pollfd_map, POLLIN | POLLERR | + POLLHUP)) + return; /* pollfds may be invalid at this point */ + + if (eloop_sock_table_dispatch_table(writers, pollfds_map, + max_pollfd_map, POLLOUT)) + return; /* pollfds may be invalid at this point */ + + eloop_sock_table_dispatch_table(exceptions, pollfds_map, + max_pollfd_map, POLLERR | POLLHUP); } +#else /* CONFIG_ELOOP_POLL */ static void eloop_sock_table_set_fds(struct eloop_sock_table *table, fd_set *fds) @@ -165,18 +399,25 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *table, } } +#endif /* CONFIG_ELOOP_POLL */ + static void eloop_sock_table_destroy(struct eloop_sock_table *table) { if (table) { int i; for (i = 0; i < table->count && table->table; i++) { - printf("ELOOP: remaining socket: sock=%d " - "eloop_data=%p user_data=%p handler=%p\n", - table->table[i].sock, - table->table[i].eloop_data, - table->table[i].user_data, - table->table[i].handler); + 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); } @@ -237,16 +478,28 @@ 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; + struct eloop_timeout *timeout, *tmp; + os_time_t now_sec; - timeout = os_malloc(sizeof(*timeout)); + timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) return -1; - if (os_get_time(&timeout->time) < 0) { + if (os_get_reltime(&timeout->time) < 0) { os_free(timeout); return -1; } + now_sec = timeout->time.sec; timeout->time.sec += secs; + if (timeout->time.sec < now_sec) { + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to " + "ever happen - ignore it", secs); + os_free(timeout); + return 0; + } timeout->time.usec += usecs; while (timeout->time.usec >= 1000000) { timeout->time.sec++; @@ -255,93 +508,169 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, 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; + 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_reltime_before(&timeout->time, &tmp->time)) { + dl_list_add(tmp->list.prev, &timeout->list); + return 0; + } } + dl_list_add_tail(&eloop.timeout, &timeout->list); - prev = NULL; - tmp = eloop.timeout; - while (tmp != NULL) { - if (os_time_before(&timeout->time, &tmp->time)) - break; - prev = tmp; - tmp = tmp->next; - } + return 0; +} - if (prev == NULL) { - timeout->next = eloop.timeout; - eloop.timeout = timeout; - } else { - timeout->next = prev->next; - prev->next = timeout; - } - 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, *next; + struct eloop_timeout *timeout, *prev; int removed = 0; - prev = NULL; - timeout = eloop.timeout; - while (timeout != NULL) { - next = timeout->next; - + 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)) { - if (prev == NULL) - eloop.timeout = next; - else - prev->next = next; - os_free(timeout); + eloop_remove_timeout(timeout); removed++; - } else - prev = timeout; - - timeout = next; + } } return removed; } +int eloop_cancel_timeout_one(eloop_timeout_handler handler, + void *eloop_data, void *user_data, + struct os_reltime *remaining) +{ + struct eloop_timeout *timeout, *prev; + int removed = 0; + struct os_reltime now; + + os_get_reltime(&now); + remaining->sec = remaining->usec = 0; + + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data) && + (timeout->user_data == user_data)) { + removed = 1; + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, remaining); + eloop_remove_timeout(timeout); + break; + } + } + 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) { + 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; - - tmp = tmp->next; } return 0; } +int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + 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) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&requested, &remaining)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + +int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + 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) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&remaining, &requested)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + #ifndef CONFIG_NATIVE_WINDOWS static void eloop_handle_alarm(int sig) { - fprintf(stderr, "eloop: could not process SIGINT or SIGTERM in two " - "seconds. Looks like there\n" - "is a bug that ends up in a busy loop that " - "prevents clean shutdown.\n" - "Killing program forcefully.\n"); + 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 */ @@ -390,7 +719,6 @@ static void eloop_process_pending_signals(void) 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); } } @@ -402,10 +730,8 @@ int eloop_register_signal(int sig, eloop_signal_handler handler, { struct eloop_signal *tmp; - tmp = (struct eloop_signal *) - os_realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); if (tmp == NULL) return -1; @@ -444,58 +770,83 @@ int eloop_register_signal_reconfig(eloop_signal_handler handler, void eloop_run(void) { +#ifdef CONFIG_ELOOP_POLL + int num_poll_fds; + int timeout_ms = 0; +#else /* CONFIG_ELOOP_POLL */ fd_set *rfds, *wfds, *efds; - int res; struct timeval _tv; - struct os_time tv, now; +#endif /* CONFIG_ELOOP_POLL */ + int res; + struct os_reltime tv, now; +#ifndef CONFIG_ELOOP_POLL rfds = os_malloc(sizeof(*rfds)); wfds = os_malloc(sizeof(*wfds)); efds = os_malloc(sizeof(*efds)); - if (rfds == NULL || wfds == NULL || efds == NULL) { - printf("eloop_run - malloc failed\n"); + if (rfds == NULL || wfds == NULL || efds == NULL) goto out; - } +#endif /* CONFIG_ELOOP_POLL */ while (!eloop.terminate && - (eloop.timeout || eloop.readers.count > 0 || + (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || eloop.writers.count > 0 || eloop.exceptions.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); + struct eloop_timeout *timeout; + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, &tv); else tv.sec = tv.usec = 0; -#if 0 - printf("next timeout in %lu.%06lu sec\n", - tv.sec, tv.usec); -#endif +#ifdef CONFIG_ELOOP_POLL + timeout_ms = tv.sec * 1000 + tv.usec / 1000; +#else /* CONFIG_ELOOP_POLL */ _tv.tv_sec = tv.sec; _tv.tv_usec = tv.usec; +#endif /* CONFIG_ELOOP_POLL */ } +#ifdef CONFIG_ELOOP_POLL + num_poll_fds = eloop_sock_table_set_fds( + &eloop.readers, &eloop.writers, &eloop.exceptions, + eloop.pollfds, eloop.pollfds_map, + eloop.max_pollfd_map); + res = poll(eloop.pollfds, num_poll_fds, + timeout ? timeout_ms : -1); + + if (res < 0 && errno != EINTR && errno != 0) { + wpa_printf(MSG_INFO, "eloop: poll: %s", + strerror(errno)); + goto out; + } +#else /* CONFIG_ELOOP_POLL */ 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, - eloop.timeout ? &_tv : NULL); + timeout ? &_tv : NULL); if (res < 0 && errno != EINTR && errno != 0) { - perror("select"); + wpa_printf(MSG_INFO, "eloop: select: %s", + strerror(errno)); goto out; } +#endif /* CONFIG_ELOOP_POLL */ 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); + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (!os_reltime_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); } } @@ -503,15 +854,25 @@ void eloop_run(void) if (res <= 0) continue; +#ifdef CONFIG_ELOOP_POLL + eloop_sock_table_dispatch(&eloop.readers, &eloop.writers, + &eloop.exceptions, eloop.pollfds_map, + eloop.max_pollfd_map); +#else /* CONFIG_ELOOP_POLL */ eloop_sock_table_dispatch(&eloop.readers, rfds); eloop_sock_table_dispatch(&eloop.writers, wfds); eloop_sock_table_dispatch(&eloop.exceptions, efds); +#endif /* CONFIG_ELOOP_POLL */ } + eloop.terminate = 0; out: +#ifndef CONFIG_ELOOP_POLL os_free(rfds); os_free(wfds); os_free(efds); +#endif /* CONFIG_ELOOP_POLL */ + return; } @@ -524,31 +885,36 @@ void eloop_terminate(void) void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; - struct os_time now; + struct os_reltime now; - timeout = eloop.timeout; - if (timeout) - os_get_time(&now); - while (timeout != NULL) { + os_get_reltime(&now); + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { int sec, usec; - prev = timeout; - timeout = timeout->next; - sec = prev->time.sec - now.sec; - usec = prev->time.usec - now.usec; - if (prev->time.usec < now.usec) { + sec = timeout->time.sec - now.sec; + usec = timeout->time.usec - now.usec; + if (timeout->time.usec < now.usec) { sec--; usec += 1000000; } - printf("ELOOP: remaining timeout: %d.%06d eloop_data=%p " - "user_data=%p handler=%p\n", - sec, usec, prev->eloop_data, prev->user_data, - prev->handler); - os_free(prev); + 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); + +#ifdef CONFIG_ELOOP_POLL + os_free(eloop.pollfds); + os_free(eloop.pollfds_map); +#endif /* CONFIG_ELOOP_POLL */ } @@ -560,6 +926,18 @@ int eloop_terminated(void) void eloop_wait_for_read_sock(int sock) { +#ifdef CONFIG_ELOOP_POLL + struct pollfd pfd; + + if (sock < 0) + return; + + os_memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sock; + pfd.events = POLLIN; + + poll(&pfd, 1, -1); +#else /* CONFIG_ELOOP_POLL */ fd_set rfds; if (sock < 0) @@ -568,10 +946,5 @@ void eloop_wait_for_read_sock(int sock) FD_ZERO(&rfds); FD_SET(sock, &rfds); select(sock + 1, &rfds, NULL, NULL, NULL); -} - - -void * eloop_get_user_data(void) -{ - return eloop.user_data; +#endif /* CONFIG_ELOOP_POLL */ } diff --git a/contrib/hostapd/src/utils/eloop.h b/contrib/hostapd/src/utils/eloop.h index cf83f38365..07b8c0dc33 100644 --- a/contrib/hostapd/src/utils/eloop.h +++ b/contrib/hostapd/src/utils/eloop.h @@ -2,14 +2,8 @@ * Event loop * Copyright (c) 2002-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -65,25 +59,19 @@ typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); /** * eloop_signal_handler - eloop signal event callback type * @sig: Signal number - * @eloop_ctx: Registered callback context data (global user_data from - * eloop_init() call) * @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 *eloop_ctx, - void *signal_ctx); +typedef void (*eloop_signal_handler)(int sig, void *signal_ctx); /** * eloop_init() - Initialize global event loop data - * @user_data: Pointer to global data passed as eloop_ctx to signal handlers * Returns: 0 on success, -1 on failure * - * This function must be called before any other eloop_* function. user_data - * can be used to configure a global (to the process) pointer that will be - * passed as eloop_ctx parameter to signal handlers. + * This function must be called before any other eloop_* function. */ -int eloop_init(void *user_data); +int eloop_init(void); /** * eloop_register_read_sock - Register handler for read events @@ -150,7 +138,7 @@ void eloop_unregister_sock(int sock, eloop_event_type type); * 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 + * register eloop implementation specific events which are mainly targeted 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 @@ -206,6 +194,21 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data); +/** + * eloop_cancel_timeout_one - Cancel a single timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * @remaining: Time left on the cancelled timer + * Returns: Number of cancelled timeouts + * + * Cancel matching timeout registered with + * eloop_register_timeout() and return the remaining time left. + */ +int eloop_cancel_timeout_one(eloop_timeout_handler handler, + void *eloop_data, void *user_data, + struct os_reltime *remaining); + /** * eloop_is_timeout_registered - Check if a timeout is already registered * @handler: Matching callback function @@ -219,6 +222,40 @@ int eloop_cancel_timeout(eloop_timeout_handler handler, int eloop_is_timeout_registered(eloop_timeout_handler handler, void *eloop_data, void *user_data); +/** + * eloop_deplete_timeout - Deplete a timeout that is already registered + * @req_secs: Requested number of seconds to the timeout + * @req_usecs: Requested number of microseconds to the timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is depleted, 0 if no change is made, -1 if no + * timeout matched + * + * Find a registered matching timeout. If found, + * deplete the timeout if remaining time is more than the requested time. + */ +int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data); + +/** + * eloop_replenish_timeout - Replenish a timeout that is already registered + * @req_secs: Requested number of seconds to the timeout + * @req_usecs: Requested number of microseconds to the timeout + * @handler: Matching callback function + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is replenished, 0 if no change is made, -1 if no + * timeout matched + * + * Find a registered matching timeout. If found, + * replenish the timeout if remaining time is less than the requested time. + */ +int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data); + /** * eloop_register_signal - Register handler for signals * @sig: Signal number (e.g., SIGHUP) @@ -231,10 +268,6 @@ int eloop_is_timeout_registered(eloop_timeout_handler handler, * handler has returned. This means that the normal limits for sighandlers * (i.e., only "safe functions" allowed) do not apply for the registered * callback. - * - * Signals are 'global' events and there is no local eloop_data pointer like - * with other handlers. The global user_data pointer registered with - * eloop_init() will be used as eloop_ctx for signal handlers. */ int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data); @@ -251,10 +284,6 @@ int eloop_register_signal(int sig, eloop_signal_handler handler, * sighandlers (i.e., only "safe functions" allowed) do not apply for the * registered callback. * - * Signals are 'global' events and there is no local eloop_data pointer like - * with other handlers. The global user_data pointer registered with - * eloop_init() will be used as eloop_ctx for signal handlers. - * * 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 @@ -275,10 +304,6 @@ int eloop_register_signal_terminate(eloop_signal_handler handler, * limits for sighandlers (i.e., only "safe functions" allowed) do not apply * for the registered callback. * - * Signals are 'global' events and there is no local eloop_data pointer like - * with other handlers. The global user_data pointer registered with - * eloop_init() will be used as eloop_ctx for signal handlers. - * * 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 @@ -331,10 +356,4 @@ int eloop_terminated(void); */ void eloop_wait_for_read_sock(int sock); -/** - * eloop_get_user_data - Get global user data - * Returns: user_data pointer that was registered with eloop_init() - */ -void * eloop_get_user_data(void); - #endif /* ELOOP_H */ diff --git a/contrib/hostapd/src/utils/eloop_none.c b/contrib/hostapd/src/utils/eloop_none.c deleted file mode 100644 index 215030b213..0000000000 --- a/contrib/hostapd/src/utils/eloop_none.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Event loop - empty template (basic structure, but no OS specific operations) - * Copyright (c) 2002-2005, Jouni Malinen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "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 { - void *user_data; - - int max_sock, reader_count; - struct eloop_sock *readers; - - struct eloop_timeout *timeout; - - int signal_count; - struct eloop_signal *signals; - int signaled; - int pending_terminate; - - int terminate; - int reader_table_changed; -}; - -static struct eloop_data eloop; - - -int eloop_init(void *user_data) -{ - memset(&eloop, 0, sizeof(eloop)); - eloop.user_data = user_data; - 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 - */ -} - - -void * eloop_get_user_data(void) -{ - return eloop.user_data; -} diff --git a/contrib/hostapd/src/utils/eloop_win.c b/contrib/hostapd/src/utils/eloop_win.c index c95aa76b78..de47fb2183 100644 --- a/contrib/hostapd/src/utils/eloop_win.c +++ b/contrib/hostapd/src/utils/eloop_win.c @@ -1,21 +1,16 @@ /* * Event loop based on Windows events and WaitForMultipleObjects - * Copyright (c) 2002-2006, Jouni Malinen + * Copyright (c) 2002-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include #include "common.h" +#include "list.h" #include "eloop.h" @@ -35,11 +30,11 @@ struct eloop_event { }; struct eloop_timeout { - struct os_time time; + struct dl_list list; + struct os_reltime time; void *eloop_data; void *user_data; eloop_timeout_handler handler; - struct eloop_timeout *next; }; struct eloop_signal { @@ -50,8 +45,6 @@ struct eloop_signal { }; struct eloop_data { - void *user_data; - int max_sock; size_t reader_count; struct eloop_sock *readers; @@ -59,7 +52,7 @@ struct eloop_data { size_t event_count; struct eloop_event *events; - struct eloop_timeout *timeout; + struct dl_list timeout; int signal_count; struct eloop_signal *signals; @@ -79,10 +72,10 @@ struct eloop_data { static struct eloop_data eloop; -int eloop_init(void *user_data) +int eloop_init(void) { os_memset(&eloop, 0, sizeof(eloop)); - eloop.user_data = user_data; + dl_list_init(&eloop.timeout); eloop.num_handles = 1; eloop.handles = os_malloc(eloop.num_handles * sizeof(eloop.handles[0])); @@ -107,8 +100,8 @@ static int eloop_prepare_handles(void) 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])); + n = os_realloc_array(eloop.handles, eloop.num_handles * 2, + sizeof(eloop.handles[0])); if (n == NULL) return -1; eloop.handles = n; @@ -137,8 +130,8 @@ int eloop_register_read_sock(int sock, eloop_sock_handler handler, WSACloseEvent(event); return -1; } - tmp = os_realloc(eloop.readers, - (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1, + sizeof(struct eloop_sock)); if (tmp == NULL) { WSAEventSelect(sock, event, 0); WSACloseEvent(event); @@ -200,8 +193,8 @@ int eloop_register_event(void *event, size_t event_size, if (eloop_prepare_handles()) return -1; - tmp = os_realloc(eloop.events, - (eloop.event_count + 1) * sizeof(struct eloop_event)); + tmp = os_realloc_array(eloop.events, eloop.event_count + 1, + sizeof(struct eloop_event)); if (tmp == NULL) return -1; @@ -245,13 +238,28 @@ 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; + struct eloop_timeout *timeout, *tmp; + os_time_t now_sec; - timeout = os_malloc(sizeof(*timeout)); + timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) return -1; - os_get_time(&timeout->time); + if (os_get_reltime(&timeout->time) < 0) { + os_free(timeout); + return -1; + } + now_sec = timeout->time.sec; timeout->time.sec += secs; + if (timeout->time.sec < now_sec) { + /* + * Integer overflow - assume long enough timeout to be assumed + * to be infinite, i.e., the timeout would never happen. + */ + wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to " + "ever happen - ignore it", secs); + os_free(timeout); + return 0; + } timeout->time.usec += usecs; while (timeout->time.usec >= 1000000) { timeout->time.sec++; @@ -260,85 +268,156 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, 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; + /* Maintain timeouts in order of increasing time */ + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (os_reltime_before(&timeout->time, &tmp->time)) { + dl_list_add(tmp->list.prev, &timeout->list); + return 0; + } } + dl_list_add_tail(&eloop.timeout, &timeout->list); - prev = NULL; - tmp = eloop.timeout; - while (tmp != NULL) { - if (os_time_before(&timeout->time, &tmp->time)) - break; - prev = tmp; - tmp = tmp->next; - } + return 0; +} - if (prev == NULL) { - timeout->next = eloop.timeout; - eloop.timeout = timeout; - } else { - timeout->next = prev->next; - prev->next = timeout; - } - return 0; +static void eloop_remove_timeout(struct eloop_timeout *timeout) +{ + dl_list_del(&timeout->list); + os_free(timeout); } int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data) { - struct eloop_timeout *timeout, *prev, *next; + struct eloop_timeout *timeout, *prev; int removed = 0; - prev = NULL; - timeout = eloop.timeout; - while (timeout != NULL) { - next = timeout->next; - + 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)) { - if (prev == NULL) - eloop.timeout = next; - else - prev->next = next; - os_free(timeout); + eloop_remove_timeout(timeout); removed++; - } else - prev = timeout; - - timeout = next; + } } return removed; } +int eloop_cancel_timeout_one(eloop_timeout_handler handler, + void *eloop_data, void *user_data, + struct os_reltime *remaining) +{ + struct eloop_timeout *timeout, *prev; + int removed = 0; + struct os_reltime now; + + os_get_reltime(&now); + remaining->sec = remaining->usec = 0; + + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data) && + (timeout->user_data == user_data)) { + removed = 1; + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, remaining); + eloop_remove_timeout(timeout); + break; + } + } + 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) { + 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; - - tmp = tmp->next; } return 0; } +int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + 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) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&requested, &remaining)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + +int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs, + eloop_timeout_handler handler, void *eloop_data, + void *user_data) +{ + struct os_reltime now, requested, remaining; + 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) { + requested.sec = req_secs; + requested.usec = req_usecs; + os_get_reltime(&now); + os_reltime_sub(&tmp->time, &now, &remaining); + if (os_reltime_before(&remaining, &requested)) { + eloop_cancel_timeout(handler, eloop_data, + user_data); + eloop_register_timeout(requested.sec, + requested.usec, + handler, eloop_data, + user_data); + return 1; + } + return 0; + } + } + + return -1; +} + + /* TODO: replace with suitable signal handler */ #if 0 static void eloop_handle_signal(int sig) @@ -372,7 +451,6 @@ static void eloop_process_pending_signals(void) 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); } } @@ -380,7 +458,6 @@ static void eloop_process_pending_signals(void) if (eloop.term_signal.signaled) { eloop.term_signal.signaled = 0; eloop.term_signal.handler(eloop.term_signal.sig, - eloop.user_data, eloop.term_signal.user_data); } } @@ -391,9 +468,8 @@ int eloop_register_signal(int sig, eloop_signal_handler handler, { struct eloop_signal *tmp; - tmp = os_realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); if (tmp == NULL) return -1; @@ -456,18 +532,21 @@ int eloop_register_signal_reconfig(eloop_signal_handler handler, void eloop_run(void) { - struct os_time tv, now; - DWORD count, ret, timeout, err; + struct os_reltime tv, now; + DWORD count, ret, timeout_val, err; size_t i; while (!eloop.terminate && - (eloop.timeout || eloop.reader_count > 0 || + (!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0 || eloop.event_count > 0)) { + struct eloop_timeout *timeout; 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); + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, &tv); } count = 0; @@ -480,10 +559,10 @@ void eloop_run(void) if (eloop.term_event) eloop.handles[count++] = eloop.term_event; - if (eloop.timeout) - timeout = tv.sec * 1000 + tv.usec / 1000; + if (timeout) + timeout_val = tv.sec * 1000 + tv.usec / 1000; else - timeout = INFINITE; + timeout_val = INFINITE; if (count > MAXIMUM_WAIT_OBJECTS) { printf("WaitForMultipleObjects: Too many events: " @@ -493,26 +572,27 @@ void eloop_run(void) } #ifdef _WIN32_WCE ret = WaitForMultipleObjects(count, eloop.handles, FALSE, - timeout); + timeout_val); #else /* _WIN32_WCE */ ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE, - timeout, TRUE); + timeout_val, 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); + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (!os_reltime_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); } } @@ -571,11 +651,9 @@ void eloop_destroy(void) { struct eloop_timeout *timeout, *prev; - timeout = eloop.timeout; - while (timeout != NULL) { - prev = timeout; - timeout = timeout->next; - os_free(prev); + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + eloop_remove_timeout(timeout); } os_free(eloop.readers); os_free(eloop.signals); @@ -614,9 +692,3 @@ void eloop_wait_for_read_sock(int sock) WSAEventSelect(sock, event, 0); WSACloseEvent(event); } - - -void * eloop_get_user_data(void) -{ - return eloop.user_data; -} diff --git a/contrib/hostapd/src/utils/ext_password.c b/contrib/hostapd/src/utils/ext_password.c new file mode 100644 index 0000000000..06131197a3 --- /dev/null +++ b/contrib/hostapd/src/utils/ext_password.c @@ -0,0 +1,116 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#ifdef __linux__ +#include +#endif /* __linux__ */ + +#include "common.h" +#include "ext_password_i.h" + + +#ifdef CONFIG_EXT_PASSWORD_TEST +extern struct ext_password_backend ext_password_test; +#endif /* CONFIG_EXT_PASSWORD_TEST */ + +static const struct ext_password_backend *backends[] = { +#ifdef CONFIG_EXT_PASSWORD_TEST + &ext_password_test, +#endif /* CONFIG_EXT_PASSWORD_TEST */ + NULL +}; + +struct ext_password_data { + const struct ext_password_backend *backend; + void *priv; +}; + + +struct ext_password_data * ext_password_init(const char *backend, + const char *params) +{ + struct ext_password_data *data; + int i; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + for (i = 0; backends[i]; i++) { + if (os_strcmp(backends[i]->name, backend) == 0) { + data->backend = backends[i]; + break; + } + } + + if (!data->backend) { + os_free(data); + return NULL; + } + + data->priv = data->backend->init(params); + if (data->priv == NULL) { + os_free(data); + return NULL; + } + + return data; +} + + +void ext_password_deinit(struct ext_password_data *data) +{ + if (data && data->backend && data->priv) + data->backend->deinit(data->priv); + os_free(data); +} + + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name) +{ + if (data == NULL) + return NULL; + return data->backend->get(data->priv, name); +} + + +struct wpabuf * ext_password_alloc(size_t len) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return NULL; + +#ifdef __linux__ + if (mlock(wpabuf_head(buf), wpabuf_len(buf)) < 0) { + wpa_printf(MSG_ERROR, "EXT PW: mlock failed: %s", + strerror(errno)); + } +#endif /* __linux__ */ + + return buf; +} + + +void ext_password_free(struct wpabuf *pw) +{ + if (pw == NULL) + return; + os_memset(wpabuf_mhead(pw), 0, wpabuf_len(pw)); +#ifdef __linux__ + if (munlock(wpabuf_head(pw), wpabuf_len(pw)) < 0) { + wpa_printf(MSG_ERROR, "EXT PW: munlock failed: %s", + strerror(errno)); + } +#endif /* __linux__ */ + wpabuf_free(pw); +} diff --git a/contrib/hostapd/src/utils/ext_password.h b/contrib/hostapd/src/utils/ext_password.h new file mode 100644 index 0000000000..e3e46ea084 --- /dev/null +++ b/contrib/hostapd/src/utils/ext_password.h @@ -0,0 +1,33 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_H +#define EXT_PASSWORD_H + +struct ext_password_data; + +#ifdef CONFIG_EXT_PASSWORD + +struct ext_password_data * ext_password_init(const char *backend, + const char *params); +void ext_password_deinit(struct ext_password_data *data); + +struct wpabuf * ext_password_get(struct ext_password_data *data, + const char *name); +void ext_password_free(struct wpabuf *pw); + +#else /* CONFIG_EXT_PASSWORD */ + +#define ext_password_init(b, p) ((void *) 1) +#define ext_password_deinit(d) do { } while (0) +#define ext_password_get(d, n) (NULL) +#define ext_password_free(p) do { } while (0) + +#endif /* CONFIG_EXT_PASSWORD */ + +#endif /* EXT_PASSWORD_H */ diff --git a/contrib/hostapd/src/utils/ext_password_i.h b/contrib/hostapd/src/utils/ext_password_i.h new file mode 100644 index 0000000000..043e7312c6 --- /dev/null +++ b/contrib/hostapd/src/utils/ext_password_i.h @@ -0,0 +1,23 @@ +/* + * External password backend - internal definitions + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EXT_PASSWORD_I_H +#define EXT_PASSWORD_I_H + +#include "ext_password.h" + +struct ext_password_backend { + const char *name; + void * (*init)(const char *params); + void (*deinit)(void *ctx); + struct wpabuf * (*get)(void *ctx, const char *name); +}; + +struct wpabuf * ext_password_alloc(size_t len); + +#endif /* EXT_PASSWORD_I_H */ diff --git a/contrib/hostapd/src/utils/ext_password_test.c b/contrib/hostapd/src/utils/ext_password_test.c new file mode 100644 index 0000000000..3801bb8544 --- /dev/null +++ b/contrib/hostapd/src/utils/ext_password_test.c @@ -0,0 +1,90 @@ +/* + * External password backend + * Copyright (c) 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "ext_password_i.h" + + +struct ext_password_test_data { + char *params; +}; + + +static void * ext_password_test_init(const char *params) +{ + struct ext_password_test_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (params) + data->params = os_strdup(params); + + return data; +} + + +static void ext_password_test_deinit(void *ctx) +{ + struct ext_password_test_data *data = ctx; + + os_free(data->params); + os_free(data); +} + + +static struct wpabuf * ext_password_test_get(void *ctx, const char *name) +{ + struct ext_password_test_data *data = ctx; + char *pos, *pos2; + size_t nlen; + + wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s)", name); + + pos = data->params; + if (pos == NULL) + return NULL; + nlen = os_strlen(name); + + while (pos && *pos) { + if (os_strncmp(pos, name, nlen) == 0 && pos[nlen] == '=') { + struct wpabuf *buf; + pos += nlen + 1; + pos2 = pos; + while (*pos2 != '|' && *pos2 != '\0') + pos2++; + buf = ext_password_alloc(pos2 - pos); + if (buf == NULL) + return NULL; + wpabuf_put_data(buf, pos, pos2 - pos); + wpa_hexdump_ascii_key(MSG_DEBUG, "EXT PW TEST: value", + wpabuf_head(buf), + wpabuf_len(buf)); + return buf; + } + + pos = os_strchr(pos + 1, '|'); + if (pos) + pos++; + } + + wpa_printf(MSG_DEBUG, "EXT PW TEST: get(%s) - not found", name); + + return NULL; +} + + +const struct ext_password_backend ext_password_test = { + .name = "test", + .init = ext_password_test_init, + .deinit = ext_password_test_deinit, + .get = ext_password_test_get, +}; diff --git a/contrib/hostapd/src/utils/includes.h b/contrib/hostapd/src/utils/includes.h index 63b5c23d84..6c6ec87d0e 100644 --- a/contrib/hostapd/src/utils/includes.h +++ b/contrib/hostapd/src/utils/includes.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd - Default include files * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -34,7 +28,6 @@ #include #endif /* _WIN32_WCE */ #include -#include #ifndef CONFIG_TI_COMPILER #ifndef _MSC_VER @@ -48,9 +41,7 @@ #include #include #ifndef __vxworks -#ifndef __SYMBIAN32__ #include -#endif /* __SYMBIAN32__ */ #include #endif /* __vxworks */ #endif /* CONFIG_TI_COMPILER */ diff --git a/contrib/hostapd/src/utils/ip_addr.c b/contrib/hostapd/src/utils/ip_addr.c index 158fd57ed0..3647c764e6 100644 --- a/contrib/hostapd/src/utils/ip_addr.c +++ b/contrib/hostapd/src/utils/ip_addr.c @@ -2,14 +2,8 @@ * IP address processing * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" diff --git a/contrib/hostapd/src/utils/ip_addr.h b/contrib/hostapd/src/utils/ip_addr.h index 192049a583..79ac20cdbd 100644 --- a/contrib/hostapd/src/utils/ip_addr.h +++ b/contrib/hostapd/src/utils/ip_addr.h @@ -2,27 +2,22 @@ * IP address processing * Copyright (c) 2003-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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; - int af; /* AF_INET / AF_INET6 */ }; const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, diff --git a/contrib/hostapd/src/utils/list.h b/contrib/hostapd/src/utils/list.h new file mode 100644 index 0000000000..6881130957 --- /dev/null +++ b/contrib/hostapd/src/utils/list.h @@ -0,0 +1,95 @@ +/* + * Doubly-linked list + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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_last(list, type, member) \ + (dl_list_empty((list)) ? NULL : \ + dl_list_entry((list)->prev, 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)) + +#define dl_list_for_each_reverse(item, list, type, member) \ + for (item = dl_list_entry((list)->prev, type, member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.prev, type, member)) + +#define DEFINE_DL_LIST(name) \ + struct dl_list name = { &(name), &(name) } + +#endif /* LIST_H */ diff --git a/contrib/hostapd/src/utils/os.h b/contrib/hostapd/src/utils/os.h index d6dfea682c..2e2350a783 100644 --- a/contrib/hostapd/src/utils/os.h +++ b/contrib/hostapd/src/utils/os.h @@ -1,15 +1,9 @@ /* - * wpa_supplicant/hostapd / OS specific functions - * Copyright (c) 2005-2006, Jouni Malinen + * OS specific functions + * Copyright (c) 2005-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef OS_H @@ -29,6 +23,11 @@ struct os_time { os_time_t usec; }; +struct os_reltime { + os_time_t sec; + os_time_t usec; +}; + /** * os_get_time - Get current time (sec, usec) * @t: Pointer to buffer for the time @@ -36,21 +35,84 @@ struct os_time { */ int os_get_time(struct os_time *t); +/** + * os_get_reltime - Get relative time (sec, usec) + * @t: Pointer to buffer for the time + * Returns: 0 on success, -1 on failure + */ +int os_get_reltime(struct os_reltime *t); + + +/* Helpers for handling struct os_time */ + +static inline int os_time_before(struct os_time *a, struct os_time *b) +{ + return (a->sec < b->sec) || + (a->sec == b->sec && a->usec < b->usec); +} + + +static inline void os_time_sub(struct os_time *a, struct os_time *b, + struct os_time *res) +{ + res->sec = a->sec - b->sec; + res->usec = a->usec - b->usec; + if (res->usec < 0) { + res->sec--; + res->usec += 1000000; + } +} + -/* Helper macros for handling struct os_time */ +/* Helpers for handling struct os_reltime */ -#define os_time_before(a, b) \ - ((a)->sec < (b)->sec || \ - ((a)->sec == (b)->sec && (a)->usec < (b)->usec)) +static inline int os_reltime_before(struct os_reltime *a, + struct os_reltime *b) +{ + return (a->sec < b->sec) || + (a->sec == b->sec && a->usec < b->usec); +} + + +static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b, + struct os_reltime *res) +{ + res->sec = a->sec - b->sec; + res->usec = a->usec - b->usec; + if (res->usec < 0) { + res->sec--; + res->usec += 1000000; + } +} + + +static inline void os_reltime_age(struct os_reltime *start, + struct os_reltime *age) +{ + struct os_reltime now; + + os_get_reltime(&now); + os_reltime_sub(&now, start, age); +} + + +static inline int os_reltime_expired(struct os_reltime *now, + struct os_reltime *ts, + os_time_t timeout_secs) +{ + struct os_reltime age; + + os_reltime_sub(now, ts, &age); + return (age.sec > timeout_secs) || + (age.sec == timeout_secs && age.usec > 0); +} + + +static inline int os_reltime_initialized(struct os_reltime *t) +{ + return t->sec != 0 || t->usec != 0; +} -#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 @@ -70,6 +132,16 @@ int os_get_time(struct os_time *t); int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t); +struct os_tm { + int sec; /* 0..59 or 60 for leap seconds */ + int min; /* 0..59 */ + int hour; /* 0..23 */ + int day; /* 1..31 */ + int month; /* 1..12 */ + int year; /* Four digit year */ +}; + +int os_gmtime(os_time_t t, struct os_tm *tm); /** * os_daemonize - Run in the background (detach from the controlling terminal) @@ -176,6 +248,25 @@ char * os_readfile(const char *name, size_t *len); */ void * os_zalloc(size_t size); +/** + * os_calloc - Allocate and zero memory for an array + * @nmemb: Number of members in the array + * @size: Number of bytes in each member + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * This function can be used as a wrapper for os_zalloc(nmemb * size) when an + * allocation is used for an array. The main benefit over os_zalloc() is in + * having an extra check to catch integer overflows in multiplication. + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +static inline void * os_calloc(size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_zalloc(nmemb * size); +} + /* * The following functions are wrapper for standard ANSI C or POSIX functions. @@ -337,15 +428,6 @@ int os_strcmp(const char *s1, const char *s2); */ 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 @@ -379,6 +461,12 @@ 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 @@ -388,6 +476,14 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #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)) @@ -402,13 +498,6 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) #endif -#ifndef os_strdup -#ifdef _MSC_VER -#define os_strdup(s) _strdup(s) -#else -#define os_strdup(s) strdup(s) -#endif -#endif #ifndef os_strlen #define os_strlen(s) strlen(s) #endif @@ -435,9 +524,6 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #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 @@ -456,6 +542,14 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #endif /* OS_NO_C_LIB_DEFINES */ +static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_realloc(ptr, nmemb * size); +} + + /** * os_strlcpy - Copy a string with size bound and NUL-termination * @dest: Destination diff --git a/contrib/hostapd/src/utils/os_internal.c b/contrib/hostapd/src/utils/os_internal.c index 7b74bbf4ab..2cb0d1262d 100644 --- a/contrib/hostapd/src/utils/os_internal.c +++ b/contrib/hostapd/src/utils/os_internal.c @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / Internal implementation of OS specific functions * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -47,6 +41,17 @@ int os_get_time(struct os_time *t) } +int os_get_reltime(struct os_reltime *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) { @@ -70,6 +75,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + struct tm *tm2; + time_t t2 = t; + + tm2 = gmtime(&t2); + if (tm2 == NULL) + return -1; + tm->sec = tm2->tm_sec; + tm->min = tm2->tm_min; + tm->hour = tm2->tm_hour; + tm->day = tm2->tm_mday; + tm->month = tm2->tm_mon + 1; + tm->year = tm2->tm_year + 1900; + return 0; +} + + int os_daemonize(const char *pid_file) { if (daemon(0, 0)) { @@ -206,7 +229,12 @@ char * os_readfile(const char *name, size_t *len) return NULL; } - fread(buf, 1, *len, f); + if (fread(buf, 1, *len, f) != *len) { + fclose(f); + os_free(buf); + return NULL; + } + fclose(f); return buf; diff --git a/contrib/hostapd/src/utils/os_none.c b/contrib/hostapd/src/utils/os_none.c index bab8f178c0..228c4724cd 100644 --- a/contrib/hostapd/src/utils/os_none.c +++ b/contrib/hostapd/src/utils/os_none.c @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd / Empty OS specific functions * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -32,12 +26,23 @@ int os_get_time(struct os_time *t) } +int os_get_reltime(struct os_reltime *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_gmtime(os_time_t t, struct os_tm *tm) +{ + return -1; +} + int os_daemonize(const char *pid_file) { diff --git a/contrib/hostapd/src/utils/os_unix.c b/contrib/hostapd/src/utils/os_unix.c index bc2fc40dd7..fa67fdfb68 100644 --- a/contrib/hostapd/src/utils/os_unix.c +++ b/contrib/hostapd/src/utils/os_unix.c @@ -1,20 +1,44 @@ /* - * wpa_supplicant/hostapd / OS specific functions for UNIX/POSIX systems - * Copyright (c) 2005-2006, Jouni Malinen + * OS specific functions for UNIX/POSIX systems + * Copyright (c) 2005-2009, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" +#include + +#ifdef ANDROID +#include +#include +#include +#endif /* ANDROID */ + #include "os.h" +#include "common.h" + +#ifdef WPA_TRACE + +#include "wpa_debug.h" +#include "trace.h" +#include "list.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) { @@ -36,6 +60,43 @@ int os_get_time(struct os_time *t) } +int os_get_reltime(struct os_reltime *t) +{ +#if defined(CLOCK_BOOTTIME) + static clockid_t clock_id = CLOCK_BOOTTIME; +#elif defined(CLOCK_MONOTONIC) + static clockid_t clock_id = CLOCK_MONOTONIC; +#else + static clockid_t clock_id = CLOCK_REALTIME; +#endif + struct timespec ts; + int res; + + while (1) { + res = clock_gettime(clock_id, &ts); + if (res == 0) { + t->sec = ts.tv_sec; + t->usec = ts.tv_nsec / 1000; + return 0; + } + switch (clock_id) { +#ifdef CLOCK_BOOTTIME + case CLOCK_BOOTTIME: + clock_id = CLOCK_MONOTONIC; + break; +#endif +#ifdef CLOCK_MONOTONIC + case CLOCK_MONOTONIC: + clock_id = CLOCK_REALTIME; + break; +#endif + case CLOCK_REALTIME: + return -1; + } + } +} + + int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { @@ -76,6 +137,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + struct tm *tm2; + time_t t2 = t; + + tm2 = gmtime(&t2); + if (tm2 == NULL) + return -1; + tm->sec = tm2->tm_sec; + tm->min = tm2->tm_min; + tm->hour = tm2->tm_hour; + tm->day = tm2->tm_mday; + tm->month = tm2->tm_mon + 1; + tm->year = tm2->tm_year + 1900; + return 0; +} + + #ifdef __APPLE__ #include static int os_daemon(int nochdir, int noclose) @@ -113,9 +192,9 @@ static int os_daemon(int nochdir, int noclose) int os_daemonize(const char *pid_file) { -#ifdef __uClinux__ +#if defined(__uClinux__) || defined(__sun__) return -1; -#else /* __uClinux__ */ +#else /* defined(__uClinux__) || defined(__sun__) */ if (os_daemon(0, 0)) { perror("daemon"); return -1; @@ -130,7 +209,7 @@ int os_daemonize(const char *pid_file) } return -0; -#endif /* __uClinux__ */ +#endif /* defined(__uClinux__) || defined(__sun__) */ } @@ -171,17 +250,20 @@ char * os_rel2abs_path(const char *rel_path) size_t len = 128, cwd_len, rel_len, ret_len; int last_errno; + if (!rel_path) + return NULL; + if (rel_path[0] == '/') - return strdup(rel_path); + return os_strdup(rel_path); for (;;) { - buf = malloc(len); + buf = os_malloc(len); if (buf == NULL) return NULL; cwd = getcwd(buf, len); if (cwd == NULL) { last_errno = errno; - free(buf); + os_free(buf); if (last_errno != ERANGE) return NULL; len *= 2; @@ -193,29 +275,79 @@ char * os_rel2abs_path(const char *rel_path) } } - cwd_len = strlen(cwd); - rel_len = strlen(rel_path); + cwd_len = os_strlen(cwd); + rel_len = os_strlen(rel_path); ret_len = cwd_len + 1 + rel_len + 1; - ret = malloc(ret_len); + ret = os_malloc(ret_len); if (ret) { - memcpy(ret, cwd, cwd_len); + os_memcpy(ret, cwd, cwd_len); ret[cwd_len] = '/'; - memcpy(ret + cwd_len + 1, rel_path, rel_len); + os_memcpy(ret + cwd_len + 1, rel_path, rel_len); ret[ret_len - 1] = '\0'; } - free(buf); + os_free(buf); return ret; } int os_program_init(void) { +#ifdef ANDROID + /* + * We ignore errors here since errors are normal if we + * are already running as non-root. + */ +#ifdef ANDROID_SETGROUPS_OVERRIDE + gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE }; +#else /* ANDROID_SETGROUPS_OVERRIDE */ + gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE }; +#endif /* ANDROID_SETGROUPS_OVERRIDE */ + struct __user_cap_header_struct header; + struct __user_cap_data_struct cap; + + setgroups(ARRAY_SIZE(groups), groups); + + prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); + + setgid(AID_WIFI); + setuid(AID_WIFI); + + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + cap.effective = cap.permitted = + (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW); + cap.inheritable = 0; + capset(&header, &cap); +#endif /* ANDROID */ + +#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 */ } @@ -241,16 +373,23 @@ char * os_readfile(const char *name, size_t *len) { FILE *f; char *buf; + long pos; f = fopen(name, "rb"); if (f == NULL) return NULL; - fseek(f, 0, SEEK_END); - *len = ftell(f); - fseek(f, 0, SEEK_SET); + if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) { + fclose(f); + return NULL; + } + *len = pos; + if (fseek(f, 0, SEEK_SET) < 0) { + fclose(f); + return NULL; + } - buf = malloc(*len); + buf = os_malloc(*len); if (buf == NULL) { fclose(f); return NULL; @@ -258,7 +397,7 @@ char * os_readfile(const char *name, size_t *len) if (fread(buf, 1, *len, f) != *len) { fclose(f); - free(buf); + os_free(buf); return NULL; } @@ -268,10 +407,12 @@ char * os_readfile(const char *name, size_t *len) } +#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) @@ -297,3 +438,95 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz) 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/contrib/hostapd/src/utils/os_win32.c b/contrib/hostapd/src/utils/os_win32.c index 074096480a..1cfa7a5f81 100644 --- a/contrib/hostapd/src/utils/os_win32.c +++ b/contrib/hostapd/src/utils/os_win32.c @@ -2,17 +2,12 @@ * wpa_supplicant/hostapd / OS specific functions for Win32 systems * Copyright (c) 2005-2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" +#include #include #include @@ -52,6 +47,17 @@ int os_get_time(struct os_time *t) } +int os_get_reltime(struct os_reltime *t) +{ + /* consider using performance counters or so instead */ + struct os_time now; + int res = os_get_time(&now); + t->sec = now.sec; + t->usec = now.usec; + return res; +} + + int os_mktime(int year, int month, int day, int hour, int min, int sec, os_time_t *t) { @@ -92,6 +98,24 @@ int os_mktime(int year, int month, int day, int hour, int min, int sec, } +int os_gmtime(os_time_t t, struct os_tm *tm) +{ + struct tm *tm2; + time_t t2 = t; + + tm2 = gmtime(&t2); + if (tm2 == NULL) + return -1; + tm->sec = tm2->tm_sec; + tm->min = tm2->tm_min; + tm->hour = tm2->tm_hour; + tm->day = tm2->tm_mday; + tm->month = tm2->tm_mon + 1; + tm->year = tm2->tm_year + 1900; + return 0; +} + + int os_daemonize(const char *pid_file) { /* TODO */ diff --git a/contrib/hostapd/src/utils/pcsc_funcs.c b/contrib/hostapd/src/utils/pcsc_funcs.c index bf9f04a719..ee90d25e79 100644 --- a/contrib/hostapd/src/utils/pcsc_funcs.c +++ b/contrib/hostapd/src/utils/pcsc_funcs.c @@ -1,15 +1,9 @@ /* * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM - * Copyright (c) 2004-2007, Jouni Malinen + * Copyright (c) 2004-2007, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -76,6 +70,9 @@ #define USIM_TLV_TOTAL_FILE_SIZE 0x81 #define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 #define USIM_TLV_SHORT_FILE_ID 0x88 +#define USIM_TLV_SECURITY_ATTR_8B 0x8B +#define USIM_TLV_SECURITY_ATTR_8C 0x8C +#define USIM_TLV_SECURITY_ATTR_AB 0xAB #define USIM_PS_DO_TAG 0x90 @@ -87,6 +84,27 @@ #define CK_LEN 16 +/* 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_GSM_EF_AD 0x6FAD +#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, SCARD_USIM } sim_types; struct scard_data { @@ -240,37 +258,60 @@ static int scard_read_record(struct scard_data *scard, 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; + 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) { + switch (pos[0]) { + case USIM_TLV_FILE_DESC: + wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_FILE_ID: + wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_DF_NAME: + wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_PROPR_INFO: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary " + "information TLV", pos + 2, pos[1]); + break; + case USIM_TLV_LIFE_CYCLE_STATUS: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status " + "Integer TLV", pos + 2, pos[1]); + break; + case USIM_TLV_FILE_SIZE: + wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV", + pos + 2, pos[1]); + if ((pos[1] == 1 || pos[1] == 2) && file_len) { if (pos[1] == 1) *file_len = (int) pos[2]; else @@ -279,21 +320,43 @@ static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, 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 && + break; + case USIM_TLV_TOTAL_FILE_SIZE: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV", + pos + 2, pos[1]); + break; + case USIM_TLV_PIN_STATUS_TEMPLATE: + wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template " + "DO TLV", pos + 2, pos[1]); + if (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]; } + break; + case USIM_TLV_SHORT_FILE_ID: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File " + "Identifier (SFI) TLV", pos + 2, pos[1]); + break; + case USIM_TLV_SECURITY_ATTR_8B: + case USIM_TLV_SECURITY_ATTR_8C: + case USIM_TLV_SECURITY_ATTR_AB: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute " + "TLV", pos + 2, pos[1]); + break; + default: + wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV", + pos, 2 + pos[1]); + break; + } - pos += 2 + pos[1]; + pos += 2 + pos[1]; - if (pos == end) - return 0; - } - return -1; + if (pos == end) + return 0; + } + return -1; } @@ -334,7 +397,7 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, unsigned char rid[5]; unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ } *efdir; - unsigned char buf[100]; + unsigned char buf[127]; size_t blen; efdir = (struct efdir *) buf; @@ -422,19 +485,18 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, /** * scard_init - Initialize SIM/USIM connection using PC/SC - * @sim_type: Allowed SIM types (SIM, USIM, or both) + * @reader: Reader name prefix to search for * 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. + * open connection to the SIM/USIM card. 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) +struct scard_data * scard_init(const char *reader) { long ret; - unsigned long len; + unsigned long len, pos; struct scard_data *scard; #ifdef CONFIG_NATIVE_WINDOWS TCHAR *readers = NULL; @@ -488,18 +550,41 @@ struct scard_data * scard_init(scard_sim_type sim_type) "available."); goto failed; } - /* readers is a list of available reader. Last entry is terminated with - * double NUL. - * TODO: add support for selecting the reader; now just use the first - * one.. */ + wpa_hexdump_ascii(MSG_DEBUG, "SCARD: Readers", (u8 *) readers, len); + /* + * readers is a list of available readers. The last entry is terminated + * with double null. + */ + pos = 0; +#ifdef UNICODE + /* TODO */ +#else /* UNICODE */ + while (pos < len) { + if (reader == NULL || + os_strncmp(&readers[pos], reader, os_strlen(reader)) == 0) + break; + while (pos < len && readers[pos]) + pos++; + pos++; /* skip separating null */ + if (pos < len && readers[pos] == '\0') + pos = len; /* double null terminates list */ + } +#endif /* UNICODE */ + if (pos >= len) { + wpa_printf(MSG_WARNING, "SCARD: No reader with prefix '%s' " + "found", reader); + goto failed; + } + #ifdef UNICODE - wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers); + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", &readers[pos]); #else /* UNICODE */ - wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers); + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]); #endif /* UNICODE */ - ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED, - SCARD_PROTOCOL_T0, &scard->card, &scard->protocol); + ret = SCardConnect(scard->ctx, &readers[pos], SCARD_SHARE_SHARED, + SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, + &scard->card, &scard->protocol); if (ret != SCARD_S_SUCCESS) { if (ret == (long) SCARD_E_NO_SMARTCARD) wpa_printf(MSG_INFO, "No smart card inserted."); @@ -525,20 +610,14 @@ struct scard_data * scard_init(scard_sim_type sim_type) 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; - } + 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. 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) { @@ -588,7 +667,8 @@ struct scard_data * scard_init(scard_sim_type sim_type) } if (pin_needed) { scard->pin1_required = 1; - wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access"); + wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry " + "counter=%d)", scard_get_pin_retry_counter(scard)); } ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); @@ -812,7 +892,7 @@ static int scard_get_record_len(struct scard_data *scard, unsigned char recnum, wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response", buf, blen); - if (blen < 2 || buf[0] != 0x6c) { + if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) { wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file " "length determination"); return -1; @@ -945,6 +1025,46 @@ static int scard_verify_pin(struct scard_data *scard, const char *pin) } +int scard_get_pin_retry_counter(struct scard_data *scard) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[5] = { SIM_CMD_VERIFY_CHV1 }; + size_t len; + u16 val; + + wpa_printf(MSG_DEBUG, "SCARD: fetching PIN retry counter"); + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[4] = 0; /* Empty data */ + + len = sizeof(resp); + ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if (len != 2) { + wpa_printf(MSG_WARNING, "SCARD: failed to fetch PIN retry " + "counter"); + return -1; + } + + val = WPA_GET_BE16(resp); + if (val == 0x63c0 || val == 0x6983) { + wpa_printf(MSG_DEBUG, "SCARD: PIN has been blocked"); + return 0; + } + + if (val >= 0x63c0 && val <= 0x63cf) + return val & 0x000f; + + wpa_printf(MSG_DEBUG, "SCARD: Unexpected PIN retry counter response " + "value 0x%x", val); + return 0; +} + + /** * scard_get_imsi - Read IMSI from SIM/USIM card * @scard: Pointer to private data from scard_init() @@ -1023,6 +1143,61 @@ int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) } +/** + * scard_get_mnc_len - Read length of MNC in the IMSI from SIM/USIM card + * @scard: Pointer to private data from scard_init() + * Returns: length (>0) on success, -1 if administrative data file cannot be + * selected, -2 if administrative data file selection returns invalid result + * code, -3 if parsing FSP template file fails (USIM only), -4 if length of + * the file is unexpected, -5 if reading file fails, -6 if MNC length is not + * in range (i.e. 2 or 3), -7 if MNC length is not available. + * + */ +int scard_get_mnc_len(struct scard_data *scard) +{ + unsigned char buf[100]; + size_t blen; + int file_size; + + wpa_printf(MSG_DEBUG, "SCARD: reading MNC len from (GSM) EF-AD"); + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_GSM_EF_AD, buf, &blen)) + return -1; + if (blen < 4) { + wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-AD " + "header (len=%ld)", (long) blen); + return -2; + } + + if (scard->sim_type == SCARD_GSM_SIM) { + file_size = (buf[2] << 8) | buf[3]; + } else { + if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) + return -3; + } + if (file_size == 3) { + wpa_printf(MSG_DEBUG, "SCARD: MNC length not available"); + return -7; + } + if (file_size < 4 || file_size > (int) sizeof(buf)) { + wpa_printf(MSG_DEBUG, "SCARD: invalid file length=%ld", + (long) file_size); + return -4; + } + + if (scard_read_file(scard, buf, file_size)) + return -5; + buf[3] = buf[3] & 0x0f; /* upper nibble reserved for future use */ + if (buf[3] < 2 || buf[3] > 3) { + wpa_printf(MSG_DEBUG, "SCARD: invalid MNC length=%ld", + (long) buf[3]); + return -6; + } + wpa_printf(MSG_DEBUG, "SCARD: MNC length=%ld", (long) buf[3]); + return buf[3]; +} + + /** * scard_gsm_auth - Run GSM authentication command on SIM card * @scard: Pointer to private data from scard_init() @@ -1236,3 +1411,9 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response"); return -1; } + + +int scard_supports_umts(struct scard_data *scard) +{ + return scard->sim_type == SCARD_USIM; +} diff --git a/contrib/hostapd/src/utils/pcsc_funcs.h b/contrib/hostapd/src/utils/pcsc_funcs.h index 543f7c5984..eacd2a2d70 100644 --- a/contrib/hostapd/src/utils/pcsc_funcs.h +++ b/contrib/hostapd/src/utils/pcsc_funcs.h @@ -1,67 +1,41 @@ /* * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM - * Copyright (c) 2004-2006, Jouni Malinen + * Copyright (c) 2004-2006, 2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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); +struct scard_data * scard_init(const char *reader); 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_get_mnc_len(struct scard_data *scard); 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); +int scard_get_pin_retry_counter(struct scard_data *scard); +int scard_supports_umts(struct scard_data *scard); #else /* PCSC_FUNCS */ -#define scard_init(s) NULL +#define scard_init(r) 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_get_mnc_len(s) -1 #define scard_gsm_auth(s, r, s2, k) -1 #define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1 +#define scard_get_pin_retry_counter(s) -1 +#define scard_supports_umts(s) 0 #endif /* PCSC_FUNCS */ diff --git a/contrib/hostapd/hostapd/radiotap.c b/contrib/hostapd/src/utils/radiotap.c similarity index 100% rename from contrib/hostapd/hostapd/radiotap.c rename to contrib/hostapd/src/utils/radiotap.c diff --git a/contrib/hostapd/src/drivers/radiotap.h b/contrib/hostapd/src/utils/radiotap.h similarity index 99% rename from contrib/hostapd/src/drivers/radiotap.h rename to contrib/hostapd/src/utils/radiotap.h index 508264c4cf..137288f9a1 100644 --- a/contrib/hostapd/src/drivers/radiotap.h +++ b/contrib/hostapd/src/utils/radiotap.h @@ -238,5 +238,6 @@ enum ieee80211_radiotap_type { * retries */ #define IEEE80211_RADIOTAP_F_TX_CTS 0x0002 /* used cts 'protection' */ #define IEEE80211_RADIOTAP_F_TX_RTS 0x0004 /* used rts/cts handshake */ +#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ACK */ #endif /* IEEE80211_RADIOTAP_H */ diff --git a/contrib/hostapd/hostapd/radiotap_iter.h b/contrib/hostapd/src/utils/radiotap_iter.h similarity index 75% rename from contrib/hostapd/hostapd/radiotap_iter.h rename to contrib/hostapd/src/utils/radiotap_iter.h index 92a798a670..2e0e87296e 100644 --- a/contrib/hostapd/hostapd/radiotap_iter.h +++ b/contrib/hostapd/src/utils/radiotap_iter.h @@ -1,3 +1,18 @@ +/* + * Radiotap parser + * + * Copyright 2007 Andy Green + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 __RADIOTAP_ITER_H #define __RADIOTAP_ITER_H diff --git a/contrib/hostapd/src/utils/state_machine.h b/contrib/hostapd/src/utils/state_machine.h index 31f667217f..a514315784 100644 --- a/contrib/hostapd/src/utils/state_machine.h +++ b/contrib/hostapd/src/utils/state_machine.h @@ -2,14 +2,8 @@ * wpa_supplicant/hostapd - State machine definitions * Copyright (c) 2002-2005, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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 diff --git a/contrib/hostapd/src/utils/trace.c b/contrib/hostapd/src/utils/trace.c new file mode 100644 index 0000000000..6795d417d5 --- /dev/null +++ b/contrib/hostapd/src/utils/trace.c @@ -0,0 +1,323 @@ +/* + * Backtrace debugging + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 +#ifdef __linux__ +#include +#else /* __linux__ */ +#include +#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/contrib/hostapd/src/utils/trace.h b/contrib/hostapd/src/utils/trace.h new file mode 100644 index 0000000000..38f43fbfab --- /dev/null +++ b/contrib/hostapd/src/utils/trace.h @@ -0,0 +1,68 @@ +/* + * Backtrace debugging + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TRACE_H +#define TRACE_H + +#define WPA_TRACE_LEN 16 + +#ifdef WPA_TRACE +#include + +#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/contrib/hostapd/src/utils/uuid.c b/contrib/hostapd/src/utils/uuid.c index 620d3d610c..2aa4bcb5fa 100644 --- a/contrib/hostapd/src/utils/uuid.c +++ b/contrib/hostapd/src/utils/uuid.c @@ -2,21 +2,13 @@ * Universally Unique IDentifier (UUID) * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" -#include "crypto.h" -#include "sha1.h" #include "uuid.h" int uuid_str2bin(const char *str, u8 *bin) @@ -77,31 +69,3 @@ int is_nil_uuid(const u8 *uuid) return 0; return 1; } - - -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); -} diff --git a/contrib/hostapd/src/utils/uuid.h b/contrib/hostapd/src/utils/uuid.h index 9fc2ba0682..5e860cbc59 100644 --- a/contrib/hostapd/src/utils/uuid.h +++ b/contrib/hostapd/src/utils/uuid.h @@ -2,14 +2,8 @@ * Universally Unique IDentifier (UUID) * Copyright (c) 2008, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef UUID_H @@ -20,6 +14,5 @@ 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); -void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); #endif /* UUID_H */ diff --git a/contrib/hostapd/src/utils/wpa_debug.c b/contrib/hostapd/src/utils/wpa_debug.c index 4dd073230d..7846c1e5f2 100644 --- a/contrib/hostapd/src/utils/wpa_debug.c +++ b/contrib/hostapd/src/utils/wpa_debug.c @@ -1,34 +1,70 @@ /* * wpa_supplicant/hostapd / Debug prints - * Copyright (c) 2002-2007, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include "common.h" +#ifdef CONFIG_DEBUG_SYSLOG +#include + +static int wpa_debug_syslog = 0; +#endif /* CONFIG_DEBUG_SYSLOG */ + +#ifdef CONFIG_DEBUG_LINUX_TRACING +#include +#include +#include +#include +#include + +static FILE *wpa_debug_tracing_file = NULL; + +#define WPAS_TRACE_PFX "wpas <%d>: " +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + -#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; +#ifdef CONFIG_ANDROID_LOG + +#include + +#ifndef ANDROID_LOG_NAME +#define ANDROID_LOG_NAME "wpa_supplicant" +#endif /* ANDROID_LOG_NAME */ + +static int wpa_to_android_level(int level) +{ + if (level == MSG_ERROR) + return ANDROID_LOG_ERROR; + if (level == MSG_WARNING) + return ANDROID_LOG_WARN; + if (level == MSG_INFO) + return ANDROID_LOG_INFO; + return ANDROID_LOG_DEBUG; +} + +#endif /* CONFIG_ANDROID_LOG */ + #ifndef CONFIG_NO_STDOUT_DEBUG +#ifdef CONFIG_DEBUG_FILE +static FILE *out_file = NULL; +#endif /* CONFIG_DEBUG_FILE */ + + void wpa_debug_print_timestamp(void) { +#ifndef CONFIG_ANDROID_LOG struct os_time tv; if (!wpa_debug_timestamp) @@ -42,8 +78,117 @@ void wpa_debug_print_timestamp(void) } else #endif /* CONFIG_DEBUG_FILE */ printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); +#endif /* CONFIG_ANDROID_LOG */ +} + + +#ifdef CONFIG_DEBUG_SYSLOG +#ifndef LOG_HOSTAPD +#define LOG_HOSTAPD LOG_DAEMON +#endif /* LOG_HOSTAPD */ + +void wpa_debug_open_syslog(void) +{ + openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD); + 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 */ + + +#ifdef CONFIG_DEBUG_LINUX_TRACING + +int wpa_debug_open_linux_tracing(void) +{ + int mounts, trace_fd; + char buf[4096] = {}; + ssize_t buflen; + char *line, *tmp1, *path = NULL; + + mounts = open("/proc/mounts", O_RDONLY); + if (mounts < 0) { + printf("no /proc/mounts\n"); + return -1; + } + + buflen = read(mounts, buf, sizeof(buf) - 1); + close(mounts); + if (buflen < 0) { + printf("failed to read /proc/mounts\n"); + return -1; + } + + line = strtok_r(buf, "\n", &tmp1); + while (line) { + char *tmp2, *tmp_path, *fstype; + /* " ..." */ + strtok_r(line, " ", &tmp2); + tmp_path = strtok_r(NULL, " ", &tmp2); + fstype = strtok_r(NULL, " ", &tmp2); + if (strcmp(fstype, "debugfs") == 0) { + path = tmp_path; + break; + } + + line = strtok_r(NULL, "\n", &tmp1); + } + + if (path == NULL) { + printf("debugfs mountpoint not found\n"); + return -1; + } + + snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path); + + trace_fd = open(buf, O_WRONLY); + if (trace_fd < 0) { + printf("failed to open trace_marker file\n"); + return -1; + } + wpa_debug_tracing_file = fdopen(trace_fd, "w"); + if (wpa_debug_tracing_file == NULL) { + close(trace_fd); + printf("failed to fdopen()\n"); + return -1; + } + + return 0; +} + + +void wpa_debug_close_linux_tracing(void) +{ + if (wpa_debug_tracing_file == NULL) + return; + fclose(wpa_debug_tracing_file); + wpa_debug_tracing_file = NULL; } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + /** * wpa_printf - conditional printf @@ -62,6 +207,15 @@ void wpa_printf(int level, const char *fmt, ...) va_start(ap, fmt); if (level >= wpa_debug_level) { +#ifdef CONFIG_ANDROID_LOG + __android_log_vprint(wpa_to_android_level(level), + ANDROID_LOG_NAME, fmt, ap); +#else /* CONFIG_ANDROID_LOG */ +#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) { @@ -74,8 +228,23 @@ void wpa_printf(int level, const char *fmt, ...) #ifdef CONFIG_DEBUG_FILE } #endif /* CONFIG_DEBUG_FILE */ +#ifdef CONFIG_DEBUG_SYSLOG + } +#endif /* CONFIG_DEBUG_SYSLOG */ +#endif /* CONFIG_ANDROID_LOG */ } va_end(ap); + +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + va_start(ap, fmt); + fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level); + vfprintf(wpa_debug_tracing_file, fmt, ap); + fprintf(wpa_debug_tracing_file, "\n"); + fflush(wpa_debug_tracing_file); + va_end(ap); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ } @@ -83,8 +252,97 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, size_t len, int show) { size_t i; + +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + fprintf(wpa_debug_tracing_file, + WPAS_TRACE_PFX "%s - hexdump(len=%lu):", + level, title, (unsigned long) len); + if (buf == NULL) { + fprintf(wpa_debug_tracing_file, " [NULL]\n"); + } else if (!show) { + fprintf(wpa_debug_tracing_file, " [REMOVED]\n"); + } else { + for (i = 0; i < len; i++) + fprintf(wpa_debug_tracing_file, + " %02x", buf[i]); + } + fflush(wpa_debug_tracing_file); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + if (level < wpa_debug_level) return; +#ifdef CONFIG_ANDROID_LOG + { + const char *display; + char *strbuf = NULL; + size_t slen = len; + if (buf == NULL) { + display = " [NULL]"; + } else if (len == 0) { + display = ""; + } else if (show && len) { + /* Limit debug message length for Android log */ + if (slen > 32) + slen = 32; + strbuf = os_malloc(1 + 3 * slen); + if (strbuf == NULL) { + wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to " + "allocate message buffer"); + return; + } + + for (i = 0; i < slen; i++) + os_snprintf(&strbuf[i * 3], 4, " %02x", + buf[i]); + + display = strbuf; + } else { + display = " [REMOVED]"; + } + + __android_log_print(wpa_to_android_level(level), + ANDROID_LOG_NAME, + "%s - hexdump(len=%lu):%s%s", + title, (long unsigned int) len, display, + len > slen ? " ..." : ""); + os_free(strbuf); + return; + } +#else /* CONFIG_ANDROID_LOG */ +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) { + const char *display; + char *strbuf = NULL; + + if (buf == NULL) { + display = " [NULL]"; + } else if (len == 0) { + display = ""; + } else if (show && len) { + strbuf = os_malloc(1 + 3 * len); + if (strbuf == NULL) { + wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to " + "allocate message buffer"); + return; + } + + for (i = 0; i < len; i++) + os_snprintf(&strbuf[i * 3], 4, " %02x", + buf[i]); + + display = strbuf; + } else { + display = " [REMOVED]"; + } + + syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s", + title, (unsigned long) len, display); + os_free(strbuf); + return; + } +#endif /* CONFIG_DEBUG_SYSLOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { @@ -114,29 +372,52 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, #ifdef CONFIG_DEBUG_FILE } #endif /* CONFIG_DEBUG_FILE */ +#endif /* CONFIG_ANDROID_LOG */ } -void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len) +void wpa_hexdump(int level, const char *title, const void *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) +void wpa_hexdump_key(int level, const char *title, const void *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, +static void _wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len, int show) { size_t i, llen; const u8 *pos = buf; const size_t line_len = 16; +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (wpa_debug_tracing_file != NULL) { + fprintf(wpa_debug_tracing_file, + WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):", + level, title, (unsigned long) len); + if (buf == NULL) { + fprintf(wpa_debug_tracing_file, " [NULL]\n"); + } else if (!show) { + fprintf(wpa_debug_tracing_file, " [REMOVED]\n"); + } else { + /* can do ascii processing in userspace */ + for (i = 0; i < len; i++) + fprintf(wpa_debug_tracing_file, + " %02x", pos[i]); + } + fflush(wpa_debug_tracing_file); + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + if (level < wpa_debug_level) return; +#ifdef CONFIG_ANDROID_LOG + _wpa_hexdump(level, title, buf, len, show); +#else /* CONFIG_ANDROID_LOG */ wpa_debug_print_timestamp(); #ifdef CONFIG_DEBUG_FILE if (out_file) { @@ -210,27 +491,61 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, #ifdef CONFIG_DEBUG_FILE } #endif /* CONFIG_DEBUG_FILE */ +#endif /* CONFIG_ANDROID_LOG */ } -void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) +void wpa_hexdump_ascii(int level, const char *title, const void *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, +void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len) { _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); } +#ifdef CONFIG_DEBUG_FILE +static char *last_path = NULL; +#endif /* CONFIG_DEBUG_FILE */ + +int wpa_debug_reopen_file(void) +{ +#ifdef CONFIG_DEBUG_FILE + int rv; + if (last_path) { + char *tmp = os_strdup(last_path); + wpa_debug_close_file(); + rv = wpa_debug_open_file(tmp); + os_free(tmp); + } else { + wpa_printf(MSG_ERROR, "Last-path was not set, cannot " + "re-open log file."); + rv = -1; + } + return rv; +#else /* CONFIG_DEBUG_FILE */ + return 0; +#endif /* CONFIG_DEBUG_FILE */ +} + + int wpa_debug_open_file(const char *path) { #ifdef CONFIG_DEBUG_FILE if (!path) return 0; + + if (last_path == NULL || os_strcmp(last_path, path) != 0) { + /* Save our path to enable re-open */ + os_free(last_path); + last_path = os_strdup(path); + } + out_file = fopen(path, "a"); if (out_file == NULL) { wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open " @@ -252,6 +567,8 @@ void wpa_debug_close_file(void) return; fclose(out_file); out_file = NULL; + os_free(last_path); + last_path = NULL; #endif /* CONFIG_DEBUG_FILE */ } @@ -267,12 +584,21 @@ void wpa_msg_register_cb(wpa_msg_cb_func func) } +static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL; + +void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func) +{ + wpa_msg_ifname_cb = func; +} + + void wpa_msg(void *ctx, int level, const char *fmt, ...) { va_list ap; char *buf; const int buflen = 2048; int len; + char prefix[130]; buf = os_malloc(buflen); if (buf == NULL) { @@ -281,11 +607,21 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) return; } va_start(ap, fmt); + prefix[0] = '\0'; + if (wpa_msg_ifname_cb) { + const char *ifname = wpa_msg_ifname_cb(ctx); + if (ifname) { + int res = os_snprintf(prefix, sizeof(prefix), "%s: ", + ifname); + if (res < 0 || res >= (int) sizeof(prefix)) + prefix[0] = '\0'; + } + } len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_printf(level, "%s", buf); + wpa_printf(level, "%s%s", prefix, buf); if (wpa_msg_cb) - wpa_msg_cb(ctx, level, buf, len); + wpa_msg_cb(ctx, level, 0, buf, len); os_free(buf); } @@ -309,9 +645,56 @@ void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) va_start(ap, fmt); len = vsnprintf(buf, buflen, fmt, ap); va_end(ap); - wpa_msg_cb(ctx, level, buf, len); + wpa_msg_cb(ctx, level, 0, buf, len); + os_free(buf); +} + + +void wpa_msg_global(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_global: 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, 1, buf, len); + os_free(buf); +} + + +void wpa_msg_no_global(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_no_global: 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, 2, buf, len); os_free(buf); } + #endif /* CONFIG_NO_WPA_MSG */ @@ -343,6 +726,9 @@ void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, 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); diff --git a/contrib/hostapd/src/utils/wpa_debug.h b/contrib/hostapd/src/utils/wpa_debug.h index b4010d5427..50e8ae9d55 100644 --- a/contrib/hostapd/src/utils/wpa_debug.h +++ b/contrib/hostapd/src/utils/wpa_debug.h @@ -1,15 +1,9 @@ /* * wpa_supplicant/hostapd / Debug prints - * Copyright (c) 2002-2007, Jouni Malinen + * Copyright (c) 2002-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPA_DEBUG_H @@ -17,10 +11,16 @@ #include "wpabuf.h" +extern int wpa_debug_level; +extern int wpa_debug_show_keys; +extern int wpa_debug_timestamp; + /* 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 }; +enum { + MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR +}; #ifdef CONFIG_NO_STDOUT_DEBUG @@ -34,10 +34,17 @@ enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; #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) +#define wpa_dbg(args...) do { } while (0) + +static inline int wpa_debug_reopen_file(void) +{ + return 0; +} #else /* CONFIG_NO_STDOUT_DEBUG */ int wpa_debug_open_file(const char *path); +int wpa_debug_reopen_file(void); void wpa_debug_close_file(void); /** @@ -74,12 +81,13 @@ PRINTF_FORMAT(2, 3); * 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); +void wpa_hexdump(int level, const char *title, const void *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(level, title, buf ? wpabuf_head(buf) : NULL, + buf ? wpabuf_len(buf) : 0); } /** @@ -95,12 +103,13 @@ static inline void wpa_hexdump_buf(int level, const char *title, * 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); +void wpa_hexdump_key(int level, const char *title, const void *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_key(level, title, buf ? wpabuf_head(buf) : NULL, + buf ? wpabuf_len(buf) : 0); } /** @@ -116,7 +125,7 @@ static inline void wpa_hexdump_buf_key(int level, const char *title, * 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, +void wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len); /** @@ -133,16 +142,27 @@ void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, * 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, +void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len); +/* + * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce + * binary size. As such, it should be used with debugging messages that are not + * needed in the control interface while wpa_msg() has to be used for anything + * that needs to shown to control interface monitors. + */ +#define wpa_dbg(args...) wpa_msg(args) + #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_global(args...) do { } while (0) +#define wpa_msg_no_global(args...) do { } while (0) #define wpa_msg_register_cb(f) do { } while (0) +#define wpa_msg_register_ifname_cb(f) do { } while (0) #else /* CONFIG_NO_WPA_MSG */ /** * wpa_msg - Conditional printf for default target and ctrl_iface monitors @@ -175,16 +195,49 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); 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_global - Global 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 as a global event, + * i.e., without being specific to an interface. For backwards compatibility, + * an old style event is also delivered on one of the interfaces (the one + * specified by the context data). + */ +void wpa_msg_global(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_no_global - 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 does not send the output as a global + * event. + */ +void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +typedef void (*wpa_msg_cb_func)(void *ctx, int level, int global, + 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 */ +typedef const char * (*wpa_msg_get_ifname_func)(void *ctx); +void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func); + +#endif /* CONFIG_NO_WPA_MSG */ #ifdef CONFIG_NO_HOSTAPD_LOGGER #define hostapd_logger(args...) do { } while (0) @@ -221,6 +274,41 @@ enum hostapd_logger_level { }; +#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 CONFIG_DEBUG_LINUX_TRACING + +int wpa_debug_open_linux_tracing(void); +void wpa_debug_close_linux_tracing(void); + +#else /* CONFIG_DEBUG_LINUX_TRACING */ + +static inline int wpa_debug_open_linux_tracing(void) +{ + return 0; +} + +static inline void wpa_debug_close_linux_tracing(void) +{ +} + +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + #ifdef EAPOL_TEST #define WPA_ASSERT(a) \ diff --git a/contrib/hostapd/src/utils/wpabuf.c b/contrib/hostapd/src/utils/wpabuf.c index 8181912ea9..b257b365c7 100644 --- a/contrib/hostapd/src/utils/wpabuf.c +++ b/contrib/hostapd/src/utils/wpabuf.c @@ -1,27 +1,45 @@ /* * Dynamic data buffer - * Copyright (c) 2007-2009, Jouni Malinen + * Copyright (c) 2007-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README 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(); } @@ -29,19 +47,46 @@ static void wpabuf_overflow(const struct wpabuf *buf, size_t len) 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 (buf->flags & WPABUF_FLAG_EXT_DATA) { + nbuf = os_realloc(buf->buf, buf->used + add_len); if (nbuf == NULL) return -1; os_memset(nbuf + buf->used, 0, add_len); - buf->ext_data = nbuf; + buf->buf = 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) @@ -49,6 +94,8 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len) buf = (struct wpabuf *) nbuf; os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0, add_len); +#endif /* WPA_TRACE */ + buf->buf = (u8 *) (buf + 1); *_buf = buf; } buf->size = buf->used + add_len; @@ -65,23 +112,46 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len) */ 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; + buf->buf = (u8 *) (buf + 1); 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; + buf->buf = data; + buf->flags |= WPABUF_FLAG_EXT_DATA; return buf; } @@ -111,10 +181,27 @@ struct wpabuf * wpabuf_dup(const struct wpabuf *src) */ 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(); + } + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); + os_free(trace); +#else /* WPA_TRACE */ if (buf == NULL) return; - os_free(buf->ext_data); + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); os_free(buf); +#endif /* WPA_TRACE */ } diff --git a/contrib/hostapd/src/utils/wpabuf.h b/contrib/hostapd/src/utils/wpabuf.h index bd8f09e94f..dbce925ca1 100644 --- a/contrib/hostapd/src/utils/wpabuf.h +++ b/contrib/hostapd/src/utils/wpabuf.h @@ -1,20 +1,17 @@ /* * Dynamic data buffer - * Copyright (c) 2007-2009, Jouni Malinen + * Copyright (c) 2007-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef WPABUF_H #define WPABUF_H +/* wpabuf::buf is a pointer to external data */ +#define WPABUF_FLAG_EXT_DATA BIT(0) + /* * Internal data structure for wpabuf. Please do not touch this directly from * elsewhere. This is only defined in header file to allow inline functions @@ -23,8 +20,8 @@ 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 */ + u8 *buf; /* pointer to the head of the buffer */ + unsigned int flags; /* optionally followed by the allocated buffer */ }; @@ -78,9 +75,7 @@ static inline size_t wpabuf_tailroom(const struct wpabuf *buf) */ static inline const void * wpabuf_head(const struct wpabuf *buf) { - if (buf->ext_data) - return buf->ext_data; - return buf + 1; + return buf->buf; } static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) @@ -95,9 +90,7 @@ static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) */ static inline void * wpabuf_mhead(struct wpabuf *buf) { - if (buf->ext_data) - return buf->ext_data; - return buf + 1; + return buf->buf; } static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) @@ -111,6 +104,18 @@ static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) *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_le32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_LE32(pos, data); +} + static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) { u8 *pos = wpabuf_put(buf, 2); @@ -144,7 +149,8 @@ static inline void wpabuf_put_buf(struct wpabuf *dst, static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len) { - buf->ext_data = (u8 *) data; + buf->buf = (u8 *) data; + buf->flags = WPABUF_FLAG_EXT_DATA; buf->size = buf->used = len; } diff --git a/contrib/hostapd/src/wps/http.h b/contrib/hostapd/src/wps/http.h new file mode 100644 index 0000000000..2fee3a8f87 --- /dev/null +++ b/contrib/hostapd/src/wps/http.h @@ -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 + * + * 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/contrib/hostapd/src/wps/http_client.c b/contrib/hostapd/src/wps/http_client.c new file mode 100644 index 0000000000..029001306c --- /dev/null +++ b/contrib/hostapd/src/wps/http_client.c @@ -0,0 +1,368 @@ +/* + * http_client - HTTP client + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include "common.h" +#include "eloop.h" +#include "httpread.h" +#include "http_client.h" + + +#define HTTP_CLIENT_TIMEOUT_SEC 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=%p)", c); + 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; + + wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p " + "e=%d", handle, cookie, e); + + 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_u8(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_SEC); + 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_SEC, 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/contrib/hostapd/src/wps/http_client.h b/contrib/hostapd/src/wps/http_client.h new file mode 100644 index 0000000000..ddee2adb69 --- /dev/null +++ b/contrib/hostapd/src/wps/http_client.h @@ -0,0 +1,40 @@ +/* + * http_client - HTTP client + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/wps/http_server.c b/contrib/hostapd/src/wps/http_server.c new file mode 100644 index 0000000000..06c8bee24c --- /dev/null +++ b/contrib/hostapd/src/wps/http_server.c @@ -0,0 +1,310 @@ +/* + * http_server - HTTP server + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#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; + int on = 1; + + 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; + + setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + 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/contrib/hostapd/src/wps/http_server.h b/contrib/hostapd/src/wps/http_server.h new file mode 100644 index 0000000000..4b2b749f16 --- /dev/null +++ b/contrib/hostapd/src/wps/http_server.h @@ -0,0 +1,33 @@ +/* + * http_server - HTTP server + * Copyright (c) 2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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/contrib/hostapd/src/wps/httpread.c b/contrib/hostapd/src/wps/httpread.c index 0d7165e9c2..b51d975712 100644 --- a/contrib/hostapd/src/wps/httpread.c +++ b/contrib/hostapd/src/wps/httpread.c @@ -1,16 +1,10 @@ -/** +/* * 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. + * This software may be distributed under the terms of the BSD license. + * See README 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 @@ -73,8 +67,6 @@ struct httpread { 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 */ @@ -135,19 +127,6 @@ static int word_eq(char *s1, char *s2) } -/* 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 @@ -162,12 +141,8 @@ void httpread_destroy(struct httpread *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; + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); os_free(h->body); os_free(h->uri); os_memset(h, 0, sizeof(*h)); /* aid debugging */ @@ -182,7 +157,6 @@ 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); } @@ -301,8 +275,7 @@ static int httpread_hdr_analyze(struct httpread *h) int c = *rawuri; if (c == '%' && isxdigit(rawuri[1]) && isxdigit(rawuri[2])) { - *uri++ = (hex_value(rawuri[1]) << 4) | - hex_value(rawuri[2]); + *uri++ = hex2byte(rawuri + 1); rawuri += 3; } else { *uri++ = c; @@ -709,15 +682,11 @@ got_file: * 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; + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); /* 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; + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY); } @@ -755,21 +724,17 @@ struct httpread * httpread_create( 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 (timeout_seconds > 0 && + eloop_register_timeout(timeout_seconds, 0, + httpread_timeout_handler, NULL, h)) { + /* No way to recover (from malloc failure) */ + goto fail; } 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: diff --git a/contrib/hostapd/src/wps/httpread.h b/contrib/hostapd/src/wps/httpread.h index fb1ecb7de3..454b618bf4 100644 --- a/contrib/hostapd/src/wps/httpread.h +++ b/contrib/hostapd/src/wps/httpread.h @@ -1,16 +1,10 @@ -/** +/* * 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. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef HTTPREAD_H diff --git a/contrib/hostapd/src/wps/ndef.c b/contrib/hostapd/src/wps/ndef.c new file mode 100644 index 0000000000..2b3506476e --- /dev/null +++ b/contrib/hostapd/src/wps/ndef.c @@ -0,0 +1,196 @@ +/* + * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup + * Reference is "NFCForum-TS-NDEF_1.0 2006-07-24". + * Copyright (c) 2009-2012, Masashi Honma + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include "common.h" +#include "wps/wps.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_NFC_FORUM (0x01) +#define FLAG_TNF_RFC2046 (0x02) + +struct ndef_record { + const u8 *type; + const u8 *id; + const u8 *payload; + u8 type_length; + u8 id_length; + u32 payload_length; + u32 total_length; +}; + +static char wifi_handover_type[] = "application/vnd.wfa.wsc"; +static char p2p_handover_type[] = "application/vnd.wfa.p2p"; + +static int ndef_parse_record(const u8 *data, u32 size, + struct ndef_record *record) +{ + const 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(const struct wpabuf *buf, + int (*filter)(struct ndef_record *)) +{ + struct ndef_record record; + int len = wpabuf_len(buf); + const u8 *data = wpabuf_head(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, + const struct wpabuf *payload) +{ + struct wpabuf *record; + size_t total_len; + int short_record; + u8 local_flag; + size_t payload_length = wpabuf_len(payload); + + 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_buf(record, payload); + 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(const struct wpabuf *buf) +{ + return ndef_parse_records(buf, wifi_filter); +} + + +struct wpabuf * ndef_build_wifi(const 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, buf); +} + + +static int p2p_filter(struct ndef_record *record) +{ + if (record->type_length != os_strlen(p2p_handover_type)) + return 0; + if (os_memcmp(record->type, p2p_handover_type, + os_strlen(p2p_handover_type)) != 0) + return 0; + return 1; +} + + +struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf) +{ + return ndef_parse_records(buf, p2p_filter); +} + + +struct wpabuf * ndef_build_p2p(const struct wpabuf *buf) +{ + return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | + FLAG_TNF_RFC2046, p2p_handover_type, + os_strlen(p2p_handover_type), NULL, 0, buf); +} diff --git a/contrib/hostapd/src/wps/upnp_xml.c b/contrib/hostapd/src/wps/upnp_xml.c new file mode 100644 index 0000000000..a9958eeda8 --- /dev/null +++ b/contrib/hostapd/src/wps/upnp_xml.c @@ -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 + * + * 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., & for ampersand(&) and < 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 with + * matching label. (There is also a self-closing tag